package config import ( "fmt" "log" "path/filepath" "encoding/json" "github.com/aws/aws-sdk-go/aws" "github.com/pkg/errors" "gitlab.com/geeks-accelerator/oss/devops/pkg/devdeploy" ) // Function define the name of a function. type Function = string var ( Function_Ddlogscollector Function = "ddlogscollector" ) // List of function names used by main.go for help. var FunctionNames = []Function{ // Python Datadog Logs Collector Function_Ddlogscollector, } // NewFunction returns the *devdeploy.ProjectFunction. func NewFunction(funcName string, cfg *devdeploy.Config) (*devdeploy.ProjectFunction, error) { ctx := &devdeploy.ProjectFunction{ Name: fmt.Sprintf("%s-%s-%s", cfg.Env, cfg.ProjectName, funcName), CodeDir: filepath.Join(cfg.ProjectRoot, "cmd", funcName), 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, funcName), } switch funcName { case Function_Ddlogscollector: // Python Datadog Logs Collector is ctx.CodeDir = filepath.Join(cfg.ProjectRoot, "deployments", funcName) // Change the build directory to the function directory instead of project root. ctx.DockerBuildDir = ctx.CodeDir // AwsLambdaFunction defines the details needed to create an lambda function. ctx.AwsLambdaFunction = &devdeploy.AwsLambdaFunction{ FunctionName: ctx.Name, Description: "Ship logs from cloudwatch to datadog", Handler: "lambda_function.lambda_handler", Runtime: "python2.7", MemorySize: 512, Timeout: aws.Int64(300), Environment: map[string]string{ "DD_API_KEY": "", "LAMBDA_FUNC": ctx.Name, }, Tags: []devdeploy.Tag{ {Key: devdeploy.AwsTagNameProject, Value: cfg.ProjectName}, {Key: devdeploy.AwsTagNameEnv, Value: cfg.Env}, }, } ctx.AwsIamRole = &devdeploy.AwsIamRole{ RoleName: "DatadogAWSIntegrationLambdaRole", Description: "Allows Datadog to run Lambda functions to call AWS services on your behalf.", AssumeRolePolicyDocument: "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":[\"lambda.amazonaws.com\"]},\"Action\":[\"sts:AssumeRole\"]}]}", Tags: []devdeploy.Tag{ {Key: devdeploy.AwsTagNameProject, Value: cfg.ProjectName}, {Key: devdeploy.AwsTagNameEnv, Value: cfg.Env}, }, } ctx.AwsIamPolicy = &devdeploy.AwsIamPolicy{ PolicyName: "DatadogAWSIntegrationPolicy", Description: "Provides Datadog Lambda function the ability to ship AWS service related logs back to Datadog.", PolicyDocument: devdeploy.AwsIamPolicyDocument{ Version: "2012-10-17", Statement: []devdeploy.AwsIamStatementEntry{ { Action: []string{ "apigateway:GET", "autoscaling:Describe*", "budgets:ViewBudget", "cloudfront:GetDistributionConfig", "cloudfront:ListDistributions", "cloudtrail:DescribeTrails", "cloudtrail:GetTrailStatus", "cloudwatch:Describe*", "cloudwatch:Get*", "cloudwatch:List*", "codedeploy:List*", "codedeploy:BatchGet*", "directconnect:Describe*", "dynamodb:List*", "dynamodb:Describe*", "ec2:Describe*", "ecs:Describe*", "ecs:List*", "elasticache:Describe*", "elasticache:List*", "elasticfilesystem:DescribeFileSystems", "elasticfilesystem:DescribeTags", "elasticloadbalancing:Describe*", "elasticmapreduce:List*", "elasticmapreduce:Describe*", "es:ListTags", "es:ListDomainNames", "es:DescribeElasticsearchDomains", "health:DescribeEvents", "health:DescribeEventDetails", "health:DescribeAffectedEntities", "kinesis:List*", "kinesis:Describe*", "lambda:AddPermission", "lambda:GetPolicy", "lambda:List*", "lambda:RemovePermission", "logs:Get*", "logs:Describe*", "logs:FilterLogEvents", "logs:TestMetricFilter", "logs:PutSubscriptionFilter", "logs:DeleteSubscriptionFilter", "logs:DescribeSubscriptionFilters", "rds:Describe*", "rds:List*", "redshift:DescribeClusters", "redshift:DescribeLoggingStatus", "route53:List*", "s3:GetBucketLogging", "s3:GetBucketLocation", "s3:GetBucketNotification", "s3:GetBucketTagging", "s3:ListAllMyBuckets", "s3:PutBucketNotification", "ses:Get*", "sns:List*", "sns:Publish", "sqs:ListQueues", "support:*", "tag:GetResources", "tag:GetTagKeys", "tag:GetTagValues", "xray:BatchGetTraces", "xray:GetTraceSummaries", "lambda:List*", "logs:DescribeLogGroups", "logs:DescribeLogStreams", "logs:FilterLogEvents", "tag:GetResources", "cloudfront:GetDistributionConfig", "cloudfront:ListDistributions", "elasticloadbalancing:DescribeLoadBalancers", "elasticloadbalancing:DescribeLoadBalancerAttributes", "lambda:AddPermission", "lambda:GetPolicy", "lambda:RemovePermission", "redshift:DescribeClusters", "redshift:DescribeLoggingStatus", "s3:GetBucketLogging", "s3:GetBucketLocation", "s3:GetBucketNotification", "s3:ListAllMyBuckets", "s3:PutBucketNotification", "logs:PutSubscriptionFilter", "logs:DeleteSubscriptionFilter", "logs:DescribeSubscriptionFilters", }, Effect: "Allow", Resource: "*", }, }, }, } default: return nil, errors.Wrapf(devdeploy.ErrInvalidFunction, "No function context defined for function '%s'", funcName) } // Append the datadog api key before execution. ctx.AwsLambdaFunction.UpdateEnvironment = func(vars map[string]string) error { datadogApiKey, err := getDatadogApiKey(cfg) if err != nil { return err } vars["DD_API_KEY"] = datadogApiKey return nil } ctx.CodeS3Bucket = cfg.AwsS3BucketPrivate.BucketName ctx.CodeS3Key = filepath.Join("src", "aws", "lambda", cfg.Env, ctx.Name, ctx.ReleaseTag+".zip") // 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 } // BuildFunctionForTargetEnv executes the build commands for a target function. func BuildFunctionForTargetEnv(log *log.Logger, awsCredentials devdeploy.AwsCredentials, targetEnv Env, functionName, releaseTag string, dryRun, noCache, noPush bool) error { cfg, err := NewConfig(log, targetEnv, awsCredentials) if err != nil { return err } targetFunc, err := NewFunction(functionName, cfg) if err != nil { return err } // Override the release tag if set. if releaseTag != "" { targetFunc.ReleaseTag = releaseTag } // Append build args to be used for all functions. if targetFunc.DockerBuildArgs == nil { targetFunc.DockerBuildArgs = make(map[string]string) } // funcPath is used to copy the service specific code in the Dockerfile. codePath, err := filepath.Rel(cfg.ProjectRoot, targetFunc.CodeDir) if err != nil { return err } targetFunc.DockerBuildArgs["code_path"] = codePath // commitRef is used by main.go:build constant. commitRef := getCommitRef() if commitRef == "" { commitRef = targetFunc.ReleaseTag } targetFunc.DockerBuildArgs["commit_ref"] = commitRef 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(targetFunc, "", " ") if err != nil { log.Fatalf("BuildFunctionForTargetEnv : Marshalling details to JSON : %+v", err) } log.Printf("BuildFunctionForTargetEnv : details : %v\n", string(detailsJSON)) return nil } return devdeploy.BuildLambdaForTargetEnv(log, cfg, targetFunc, noCache, noPush) } // DeployFunctionForTargetEnv executes the deploy commands for a target function. func DeployFunctionForTargetEnv(log *log.Logger, awsCredentials devdeploy.AwsCredentials, targetEnv Env, functionName, releaseTag string, dryRun bool) error { cfg, err := NewConfig(log, targetEnv, awsCredentials) if err != nil { return err } targetFunc, err := NewFunction(functionName, cfg) if err != nil { return err } // Override the release tag if set. if releaseTag != "" { targetFunc.ReleaseTag = releaseTag } if dryRun { cfgJSON, err := json.MarshalIndent(cfg, "", " ") if err != nil { log.Fatalf("DeployFunctionForTargetEnv : Marshalling config to JSON : %+v", err) } log.Printf("DeployFunctionForTargetEnv : config : %v\n", string(cfgJSON)) detailsJSON, err := json.MarshalIndent(targetFunc, "", " ") if err != nil { log.Fatalf("DeployFunctionForTargetEnv : Marshalling details to JSON : %+v", err) } log.Printf("DeployFunctionForTargetEnv : details : %v\n", string(detailsJSON)) return nil } return devdeploy.DeployLambdaToTargetEnv(log, cfg, targetFunc) }