You've already forked golang-saas-starter-kit
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:
76
example-project/tools/truss/cmd/devops/README.md
Normal file
76
example-project/tools/truss/cmd/devops/README.md
Normal 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
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -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
Reference in New Issue
Block a user