From bb7c895a1c6e7a9602bcca9fc9063c9f2bdbd854 Mon Sep 17 00:00:00 2001 From: Peter Baumgartner Date: Tue, 26 Jan 2021 08:04:27 -0700 Subject: [PATCH] Add create region command Some of the account-level resources were actually unique per region. They were moved into a new region template. --- .github/workflows/functional_tests.yml | 12 +++- cmd/create.go | 98 ++++++++++++++++++++------ 2 files changed, 89 insertions(+), 21 deletions(-) diff --git a/.github/workflows/functional_tests.yml b/.github/workflows/functional_tests.yml index 87a4a43..56ce8b3 100644 --- a/.github/workflows/functional_tests.yml +++ b/.github/workflows/functional_tests.yml @@ -22,7 +22,7 @@ jobs: run: go build - name: Create account - run: ./apppack create account --dockerhub-username $DOCKERHUB_USERNAME --dockerhub-access-token $DOCKERHUB_ACCESS_TOKEN | tee account_create_output.txt + run: ./apppack create account | tee account_create_output.txt timeout-minutes: 3 env: DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} @@ -42,6 +42,16 @@ jobs: AWS_ACCESS_KEY_ID: ${{ secrets.APPPACK_AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.APPPACK_AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: us-east-1 + - + name: Create region + run: ./apppack create region --dockerhub-username $DOCKERHUB_USERNAME --dockerhub-access-token $DOCKERHUB_ACCESS_TOKEN + timeout-minutes: 3 + env: + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKERHUB_ACCESS_TOKEN: ${{ secrets.DOCKERHUB_ACCESS_TOKEN }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: us-east-1 - name: Create cluster run: ./apppack create cluster --domain testclusters.apppack.io --hosted-zone-id Z05906472T84V7X7Q6UDY --instance-class t3.micro diff --git a/cmd/create.go b/cmd/create.go index 03da6c7..e199d79 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -45,6 +45,7 @@ const ( appFormationURL = "https://s3.amazonaws.com/apppack-cloudformations/latest/app.json" clusterFormationURL = "https://s3.amazonaws.com/apppack-cloudformations/latest/cluster.json" accountFormationURL = "https://s3.amazonaws.com/apppack-cloudformations/latest/account.json" + regionFormationURL = "https://s3.amazonaws.com/apppack-cloudformations/latest/region.json" postgresFormationURL = "https://s3.amazonaws.com/apppack-cloudformations/latest/postgres.json" mysqlFormationURL = "https://s3.amazonaws.com/apppack-cloudformations/latest/mysql.json" redisFormationURL = "https://s3.amazonaws.com/apppack-cloudformations/latest/redis.json" @@ -434,11 +435,9 @@ var accountCmd = &cobra.Command{ Long: "*Requires AWS credentials.*", DisableFlagsInUseLine: true, Run: func(cmd *cobra.Command, args []string) { - answers, err := askForMissingArgs(cmd, nil) - checkErr(err) sess := session.Must(session.NewSession()) ssmSvc := ssm.New(sess) - _, err = ssmSvc.GetParameter(&ssm.GetParameterInput{ + _, err := ssmSvc.GetParameter(&ssm.GetParameterInput{ Name: aws.String("/apppack/account"), }) @@ -451,10 +450,78 @@ var accountCmd = &cobra.Command{ fmt.Println("Creating account-level resources...") } startSpinner() - tags := []*ssm.Tag{ + cfnTags := []*cloudformation.Tag{ {Key: aws.String("apppack:account"), Value: aws.String("true")}, {Key: aws.String("apppack"), Value: aws.String("true")}, } + + input := cloudformation.CreateStackInput{ + StackName: aws.String("apppack-account"), + TemplateURL: aws.String(accountFormationURL), + Parameters: []*cloudformation.Parameter{ + { + ParameterKey: aws.String("AppPackRoleExternalId"), + ParameterValue: aws.String(strings.Replace(uuid.New().String(), "-", "", -1)), + }, + }, + Capabilities: []*string{aws.String("CAPABILITY_IAM")}, + Tags: cfnTags, + } + var statusURL string + if createChangeSet { + changeSet, err := createChangeSetAndWait(sess, &input) + Spinner.Stop() + checkErr(err) + statusURL = fmt.Sprintf("https://console.aws.amazon.com/cloudformation/home#/stacks/events?stackId=%s", url.QueryEscape(*changeSet.ChangeSetId)) + if *changeSet.Status != "CREATE_COMPLETE" { + checkErr(fmt.Errorf("Stack ChangeSet creation Failed.\nView status at %s", statusURL)) + } else { + fmt.Println("View ChangeSet at:") + fmt.Println(aurora.White(statusURL)) + fmt.Println("Once your stack is created send the 'Outputs' to pete@lincolnloop.com for account approval.") + } + } else { + stack, err := createStackAndWait(sess, &input) + Spinner.Stop() + checkErr(err) + statusURL := fmt.Sprintf("https://console.aws.amazon.com/cloudformation/home#/stacks/events?stackId=%s", url.QueryEscape(*stack.StackId)) + if *stack.StackStatus != "CREATE_COMPLETE" { + checkErr(fmt.Errorf("Stack creation Failed.\nView status at %s", statusURL)) + } else { + printSuccess("AppPack account created") + fmt.Println(aurora.Bold("Send the following information to pete@lincolnloop.com for account approval:")) + for _, output := range stack.Outputs { + fmt.Println(fmt.Sprintf("%s: %s", *output.OutputKey, *output.OutputValue)) + } + + } + } + + }, +} + +// createRegionCmd represents the create command +var createRegionCmd = &cobra.Command{ + Use: "region", + Short: "setup AppPack resources for an AWS region", + Long: "*Requires AWS credentials.*", + DisableFlagsInUseLine: true, + Run: func(cmd *cobra.Command, args []string) { + answers, err := askForMissingArgs(cmd, nil) + checkErr(err) + sess := session.Must(session.NewSession()) + ssmSvc := ssm.New(sess) + if createChangeSet { + fmt.Println("Creating Cloudformation Change Set for region-level resources...") + } else { + fmt.Println("Creating region-level resources...") + } + startSpinner() + region := sess.Config.Region + tags := []*ssm.Tag{ + {Key: aws.String("apppack:region"), Value: region}, + {Key: aws.String("apppack"), Value: aws.String("true")}, + } _, err = ssmSvc.PutParameter(&ssm.PutParameterInput{ Name: aws.String("/apppack/account/dockerhub-access-token"), Value: getArgValue(cmd, answers, "dockerhub-access-token", true), @@ -463,18 +530,14 @@ var accountCmd = &cobra.Command{ }) checkErr(err) cfnTags := []*cloudformation.Tag{ - {Key: aws.String("apppack:account"), Value: aws.String("true")}, + {Key: aws.String("apppack:region"), Value: region}, {Key: aws.String("apppack"), Value: aws.String("true")}, } input := cloudformation.CreateStackInput{ - StackName: aws.String("apppack-account"), - TemplateURL: aws.String(accountFormationURL), + StackName: aws.String(fmt.Sprintf("apppack-region-%s", *region)), + TemplateURL: aws.String(regionFormationURL), Parameters: []*cloudformation.Parameter{ - { - ParameterKey: aws.String("AppPackRoleExternalId"), - ParameterValue: aws.String(strings.Replace(uuid.New().String(), "-", "", -1)), - }, { ParameterKey: aws.String("DockerhubUsername"), ParameterValue: getArgValue(cmd, answers, "dockerhub-username", true), @@ -494,7 +557,6 @@ var accountCmd = &cobra.Command{ } else { fmt.Println("View ChangeSet at:") fmt.Println(aurora.White(statusURL)) - fmt.Println("Once your stack is created send the 'Outputs' to pete@lincolnloop.com for account approval.") } } else { stack, err := createStackAndWait(sess, &input) @@ -504,11 +566,7 @@ var accountCmd = &cobra.Command{ if *stack.StackStatus != "CREATE_COMPLETE" { checkErr(fmt.Errorf("Stack creation Failed.\nView status at %s", statusURL)) } else { - printSuccess("AppPack account created") - fmt.Println(aurora.Bold("Send the following information to pete@lincolnloop.com for account approval:")) - for _, output := range stack.Outputs { - fmt.Println(fmt.Sprintf("%s: %s", *output.OutputKey, *output.OutputValue)) - } + printSuccess(fmt.Sprintf("AppPack region %s created", *region)) } } @@ -1157,9 +1215,9 @@ func init() { createCmd.PersistentFlags().BoolVar(&nonInteractive, "non-interactive", false, "do not prompt for missing flags") createCmd.AddCommand(accountCmd) - appCmd.Flags().SortFlags = false - accountCmd.Flags().StringP("dockerhub-username", "u", "", "Docker Hub username") - accountCmd.Flags().StringP("dockerhub-access-token", "t", "", "Docker Hub Access Token (https://hub.docker.com/settings/security)") + createCmd.AddCommand(createRegionCmd) + createRegionCmd.Flags().StringP("dockerhub-username", "u", "", "Docker Hub username") + createRegionCmd.Flags().StringP("dockerhub-access-token", "t", "", "Docker Hub Access Token (https://hub.docker.com/settings/security)") createCmd.AddCommand(appCmd) appCmd.Flags().SortFlags = false