Apply linter recommendations

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
Nicolas De Loof 2020-08-18 16:56:42 +02:00
parent 0a6d7b1c6d
commit ec4615ae57
No known key found for this signature in database
GPG Key ID: 9858809D6F8F6E7E
25 changed files with 213 additions and 202 deletions

View File

@ -132,7 +132,6 @@ func (a *aciAPIService) SecretsService() secrets.Service {
return nil
}
type aciContainerService struct {
ctx store.AciContext
}

View File

@ -20,12 +20,13 @@ import (
"context"
"errors"
"fmt"
"github.com/docker/api/secrets"
"github.com/sirupsen/logrus"
"github.com/docker/api/compose"
"github.com/docker/api/containers"
"github.com/docker/api/context/cloud"
"github.com/docker/api/secrets"
)
var (

View File

@ -18,8 +18,8 @@ package compose
import (
"context"
"github.com/compose-spec/compose-go/cli"
"github.com/compose-spec/compose-go/cli"
"github.com/pkg/errors"
"github.com/spf13/cobra"

View File

@ -18,14 +18,14 @@ package client
import (
"context"
"github.com/docker/api/secrets"
"github.com/docker/api/context/cloud"
"github.com/docker/api/secrets"
"github.com/docker/api/backend"
"github.com/docker/api/compose"
"github.com/docker/api/containers"
apicontext "github.com/docker/api/context"
"github.com/docker/api/context/cloud"
"github.com/docker/api/context/store"
)
@ -71,7 +71,7 @@ func (c *Client) ComposeService() compose.Service {
return c.bs.ComposeService()
}
// ComposeService returns the backend service for the current context
// SecretsService returns the backend service for the current context
func (c *Client) SecretsService() secrets.Service {
return c.bs.SecretsService()
}

View File

@ -37,6 +37,7 @@ type Service interface {
Convert(ctx context.Context, opts *cli.ProjectOptions) ([]byte, error)
}
// PortPublisher hold status about published port
type PortPublisher struct {
URL string
TargetPort int
@ -44,11 +45,12 @@ type PortPublisher struct {
Protocol string
}
// ServiceStatus hold status about a service
type ServiceStatus struct {
ID string
Name string
Replicas int
Desired int
Ports []string
LoadBalancers []PortPublisher
ID string
Name string
Replicas int
Desired int
Ports []string
Publishers []PortPublisher
}

View File

@ -17,7 +17,10 @@
package compose
const (
// ProjectTag allow to track resource related to a compose project
ProjectTag = "com.docker.compose.project"
// NetworkTag allow to track resource related to a compose network
NetworkTag = "com.docker.compose.network"
// ServiceTag allow to track resource related to a compose service
ServiceTag = "com.docker.compose.service"
)

View File

@ -76,7 +76,7 @@ func getEcsAPIService(ecsCtx store.EcsContext) (*ecsAPIService, error) {
return &ecsAPIService{
ctx: ecsCtx,
Region: ecsCtx.Region,
SDK: NewSDK(sess),
SDK: newSDK(sess),
}, nil
}

View File

@ -46,11 +46,11 @@ import (
)
const (
ParameterClusterName = "ParameterClusterName"
ParameterVPCId = "ParameterVPCId"
ParameterSubnet1Id = "ParameterSubnet1Id"
ParameterSubnet2Id = "ParameterSubnet2Id"
ParameterLoadBalancerARN = "ParameterLoadBalancerARN"
parameterClusterName = "ParameterClusterName"
parameterVPCId = "ParameterVPCId"
parameterSubnet1Id = "ParameterSubnet1Id"
parameterSubnet2Id = "ParameterSubnet2Id"
parameterLoadBalancerARN = "ParameterLoadBalancerARN"
)
func (b *ecsAPIService) Convert(ctx context.Context, opts *cli.ProjectOptions) ([]byte, error) {
@ -62,12 +62,12 @@ func (b *ecsAPIService) Convert(ctx context.Context, opts *cli.ProjectOptions) (
if err != nil {
return nil, err
}
return Marshall(template)
return marshall(template)
}
// Convert a compose project into a CloudFormation template
func (b *ecsAPIService) convert(project *types.Project) (*cloudformation.Template, error) {
var checker compatibility.Checker = &FargateCompatibilityChecker{
func (b *ecsAPIService) convert(project *types.Project) (*cloudformation.Template, error) { //nolint:gocyclo
var checker compatibility.Checker = &fargateCompatibilityChecker{
compatibility.AllowList{
Supported: compatibleComposeAttributes,
},
@ -86,12 +86,12 @@ func (b *ecsAPIService) convert(project *types.Project) (*cloudformation.Templat
template := cloudformation.NewTemplate()
template.Description = "CloudFormation template created by Docker for deploying applications on Amazon ECS"
template.Parameters[ParameterClusterName] = cloudformation.Parameter{
template.Parameters[parameterClusterName] = cloudformation.Parameter{
Type: "String",
Description: "Name of the ECS cluster to deploy to (optional)",
}
template.Parameters[ParameterVPCId] = cloudformation.Parameter{
template.Parameters[parameterVPCId] = cloudformation.Parameter{
Type: "AWS::EC2::VPC::Id",
Description: "ID of the VPC",
}
@ -103,28 +103,28 @@ func (b *ecsAPIService) convert(project *types.Project) (*cloudformation.Templat
Description: "The list of SubnetIds, for at least two Availability Zones in the region in your VPC",
}
*/
template.Parameters[ParameterSubnet1Id] = cloudformation.Parameter{
template.Parameters[parameterSubnet1Id] = cloudformation.Parameter{
Type: "AWS::EC2::Subnet::Id",
Description: "SubnetId, for Availability Zone 1 in the region in your VPC",
}
template.Parameters[ParameterSubnet2Id] = cloudformation.Parameter{
template.Parameters[parameterSubnet2Id] = cloudformation.Parameter{
Type: "AWS::EC2::Subnet::Id",
Description: "SubnetId, for Availability Zone 2 in the region in your VPC",
}
template.Parameters[ParameterLoadBalancerARN] = cloudformation.Parameter{
template.Parameters[parameterLoadBalancerARN] = cloudformation.Parameter{
Type: "String",
Description: "Name of the LoadBalancer to connect to (optional)",
}
// Create Cluster is `ParameterClusterName` parameter is not set
template.Conditions["CreateCluster"] = cloudformation.Equals("", cloudformation.Ref(ParameterClusterName))
template.Conditions["CreateCluster"] = cloudformation.Equals("", cloudformation.Ref(parameterClusterName))
cluster := createCluster(project, template)
networks := map[string]string{}
for _, net := range project.Networks {
networks[net.Name] = convertNetwork(project, net, cloudformation.Ref(ParameterVPCId), template)
networks[net.Name] = convertNetwork(project, net, cloudformation.Ref(parameterVPCId), template)
}
for i, s := range project.Secrets {
@ -175,9 +175,6 @@ func (b *ecsAPIService) convert(project *types.Project) (*cloudformation.Templat
template.Resources[taskDefinition] = definition
var healthCheck *cloudmap.Service_HealthCheckConfig
if service.HealthCheck != nil && !service.HealthCheck.Disable {
// FIXME ECS only support HTTP(s) health checks, while Docker only support CMD
}
serviceRegistry := createServiceRegistry(service, template, healthCheck)
@ -242,8 +239,8 @@ func (b *ecsAPIService) convert(project *types.Project) (*cloudformation.Templat
AssignPublicIp: ecsapi.AssignPublicIpEnabled,
SecurityGroups: serviceSecurityGroups,
Subnets: []string{
cloudformation.Ref(ParameterSubnet1Id),
cloudformation.Ref(ParameterSubnet2Id),
cloudformation.Ref(parameterSubnet1Id),
cloudformation.Ref(parameterSubnet2Id),
},
},
},
@ -268,7 +265,7 @@ func (b *ecsAPIService) convert(project *types.Project) (*cloudformation.Templat
func createLogGroup(project *types.Project, template *cloudformation.Template) {
retention := 0
if v, ok := project.Extensions[ExtensionRetention]; ok {
if v, ok := project.Extensions[extensionRetention]; ok {
retention = v.(int)
}
logGroup := fmt.Sprintf("/docker-compose/%s", project.Name)
@ -285,11 +282,11 @@ func computeRollingUpdateLimits(service types.ServiceConfig) (int, int, error) {
return minPercent, maxPercent, nil
}
updateConfig := service.Deploy.UpdateConfig
min, okMin := updateConfig.Extensions[ExtensionMinPercent]
min, okMin := updateConfig.Extensions[extensionMinPercent]
if okMin {
minPercent = min.(int)
}
max, okMax := updateConfig.Extensions[ExtensionMaxPercent]
max, okMax := updateConfig.Extensions[extensionMaxPercent]
if okMax {
maxPercent = max.(int)
}
@ -333,7 +330,7 @@ func getLoadBalancerSecurityGroups(project *types.Project, template *cloudformat
securityGroups := []string{}
for _, network := range project.Networks {
if !network.Internal {
net := convertNetwork(project, network, cloudformation.Ref(ParameterVPCId), template)
net := convertNetwork(project, network, cloudformation.Ref(parameterVPCId), template)
securityGroups = append(securityGroups, net)
}
}
@ -354,7 +351,7 @@ func createLoadBalancer(project *types.Project, template *cloudformation.Templat
// load balancer names are limited to 32 characters total
loadBalancerName := fmt.Sprintf("%.32s", fmt.Sprintf("%sLoadBalancer", strings.Title(project.Name)))
// Create PortPublisher if `ParameterLoadBalancerName` is not set
template.Conditions["CreateLoadBalancer"] = cloudformation.Equals("", cloudformation.Ref(ParameterLoadBalancerARN))
template.Conditions["CreateLoadBalancer"] = cloudformation.Equals("", cloudformation.Ref(parameterLoadBalancerARN))
loadBalancerType := getLoadBalancerType(project)
securityGroups := []string{}
@ -367,8 +364,8 @@ func createLoadBalancer(project *types.Project, template *cloudformation.Templat
Scheme: elbv2.LoadBalancerSchemeEnumInternetFacing,
SecurityGroups: securityGroups,
Subnets: []string{
cloudformation.Ref(ParameterSubnet1Id),
cloudformation.Ref(ParameterSubnet2Id),
cloudformation.Ref(parameterSubnet1Id),
cloudformation.Ref(parameterSubnet2Id),
},
Tags: []tags.Tag{
{
@ -379,7 +376,7 @@ func createLoadBalancer(project *types.Project, template *cloudformation.Templat
Type: loadBalancerType,
AWSCloudFormationCondition: "CreateLoadBalancer",
}
return cloudformation.If("CreateLoadBalancer", cloudformation.Ref(loadBalancerName), cloudformation.Ref(ParameterLoadBalancerARN))
return cloudformation.If("CreateLoadBalancer", cloudformation.Ref(loadBalancerName), cloudformation.Ref(parameterLoadBalancerARN))
}
func createListener(service types.ServiceConfig, port types.ServicePortConfig, template *cloudformation.Template, targetGroupName string, loadBalancerARN string, protocol string) string {
@ -427,7 +424,7 @@ func createTargetGroup(project *types.Project, service types.ServiceConfig, port
Value: project.Name,
},
},
VpcId: cloudformation.Ref(ParameterVPCId),
VpcId: cloudformation.Ref(parameterVPCId),
TargetType: elbv2.TargetTypeEnumIp,
}
return targetGroupName
@ -462,7 +459,7 @@ func createServiceRegistry(service types.ServiceConfig, template *cloudformation
func createTaskExecutionRole(service types.ServiceConfig, err error, definition *ecs.TaskDefinition, template *cloudformation.Template) (string, error) {
taskExecutionRole := fmt.Sprintf("%sTaskExecutionRole", normalizeResourceName(service.Name))
policy, err := getPolicy(definition)
policy := getPolicy(definition)
if err != nil {
return taskExecutionRole, err
}
@ -474,16 +471,16 @@ func createTaskExecutionRole(service types.ServiceConfig, err error, definition
})
}
if roles, ok := service.Extensions[ExtensionRole]; ok {
if roles, ok := service.Extensions[extensionRole]; ok {
rolePolicies = append(rolePolicies, iam.Role_Policy{
PolicyDocument: roles,
})
}
managedPolicies := []string{
ECSTaskExecutionPolicy,
ECRReadOnlyPolicy,
ecsTaskExecutionPolicy,
ecrReadOnlyPolicy,
}
if v, ok := service.Extensions[ExtensionManagedPolicies]; ok {
if v, ok := service.Extensions[extensionManagedPolicies]; ok {
for _, s := range v.([]interface{}) {
managedPolicies = append(managedPolicies, s.(string))
}
@ -507,7 +504,7 @@ func createCluster(project *types.Project, template *cloudformation.Template) st
},
AWSCloudFormationCondition: "CreateCluster",
}
cluster := cloudformation.If("CreateCluster", cloudformation.Ref("Cluster"), cloudformation.Ref(ParameterClusterName))
cluster := cloudformation.If("CreateCluster", cloudformation.Ref("Cluster"), cloudformation.Ref(parameterClusterName))
return cluster
}
@ -515,12 +512,12 @@ func createCloudMap(project *types.Project, template *cloudformation.Template) {
template.Resources["CloudMap"] = &cloudmap.PrivateDnsNamespace{
Description: fmt.Sprintf("Service Map for Docker Compose project %s", project.Name),
Name: fmt.Sprintf("%s.local", project.Name),
Vpc: cloudformation.Ref(ParameterVPCId),
Vpc: cloudformation.Ref(parameterVPCId),
}
}
func convertNetwork(project *types.Project, net types.NetworkConfig, vpc string, template *cloudformation.Template) string {
if sg, ok := net.Extensions[ExtensionSecurityGroup]; ok {
if sg, ok := net.Extensions[extensionSecurityGroup]; ok {
logrus.Debugf("Security Group for network %q set by user to %q", net.Name, sg)
return sg.(string)
}
@ -583,7 +580,7 @@ func normalizeResourceName(s string) string {
return strings.Title(regexp.MustCompile("[^a-zA-Z0-9]+").ReplaceAllString(s, ""))
}
func getPolicy(taskDef *ecs.TaskDefinition) (*PolicyDocument, error) {
func getPolicy(taskDef *ecs.TaskDefinition) *PolicyDocument {
arns := []string{}
for _, container := range taskDef.ContainerDefinitions {
if container.RepositoryCredentials != nil {
@ -601,12 +598,12 @@ func getPolicy(taskDef *ecs.TaskDefinition) (*PolicyDocument, error) {
Statement: []PolicyStatement{
{
Effect: "Allow",
Action: []string{ActionGetSecretValue, ActionGetParameters, ActionDecrypt},
Action: []string{actionGetSecretValue, actionGetParameters, actionDecrypt},
Resource: arns,
}},
}, nil
}
}
return nil, nil
return nil
}
func uniqueStrings(items []string) []string {

View File

@ -45,7 +45,7 @@ func TestSimpleConvert(t *testing.T) {
}
func TestLogging(t *testing.T) {
template := convertYaml(t, "test", `
template := convertYaml(t, `
services:
foo:
image: hello_world
@ -64,7 +64,7 @@ x-aws-logs_retention: 10
}
func TestEnvFile(t *testing.T) {
template := convertYaml(t, "test", `
template := convertYaml(t, `
services:
foo:
image: hello_world
@ -84,7 +84,7 @@ services:
}
func TestEnvFileAndEnv(t *testing.T) {
template := convertYaml(t, "test", `
template := convertYaml(t, `
services:
foo:
image: hello_world
@ -106,7 +106,7 @@ services:
}
func TestRollingUpdateLimits(t *testing.T) {
template := convertYaml(t, "test", `
template := convertYaml(t, `
services:
foo:
image: hello_world
@ -121,7 +121,7 @@ services:
}
func TestRollingUpdateExtension(t *testing.T) {
template := convertYaml(t, "test", `
template := convertYaml(t, `
services:
foo:
image: hello_world
@ -136,16 +136,17 @@ services:
}
func TestRolePolicy(t *testing.T) {
template := convertYaml(t, "test", `
template := convertYaml(t, `
services:
foo:
image: hello_world
x-aws-pull_credentials: "secret"
`)
role := template.Resources["FooTaskExecutionRole"].(*iam.Role)
assert.Check(t, role != nil)
assert.Check(t, role.ManagedPolicyArns[0] == ECSTaskExecutionPolicy)
assert.Check(t, role.ManagedPolicyArns[1] == ECRReadOnlyPolicy)
x := template.Resources["FooTaskExecutionRole"]
assert.Check(t, x != nil)
role := *(x.(*iam.Role))
assert.Check(t, role.ManagedPolicyArns[0] == ecsTaskExecutionPolicy)
assert.Check(t, role.ManagedPolicyArns[1] == ecrReadOnlyPolicy)
// We expect an extra policy has been created for x-aws-pull_credentials
assert.Check(t, len(role.Policies) == 1)
policy := role.Policies[0].PolicyDocument.(*PolicyDocument)
@ -155,7 +156,7 @@ services:
}
func TestMapNetworksToSecurityGroups(t *testing.T) {
template := convertYaml(t, "test", `
template := convertYaml(t, `
services:
test:
image: hello_world
@ -172,29 +173,31 @@ networks:
assert.Check(t, template.Resources["TestPublicNetwork"] != nil)
assert.Check(t, template.Resources["TestBacktierNetwork"] != nil)
assert.Check(t, template.Resources["TestBacktierNetworkIngress"] != nil)
ingress := template.Resources["TestPublicNetworkIngress"].(*ec2.SecurityGroupIngress)
assert.Check(t, ingress != nil)
i := template.Resources["TestPublicNetworkIngress"]
assert.Check(t, i != nil)
ingress := *i.(*ec2.SecurityGroupIngress)
assert.Check(t, ingress.SourceSecurityGroupId == cloudformation.Ref("TestPublicNetwork"))
}
func TestLoadBalancerTypeApplication(t *testing.T) {
template := convertYaml(t, "test123456789009876543211234567890", `
template := convertYaml(t, `
services:
test:
image: nginx
ports:
- 80:80
`)
lb := template.Resources["TestLoadBalancer"].(*elasticloadbalancingv2.LoadBalancer)
lb := template.Resources["TestLoadBalancer"]
assert.Check(t, lb != nil)
assert.Check(t, len(lb.Name) <= 32)
assert.Check(t, lb.Type == elbv2.LoadBalancerTypeEnumApplication)
assert.Check(t, len(lb.SecurityGroups) > 0)
loadBalancer := *lb.(*elasticloadbalancingv2.LoadBalancer)
assert.Check(t, len(loadBalancer.Name) <= 32)
assert.Check(t, loadBalancer.Type == elbv2.LoadBalancerTypeEnumApplication)
assert.Check(t, len(loadBalancer.SecurityGroups) > 0)
}
func TestNoLoadBalancerIfNoPortExposed(t *testing.T) {
template := convertYaml(t, "test", `
template := convertYaml(t, `
services:
test:
image: nginx
@ -209,20 +212,21 @@ services:
}
func TestServiceReplicas(t *testing.T) {
template := convertYaml(t, "test", `
template := convertYaml(t, `
services:
test:
image: nginx
deploy:
replicas: 10
`)
s := template.Resources["TestService"].(*ecs.Service)
s := template.Resources["TestService"]
assert.Check(t, s != nil)
assert.Check(t, s.DesiredCount == 10)
service := *s.(*ecs.Service)
assert.Check(t, service.DesiredCount == 10)
}
func TestTaskSizeConvert(t *testing.T) {
template := convertYaml(t, "test", `
template := convertYaml(t, `
services:
test:
image: nginx
@ -239,7 +243,7 @@ services:
assert.Equal(t, def.Cpu, "512")
assert.Equal(t, def.Memory, "2048")
template = convertYaml(t, "test", `
template = convertYaml(t, `
services:
test:
image: nginx
@ -257,7 +261,7 @@ services:
assert.Equal(t, def.Memory, "8192")
}
func TestTaskSizeConvertFailure(t *testing.T) {
model := loadConfig(t, "test", `
model := loadConfig(t, `
services:
test:
image: nginx
@ -273,7 +277,7 @@ services:
}
func TestLoadBalancerTypeNetwork(t *testing.T) {
template := convertYaml(t, "test", `
template := convertYaml(t, `
services:
test:
image: nginx
@ -281,13 +285,14 @@ services:
- 80:80
- 88:88
`)
lb := template.Resources["TestLoadBalancer"].(*elasticloadbalancingv2.LoadBalancer)
lb := template.Resources["TestLoadBalancer"]
assert.Check(t, lb != nil)
assert.Check(t, lb.Type == elbv2.LoadBalancerTypeEnumNetwork)
loadBalancer := *lb.(*elasticloadbalancingv2.LoadBalancer)
assert.Check(t, loadBalancer.Type == elbv2.LoadBalancerTypeEnumNetwork)
}
func TestServiceMapping(t *testing.T) {
template := convertYaml(t, "test", `
template := convertYaml(t, `
services:
test:
image: "image"
@ -326,7 +331,7 @@ func get(l []ecs.TaskDefinition_KeyValuePair, name string) string {
}
func TestResourcesHaveProjectTagSet(t *testing.T) {
template := convertYaml(t, "test", `
template := convertYaml(t, `
services:
test:
image: nginx
@ -353,7 +358,7 @@ func convertResultAsString(t *testing.T, project *types.Project) string {
backend := &ecsAPIService{}
template, err := backend.convert(project)
assert.NilError(t, err)
resultAsJSON, err := Marshall(template)
resultAsJSON, err := marshall(template)
assert.NilError(t, err)
return fmt.Sprintf("%s\n", string(resultAsJSON))
}
@ -368,15 +373,15 @@ func load(t *testing.T, paths ...string) *types.Project {
return project
}
func convertYaml(t *testing.T, name string, yaml string) *cloudformation.Template {
project := loadConfig(t, name, yaml)
func convertYaml(t *testing.T, yaml string) *cloudformation.Template {
project := loadConfig(t, yaml)
backend := &ecsAPIService{}
template, err := backend.convert(project)
assert.NilError(t, err)
return template
}
func loadConfig(t *testing.T, name string, yaml string) *types.Project {
func loadConfig(t *testing.T, yaml string) *types.Project {
dict, err := loader.ParseYAML([]byte(yaml))
assert.NilError(t, err)
model, err := loader.Load(types.ConfigDetails{

View File

@ -21,7 +21,7 @@ import (
"strconv"
)
var NAMES = []string{
var names = []string{
"grey",
"red",
"green",
@ -32,14 +32,8 @@ var NAMES = []string{
"white",
}
var COLORS map[string]ColorFunc
// ColorFunc use ANSI codes to render colored text on console
type ColorFunc func(s string) string
var Monochrome = func(s string) string {
return s
}
// colorFunc use ANSI codes to render colored text on console
type colorFunc func(s string) string
func ansiColor(code, s string) string {
return fmt.Sprintf("%s%s%s", ansi(code), s, ansi("0"))
@ -49,38 +43,38 @@ func ansi(code string) string {
return fmt.Sprintf("\033[%sm", code)
}
func makeColorFunc(code string) ColorFunc {
func makeColorFunc(code string) colorFunc {
return func(s string) string {
return ansiColor(code, s)
}
}
var Rainbow = make(chan ColorFunc)
var loop = make(chan colorFunc)
func init() {
COLORS = map[string]ColorFunc{}
for i, name := range NAMES {
COLORS[name] = makeColorFunc(strconv.Itoa(30 + i))
COLORS["intense_"+name] = makeColorFunc(strconv.Itoa(30+i) + ";1")
colors := map[string]colorFunc{}
for i, name := range names {
colors[name] = makeColorFunc(strconv.Itoa(30 + i))
colors["intense_"+name] = makeColorFunc(strconv.Itoa(30+i) + ";1")
}
go func() {
i := 0
rainbow := []ColorFunc{
COLORS["cyan"],
COLORS["yellow"],
COLORS["green"],
COLORS["magenta"],
COLORS["blue"],
COLORS["intense_cyan"],
COLORS["intense_yellow"],
COLORS["intense_green"],
COLORS["intense_magenta"],
COLORS["intense_blue"],
rainbow := []colorFunc{
colors["cyan"],
colors["yellow"],
colors["green"],
colors["magenta"],
colors["blue"],
colors["intense_cyan"],
colors["intense_yellow"],
colors["intense_green"],
colors["intense_magenta"],
colors["intense_blue"],
}
for {
Rainbow <- rainbow[i]
loop <- rainbow[i]
i = (i + 1) % len(rainbow)
}
}()

View File

@ -21,7 +21,7 @@ import (
"github.com/compose-spec/compose-go/types"
)
type FargateCompatibilityChecker struct {
type fargateCompatibilityChecker struct {
compatibility.AllowList
}
@ -68,13 +68,13 @@ var compatibleComposeAttributes = []string{
"secrets.file",
}
func (c *FargateCompatibilityChecker) CheckImage(service *types.ServiceConfig) {
func (c *fargateCompatibilityChecker) CheckImage(service *types.ServiceConfig) {
if service.Image == "" {
c.Incompatible("service %s doesn't define a Docker image to run", service.Name)
}
}
func (c *FargateCompatibilityChecker) CheckPortsPublished(p *types.ServicePortConfig) {
func (c *fargateCompatibilityChecker) CheckPortsPublished(p *types.ServicePortConfig) {
if p.Published == 0 {
p.Published = p.Target
}
@ -83,7 +83,7 @@ func (c *FargateCompatibilityChecker) CheckPortsPublished(p *types.ServicePortCo
}
}
func (c *FargateCompatibilityChecker) CheckCapAdd(service *types.ServiceConfig) {
func (c *fargateCompatibilityChecker) CheckCapAdd(service *types.ServiceConfig) {
add := []string{}
for _, cap := range service.CapAdd {
switch cap {
@ -96,7 +96,7 @@ func (c *FargateCompatibilityChecker) CheckCapAdd(service *types.ServiceConfig)
service.CapAdd = add
}
func (c *FargateCompatibilityChecker) CheckLoggingDriver(config *types.LoggingConfig) {
func (c *fargateCompatibilityChecker) CheckLoggingDriver(config *types.LoggingConfig) {
if config.Driver != "" && config.Driver != "awslogs" {
c.Unsupported("services.logging.driver %s is not supported", config.Driver)
}

View File

@ -103,7 +103,7 @@ func (h contextCreateAWSHelper) saveCredentials(profile string, accessKeyID stri
p := credentials.SharedCredentialsProvider{Profile: profile}
_, err := p.Retrieve()
if err == nil {
return fmt.Errorf("credentials already exists!")
return fmt.Errorf("credentials already exists")
}
if err.(awserr.Error).Code() == "SharedCredsLoad" && err.(awserr.Error).Message() == "failed to load shared credentials file" {

View File

@ -44,10 +44,7 @@ func convert(project *types.Project, service types.ServiceConfig) (*ecs.TaskDefi
if err != nil {
return nil, err
}
_, memReservation, err := toContainerReservation(service)
if err != nil {
return nil, err
}
_, memReservation := toContainerReservation(service)
credential := getRepoCredentials(service)
// override resolve.conf search directive to also search <project>.local
@ -141,7 +138,11 @@ func convert(project *types.Project, service types.ServiceConfig) (*ecs.TaskDefi
}, nil
}
func createSecretsSideCar(project *types.Project, service types.ServiceConfig, logConfiguration *ecs.TaskDefinition_LogConfiguration) (ecs.TaskDefinition_Volume, ecs.TaskDefinition_MountPoint, ecs.TaskDefinition_ContainerDefinition, error) {
func createSecretsSideCar(project *types.Project, service types.ServiceConfig, logConfiguration *ecs.TaskDefinition_LogConfiguration) (
ecs.TaskDefinition_Volume,
ecs.TaskDefinition_MountPoint,
ecs.TaskDefinition_ContainerDefinition,
error) {
initContainerName := fmt.Sprintf("%s_Secrets_InitContainer", normalizeResourceName(service.Name))
secretsVolume := ecs.TaskDefinition_Volume{
Name: "secrets",
@ -166,7 +167,7 @@ func createSecretsSideCar(project *types.Project, service types.ServiceConfig, l
ValueFrom: secretConfig.Name,
})
var keys []string
if ext, ok := secretConfig.Extensions[ExtensionKeys]; ok {
if ext, ok := secretConfig.Extensions[extensionKeys]; ok {
if key, ok := ext.(string); ok {
keys = append(keys, key)
} else {
@ -275,7 +276,7 @@ func toSystemControls(sysctls types.Mapping) []ecs.TaskDefinition_SystemControl
return sys
}
const MiB = 1024 * 1024
const miB = 1024 * 1024
func toLimits(service types.ServiceConfig) (string, string, error) {
// All possible cpu/mem values for Fargate
@ -315,9 +316,9 @@ func toLimits(service types.ServiceConfig) (string, string, error) {
for _, cpu := range cpus {
mem := cpuToMem[cpu]
if v <= cpu*MiB {
if v <= cpu*miB {
for _, m := range mem {
if limits.MemoryBytes <= m*MiB {
if limits.MemoryBytes <= m*miB {
cpuLimit = strconv.FormatInt(cpu, 10)
memLimit = strconv.FormatInt(int64(m), 10)
return cpuLimit, memLimit, nil
@ -328,19 +329,19 @@ func toLimits(service types.ServiceConfig) (string, string, error) {
return "", "", fmt.Errorf("the resources requested are not supported by ECS/Fargate")
}
func toContainerReservation(service types.ServiceConfig) (string, int, error) {
func toContainerReservation(service types.ServiceConfig) (string, int) {
cpuReservation := ".0"
memReservation := 0
if service.Deploy == nil {
return cpuReservation, memReservation, nil
return cpuReservation, memReservation
}
reservations := service.Deploy.Resources.Reservations
if reservations == nil {
return cpuReservation, memReservation, nil
return cpuReservation, memReservation
}
return reservations.NanoCPUs, int(reservations.MemoryBytes / MiB), nil
return reservations.NanoCPUs, int(reservations.MemoryBytes / miB)
}
func toPlacementConstraints(deploy *types.DeployConfig) []ecs.TaskDefinition_TaskDefinitionPlacementConstraint {
@ -467,7 +468,7 @@ func toHostEntryPtr(hosts types.HostsList) []ecs.TaskDefinition_HostEntry {
func getRepoCredentials(service types.ServiceConfig) *ecs.TaskDefinition_RepositoryCredentials {
// extract registry and namespace string from image name
for key, value := range service.Extensions {
if key == ExtensionPullCredentials {
if key == extensionPullCredentials {
return &ecs.TaskDefinition_RepositoryCredentials{CredentialsParameter: value.(string)}
}
}

View File

@ -32,7 +32,7 @@ func (b *ecsAPIService) Down(ctx context.Context, options *cli.ProjectOptions) e
if err != nil {
return err
}
return b.WaitStackCompletion(ctx, name, StackDelete)
return b.WaitStackCompletion(ctx, name, stackDelete)
}
func (b *ecsAPIService) projectName(options *cli.ProjectOptions) (string, error) {

View File

@ -17,12 +17,12 @@
package ecs
const (
ECSTaskExecutionPolicy = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
ECRReadOnlyPolicy = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
ecsTaskExecutionPolicy = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
ecrReadOnlyPolicy = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
ActionGetSecretValue = "secretsmanager:GetSecretValue"
ActionGetParameters = "ssm:GetParameters"
ActionDecrypt = "kms:Decrypt"
actionGetSecretValue = "secretsmanager:GetSecretValue"
actionGetParameters = "ssm:GetParameters"
actionDecrypt = "kms:Decrypt"
)
var assumeRolePolicyDocument = PolicyDocument{
@ -38,12 +38,14 @@ var assumeRolePolicyDocument = PolicyDocument{
},
}
// PolicyDocument describes an IAM policy document
// could alternatively depend on https://github.com/kubernetes-sigs/cluster-api-provider-aws/blob/master/cmd/clusterawsadm/api/iam/v1alpha1/types.go
type PolicyDocument struct {
Version string `json:",omitempty"`
Statement []PolicyStatement `json:",omitempty"`
}
// PolicyStatement describes an IAM policy statement
type PolicyStatement struct {
Effect string `json:",omitempty"`
Action []string `json:",omitempty"`
@ -51,6 +53,7 @@ type PolicyStatement struct {
Resource []string `json:",omitempty"`
}
// PolicyPrincipal describes an IAM policy principal
type PolicyPrincipal struct {
Service string `json:",omitempty"`
}

View File

@ -35,7 +35,7 @@ func (b *ecsAPIService) Ps(ctx context.Context, options *cli.ProjectOptions) ([]
if err != nil {
return nil, err
}
cluster := parameters[ParameterClusterName]
cluster := parameters[parameterClusterName]
resources, err := b.SDK.ListStackResources(ctx, projectName)
if err != nil {
@ -61,7 +61,7 @@ func (b *ecsAPIService) Ps(ctx context.Context, options *cli.ProjectOptions) ([]
for i, state := range status {
ports := []string{}
for _, lb := range state.LoadBalancers {
for _, lb := range state.Publishers {
ports = append(ports, fmt.Sprintf(
"%s:%d->%d/%s",
lb.URL,

View File

@ -40,7 +40,7 @@ func (b *ecsAPIService) Logs(ctx context.Context, options *cli.ProjectOptions, w
}
consumer := logConsumer{
colors: map[string]ColorFunc{},
colors: map[string]colorFunc{},
width: 0,
writer: writer,
}
@ -58,7 +58,7 @@ func (b *ecsAPIService) Logs(ctx context.Context, options *cli.ProjectOptions, w
func (l *logConsumer) Log(service, container, message string) {
cf, ok := l.colors[service]
if !ok {
cf = <-Rainbow
cf = <-loop
l.colors[service] = cf
l.computeWidth()
}
@ -81,7 +81,7 @@ func (l *logConsumer) computeWidth() {
}
type logConsumer struct {
colors map[string]ColorFunc
colors map[string]colorFunc
width int
writer io.Writer
}

View File

@ -24,7 +24,7 @@ import (
"github.com/awslabs/goformation/v4/cloudformation"
)
func Marshall(template *cloudformation.Template) ([]byte, error) {
func marshall(template *cloudformation.Template) ([]byte, error) {
raw, err := template.JSON()
if err != nil {
return nil, err

View File

@ -22,11 +22,12 @@ import (
"strings"
"time"
"github.com/aws/aws-sdk-go/aws/client"
"github.com/docker/api/compose"
"github.com/docker/api/secrets"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/cloudformation"
"github.com/aws/aws-sdk-go/service/cloudformation/cloudformationiface"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
@ -46,17 +47,16 @@ import (
)
type sdk struct {
sess *session.Session
ECS ecsiface.ECSAPI
EC2 ec2iface.EC2API
ELB elbv2iface.ELBV2API
CW cloudwatchlogsiface.CloudWatchLogsAPI
IAM iamiface.IAMAPI
CF cloudformationiface.CloudFormationAPI
SM secretsmanageriface.SecretsManagerAPI
ECS ecsiface.ECSAPI
EC2 ec2iface.EC2API
ELB elbv2iface.ELBV2API
CW cloudwatchlogsiface.CloudWatchLogsAPI
IAM iamiface.IAMAPI
CF cloudformationiface.CloudFormationAPI
SM secretsmanageriface.SecretsManagerAPI
}
func NewSDK(sess *session.Session) sdk {
func newSDK(sess client.ConfigProvider) sdk {
return sdk{
ECS: ecs.New(sess),
EC2: ec2.New(sess),
@ -177,7 +177,7 @@ func (s sdk) StackExists(ctx context.Context, name string) (bool, error) {
func (s sdk) CreateStack(ctx context.Context, name string, template *cf.Template, parameters map[string]string) error {
logrus.Debug("Create CloudFormation stack")
json, err := Marshall(template)
json, err := marshall(template)
if err != nil {
return err
}
@ -205,7 +205,7 @@ func (s sdk) CreateStack(ctx context.Context, name string, template *cf.Template
func (s sdk) CreateChangeSet(ctx context.Context, name string, template *cf.Template, parameters map[string]string) (string, error) {
logrus.Debug("Create CloudFormation Changeset")
json, err := Marshall(template)
json, err := marshall(template)
if err != nil {
return "", err
}
@ -258,9 +258,9 @@ func (s sdk) UpdateStack(ctx context.Context, changeset string) error {
}
const (
StackCreate = iota
StackUpdate
StackDelete
stackCreate = iota
stackUpdate
stackDelete
)
func (s sdk) WaitStackComplete(ctx context.Context, name string, operation int) error {
@ -268,9 +268,9 @@ func (s sdk) WaitStackComplete(ctx context.Context, name string, operation int)
StackName: aws.String(name),
}
switch operation {
case StackCreate:
case stackCreate:
return s.CF.WaitUntilStackCreateCompleteWithContext(ctx, input)
case StackDelete:
case stackDelete:
return s.CF.WaitUntilStackDeleteCompleteWithContext(ctx, input)
default:
return fmt.Errorf("internal error: unexpected stack operation %d", operation)
@ -322,14 +322,14 @@ func (s sdk) ListStackParameters(ctx context.Context, name string) (map[string]s
return parameters, nil
}
type StackResource struct {
type stackResource struct {
LogicalID string
Type string
ARN string
Status string
}
func (s sdk) ListStackResources(ctx context.Context, name string) ([]StackResource, error) {
func (s sdk) ListStackResources(ctx context.Context, name string) ([]stackResource, error) {
// FIXME handle pagination
res, err := s.CF.ListStackResourcesWithContext(ctx, &cloudformation.ListStackResourcesInput{
StackName: aws.String(name),
@ -338,9 +338,9 @@ func (s sdk) ListStackResources(ctx context.Context, name string) ([]StackResour
return nil, err
}
resources := []StackResource{}
resources := []stackResource{}
for _, r := range res.StackResourceSummaries {
resources = append(resources, StackResource{
resources = append(resources, stackResource{
LogicalID: aws.StringValue(r.LogicalResourceId),
Type: aws.StringValue(r.ResourceType),
ARN: aws.StringValue(r.PhysicalResourceId),
@ -495,11 +495,11 @@ func (s sdk) DescribeServices(ctx context.Context, cluster string, arns []string
return nil, err
}
status = append(status, compose.ServiceStatus{
ID: aws.StringValue(service.ServiceName),
Name: name,
Replicas: int(aws.Int64Value(service.RunningCount)),
Desired: int(aws.Int64Value(service.DesiredCount)),
LoadBalancers: loadBalancers,
ID: aws.StringValue(service.ServiceName),
Name: name,
Replicas: int(aws.Int64Value(service.RunningCount)),
Desired: int(aws.Int64Value(service.DesiredCount)),
Publishers: loadBalancers,
})
}
return status, nil

View File

@ -24,11 +24,13 @@ import (
"path/filepath"
)
// Secret define sensitive data to be bound as file
type Secret struct {
Name string
Keys []string
}
// CreateSecretFiles retrieve sensitive data from env and store as plain text a a file in path
func CreateSecretFiles(secret Secret, path string) error {
value, ok := os.LookupEnv(secret.Name)
if !ok {

View File

@ -28,21 +28,21 @@ const secretsFolder = "/run/secrets"
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "usage: secrets <json encoded []Secret>")
fmt.Fprint(os.Stderr, "usage: secrets <json encoded []Secret>")
os.Exit(1)
}
var input []secrets.Secret
err := json.Unmarshal([]byte(os.Args[1]), &input)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
fmt.Fprint(os.Stderr, err.Error())
os.Exit(1)
}
for _, secret := range input {
err := secrets.CreateSecretFiles(secret, secretsFolder)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
fmt.Fprint(os.Stderr, err.Error())
os.Exit(1)
}
}

View File

@ -67,20 +67,20 @@ func (b *ecsAPIService) Up(ctx context.Context, options *cli.ProjectOptions) err
}
parameters := map[string]string{
ParameterClusterName: cluster,
ParameterVPCId: vpc,
ParameterSubnet1Id: subNets[0],
ParameterSubnet2Id: subNets[1],
ParameterLoadBalancerARN: lb,
parameterClusterName: cluster,
parameterVPCId: vpc,
parameterSubnet1Id: subNets[0],
parameterSubnet2Id: subNets[1],
parameterLoadBalancerARN: lb,
}
update, err := b.SDK.StackExists(ctx, project.Name)
if err != nil {
return err
}
operation := StackCreate
operation := stackCreate
if update {
operation = StackUpdate
operation = stackUpdate
changeset, err := b.SDK.CreateChangeSet(ctx, project.Name, template, parameters)
if err != nil {
return err
@ -110,7 +110,7 @@ func (b *ecsAPIService) Up(ctx context.Context, options *cli.ProjectOptions) err
func (b ecsAPIService) GetVPC(ctx context.Context, project *types.Project) (string, error) {
//check compose file for custom VPC selected
if vpc, ok := project.Extensions[ExtensionVPC]; ok {
if vpc, ok := project.Extensions[extensionVPC]; ok {
vpcID := vpc.(string)
ok, err := b.SDK.VpcExists(ctx, vpcID)
if err != nil {
@ -130,7 +130,7 @@ func (b ecsAPIService) GetVPC(ctx context.Context, project *types.Project) (stri
func (b ecsAPIService) GetLoadBalancer(ctx context.Context, project *types.Project) (string, error) {
//check compose file for custom VPC selected
if ext, ok := project.Extensions[ExtensionLB]; ok {
if ext, ok := project.Extensions[extensionLB]; ok {
lb := ext.(string)
ok, err := b.SDK.LoadBalancerExists(ctx, lb)
if err != nil {
@ -146,7 +146,7 @@ func (b ecsAPIService) GetLoadBalancer(ctx context.Context, project *types.Proje
func (b ecsAPIService) GetCluster(ctx context.Context, project *types.Project) (string, error) {
//check compose file for custom VPC selected
if ext, ok := project.Extensions[ExtensionCluster]; ok {
if ext, ok := project.Extensions[extensionCluster]; ok {
cluster := ext.(string)
ok, err := b.SDK.ClusterExists(ctx, cluster)
if err != nil {

View File

@ -28,7 +28,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
)
func (b *ecsAPIService) WaitStackCompletion(ctx context.Context, name string, operation int) error {
func (b *ecsAPIService) WaitStackCompletion(ctx context.Context, name string, operation int) error { //nolint:gocyclo
knownEvents := map[string]struct{}{}
// progress writer
w := progress.ContextWriter(ctx)
@ -76,23 +76,23 @@ func (b *ecsAPIService) WaitStackCompletion(ctx context.Context, name string, op
switch status {
case "CREATE_COMPLETE":
if operation == StackCreate {
if operation == stackCreate {
progressStatus = progress.Done
}
case "UPDATE_COMPLETE":
if operation == StackUpdate {
if operation == stackUpdate {
progressStatus = progress.Done
}
case "DELETE_COMPLETE":
if operation == StackDelete {
if operation == stackDelete {
progressStatus = progress.Done
}
default:
if strings.HasSuffix(status, "_FAILED") {
progressStatus = progress.Error
if stackErr == nil {
operation = StackDelete
operation = stackDelete
stackErr = fmt.Errorf(reason)
}
}

View File

@ -17,15 +17,15 @@
package ecs
const (
ExtensionSecurityGroup = "x-aws-securitygroup"
ExtensionVPC = "x-aws-vpc"
ExtensionPullCredentials = "x-aws-pull_credentials"
ExtensionLB = "x-aws-loadbalancer"
ExtensionCluster = "x-aws-cluster"
ExtensionKeys = "x-aws-keys"
ExtensionMinPercent = "x-aws-min_percent"
ExtensionMaxPercent = "x-aws-max_percent"
ExtensionRetention = "x-aws-logs_retention"
ExtensionRole = "x-aws-role"
ExtensionManagedPolicies = "x-aws-policies"
extensionSecurityGroup = "x-aws-securitygroup"
extensionVPC = "x-aws-vpc"
extensionPullCredentials = "x-aws-pull_credentials"
extensionLB = "x-aws-loadbalancer"
extensionCluster = "x-aws-cluster"
extensionKeys = "x-aws-keys"
extensionMinPercent = "x-aws-min_percent"
extensionMaxPercent = "x-aws-max_percent"
extensionRetention = "x-aws-logs_retention"
extensionRole = "x-aws-role"
extensionManagedPolicies = "x-aws-policies"
)

View File

@ -29,6 +29,7 @@ type Service interface {
DeleteSecret(ctx context.Context, id string, recover bool) error
}
// Secret hold sensitive data
type Secret struct {
ID string `json:"ID"`
Name string `json:"Name"`
@ -38,6 +39,7 @@ type Secret struct {
password string
}
// NewSecret builds a secret
func NewSecret(name, username, password, description string) Secret {
return Secret{
Name: name,
@ -47,6 +49,7 @@ func NewSecret(name, username, password, description string) Secret {
}
}
// ToJSON marshall a Secret into JSON string
func (s Secret) ToJSON() (string, error) {
b, err := json.MarshalIndent(&s, "", "\t")
if err != nil {
@ -55,6 +58,7 @@ func (s Secret) ToJSON() (string, error) {
return string(b), nil
}
// GetCredString marshall a Secret's sensitive data into JSON string
func (s Secret) GetCredString() (string, error) {
creds := map[string]string{
"username": s.username,