2019-08-21 14:31:28 -08:00
package config
import (
"context"
"fmt"
2019-08-22 19:40:34 -08:00
"io/ioutil"
2019-08-21 14:31:28 -08:00
"log"
"os"
"path/filepath"
"strings"
2019-08-22 19:40:34 -08:00
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
2019-08-21 19:28:23 -08:00
"geeks-accelerator/oss/saas-starter-kit/internal/schema"
2019-08-21 14:31:28 -08:00
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/cloudfront"
"github.com/aws/aws-sdk-go/service/rds"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/secretsmanager"
"github.com/iancoleman/strcase"
"github.com/jmoiron/sqlx"
"github.com/pkg/errors"
"gitlab.com/geeks-accelerator/oss/devops/pkg/devdeploy"
)
const (
// ProjectNamePrefix will be appending to the name of the project.
ProjectNamePrefix = ""
// GitLabProjectBaseUrl is the base url used to create links to a specific CI/CD job or pipeline by ID.
GitLabProjectBaseUrl = "https://gitlab.com/geeks-accelerator/oss/saas-starter-kit"
2019-08-22 18:22:46 -08:00
// EnableRdsServerless will use the Aurora database engine that scales the capacity based on database load. This is
// a good option for intermittent or unpredictable workloads.
EnableRdsServerless = true
2019-08-21 14:31:28 -08:00
)
// Env defines the target deployment environment.
type Env = string
var (
2019-08-21 17:32:51 -08:00
EnvDev Env = webcontext . Env_Dev
2019-08-21 19:28:23 -08:00
EnvStage Env = webcontext . Env_Stage
2019-08-21 17:32:51 -08:00
EnvProd Env = webcontext . Env_Prod
2019-08-21 14:31:28 -08:00
)
// List of env names used by main.go for help.
2019-08-21 17:32:51 -08:00
var EnvNames = [ ] Env {
2019-08-21 14:31:28 -08:00
EnvDev ,
EnvStage ,
EnvProd ,
}
// ConfigContext defines the flags for build env.
type ConfigContext struct {
// Env is the target environment used for the deployment.
Env string ` validate:"oneof=dev stage prod" `
// AwsCredentials defines the credentials used for deployment.
AwsCredentials devdeploy . AwsCredentials ` validate:"required,dive,required" `
}
// NewConfigContext returns the ConfigContext.
func NewConfigContext ( targetEnv Env , awsCredentials devdeploy . AwsCredentials ) ( * ConfigContext , error ) {
ctx := & ConfigContext {
Env : targetEnv ,
AwsCredentials : awsCredentials ,
}
// If AWS Credentials are not set and use role is not enabled, try to load the credentials from env vars.
if ctx . AwsCredentials . UseRole == false && ctx . AwsCredentials . AccessKeyID == "" {
var err error
ctx . AwsCredentials , err = devdeploy . GetAwsCredentialsFromEnv ( ctx . Env )
if err != nil {
return nil , err
}
} else if ctx . AwsCredentials . Region == "" {
awsCreds , err := devdeploy . GetAwsCredentialsFromEnv ( ctx . Env )
if err != nil {
return nil , err
}
ctx . AwsCredentials . Region = awsCreds . Region
}
return ctx , nil
}
// Config defines the details to setup the target environment for the project to build services and functions.
func ( cfgCtx * ConfigContext ) Config ( log * log . Logger ) ( * devdeploy . Config , error ) {
// Init a new build target environment for the project.
cfg := & devdeploy . Config {
Env : cfgCtx . Env ,
AwsCredentials : cfgCtx . AwsCredentials ,
}
// Get the current working directory. This should be somewhere contained within the project.
workDir , err := os . Getwd ( )
if err != nil {
2019-08-22 19:40:34 -08:00
return cfg , errors . Wrap ( err , "Failed to get current working directory." )
2019-08-21 14:31:28 -08:00
}
// Set the project root directory and project name. This is current set by finding the go.mod file for the project
// repo. Project name is the directory name.
modDetails , err := devdeploy . LoadModuleDetails ( workDir )
if err != nil {
return cfg , err
}
// ProjectRoot should be the root directory for the project.
cfg . ProjectRoot = modDetails . ProjectRoot
// ProjectName will be used for prefixing AWS resources. This could be changed as needed or manually defined.
cfg . ProjectName = ProjectNamePrefix + modDetails . ProjectName
2019-08-22 18:22:46 -08:00
// In a verbatim fork of the repo, a CI/CD would fail due to a conflict creating AWS resources (such as S3) since
// their name is calculated with the go.mod path. Since the name-scope of AWS resources is region/global scope,
// it will fail to create appropriate resources for the account of the forked user.
if cfg . ProjectName == "saas-starter-kit" {
2019-08-22 23:15:47 -08:00
remoteUser := gitRemoteUser ( modDetails . ProjectRoot )
log . Println ( "cfg.ProjectName " , cfg . ProjectName )
log . Println ( "remoteUser " , remoteUser )
2019-08-22 19:40:34 -08:00
// Its a true fork from the origin repo.
2019-08-22 23:15:47 -08:00
if remoteUser != "geeks-accelerator" && remoteUser != "oss" {
2019-08-22 19:40:34 -08:00
// Replace the prefix 'saas' with the parent directory name, hopefully the gitlab group/username.
cfg . ProjectName = filepath . Base ( filepath . Dir ( cfg . ProjectRoot ) ) + "-starter-kit"
2019-08-22 23:15:47 -08:00
log . Println ( "switching project name to " , cfg . ProjectName )
2019-08-22 19:40:34 -08:00
}
2019-08-22 18:22:46 -08:00
}
2019-08-21 14:31:28 -08:00
// Set default AWS ECR Repository Name.
cfg . AwsEcrRepository = & devdeploy . AwsEcrRepository {
RepositoryName : cfg . ProjectName ,
Tags : [ ] devdeploy . Tag {
{ Key : devdeploy . AwsTagNameProject , Value : cfg . ProjectName } ,
{ Key : devdeploy . AwsTagNameEnv , Value : cfg . Env } ,
} ,
}
// Set the deployment to use the default VPC for the region.
cfg . AwsEc2Vpc = & devdeploy . AwsEc2Vpc {
IsDefault : true ,
}
// Set the security group to use for the deployed services, database and cluster. This will used the VPC ID defined
// for the deployment.
cfg . AwsEc2SecurityGroup = & devdeploy . AwsEc2SecurityGroup {
GroupName : cfg . ProjectName + "-" + cfg . Env ,
Description : fmt . Sprintf ( "Security group for %s services running on ECS" , cfg . ProjectName ) ,
Tags : [ ] devdeploy . Tag {
{ Key : devdeploy . AwsTagNameProject , Value : cfg . ProjectName } ,
{ Key : devdeploy . AwsTagNameEnv , Value : cfg . Env } ,
} ,
}
// Set the name of the EC2 Security Group used by the gitlab runner. This is used to ensure the security
// group defined above has access to the RDS cluster/instance and can thus handle schema migrations.
cfg . GitlabRunnerEc2SecurityGroupName = "gitlab-runner"
// Set the s3 buckets used by the deployed services.
// S3 temp prefix used by services for short term storage. A lifecycle policy will be used for expiration.
s3BucketTempPrefix := "tmp/"
// Defines a life cycle policy to expire keys for the temp directory.
bucketLifecycleTempRule := & s3 . LifecycleRule {
ID : aws . String ( "Rule for : " + s3BucketTempPrefix ) ,
Status : aws . String ( "Enabled" ) ,
Filter : & s3 . LifecycleRuleFilter {
Prefix : aws . String ( s3BucketTempPrefix ) ,
} ,
Expiration : & s3 . LifecycleExpiration {
// Indicates the lifetime, in days, of the objects that are subject to the rule.
// The value must be a non-zero positive integer.
Days : aws . Int64 ( 1 ) ,
} ,
// Specifies the days since the initiation of an incomplete multipart upload
// that Amazon S3 will wait before permanently removing all parts of the upload.
// For more information, see Aborting Incomplete Multipart Uploads Using a Bucket
// Lifecycle Policy (https://docs.aws.amazon.com/AmazonS3/latest/dev/mpuoverview.html#mpu-abort-incomplete-mpu-lifecycle-config)
// in the Amazon Simple Storage Service Developer Guide.
AbortIncompleteMultipartUpload : & s3 . AbortIncompleteMultipartUpload {
DaysAfterInitiation : aws . Int64 ( 1 ) ,
} ,
}
// Define the public S3 bucket used to serve static files for all the services.
cfg . AwsS3BucketPublic = & devdeploy . AwsS3Bucket {
BucketName : cfg . ProjectName + "-public" ,
IsPublic : true ,
TempPrefix : s3BucketTempPrefix ,
LocationConstraint : & cfg . AwsCredentials . Region ,
LifecycleRules : [ ] * s3 . LifecycleRule { bucketLifecycleTempRule } ,
CORSRules : [ ] * s3 . CORSRule {
& s3 . CORSRule {
// Headers that are specified in the Access-Control-Request-Headers header.
// These headers are allowed in a preflight OPTIONS request. In response to
// any preflight OPTIONS request, Amazon S3 returns any requested headers that
// are allowed.
// AllowedHeaders: aws.StringSlice([]string{}),
// An HTTP method that you allow the origin to execute. Valid values are GET,
// PUT, HEAD, POST, and DELETE.
//
// AllowedMethods is a required field
AllowedMethods : aws . StringSlice ( [ ] string { "GET" , "POST" } ) ,
// One or more origins you want customers to be able to access the bucket from.
//
// AllowedOrigins is a required field
AllowedOrigins : aws . StringSlice ( [ ] string { "*" } ) ,
// One or more headers in the response that you want customers to be able to
// access from their applications (for example, from a JavaScript XMLHttpRequest
// object).
// ExposeHeaders: aws.StringSlice([]string{}),
// The time in seconds that your browser is to cache the preflight response
// for the specified resource.
// MaxAgeSeconds: aws.Int64(),
} ,
} ,
}
// The base s3 key prefix used to upload static files.
cfg . AwsS3BucketPublicKeyPrefix = "/public"
// For production, enable Cloudfront CDN for all static files to avoid serving them from the slower S3 option.
if cfg . Env == EnvProd {
cfg . AwsS3BucketPublic . CloudFront = & devdeploy . AwsS3BucketCloudFront {
// S3 key prefix to request your content from a directory in your Amazon S3 bucket.
OriginPath : cfg . AwsS3BucketPublicKeyPrefix ,
// A complex type that controls whether CloudFront caches the response to requests.
CachedMethods : [ ] string { "HEAD" , "GET" } ,
// The distribution's configuration information.
DistributionConfig : & cloudfront . DistributionConfig {
Comment : aws . String ( "" ) ,
Enabled : aws . Bool ( true ) ,
HttpVersion : aws . String ( "http2" ) ,
IsIPV6Enabled : aws . Bool ( true ) ,
DefaultCacheBehavior : & cloudfront . DefaultCacheBehavior {
Compress : aws . Bool ( true ) ,
DefaultTTL : aws . Int64 ( 1209600 ) ,
MinTTL : aws . Int64 ( 604800 ) ,
MaxTTL : aws . Int64 ( 31536000 ) ,
ForwardedValues : & cloudfront . ForwardedValues {
QueryString : aws . Bool ( true ) ,
Cookies : & cloudfront . CookiePreference {
Forward : aws . String ( "none" ) ,
} ,
} ,
TrustedSigners : & cloudfront . TrustedSigners {
Enabled : aws . Bool ( false ) ,
Quantity : aws . Int64 ( 0 ) ,
} ,
ViewerProtocolPolicy : aws . String ( "allow-all" ) ,
} ,
ViewerCertificate : & cloudfront . ViewerCertificate {
CertificateSource : aws . String ( "cloudfront" ) ,
MinimumProtocolVersion : aws . String ( "TLSv1" ) ,
CloudFrontDefaultCertificate : aws . Bool ( true ) ,
} ,
PriceClass : aws . String ( "PriceClass_All" ) ,
CallerReference : aws . String ( "devops-deploy" + cfg . AwsS3BucketPublic . BucketName ) ,
} ,
}
}
// Define the private S3 bucket used for long term file storage including but not limited to: log exports,
// AWS Lambda code, application caching.
cfg . AwsS3BucketPrivate = & devdeploy . AwsS3Bucket {
BucketName : cfg . ProjectName + "-private" ,
IsPublic : false ,
TempPrefix : s3BucketTempPrefix ,
LocationConstraint : & cfg . AwsCredentials . Region ,
LifecycleRules : [ ] * s3 . LifecycleRule { bucketLifecycleTempRule } ,
PublicAccessBlock : & s3 . PublicAccessBlockConfiguration {
// Specifies whether Amazon S3 should block public access control lists (ACLs)
// for this bucket and objects in this bucket. Setting this element to TRUE
// causes the following behavior:
//
// * PUT Bucket acl and PUT Object acl calls fail if the specified ACL is
// public.
//
// * PUT Object calls fail if the request includes a public ACL.
//
// Enabling this setting doesn't affect existing policies or ACLs.
BlockPublicAcls : aws . Bool ( true ) ,
// Specifies whether Amazon S3 should block public bucket policies for this
// bucket. Setting this element to TRUE causes Amazon S3 to reject calls to
// PUT Bucket policy if the specified bucket policy allows public access.
//
// Enabling this setting doesn't affect existing bucket policies.
BlockPublicPolicy : aws . Bool ( true ) ,
// Specifies whether Amazon S3 should restrict public bucket policies for this
// bucket. Setting this element to TRUE restricts access to this bucket to only
// AWS services and authorized users within this account if the bucket has a
// public policy.
//
// Enabling this setting doesn't affect previously stored bucket policies, except
// that public and cross-account access within any public bucket policy, including
// non-public delegation to specific accounts, is blocked.
RestrictPublicBuckets : aws . Bool ( true ) ,
// Specifies whether Amazon S3 should ignore public ACLs for this bucket and
// objects in this bucket. Setting this element to TRUE causes Amazon S3 to
// ignore all public ACLs on this bucket and objects in this bucket.
//
// Enabling this setting doesn't affect the persistence of any existing ACLs
// and doesn't prevent new public ACLs from being set.
IgnorePublicAcls : aws . Bool ( true ) ,
} ,
}
// Add a bucket policy to enable exports from Cloudwatch Logs for the private S3 bucket.
cfg . AwsS3BucketPrivate . Policy = func ( ) string {
policyResource := strings . Trim ( filepath . Join ( cfg . AwsS3BucketPrivate . BucketName , cfg . AwsS3BucketPrivate . TempPrefix ) , "/" )
return fmt . Sprintf ( ` {
"Version" : "2012-10-17" ,
"Statement" : [
{
"Action" : "s3:GetBucketAcl" ,
"Effect" : "Allow" ,
"Resource" : "arn:aws:s3:::%s" ,
"Principal" : { "Service" : "logs.%s.amazonaws.com" }
} ,
{
"Action" : "s3:PutObject" ,
"Effect" : "Allow" ,
"Resource" : "arn:aws:s3:::%s/*" ,
"Condition" : { "StringEquals" : { "s3:x-amz-acl" : "bucket-owner-full-control" } } ,
"Principal" : { "Service" : "logs.%s.amazonaws.com" }
}
]
} ` , cfg . AwsS3BucketPrivate . BucketName , cfg . AwsCredentials . Region , policyResource , cfg . AwsCredentials . Region )
} ( )
// Define the Redis Cache cluster used for ephemeral storage.
cfg . AwsElasticCacheCluster = & devdeploy . AwsElasticCacheCluster {
CacheClusterId : cfg . ProjectName + "-" + cfg . Env ,
CacheNodeType : "cache.t2.micro" ,
CacheSubnetGroupName : "default" ,
Engine : "redis" ,
EngineVersion : "5.0.4" ,
NumCacheNodes : 1 ,
Port : 6379 ,
AutoMinorVersionUpgrade : aws . Bool ( true ) ,
SnapshotRetentionLimit : aws . Int64 ( 7 ) ,
ParameterNameValues : [ ] devdeploy . AwsElasticCacheParameter {
devdeploy . AwsElasticCacheParameter {
ParameterName : "maxmemory-policy" ,
ParameterValue : "allkeys-lru" ,
} ,
} ,
}
2019-08-22 18:22:46 -08:00
// If serverless RDS is enabled, defined the RDS database cluster and link it to the database instance.
if EnableRdsServerless {
cfg . AwsRdsDBCluster = & devdeploy . AwsRdsDBCluster {
DBClusterIdentifier : cfg . ProjectName + "-" + cfg . Env ,
Engine : "aurora-postgresql" ,
EngineMode : "serverless" ,
DatabaseName : "shared" ,
MasterUsername : "god" ,
Port : 5432 ,
BackupRetentionPeriod : aws . Int64 ( 7 ) ,
CopyTagsToSnapshot : aws . Bool ( true ) ,
Tags : [ ] devdeploy . Tag {
{ Key : devdeploy . AwsTagNameProject , Value : cfg . ProjectName } ,
{ Key : devdeploy . AwsTagNameEnv , Value : cfg . Env } ,
} ,
PreCreate : func ( input * rds . CreateDBClusterInput ) error {
input . ScalingConfiguration = & rds . ScalingConfiguration {
// A value that indicates whether to allow or disallow automatic pause for an
// Aurora DB cluster in serverless DB engine mode. A DB cluster can be paused
// only when it's idle (it has no connections).
//
// If a DB cluster is paused for more than seven days, the DB cluster might
// be backed up with a snapshot. In this case, the DB cluster is restored when
// there is a request to connect to it.
AutoPause : aws . Bool ( true ) ,
// The maximum capacity for an Aurora DB cluster in serverless DB engine mode.
// Valid capacity values are 1, 2, 4, 8, 16, 32, 64, 128, and 256.
// The maximum capacity must be greater than or equal to the minimum capacity.
MaxCapacity : aws . Int64 ( 2 ) ,
// The minimum capacity for an Aurora DB cluster in serverless DB engine mode.
// Valid capacity values are 1, 2, 4, 8, 16, 32, 64, 128, and 256.
// The minimum capacity must be less than or equal to the maximum capacity.
MinCapacity : aws . Int64 ( 2 ) ,
// The time, in seconds, before an Aurora DB cluster in serverless mode is paused.
SecondsUntilAutoPause : aws . Int64 ( 3600 ) ,
// The action to take when the timeout is reached, either ForceApplyCapacityChange
// or RollbackCapacityChange.
// ForceApplyCapacityChange sets the capacity to the specified value as soon
// as possible.
// RollbackCapacityChange, the default, ignores the capacity change if a scaling
// point is not found in the timeout period.
// If you specify ForceApplyCapacityChange, connections that prevent Aurora
// Serverless from finding a scaling point might be dropped.
// For more information, see Autoscaling for Aurora Serverless (https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-serverless.how-it-works.html#aurora-serverless.how-it-works.auto-scaling)
// in the Amazon Aurora User Guide.
TimeoutAction : aws . String ( "ForceApplyCapacityChange" ) ,
}
return nil
} ,
2019-08-22 19:40:34 -08:00
AfterCreate : func ( res * rds . DBCluster , dbInfo * devdeploy . DBConnInfo , masterDb * sqlx . DB ) error {
2019-08-22 18:22:46 -08:00
return schema . Migrate ( context . Background ( ) , cfg . Env , masterDb , log , false )
} ,
}
} else {
// Define the RDS database instance for transactional data. A random password will be generated for any created instance.
cfg . AwsRdsDBInstance = & devdeploy . AwsRdsDBInstance {
DBInstanceIdentifier : cfg . ProjectName + "-" + cfg . Env ,
DBName : "shared" ,
Engine : "postgres" ,
MasterUsername : "god" ,
Port : 5432 ,
DBInstanceClass : "db.t2.small" ,
AllocatedStorage : 20 ,
PubliclyAccessible : false ,
BackupRetentionPeriod : aws . Int64 ( 7 ) ,
AutoMinorVersionUpgrade : true ,
CopyTagsToSnapshot : aws . Bool ( true ) ,
Tags : [ ] devdeploy . Tag {
{ Key : devdeploy . AwsTagNameProject , Value : cfg . ProjectName } ,
{ Key : devdeploy . AwsTagNameEnv , Value : cfg . Env } ,
} ,
2019-08-22 19:40:34 -08:00
AfterCreate : func ( res * rds . DBInstance , dbInfo * devdeploy . DBConnInfo , masterDb * sqlx . DB ) error {
2019-08-22 18:22:46 -08:00
return schema . Migrate ( context . Background ( ) , cfg . Env , masterDb , log , false )
} ,
}
2019-08-21 14:31:28 -08:00
}
// AwsIamPolicy defines the name and policy that will be attached to the task role. The policy document grants
// the permissions required for deployed services to access AWS services. If the policy already exists, the
// statements will be used to add new required actions, but not for removal.
cfg . AwsIamPolicy = & devdeploy . AwsIamPolicy {
PolicyName : fmt . Sprintf ( "%s%sServices" , cfg . ProjectNameCamel ( ) , strcase . ToCamel ( cfg . Env ) ) ,
Description : fmt . Sprintf ( "Defines access for %s services. " , cfg . ProjectName ) ,
PolicyDocument : devdeploy . AwsIamPolicyDocument {
Version : "2012-10-17" ,
Statement : [ ] devdeploy . AwsIamStatementEntry {
{
Sid : "DefaultServiceAccess" ,
Effect : "Allow" ,
Action : [ ] string {
"s3:HeadBucket" ,
"s3:ListObjects" ,
"s3:PutObject" ,
"s3:PutObjectAcl" ,
"cloudfront:ListDistributions" ,
"ec2:DescribeNetworkInterfaces" ,
"ec2:DeleteNetworkInterface" ,
"ecs:ListTasks" ,
"ecs:DescribeServices" ,
"ecs:DescribeTasks" ,
"ec2:DescribeNetworkInterfaces" ,
"route53:ListHostedZones" ,
"route53:ListResourceRecordSets" ,
"route53:ChangeResourceRecordSets" ,
"ecs:UpdateService" ,
"ses:SendEmail" ,
"ses:ListIdentities" ,
2019-08-22 00:46:20 -08:00
"ses:GetAccountSendingEnabled" ,
2019-08-21 14:31:28 -08:00
"secretsmanager:ListSecretVersionIds" ,
"secretsmanager:GetSecretValue" ,
"secretsmanager:CreateSecret" ,
"secretsmanager:UpdateSecret" ,
"secretsmanager:RestoreSecret" ,
"secretsmanager:DeleteSecret" ,
} ,
Resource : "*" ,
} ,
{
Sid : "ServiceInvokeLambda" ,
Effect : "Allow" ,
Action : [ ] string {
"iam:GetRole" ,
"lambda:InvokeFunction" ,
"lambda:ListVersionsByFunction" ,
"lambda:GetFunction" ,
"lambda:InvokeAsync" ,
"lambda:GetFunctionConfiguration" ,
"iam:PassRole" ,
"lambda:GetAlias" ,
"lambda:GetPolicy" ,
} ,
Resource : [ ] string {
"arn:aws:iam:::role/*" ,
"arn:aws:lambda:::function:*" ,
} ,
} ,
{
Sid : "datadoglambda" ,
Effect : "Allow" ,
Action : [ ] string {
"cloudwatch:Get*" ,
"cloudwatch:List*" ,
"ec2:Describe*" ,
"support:*" ,
"tag:GetResources" ,
"tag:GetTagKeys" ,
"tag:GetTagValues" ,
} ,
Resource : "*" ,
} ,
} ,
} ,
}
log . Printf ( "\t\tSet Task Policy Name to '%s'." , cfg . AwsIamPolicy . PolicyName )
return cfg , nil
}
// getDatadogApiKey tries to find the datadog api key from env variable or AWS Secrets Manager.
func getDatadogApiKey ( cfg * devdeploy . Config ) ( string , error ) {
// Load Datadog API key which can be either stored in an environment variable or in AWS Secrets Manager.
// 1. Check env vars for [DEV|STAGE|PROD]_DD_API_KEY and DD_API_KEY
apiKey := devdeploy . GetTargetEnv ( cfg . Env , "DD_API_KEY" )
// 2. Check AWS Secrets Manager for datadog entry prefixed with target environment.
if apiKey == "" {
prefixedSecretId := cfg . SecretID ( "datadog" )
var err error
apiKey , err = devdeploy . GetAwsSecretValue ( cfg . AwsCredentials , prefixedSecretId )
if err != nil {
if aerr , ok := errors . Cause ( err ) . ( awserr . Error ) ; ! ok || aerr . Code ( ) != secretsmanager . ErrCodeResourceNotFoundException {
return "" , err
}
}
}
// 3. Check AWS Secrets Manager for Datadog entry.
if apiKey == "" {
secretId := "DATADOG"
var err error
apiKey , err = devdeploy . GetAwsSecretValue ( cfg . AwsCredentials , secretId )
if err != nil {
if aerr , ok := errors . Cause ( err ) . ( awserr . Error ) ; ! ok || aerr . Code ( ) != secretsmanager . ErrCodeResourceNotFoundException {
return "" , err
}
}
}
return apiKey , nil
}
// getCommitRef returns a string that will be used by go build to replace main.go:build constant.
func getCommitRef ( ) string {
var commitRef string
// Set the commit ref based on the GitLab CI/CD environment variables.
if ev := os . Getenv ( "CI_COMMIT_TAG" ) ; ev != "" {
commitRef = "tag-" + ev
} else if ev := os . Getenv ( "CI_COMMIT_REF_NAME" ) ; ev != "" {
commitRef = "branch-" + ev
}
if commitRef != "" {
if ev := os . Getenv ( "CI_COMMIT_SHORT_SHA" ) ; ev != "" {
commitRef = commitRef + "@" + ev
}
}
return commitRef
}
2019-08-22 19:40:34 -08:00
// gitRemoteUser returns the git username/organization for the git repo
func gitRemoteUser ( projectRoot string ) string {
dat , err := ioutil . ReadFile ( filepath . Join ( projectRoot , ".git/config" ) )
if err != nil {
return ""
}
var remoteUrl string
lines := strings . Split ( string ( dat ) , "\n" )
for _ , l := range lines {
l = strings . TrimSpace ( l )
if strings . HasPrefix ( l , "url =" ) {
remoteUrl = l
break
}
}
if remoteUrl == "" {
return ""
}
remoteUrl = strings . TrimSpace ( strings . Split ( remoteUrl , "=" ) [ 1 ] )
if ! strings . Contains ( remoteUrl , ":" ) {
return ""
}
remoteUser := strings . Split ( remoteUrl , ":" ) [ 1 ]
remoteUser = strings . Split ( remoteUser , "/" ) [ 0 ]
return remoteUser
}