1
0
mirror of https://github.com/raseels-repos/golang-saas-starter-kit.git synced 2025-06-15 00:15:15 +02:00

finished inital testing of deployment script

This commit is contained in:
Lee Brown
2019-07-09 02:21:46 -08:00
parent 2f53df6ba6
commit 6851b96a04
7 changed files with 1142 additions and 710 deletions

View File

@ -0,0 +1,76 @@
1. Create new policy `saas-starter-kit-deploy` with the following permissions.
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ServiceDeployPermissions",
"Effect": "Allow",
"Action": [
"cloudwatchlogs:DescribeLogGroups",
"cloudwatchlogs:CreateLogGroup",
"ec2:DescribeSubnets",
"ec2:DescribeSubnets",
"ec2:DescribeSecurityGroups",
"ec2:CreateSecurityGroup",
"ec2:AuthorizeSecurityGroupIngress",
"elasticloadbalancing:DescribeLoadBalancers",
"elasticloadbalancing:CreateLoadBalancer",
"elasticloadbalancing:DescribeTargetGroups",
"elasticloadbalancing:CreateTargetGroup",
"elasticloadbalancing:DescribeListeners",
"elasticloadbalancing:ModifyTargetGroupAttributes",
"ecs:CreateCluster",
"ecs:CreateService",
"ecs:DescribeClusters",
"ecs:DescribeServices",
"ecs:UpdateService",
"ecs:RegisterTaskDefinition",
"ecs:ListTaskDefinitions",
"ecr:BatchDeleteImage",
"ecr:GetAuthorizationToken",
"ecr:DescribeImages",
"ecr:DescribeRepositories",
"ecr:CreateRepository",
"ecr:ListImages",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload",
"logs:DescribeLogGroups",
"logs:CreateLogGroup",
"lambda:ListFunctions",
"lambda:CreateFunction",
"lambda:UpdateFunctionCode",
"lambda:UpdateFunctionConfiguration",
"iam:GetRole",
"iam:PassRole",
"iam:CreateRole",
"iam:CreateServiceLinkedRole",
"iam:CreatePolicy",
"iam:PutRolePolicy",
"iam:TagRole",
"iam:AttachRolePolicy",
"iam:ListPolicies",
"iam:GetPolicyVersion",
"iam:CreatePolicyVersion",
"secretsmanager:ListSecrets",
"secretsmanager:GetSecretValue"
],
"Resource": "*"
}
]
}
```
2. Create new user `saas-starter-kit-deploy` with _Programmatic Access_ and _Attach existing policies directly_ with the policy created from step 1 `saas-starter-kit-deploy`
3. Try running the deploy
```bash
go run main.go deploy -service=web-api -env=dev
```

View File

@ -4,13 +4,13 @@ import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/pkg/errors"
)
// findProjectGoModFile finds the project root directory from the current working directory.
func findProjectGoModFile() (string, error) {
var err error
projectRoot, err := os.Getwd()
@ -76,7 +76,7 @@ func getTargetEnv(targetEnv, envName string) string {
if v := os.Getenv(k); v != "" {
// Set the non prefixed env var with the prefixed value.
os.Setenv(envName, v )
os.Setenv(envName, v)
return v
}
@ -84,7 +84,7 @@ func getTargetEnv(targetEnv, envName string) string {
}
// loadGoModName parses out the module name from go.mod.
func loadGoModName(goModFile string ) (string, error) {
func loadGoModName(goModFile string) (string, error) {
ok, err := exists(goModFile)
if err != nil {
return "", errors.WithMessage(err, "Failed to load go.mod for project")
@ -94,7 +94,7 @@ func loadGoModName(goModFile string ) (string, error) {
b, err := ioutil.ReadFile(goModFile)
if err != nil {
return"", errors.WithMessagef(err, "Failed to read go.mod at %s", goModFile)
return "", errors.WithMessagef(err, "Failed to read go.mod at %s", goModFile)
}
var name string
@ -121,6 +121,7 @@ func exists(path string) (bool, error) {
return true, err
}
/*
type EnvVars []string
// execCmds executes a set of commands.
@ -136,8 +137,11 @@ func execCmds(workDir string, envVars *EnvVars, cmds ...[]string) ([]string, err
cmd.Dir = workDir
cmd.Env = *envVars
out, err := cmd.CombinedOutput()
fmt.Println(string(out ))
if err != nil {
return results, errors.WithMessagef(err, "failed to execute %s - %s\n%s", strings.Join(cmdVals, " "), string(out))
return results, errors.WithMessagef(err, "failed to execute %s\n%s", strings.Join(cmdVals, " "), string(out))
}
results = append(results, string(out))
@ -147,4 +151,5 @@ func execCmds(workDir string, envVars *EnvVars, cmds ...[]string) ([]string, err
}
return results, nil
}
}
*/

View File

@ -1,85 +1,95 @@
package devops
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/iancoleman/strcase"
"github.com/urfave/cli"
)
// ServiceDeployFlags defines the flags used for executing a service deployment.
type ServiceDeployFlags struct {
// Required flags.
ServiceName string `validate:"required" example:"web-api"`
Env string `validate:"oneof=dev stage prod" example:"dev"`
ServiceName string `validate:"required" example:"web-api"`
Env string `validate:"oneof=dev stage prod" example:"dev"`
// Optional flags.
ProjectRoot string `validate:"omitempty" example:"."`
ProjectName string ` validate:"omitempty" example:"example-project"`
DockerFile string `validate:"omitempty" example:"./cmd/web-api/Dockerfile"`
EnableLambdaVPC bool `validate:"omitempty" example:"false"`
EnableEcsElb bool `validate:"omitempty" example:"false"`
NoBuild bool `validate:"omitempty" example:"false"`
NoDeploy bool `validate:"omitempty" example:"false"`
NoCache bool `validate:"omitempty" example:"false"`
NoPush bool `validate:"omitempty" example:"false"`
EnableHTTPS bool `validate:"omitempty" example:"false"`
ServiceDomainName string `validate:"omitempty" example:"example-project.com"`
ServiceDomainNameAliases cli.StringSlice `validate:"omitempty" example:"subdomain.example-project.com"`
ProjectRoot string `validate:"omitempty" example:"."`
ProjectName string ` validate:"omitempty" example:"example-project"`
DockerFile string `validate:"omitempty" example:"./cmd/web-api/Dockerfile"`
EnableLambdaVPC bool `validate:"omitempty" example:"false"`
EnableEcsElb bool `validate:"omitempty" example:"false"`
NoBuild bool `validate:"omitempty" example:"false"`
NoDeploy bool `validate:"omitempty" example:"false"`
NoCache bool `validate:"omitempty" example:"false"`
NoPush bool `validate:"omitempty" example:"false"`
RecreateService bool `validate:"omitempty" example:"false"`
}
// serviceDeployRequest defines the details needed to execute a service deployment.
type serviceDeployRequest struct {
// Required flags.
serviceName string `validate:"required"`
serviceDir string `validate:"required"`
env string `validate:"oneof=dev stage prod"`
projectRoot string `validate:"required"`
projectName string `validate:"required"`
dockerFile string `validate:"required"`
goModFile string `validate:"required"`
goModName string `validate:"required"`
ecrRepositoryName string `validate:"required"`
ecsClusterName string `validate:"required"`
ecsServiceName string `validate:"required"`
ecsExecutionRoleArn string `validate:"required"`
ecsTaskRoleArn string `validate:"required"`
ecsServiceDesiredCount int64 `validate:"required"`
ec2SecurityGroupName string `validate:"required"`
elbLoadBalancerName string `validate:"required"`
cloudWatchLogGroupName string `validate:"required"`
releaseImage string `validate:"required"`
buildTags []string `validate:"required"`
awsCreds *awsCredentials `validate:"required,dive"`
ServiceName string `validate:"required"`
ServiceDir string `validate:"required"`
Env string `validate:"oneof=dev stage prod"`
ProjectRoot string `validate:"required"`
ProjectName string `validate:"required"`
DockerFile string `validate:"required"`
GoModFile string `validate:"required"`
GoModName string `validate:"required"`
EcrRepositoryName string `validate:"required"`
EcsClusterName string `validate:"required"`
EcsServiceName string `validate:"required"`
EcsExecutionRoleName string `validate:"required"`
EcsTaskRoleName string `validate:"required"`
EcsTaskPolicyName string `validate:"required"`
EcsServiceDesiredCount int64 `validate:"required"`
Ec2SecurityGroupName string `validate:"required"`
CloudWatchLogGroupName string `validate:"required"`
AwsCreds awsCredentials `validate:"required,dive,required"`
// Optional flags.
ecrRepositoryMaxImages int `validate:"omitempty"`
ecsServiceMinimumHealthyPercent *int64 `validate:"omitempty"`
ecsServiceMaximumPercent *int64 `validate:"omitempty"`
escServiceHealthCheckGracePeriodSeconds *int64 `validate:"omitempty"`
elbDeregistrationDelay *int `validate:"omitempty"`
enableLambdaVPC bool `validate:"omitempty"`
enableEcsElb bool `validate:"omitempty"`
noBuild bool `validate:"omitempty"`
noDeploy bool `validate:"omitempty"`
noCache bool `validate:"omitempty"`
noPush bool `validate:"omitempty"`
EnableHTTPS bool `validate:"omitempty"`
ServiceDomainName string `validate:"omitempty,required_with=EnableHTTPS,fqdn"`
ServiceDomainNameAliases []string `validate:"omitempty,dive,fqdn"`
EcrRepositoryMaxImages int `validate:"omitempty"`
EcsServiceMinimumHealthyPercent *int64 `validate:"omitempty"`
EcsServiceMaximumPercent *int64 `validate:"omitempty"`
EscServiceHealthCheckGracePeriodSeconds *int64 `validate:"omitempty"`
ElbDeregistrationDelay *int `validate:"omitempty"`
EnableLambdaVPC bool `validate:"omitempty"`
EnableEcsElb bool `validate:"omitempty"`
ElbLoadBalancerName string `validate:"omitempty"`
NoBuild bool `validate:"omitempty"`
NoDeploy bool `validate:"omitempty"`
NoCache bool `validate:"omitempty"`
NoPush bool `validate:"omitempty"`
RecreateService bool `validate:"omitempty"`
_awsSession *session.Session
ReleaseImage string
BuildTags []string
flags ServiceDeployFlags
_awsSession *session.Session
}
// projectNameCamel takes a project name and returns the camel cased version.
func (r *serviceDeployRequest) projectNameCamel() string {
s := strings.Replace(r.projectName, "_", " ", -1)
func (r *serviceDeployRequest) ProjectNameCamel() string {
s := strings.Replace(r.ProjectName, "_", " ", -1)
s = strings.Replace(s, "-", " ", -1)
s = strcase.ToCamel(s)
s = strcase.ToCamel(s)
return s
}
// awsSession returns the current AWS session for the serviceDeployRequest.
func (r *serviceDeployRequest) awsSession() *session.Session {
if r._awsSession == nil {
r._awsSession = r.awsCreds.Session()
r._awsSession = r.AwsCreds.Session()
}
return r._awsSession
@ -88,30 +98,30 @@ func (r *serviceDeployRequest) awsSession() *session.Session {
// AwsCredentials defines AWS credentials used for deployment. Unable to use roles when deploying
// using gitlab CI/CD pipeline.
type awsCredentials struct {
accessKeyID string `validate:"required"`
secretAccessKey string `validate:"required"`
region string `validate:"required"`
AccessKeyID string `validate:"required"`
SecretAccessKey string `validate:"required"`
Region string `validate:"required"`
}
// Session returns a new AWS Session used to access AWS services.
func (creds awsCredentials) Session() *session.Session {
return session.New(
&aws.Config{
Region: aws.String(creds.region),
Credentials: credentials.NewStaticCredentials(creds.accessKeyID, creds.secretAccessKey, ""),
Region: aws.String(creds.Region),
Credentials: credentials.NewStaticCredentials(creds.AccessKeyID, creds.SecretAccessKey, ""),
})
}
// IamPolicyDocument defines an AWS IAM policy used for defining access for IAM roles, users, and groups.
type IamPolicyDocument struct {
Version string `json:"Version"`
Statement []IamStatementEntry `json:"Statement"`
Version string `json:"Version"`
Statement []IamStatementEntry `json:"Statement"`
}
// IamStatementEntry defines a single statement for an IAM policy.
type IamStatementEntry struct {
Sid string `json:"Sid"`
Effect string `json:"Effect"`
Action []string `json:"Action"`
Resource interface{} `json:"Resource"`
Sid string `json:"Sid"`
Effect string `json:"Effect"`
Action []string `json:"Action"`
Resource interface{} `json:"Resource"`
}

File diff suppressed because it is too large Load Diff