diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 58df7ca..9a8e6ee 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -87,8 +87,8 @@ webapp:deploy:dev: SERVICE: 'web-app' ENABLE_HTTPS: 1 ENABLE_ELB: 0 - PRIMARY_HOST: 'eproc.tech' - HOST_NAMES: 'www.eproc.tech,dev.eproc.tech' + PRIMARY_HOST: 'example.saasstartupkit.com' + HOST_NAMES: 'example.saasstartupkit.com,dev.example.saasstartupkit.com' S3_BUCKET_PRIVATE: 'saas-starter-kit-private' S3_BUCKET_PUBLIC: 'saas-starter-kit-public' S3_BUCKET_PUBLIC_CLOUDFRONT: 'true' @@ -96,7 +96,7 @@ webapp:deploy:dev: STATIC_FILES_IMG_RESIZE: 'true' AWS_USE_ROLE: 'true' EMAIL_SENDER: 'lee+saas-starter-kit@geeksinthewoods.com' - WEB_API_BASE_URL: https://api.eproc.tech + WEB_API_BASE_URL: https://api.example.saasstartupkit.com webapi:build:dev: <<: *build_tmpl @@ -128,8 +128,8 @@ webapi:deploy:dev: SERVICE: 'web-api' ENABLE_HTTPS: 1 ENABLE_ELB: 0 - PRIMARY_HOST: 'api.eproc.tech' - HOST_NAMES: 'api.dev.eproc.tech' + PRIMARY_HOST: 'api.example.saasstartupkit.com' + HOST_NAMES: 'api.dev.example.saasstartupkit.com' S3_BUCKET_PRIVATE: 'saas-starter-kit-private' S3_BUCKET_PUBLIC: 'saas-starter-kit-public' S3_BUCKET_PUBLIC_CLOUDFRONT: 'false' @@ -137,7 +137,7 @@ webapi:deploy:dev: STATIC_FILES_IMG_RESIZE: 'false' AWS_USE_ROLE: 'true' EMAIL_SENDER: 'lee+saas-starter-kit@geeksinthewoods.com' - WEB_APP_BASE_URL: https://eproc.tech + WEB_APP_BASE_URL: https://example.saasstartupkit.com #ddlogscollector:deploy:stage: # <<: *deploy_stage_tmpl diff --git a/cmd/web-api/main.go b/cmd/web-api/main.go index 0a1b2b9..49595c8 100644 --- a/cmd/web-api/main.go +++ b/cmd/web-api/main.go @@ -87,11 +87,11 @@ func main() { Service struct { Name string `default:"web-api" envconfig:"NAME"` Project string `default:"" envconfig:"PROJECT"` - BaseUrl string `default:"" envconfig:"BASE_URL" example:"http://api.eproc.tech"` - HostNames []string `envconfig:"HOST_NAMES" example:"alternative-subdomain.eproc.tech"` + BaseUrl string `default:"" envconfig:"BASE_URL" example:"http://api.example.saasstartupkit.com"` + HostNames []string `envconfig:"HOST_NAMES" example:"alternative-subdomain.example.saasstartupkit.com"` EnableHTTPS bool `default:"false" envconfig:"ENABLE_HTTPS"` TemplateDir string `default:"./templates" envconfig:"TEMPLATE_DIR"` - WebAppBaseUrl string `default:"http://127.0.0.1:3000" envconfig:"WEB_APP_BASE_URL" example:"www.eproc.tech"` + WebAppBaseUrl string `default:"http://127.0.0.1:3000" envconfig:"WEB_APP_BASE_URL" example:"www.example.saasstartupkit.com"` DebugHost string `default:"0.0.0.0:4000" envconfig:"DEBUG_HOST"` ShutdownTimeout time.Duration `default:"5s" envconfig:"SHUTDOWN_TIMEOUT"` } diff --git a/cmd/web-app/main.go b/cmd/web-app/main.go index 26d52d0..346acce 100644 --- a/cmd/web-app/main.go +++ b/cmd/web-app/main.go @@ -87,8 +87,8 @@ func main() { Service struct { Name string `default:"web-app" envconfig:"NAME"` Project string `default:"" envconfig:"PROJECT"` - BaseUrl string `default:"" envconfig:"BASE_URL" example:"http://eproc.tech"` - HostNames []string `envconfig:"HOST_NAMES" example:"www.eproc.tech"` + BaseUrl string `default:"" envconfig:"BASE_URL" example:"http://example.saasstartupkit.com"` + HostNames []string `envconfig:"HOST_NAMES" example:"www.example.saasstartupkit.com"` EnableHTTPS bool `default:"false" envconfig:"ENABLE_HTTPS"` TemplateDir string `default:"./templates" envconfig:"TEMPLATE_DIR"` SharedTemplateDir string `default:"../../resources/templates/shared" envconfig:"SHARED_TEMPLATE_DIR"` @@ -99,10 +99,10 @@ func main() { CloudFrontEnabled bool `envconfig:"CLOUDFRONT_ENABLED"` ImgResizeEnabled bool `envconfig:"IMG_RESIZE_ENABLED"` } - WebApiBaseUrl string `default:"http://127.0.0.1:3001" envconfig:"WEB_API_BASE_URL" example:"http://api.eproc.tech"` + WebApiBaseUrl string `default:"http://127.0.0.1:3001" envconfig:"WEB_API_BASE_URL" example:"http://api.example.saasstartupkit.com"` SessionKey string `default:"" envconfig:"SESSION_KEY"` SessionName string `default:"" envconfig:"SESSION_NAME"` - EmailSender string `default:"test@eproc.tech" envconfig:"EMAIL_SENDER"` + EmailSender string `default:"test@example.saasstartupkit.com" envconfig:"EMAIL_SENDER"` DebugHost string `default:"0.0.0.0:4000" envconfig:"DEBUG_HOST"` ShutdownTimeout time.Duration `default:"5s" envconfig:"SHUTDOWN_TIMEOUT"` } @@ -774,6 +774,9 @@ func main() { } imgUrlFormatter = func(p string) string { + if strings.HasPrefix(p, "http") { + return p + } baseUrl.Path = p return baseUrl.String() } diff --git a/internal/platform/tests/main.go b/internal/platform/tests/main.go index df6e747..ca1e53b 100644 --- a/internal/platform/tests/main.go +++ b/internal/platform/tests/main.go @@ -2,8 +2,8 @@ package tests import ( "context" + "encoding/json" "fmt" - "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext" "io" "log" "os" @@ -12,6 +12,11 @@ import ( "testing" "time" + "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/ec2metadata" + "github.com/kelseyhightower/envconfig" "geeks-accelerator/oss/saas-starter-kit/internal/platform/docker" "geeks-accelerator/oss/saas-starter-kit/internal/schema" "github.com/aws/aws-sdk-go/aws/session" @@ -43,10 +48,76 @@ func New() *Test { log := log.New(os.Stdout, "TEST : ", log.LstdFlags|log.Lmicroseconds|log.Lshortfile) + + // ========================================================================= + // Configuration + var cfg struct { + Aws struct { + AccessKeyID string `envconfig:"AWS_ACCESS_KEY_ID"` // WEB_API_AWS_AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY_ID + SecretAccessKey string `envconfig:"AWS_SECRET_ACCESS_KEY" json:"-"` // don't print + Region string `default:"us-west-2" envconfig:"AWS_REGION"` + UseRole bool `envconfig:"AWS_USE_ROLE"` + } + } + + // For additional details refer to https://github.com/kelseyhightower/envconfig + if err := envconfig.Process("TESTS", &cfg); err != nil { + log.Fatalf("startup : Parsing Config : %+v", err) + } + + + // AWS access keys are required, if roles are enabled, remove any placeholders. + if cfg.Aws.UseRole { + cfg.Aws.AccessKeyID = "" + cfg.Aws.SecretAccessKey = "" + + // Get an AWS session from an implicit source if no explicit + // configuration is provided. This is useful for taking advantage of + // EC2/ECS instance roles. + if cfg.Aws.Region == "" { + sess := session.Must(session.NewSession()) + md := ec2metadata.New(sess) + + var err error + cfg.Aws.Region, err = md.Region() + if err != nil { + log.Fatalf("startup : Load region of ecs metadata : %+v", err) + } + } + } + + // Print the config for our logs. It's important to any credentials in the config + // that could expose a security risk are excluded from being json encoded by + // applying the tag `json:"-"` to the struct var. + { + cfgJSON, err := json.MarshalIndent(cfg, "", " ") + if err != nil { + log.Fatalf("startup : Marshalling Config to JSON : %+v", err) + } + log.Printf("startup : Config : %v\n", string(cfgJSON)) + } + + // ============================================================ // Init AWS Session + var awsSession *session.Session + if cfg.Aws.UseRole { + // Get an AWS session from an implicit source if no explicit + // configuration is provided. This is useful for taking advantage of + // EC2/ECS instance roles. + awsSession = session.Must(session.NewSession()) + if cfg.Aws.Region != "" { + awsSession.Config.WithRegion(cfg.Aws.Region) + } + + log.Printf("startup : AWS : Using role.\n") + } else if cfg.Aws.AccessKeyID != "" { + creds := credentials.NewStaticCredentials(cfg.Aws.AccessKeyID, cfg.Aws.SecretAccessKey, "") + awsSession = session.New(&aws.Config{Region: aws.String(cfg.Aws.Region), Credentials: creds}) + + log.Printf("startup : AWS : Using static credentials\n") + } - awsSession := session.Must(session.NewSession()) // ============================================================ // Startup Postgres container diff --git a/sample.env_docker_compose b/sample.env_docker_compose index 17a3992..42d4f3f 100644 --- a/sample.env_docker_compose +++ b/sample.env_docker_compose @@ -4,4 +4,7 @@ #AWS_USE_ROLE=false #DD_API_KEY= #WEB_APP_AWS_S3_BUCKET_PRIVATE= -#WEB_APP_AWS_S3_BUCKET_PUBLIC= \ No newline at end of file +#WEB_APP_AWS_S3_BUCKET_PUBLIC= +#WEB_API_AWS_S3_BUCKET_PRIVATE= +#WEB_API_AWS_S3_BUCKET_PUBLIC= +#EMAIL_SENDER= diff --git a/tools/devops/cmd/cicd/service_deploy.go b/tools/devops/cmd/cicd/service_deploy.go index 39a2bcb..2d09cc7 100644 --- a/tools/devops/cmd/cicd/service_deploy.go +++ b/tools/devops/cmd/cicd/service_deploy.go @@ -507,10 +507,8 @@ func NewServiceDeployRequest(log *log.Logger, flags ServiceDeployFlags) (*servic Sid: "DefaultServiceAccess", Effect: "Allow", Action: []string{ + "s3:ListBucket", "s3:HeadBucket", - "s3:ListObjects", - "s3:PutObject", - "s3:PutObjectAcl", "cloudfront:ListDistributions", "ec2:DescribeNetworkInterfaces", "ec2:DeleteNetworkInterface", @@ -570,6 +568,33 @@ func NewServiceDeployRequest(log *log.Logger, flags ServiceDeployFlags) (*servic }, } + if req.S3BucketPublicName != "" || req.S3BucketPrivateName != "" { + var bpr []string + if req.S3BucketPublicName != "" { + bpr = append(bpr, "arn:aws:s3:::"+req.S3BucketPublicName ) + bpr = append(bpr, "arn:aws:s3:::"+req.S3BucketPublicName + "/*" ) + } + if req.S3BucketPrivateName != "" { + bpr = append(bpr, "arn:aws:s3:::"+req.S3BucketPrivateName ) + bpr = append(bpr, "arn:aws:s3:::"+req.S3BucketPrivateName + "/*" ) + } + + bp := IamStatementEntry{ + Sid: "S3BucketAccess", + Effect: "Allow", + Action: []string{ + "s3:ListObjects", + "s3:PutObject", + "s3:PutObjectAcl", + "s3:GetObject", + "s3:HeadObject", + }, + Resource: bpr, + } + + req.EcsTaskPolicyDocument.Statement = append(req.EcsTaskPolicyDocument.Statement, bp) + } + // Set default Cloudwatch Log Group Name. req.CloudWatchLogGroupName = fmt.Sprintf("logs/env_%s/aws/ecs/cluster_%s/service_%s", req.Env, req.EcsClusterName, req.ServiceName) req.CloudWatchLogGroup = &cloudwatchlogs.CreateLogGroupInput{