diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 596334a..8992f4e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,7 +5,8 @@ services: variables: DOCKER_TLS_CERTDIR: "" - GIT_DEPTH: "1" + GIT_DEPTH: "10" + stages: - setup:dev - build:dev @@ -90,6 +91,8 @@ webapp:build:prod: - master - prod - prod-web-app + dependencies: + - 'infra:setup:prod' variables: TARGET_ENV: 'prod' TARGET_TYPE: 'service' @@ -124,6 +127,8 @@ webapi:build:prod: - master - prod - prod-web-api + dependencies: + - 'infra:setup:prod' variables: TARGET_ENV: 'prod' TARGET_TYPE: 'service' @@ -158,6 +163,8 @@ ddlogscollector:build:prod: - master - prod - prod-ddlogs + dependencies: + - 'infra:setup:prod' variables: TARGET_ENV: 'prod' TARGET_TYPE: 'function' diff --git a/build/cicd/README.md b/build/cicd/README.md index 62296ff..1f8f2f6 100644 --- a/build/cicd/README.md +++ b/build/cicd/README.md @@ -358,6 +358,21 @@ Access/Secret Keys are required --no-push disable pushing release image to remote repository ``` +* `build image` - Executes a build for a single image + + ```bash + $ cicd -env [dev|stage|prod] build image -name NNNNN [command options] + ``` + + Options: + ```bash + --name value, -n value target image, required + --release-tag value, --tag value optional tag to override default CI_COMMIT_SHORT_SHA + --dry-run print out the build details + --no-cache skip caching for the docker build + --no-push disable pushing release image to remote repository + ``` + * `deploy infrastructure` - Executes a deploy to setup the infrastructure for the deployment environment. ```bash diff --git a/build/cicd/internal/config/config.go b/build/cicd/internal/config/config.go index ca124a8..580d648 100644 --- a/build/cicd/internal/config/config.go +++ b/build/cicd/internal/config/config.go @@ -538,7 +538,7 @@ func getDatadogApiKey(cfg *devdeploy.Config) (string, error) { // 3. Check AWS Secrets Manager for Datadog entry. if apiKey == "" { - secretId := "DATADOG" + secretId := "datadog" var err error apiKey, err = devdeploy.GetAwsSecretValue(cfg.AwsCredentials, secretId) if err != nil { diff --git a/build/cicd/internal/config/function.go b/build/cicd/internal/config/function.go index a9475e8..731010b 100644 --- a/build/cicd/internal/config/function.go +++ b/build/cicd/internal/config/function.go @@ -15,7 +15,7 @@ import ( type Function = string var ( - Function_Ddlogscollector = "ddlogscollector" + Function_Ddlogscollector Function = "ddlogscollector" ) // List of function names used by main.go for help. diff --git a/build/cicd/internal/config/image.go b/build/cicd/internal/config/image.go new file mode 100644 index 0000000..83f8e0e --- /dev/null +++ b/build/cicd/internal/config/image.go @@ -0,0 +1,103 @@ +package config + +import ( + "encoding/json" + "fmt" + "log" + "path/filepath" + + "github.com/pkg/errors" + "gitlab.com/geeks-accelerator/oss/devops/pkg/devdeploy" +) + +// Image define the name of an image. +type Image = string + +var ( + ImageYourBaseImage Image = "your-base-image" +) + +// List of images names used by main.go for help and append the functions to config. +var ImageNames = []Image{ + ImageYourBaseImage, +} + +// NewImage returns the *devdeploy.ProjectImage. +func NewImage(imageName string, cfg *devdeploy.Config) (*devdeploy.ProjectImage, error) { + + ctx := &devdeploy.ProjectImage{ + Name: fmt.Sprintf("%s-%s-%s", cfg.Env, cfg.ProjectName, imageName), + CodeDir: filepath.Join(cfg.ProjectRoot, "build/docker", imageName), + DockerBuildDir: cfg.ProjectRoot, + DockerBuildContext: ".", + + // Set the release tag for the image to use include env + function name + commit hash/tag. + ReleaseTag: devdeploy.GitLabCiReleaseTag(cfg.Env, imageName), + } + + switch imageName { + case ImageYourBaseImage: + // No specific settings. + + default: + return nil, errors.Wrapf(devdeploy.ErrInvalidFunction, + "No context defined for image '%s'", + imageName) + } + + // Set the docker file if no custom one has been defined for the service. + if ctx.Dockerfile == "" { + ctx.Dockerfile = filepath.Join(ctx.CodeDir, "Dockerfile") + } + + return ctx, nil +} + +// BuildImageForTargetEnv executes the build commands for a target image. +func BuildImageForTargetEnv(log *log.Logger, awsCredentials devdeploy.AwsCredentials, targetEnv Env, imageName, releaseTag string, dryRun, noCache, noPush bool) error { + + cfg, err := NewConfig(log, targetEnv, awsCredentials) + if err != nil { + return err + } + + targetImage, err := NewImage(imageName, cfg) + if err != nil { + return err + } + + // Override the release tag if set. + if releaseTag != "" { + targetImage.ReleaseTag = releaseTag + } + + // Append build args to be used for all functions. + if targetImage.DockerBuildArgs == nil { + targetImage.DockerBuildArgs = make(map[string]string) + } + + // funcPath is used to copy the service specific code in the Dockerfile. + codePath, err := filepath.Rel(cfg.ProjectRoot, targetImage.CodeDir) + if err != nil { + return err + } + targetImage.DockerBuildArgs["code_path"] = codePath + + if dryRun { + cfgJSON, err := json.MarshalIndent(cfg, "", " ") + if err != nil { + log.Fatalf("BuildFunctionForTargetEnv : Marshalling config to JSON : %+v", err) + } + log.Printf("BuildFunctionForTargetEnv : config : %v\n", string(cfgJSON)) + + detailsJSON, err := json.MarshalIndent(targetImage, "", " ") + if err != nil { + log.Fatalf("BuildFunctionForTargetEnv : Marshalling details to JSON : %+v", err) + } + log.Printf("BuildFunctionForTargetEnv : details : %v\n", string(detailsJSON)) + + return nil + } + + return devdeploy.BuildImageForTargetEnv(log, cfg, targetImage, noCache, noPush) +} diff --git a/build/cicd/internal/config/service.go b/build/cicd/internal/config/service.go index a3d91b3..b658dd9 100644 --- a/build/cicd/internal/config/service.go +++ b/build/cicd/internal/config/service.go @@ -6,6 +6,7 @@ import ( "log" "os" "path/filepath" + "strconv" "strings" "github.com/aws/aws-sdk-go/aws" @@ -41,8 +42,8 @@ const ( type Service = string var ( - ServiceWebApi = "web-api" - ServiceWebApp = "web-app" + ServiceWebApi Service = "web-api" + ServiceWebApp Service = "web-app" ) // List of service names used by main.go for help. @@ -185,7 +186,6 @@ func NewService(serviceName string, cfg *devdeploy.Config) (*devdeploy.ProjectSe srv.StaticFilesS3Prefix = filepath.Join(cfg.AwsS3BucketPublicKeyPrefix, ctx.ReleaseTag, "static") } - // ========================================================================= // Service settings based on target env. var enableElb bool @@ -293,7 +293,7 @@ func NewService(serviceName string, cfg *devdeploy.Config) (*devdeploy.ProjectSe srv.AwsElbLoadBalancer.TargetGroups[0].Name) // Set ECS configs based on specified env. - if cfg.Env == "prod" { + if cfg.Env == EnvProd { srv.AwsElbLoadBalancer.EcsTaskDeregistrationDelay = 300 } else { // Force staging to deploy immediately without waiting for connections to drain @@ -311,12 +311,12 @@ func NewService(serviceName string, cfg *devdeploy.Config) (*devdeploy.ProjectSe } // Set ECS configs based on specified env. - if cfg.Env == "prod" { + if cfg.Env == EnvProd { srv.AwsEcsService.DeploymentMinimumHealthyPercent = 100 srv.AwsEcsService.DeploymentMaximumPercent = 200 } else { - srv.AwsEcsService.DeploymentMinimumHealthyPercent = 100 - srv.AwsEcsService.DeploymentMaximumPercent = 200 + srv.AwsEcsService.DeploymentMinimumHealthyPercent = 0 + srv.AwsEcsService.DeploymentMaximumPercent = 100 } if EnableServiceAutoscaling { @@ -415,6 +415,55 @@ func NewService(serviceName string, cfg *devdeploy.Config) (*devdeploy.ProjectSe return nil, err } + // Try to find the Datadog API key, this value is optional. + // If Datadog API key is not specified, then integration with Datadog for observability will not be active. + datadogApiKey, err := getDatadogApiKey(cfg) + if err != nil { + return srv, err + } + + // Add the Datadog container to the task definition if an API Key is set. + var ddContainer *ecs.ContainerDefinition + if datadogApiKey != "" { + ddTags := []string{ + "source:docker", + "service:" + srv.AwsEcsService.ServiceName, + "service_name:" + ctx.Name, + "cluster:" + srv.AwsEcsCluster.ClusterName, + "env:" + cfg.Env, + } + + // Defined a container definition for the specific service. + ddContainer = &ecs.ContainerDefinition{ + Name: aws.String("datadog-agent"), + Image: aws.String(srv.ReleaseImage), + Essential: aws.Bool(true), + PortMappings: []*ecs.PortMapping{ + &ecs.PortMapping{ + ContainerPort: aws.Int64(8125), + }, + &ecs.PortMapping{ + ContainerPort: aws.Int64(8126), + }, + }, + Cpu: aws.Int64(128), + MemoryReservation: aws.Int64(256), + Environment: []*ecs.KeyValuePair{ + ecsKeyValuePair("DD_API_KEY", datadogApiKey), + ecsKeyValuePair("DD_LOGS_ENABLED", "true"), + ecsKeyValuePair("DD_APM_ENABLED", "true"), + ecsKeyValuePair("DD_RECEIVER_PORT", "8126"), + ecsKeyValuePair("DD_APM_NON_LOCAL_TRAFFIC", "true"), + ecsKeyValuePair("DD_LOGS_CONFIG_CONTAINER_COLLECT_ALL", "true"), + ecsKeyValuePair("DD_TAGS", strings.Join(ddTags, " ")), + ecsKeyValuePair("DD_DOGSTATSD_ORIGIN_DETECTION", "true"), + ecsKeyValuePair("DD_DOGSTATSD_NON_LOCAL_TRAFFIC", "true"), + ecsKeyValuePair("ECS_FARGATE", "true"), + }, + } + + } + // Define a base set of environment variables that can be assigned to individual container definitions. baseEnvVals := func() []*ecs.KeyValuePair { @@ -428,7 +477,7 @@ func NewService(serviceName string, cfg *devdeploy.Config) (*devdeploy.ProjectSe ciPipelineURL = strings.TrimRight(GitLabProjectBaseUrl, "/") + "/pipelines/" + os.Getenv("CI_PIPELINE_ID") } - return []*ecs.KeyValuePair{ + envVars := []*ecs.KeyValuePair{ ecsKeyValuePair(devdeploy.ENV_KEY_ECS_CLUSTER, srv.AwsEcsCluster.ClusterName), ecsKeyValuePair(devdeploy.ENV_KEY_ECS_SERVICE, srv.AwsEcsService.ServiceName), ecsKeyValuePair("AWS_DEFAULT_REGION", cfg.AwsCredentials.Region), @@ -447,6 +496,16 @@ func NewService(serviceName string, cfg *devdeploy.Config) (*devdeploy.ProjectSe ecsKeyValuePair("WEB_API_BASE_URL", webApiCtx.BaseUrl()), ecsKeyValuePair("EMAIL_SENDER", "lee+saas-starter-kit@geeksinthewoods.com"), } + + if datadogApiKey != "" { + envVars = append(envVars, ecsKeyValuePair("DATADOG_ADDR", "127.0.0.1:8125"), + ecsKeyValuePair("DD_API_KEY", datadogApiKey), + ecsKeyValuePair("DD_TRACE_AGENT_PORT", "8126"), + ecsKeyValuePair("DD_SERVICE_NAME", srv.AwsEcsService.ServiceName), + ecsKeyValuePair("DD_ENV", cfg.Env)) + } + + return envVars } // ========================================================================= @@ -508,33 +567,6 @@ func NewService(serviceName string, cfg *devdeploy.Config) (*devdeploy.ProjectSe }) } - // Append env vars for the service task. - container1.Environment = append(container1.Environment, - ecsKeyValuePair("SERVICE_NAME", ctx.Name), - ecsKeyValuePair("PROJECT_NAME", cfg.ProjectName), - - // Use placeholders for these environment variables that will be replaced with devdeploy.DeployServiceToTargetEnv - ecsKeyValuePair("WEB_APP_HTTP_HOST", "{HTTP_HOST}"), - ecsKeyValuePair("WEB_APP_HTTPS_HOST", "{HTTPS_HOST}"), - ecsKeyValuePair("WEB_APP_SERVICE_ENABLE_HTTPS", "{HTTPS_ENABLED}"), - ecsKeyValuePair("WEB_APP_SERVICE_BASE_URL", "{APP_BASE_URL}"), - ecsKeyValuePair("WEB_APP_SERVICE_HOST_NAMES", "{HOST_NAMES}"), - ecsKeyValuePair("WEB_APP_SERVICE_STATICFILES_S3_ENABLED", "{STATIC_FILES_S3_ENABLED}"), - ecsKeyValuePair("WEB_APP_SERVICE_STATICFILES_S3_PREFIX", "{STATIC_FILES_S3_PREFIX}"), - ecsKeyValuePair("WEB_APP_SERVICE_STATICFILES_CLOUDFRONT_ENABLED", "{STATIC_FILES_CLOUDFRONT_ENABLED}"), - ecsKeyValuePair("WEB_APP_REDIS_HOST", "{CACHE_HOST}"), - ecsKeyValuePair("WEB_APP_DB_HOST", "{DB_HOST}"), - ecsKeyValuePair("WEB_APP_DB_USERNAME", "{DB_USER}"), - ecsKeyValuePair("WEB_APP_DB_PASSWORD", "{DB_PASS}"), - ecsKeyValuePair("WEB_APP_DB_DATABASE", "{DB_DATABASE}"), - ecsKeyValuePair("WEB_APP_DB_DRIVER", "{DB_DRIVER}"), - ecsKeyValuePair("WEB_APP_DB_DISABLE_TLS", "{DB_DISABLE_TLS}"), - ecsKeyValuePair("WEB_APP_AWS_S3_BUCKET_PRIVATE", "{AWS_S3_BUCKET_PRIVATE}"), - ecsKeyValuePair("WEB_APP_AWS_S3_BUCKET_PUBLIC", "{AWS_S3_BUCKET_PUBLIC}"), - ecsKeyValuePair(devdeploy.ENV_KEY_ROUTE53_UPDATE_TASK_IPS, "{ROUTE53_UPDATE_TASK_IPS}"), - ecsKeyValuePair(devdeploy.ENV_KEY_ROUTE53_ZONES, "{ROUTE53_ZONES}"), - ) - // Define the full task definition for the service. taskDef := &ecs.RegisterTaskDefinitionInput{ Family: aws.String(fmt.Sprintf("%s-%s-%s", cfg.Env, srv.AwsEcsCluster.ClusterName, ctx.Name)), @@ -547,32 +579,44 @@ func NewService(serviceName string, cfg *devdeploy.Config) (*devdeploy.ProjectSe RequiresCompatibilities: aws.StringSlice([]string{"FARGATE"}), } + // Append the datadog container if defined. + if ddContainer != nil { + taskDef.ContainerDefinitions = append(taskDef.ContainerDefinitions, ddContainer) + } + srv.AwsEcsTaskDefinition = &devdeploy.AwsEcsTaskDefinition{ RegisterInput: taskDef, - UpdatePlaceholders: func(placeholders map[string]string) error { + PreRegister: func(input *ecs.RegisterTaskDefinitionInput, vars devdeploy.AwsEcsServiceDeployVariables) error { + // Append env vars for the service task. + input.ContainerDefinitions[0].Environment = append(input.ContainerDefinitions[0].Environment, + ecsKeyValuePair("SERVICE_NAME", ctx.Name), + ecsKeyValuePair("PROJECT_NAME", cfg.ProjectName), - // Try to find the Datadog API key, this value is optional. - // If Datadog API key is not specified, then integration with Datadog for observability will not be active. - { - datadogApiKey, err := getDatadogApiKey(cfg) - if err != nil { - return err - } + // Use placeholders for these environment variables that will be replaced with devdeploy.DeployServiceToTargetEnv + ecsKeyValuePair("WEB_APP_HTTP_HOST", vars.HTTPHost), + ecsKeyValuePair("WEB_APP_HTTPS_HOST", vars.HTTPSHost), + ecsKeyValuePair("WEB_APP_SERVICE_ENABLE_HTTPS", strconv.FormatBool(vars.HTTPSEnabled)), + ecsKeyValuePair("WEB_APP_SERVICE_BASE_URL", vars.ServiceBaseUrl), + ecsKeyValuePair("WEB_APP_SERVICE_HOST_NAMES", strings.Join(vars.AlternativeHostnames, ",")), + ecsKeyValuePair("WEB_APP_SERVICE_STATICFILES_S3_ENABLED", strconv.FormatBool(vars.StaticFilesS3Enabled)), + ecsKeyValuePair("WEB_APP_SERVICE_STATICFILES_S3_PREFIX", vars.StaticFilesS3Prefix), + ecsKeyValuePair("WEB_APP_SERVICE_STATICFILES_CLOUDFRONT_ENABLED", strconv.FormatBool(vars.StaticFilesCloudfrontEnabled)), + ecsKeyValuePair("WEB_APP_REDIS_HOST", vars.CacheHost), + ecsKeyValuePair("WEB_APP_DB_HOST", vars.DbHost), + ecsKeyValuePair("WEB_APP_DB_USERNAME", vars.DbUser), + ecsKeyValuePair("WEB_APP_DB_PASSWORD", vars.DbPass), + ecsKeyValuePair("WEB_APP_DB_DATABASE", vars.DbName), + ecsKeyValuePair("WEB_APP_DB_DRIVER", vars.DbDriver), + ecsKeyValuePair("WEB_APP_DB_DISABLE_TLS", strconv.FormatBool(vars.DbDisableTLS)), + ecsKeyValuePair("WEB_APP_AWS_S3_BUCKET_PRIVATE", vars.AwsS3BucketNamePrivate), + ecsKeyValuePair("WEB_APP_AWS_S3_BUCKET_PUBLIC", vars.AwsS3BucketNamePublic), + ) - if datadogApiKey != "" { - log.Println("DATADOG API Key set.") - } else { - log.Printf("DATADOG API Key NOT set.") - } - - placeholders["{DATADOG_APIKEY}"] = datadogApiKey - - // When the datadog API key is empty, don't force the container to be essential have have the whole task fail. - if datadogApiKey != "" { - placeholders["{DATADOG_ESSENTIAL}"] = "true" - } else { - placeholders["{DATADOG_ESSENTIAL}"] = "false" - } + // When no Elastic Load Balance is used, tasks need to be able to directly update the Route 53 records. + if vars.AwsElbLoadBalancer == nil { + input.ContainerDefinitions[0].Environment = append(input.ContainerDefinitions[0].Environment, + ecsKeyValuePair(devdeploy.ENV_KEY_ROUTE53_ZONES, vars.EncodeRoute53Zones()), + ecsKeyValuePair(devdeploy.ENV_KEY_ROUTE53_UPDATE_TASK_IPS, "true")) } return nil @@ -634,33 +678,6 @@ func NewService(serviceName string, cfg *devdeploy.Config) (*devdeploy.ProjectSe }) } - // Append env vars for the service task. - container1.Environment = append(container1.Environment, - ecsKeyValuePair("SERVICE_NAME", ctx.Name), - ecsKeyValuePair("PROJECT_NAME", cfg.ProjectName), - - // Use placeholders for these environment variables that will be replaced with devdeploy.DeployServiceToTargetEnv - ecsKeyValuePair("WEB_API_HTTP_HOST", "{HTTP_HOST}"), - ecsKeyValuePair("WEB_API_HTTPS_HOST", "{HTTPS_HOST}"), - ecsKeyValuePair("WEB_API_SERVICE_ENABLE_HTTPS", "{HTTPS_ENABLED}"), - ecsKeyValuePair("WEB_API_SERVICE_BASE_URL", "{APP_BASE_URL}"), - ecsKeyValuePair("WEB_API_SERVICE_HOST_NAMES", "{HOST_NAMES}"), - ecsKeyValuePair("WEB_API_SERVICE_STATICFILES_S3_ENABLED", "{STATIC_FILES_S3_ENABLED}"), - ecsKeyValuePair("WEB_API_SERVICE_STATICFILES_S3_PREFIX", "{STATIC_FILES_S3_PREFIX}"), - ecsKeyValuePair("WEB_API_SERVICE_STATICFILES_CLOUDFRONT_ENABLED", "{STATIC_FILES_CLOUDFRONT_ENABLED}"), - ecsKeyValuePair("WEB_API_REDIS_HOST", "{CACHE_HOST}"), - ecsKeyValuePair("WEB_API_DB_HOST", "{DB_HOST}"), - ecsKeyValuePair("WEB_API_DB_USERNAME", "{DB_USER}"), - ecsKeyValuePair("WEB_API_DB_PASSWORD", "{DB_PASS}"), - ecsKeyValuePair("WEB_API_DB_DATABASE", "{DB_DATABASE}"), - ecsKeyValuePair("WEB_API_DB_DRIVER", "{DB_DRIVER}"), - ecsKeyValuePair("WEB_API_DB_DISABLE_TLS", "{DB_DISABLE_TLS}"), - ecsKeyValuePair("WEB_API_AWS_S3_BUCKET_PRIVATE", "{AWS_S3_BUCKET_PRIVATE}"), - ecsKeyValuePair("WEB_API_AWS_S3_BUCKET_PUBLIC", "{AWS_S3_BUCKET_PUBLIC}"), - ecsKeyValuePair(devdeploy.ENV_KEY_ROUTE53_UPDATE_TASK_IPS, "{ROUTE53_UPDATE_TASK_IPS}"), - ecsKeyValuePair(devdeploy.ENV_KEY_ROUTE53_ZONES, "{ROUTE53_ZONES}"), - ) - // Define the full task definition for the service. taskDef := &ecs.RegisterTaskDefinitionInput{ Family: aws.String(fmt.Sprintf("%s-%s-%s", cfg.Env, srv.AwsEcsCluster.ClusterName, ctx.Name)), @@ -673,32 +690,44 @@ func NewService(serviceName string, cfg *devdeploy.Config) (*devdeploy.ProjectSe RequiresCompatibilities: aws.StringSlice([]string{"FARGATE"}), } + // Append the datadog container if defined. + if ddContainer != nil { + taskDef.ContainerDefinitions = append(taskDef.ContainerDefinitions, ddContainer) + } + srv.AwsEcsTaskDefinition = &devdeploy.AwsEcsTaskDefinition{ RegisterInput: taskDef, - UpdatePlaceholders: func(placeholders map[string]string) error { + PreRegister: func(input *ecs.RegisterTaskDefinitionInput, vars devdeploy.AwsEcsServiceDeployVariables) error { + // Append env vars for the service task. + input.ContainerDefinitions[0].Environment = append(input.ContainerDefinitions[0].Environment, + ecsKeyValuePair("SERVICE_NAME", ctx.Name), + ecsKeyValuePair("PROJECT_NAME", cfg.ProjectName), - // Try to find the Datadog API key, this value is optional. - // If Datadog API key is not specified, then integration with Datadog for observability will not be active. - { - datadogApiKey, err := getDatadogApiKey(cfg) - if err != nil { - return err - } + // Use placeholders for these environment variables that will be replaced with devdeploy.DeployServiceToTargetEnv + ecsKeyValuePair("WEB_API_HTTP_HOST", vars.HTTPHost), + ecsKeyValuePair("WEB_API_HTTPS_HOST", vars.HTTPSHost), + ecsKeyValuePair("WEB_API_SERVICE_ENABLE_HTTPS", strconv.FormatBool(vars.HTTPSEnabled)), + ecsKeyValuePair("WEB_API_SERVICE_BASE_URL", vars.ServiceBaseUrl), + ecsKeyValuePair("WEB_API_SERVICE_HOST_NAMES", strings.Join(vars.AlternativeHostnames, ",")), + ecsKeyValuePair("WEB_API_SERVICE_STATICFILES_S3_ENABLED", strconv.FormatBool(vars.StaticFilesS3Enabled)), + ecsKeyValuePair("WEB_API_SERVICE_STATICFILES_S3_PREFIX", vars.StaticFilesS3Prefix), + ecsKeyValuePair("WEB_API_SERVICE_STATICFILES_CLOUDFRONT_ENABLED", strconv.FormatBool(vars.StaticFilesCloudfrontEnabled)), + ecsKeyValuePair("WEB_API_REDIS_HOST", vars.CacheHost), + ecsKeyValuePair("WEB_API_DB_HOST", vars.DbHost), + ecsKeyValuePair("WEB_API_DB_USERNAME", vars.DbUser), + ecsKeyValuePair("WEB_API_DB_PASSWORD", vars.DbPass), + ecsKeyValuePair("WEB_API_DB_DATABASE", vars.DbName), + ecsKeyValuePair("WEB_API_DB_DRIVER", vars.DbDriver), + ecsKeyValuePair("WEB_API_DB_DISABLE_TLS", strconv.FormatBool(vars.DbDisableTLS)), + ecsKeyValuePair("WEB_API_AWS_S3_BUCKET_PRIVATE", vars.AwsS3BucketNamePrivate), + ecsKeyValuePair("WEB_API_AWS_S3_BUCKET_PUBLIC", vars.AwsS3BucketNamePublic), + ) - if datadogApiKey != "" { - log.Println("DATADOG API Key set.") - } else { - log.Printf("DATADOG API Key NOT set.") - } - - placeholders["{DATADOG_APIKEY}"] = datadogApiKey - - // When the datadog API key is empty, don't force the container to be essential have have the whole task fail. - if datadogApiKey != "" { - placeholders["{DATADOG_ESSENTIAL}"] = "true" - } else { - placeholders["{DATADOG_ESSENTIAL}"] = "false" - } + // When no Elastic Load Balance is used, tasks need to be able to directly update the Route 53 records. + if vars.AwsElbLoadBalancer == nil { + input.ContainerDefinitions[0].Environment = append(input.ContainerDefinitions[0].Environment, + ecsKeyValuePair(devdeploy.ENV_KEY_ROUTE53_ZONES, vars.EncodeRoute53Zones()), + ecsKeyValuePair(devdeploy.ENV_KEY_ROUTE53_UPDATE_TASK_IPS, "true")) } return nil diff --git a/build/cicd/main.go b/build/cicd/main.go index 197a6a9..f111e70 100644 --- a/build/cicd/main.go +++ b/build/cicd/main.go @@ -149,6 +149,44 @@ func main() { return config.BuildFunctionForTargetEnv(log, awsCredentials, targetEnv, funcName, releaseTag, dryRun, noCache, noPush) }, }, + { + Name: "image", + Usage: "build an image", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "name, n", + Usage: fmt.Sprintf("target image, one of [%s]", + strings.Join(config.ImageNames, ", ")), + Required: true, + }, + cli.StringFlag{ + Name: "release-tag, tag", + Usage: "optional tag to override default CI_COMMIT_SHORT_SHA", + }, + cli.BoolFlag{ + Name: "dry-run", + Usage: "print out the build details", + }, + cli.BoolFlag{ + Name: "no-cache", + Usage: "skip caching for the docker build", + }, + cli.BoolFlag{ + Name: "no-push", + Usage: "disable pushing release image to remote repository", + }, + }, + Action: func(c *cli.Context) error { + targetEnv := c.GlobalString("env") + funcName := c.String("name") + releaseTag := c.String("release-tag") + dryRun := c.Bool("dry-run") + noCache := c.Bool("no-cache") + noPush := c.Bool("no-push") + + return config.BuildImageForTargetEnv(log, awsCredentials, targetEnv, funcName, releaseTag, dryRun, noCache, noPush) + }, + }, }, }, diff --git a/go.mod b/go.mod index c2b9268..282b4cb 100644 --- a/go.mod +++ b/go.mod @@ -41,7 +41,7 @@ require ( github.com/tinylib/msgp v1.1.0 // indirect github.com/urfave/cli v1.21.0 github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 - gitlab.com/geeks-accelerator/oss/devops v1.0.18 + gitlab.com/geeks-accelerator/oss/devops v1.0.19 golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 golang.org/x/tools v0.0.0-20190807223507-b346f7fd45de // indirect diff --git a/go.sum b/go.sum index 5df1e2d..c94c1da 100644 --- a/go.sum +++ b/go.sum @@ -215,8 +215,8 @@ github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVU github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 h1:zzrxE1FKn5ryBNl9eKOeqQ58Y/Qpo3Q9QNxKHX5uzzQ= github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2/go.mod h1:hzfGeIUDq/j97IG+FhNqkowIyEcD88LrW6fyU3K3WqY= -gitlab.com/geeks-accelerator/oss/devops v1.0.18 h1:Vkk7WrTIvGd+Nnb6ru3o4r1yw4h7lJBdcnGLG71d390= -gitlab.com/geeks-accelerator/oss/devops v1.0.18/go.mod h1:xr+rhNSDXrEh0A6bkBPnfMiRIou3OiPZK0oD5h9GAAM= +gitlab.com/geeks-accelerator/oss/devops v1.0.19 h1:x/PknYjZFZNfrm9TW4wWXlI73Jd56HYrQt3a1IUbpK8= +gitlab.com/geeks-accelerator/oss/devops v1.0.19/go.mod h1:xr+rhNSDXrEh0A6bkBPnfMiRIou3OiPZK0oD5h9GAAM= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=