command

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 9, 2023 License: MIT Imports: 27 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var CreateStack = cli.Command{
	Name:  "create",
	Usage: "Generate an 'aws cloudformation create-stack' command",
	Flags: []cli.Flag{
		&cli.StringFlag{Name: "provider-id", Required: true, Usage: "publisher/name@version"},
		&cli.StringFlag{Name: "handler-id", Required: true, Usage: "The ID of the Handler (for example, 'cf-handler-aws')"},
		&cli.StringFlag{Name: "bootstrap-bucket", Required: true},
		&cli.StringFlag{Name: "common-fate-aws-account", Usage: "The AWS account where Common Fate is deployed"},
		&cli.StringFlag{Name: "region", Usage: "The region to deploy the handler"},
	},
	Action: func(c *cli.Context) error {
		ctx := c.Context
		bootstrapBucket := c.String("bootstrap-bucket")
		handlerID := c.String("handler-id")
		commonFateAWSAccountID := c.String("common-fate-aws-account")
		registry, err := registryclient.New(ctx)
		if err != nil {
			return errors.Wrap(err, "configuring registry client")
		}

		providerString := c.String("provider-id")
		provider, err := providerregistrysdk.ParseProvider(providerString)
		if err != nil {
			return err
		}

		res, err := registry.GetProviderWithResponse(ctx, provider.Publisher, provider.Name, provider.Version)
		if err != nil {
			return err
		}

		var stackname = c.String("handler-id")
		if stackname == "" {
			err = survey.AskOne(&survey.Input{Message: "enter the cloudformation stackname:", Default: handlerID}, &stackname)
			if err != nil {
				return err
			}
		}

		var region = c.String("region")
		if region == "" {
			err = survey.AskOne(&survey.Input{Message: "enter the region of cloudformation stack deployment"}, &region)
			if err != nil {
				return err
			}
		}

		values := make(map[string]string)

		values["BootstrapBucketName"] = bootstrapBucket
		values["HandlerID"] = handlerID
		values["CommonFateAWSAccountID"] = commonFateAWSAccountID
		lambdaAssetPath := path.Join("registry.commonfate.io", "v1alpha1", "providers", provider.Publisher, provider.Name, provider.Version)
		values["AssetPath"] = path.Join(lambdaAssetPath, "handler.zip")

		awsCfg, err := awsconfig.LoadDefaultConfig(ctx, awsconfig.WithRegion(region))
		if err != nil {
			return err
		}

		config := res.JSON200.Schema.Config
		if config != nil {
			clio.Info("Enter the values for your configurations:")
			for k, v := range *config {
				if v.Secret != nil && *v.Secret {
					client := ssm.NewFromConfig(awsCfg)

					var secret string
					name := SSMKey(SSMKeyOpts{
						HandlerID:    handlerID,
						Key:          k,
						Publisher:    provider.Publisher,
						ProviderName: provider.Name,
					})

					helpMsg := fmt.Sprintf("This will be stored in AWS SSM Parameter Store with name '%s'", name)
					err = survey.AskOne(&survey.Password{Message: k + ":", Help: helpMsg}, &secret)
					if err != nil {
						return err
					}

					_, err = client.PutParameter(ctx, &ssm.PutParameterInput{
						Name:      aws.String(name),
						Value:     aws.String(secret),
						Type:      types.ParameterTypeSecureString,
						Overwrite: aws.Bool(true),
					})
					if err != nil {
						return err
					}

					clio.Successf("Added to AWS SSM Parameter Store with name '%s'", name)

					values[ConvertToPascalCase(k)+"Secret"] = name

					continue
				}

				var v string
				err = survey.AskOne(&survey.Input{Message: k + ":"}, &v)
				if err != nil {
					return err
				}
				values[ConvertToPascalCase(k)] = v

			}
		}

		if commonFateAWSAccountID == "" {
			var v string
			err = survey.AskOne(&survey.Input{Message: "The ID of the AWS account where Common Fate is deployed:"}, &v)
			if err != nil {
				return err
			}
			values["CommonFateAWSAccountID"] = v
		}

		parameterKeys := convertValuesToCloudformationParameter(values)

		s3client := s3.NewFromConfig(awsCfg)
		preSignedClient := s3.NewPresignClient(s3client)

		presigner := Presigner{
			PresignClient: preSignedClient,
		}

		req, err := presigner.GetObject(bootstrapBucket, path.Join(lambdaAssetPath, "cloudformation.json"), int64(time.Hour))
		if err != nil {
			return nil
		}

		templateUrl := fmt.Sprintf(" --template-url \"%s\" ", req.URL)
		stackNameFlag := fmt.Sprintf(" --stack-name %s ", stackname)
		regionFlag := fmt.Sprintf(" --region %s ", region)

		output := strings.Join([]string{"aws cloudformation create-stack", stackNameFlag, regionFlag, templateUrl, parameterKeys, "--capabilities CAPABILITY_NAMED_IAM"}, "")

		fmt.Printf("%v \n", output)

		return nil
	},
}
View Source
var GenerateCfOutput = cli.Command{
	Name:  "cloudformation",
	Usage: "Manage CloudFormation templates for Providers",
	Subcommands: []*cli.Command{
		&cfnCommandCommand,
	},
}
View Source
var Login = cli.Command{
	Name:   "login",
	Usage:  "Log in to Common Fate",
	Action: defaultLoginFlow.LoginAction,
}
View Source
var Logout = cli.Command{
	Name:  "logout",
	Usage: "Log out of Common Fate",
	Action: func(c *cli.Context) error {
		cfg, err := config.Load()
		if err != nil {
			return err
		}

		ts := tokenstore.New(cfg.CurrentContext)
		err = ts.Clear()
		if err != nil {
			return err
		}

		clio.Success("logged out")

		return nil
	},
}
View Source
var UpdateStack = cli.Command{
	Name:  "update",
	Usage: "Generate an 'aws cloudformation update-stack' command",
	Flags: []cli.Flag{
		&cli.StringFlag{Name: "handler-id", Usage: "The Handler ID and name of the CloudFormation stack", Required: true},
		&cli.StringFlag{Name: "region", Usage: "The region to deploy the handler", Required: true},
		&cli.StringFlag{Name: "provider-id", Usage: "Update the provider-id for the current stack"},
		&cli.BoolFlag{Name: "use-previous-value", Usage: "use the previous stack values for the parameters"},
	},
	Action: func(c *cli.Context) error {
		stackname := c.String("handler-id")
		region := c.String("region")

		ctx := c.Context

		awsCfg, err := awsconfig.LoadDefaultConfig(ctx, awsconfig.WithRegion(region))
		if err != nil {
			return err
		}

		cfn := cloudformation.NewFromConfig(awsCfg)

		out, err := cfn.DescribeStacks(ctx, &cloudformation.DescribeStacksInput{
			StackName: &stackname,
		})
		if err != nil {
			return err
		}

		if len(out.Stacks) > 0 {
			stack := out.Stacks[0]
			values := make(map[string]string)

			if c.String("provider-id") != "" {
				registry, err := registryclient.New(ctx)
				if err != nil {
					return errors.Wrap(err, "configuring provider registry client")
				}

				provider, err := targetsvc.SplitProviderString(c.String("provider-id"))
				if err != nil {
					return err
				}

				_, err = registry.GetProviderWithResponse(ctx, provider.Publisher, provider.Name, provider.Version)
				if err != nil {
					return err
				}

				values["AssetPath"] = path.Join("registry.commonfate.io", "v1alpha1", "providers", provider.Publisher, provider.Name, provider.Version, "handler.zip")
			}

			for _, parameter := range stack.Parameters {

				if contains([]string{"HandlerID", "BootstrapBucketName"}, *parameter.ParameterKey) || c.Bool("use-previous-value") {
					values[*parameter.ParameterKey] = *parameter.ParameterValue
					continue
				}

				if strings.HasPrefix(*parameter.ParameterValue, "awsssm:///common-fate/provider/") {
					var shouldUpdate bool

					err = survey.AskOne(&survey.Confirm{Message: "Do you want to update value for " + *parameter.ParameterKey + " in AWS parameter store?"}, &shouldUpdate)
					if err != nil {
						return err
					}

					if shouldUpdate {
						client := ssm.NewFromConfig(awsCfg)

						var secret string
						name := *parameter.ParameterValue
						helpMsg := fmt.Sprintf("This will be stored in aws system manager parameter store with name '%s'", name)
						err = survey.AskOne(&survey.Password{Message: *parameter.ParameterKey + ":", Help: helpMsg}, &secret)
						if err != nil {
							return err
						}

						_, err = client.PutParameter(ctx, &ssm.PutParameterInput{
							Name:      aws.String(name),
							Value:     aws.String(secret),
							Type:      types.ParameterTypeSecureString,
							Overwrite: aws.Bool(true),
						})
						if err != nil {
							return err
						}

						clio.Successf("Updated value in AWS System Manager Parameter Store for key with name '%s'", name)
					}

					continue
				}

				if c.String("provider-id") != "" && *parameter.ParameterKey == "AssetPath" {
					continue
				}

				var v string
				err = survey.AskOne(&survey.Input{Message: *parameter.ParameterKey + ":", Default: *parameter.ParameterValue}, &v)
				if err != nil {
					return err
				}

				if v != *parameter.ParameterValue {
					values[*parameter.ParameterKey] = v
				} else {
					values[*parameter.ParameterKey] = *parameter.ParameterValue
				}
			}

			parameterKeys := convertValuesToCloudformationParameter(values)

			s3client := s3.NewFromConfig(awsCfg)
			preSignedClient := s3.NewPresignClient(s3client)

			presigner := Presigner{
				PresignClient: preSignedClient,
			}

			bootstrapBucket := values["BootstrapBucketName"]
			lambdaAssetPath := values["AssetPath"]

			req, err := presigner.GetObject(bootstrapBucket, strings.Replace(lambdaAssetPath, "handler.zip", "cloudformation.json", 1), int64(time.Hour)*1)
			if err != nil {
				return nil
			}

			templateUrl := fmt.Sprintf(" --template-url \"%s\" ", req.URL)
			stackNameFlag := fmt.Sprintf(" --stack-name %s ", stackname)
			regionFlag := fmt.Sprintf(" --region %s ", region)

			output := strings.Join([]string{"aws cloudformation update-stack", stackNameFlag, regionFlag, templateUrl, parameterKeys, "--capabilities CAPABILITY_NAMED_IAM"}, "")

			fmt.Printf("%v \n", output)
		}

		return nil
	},
}

Functions

func ConvertToPascalCase added in v0.2.0

func ConvertToPascalCase(s string) string

func SSMKey added in v0.2.0

func SSMKey(opts SSMKeyOpts) string

this will create a unique identifier for AWS System Manager Parameter Store for configuration field "api_url" this will result: 'publisher/provider-name/version/configuration/api_url'

Types

type LoginFlow

type LoginFlow struct {
	// Keyring optionally overrides the keyring that auth tokens are saved to.
	Keyring keyring.Keyring
	// ForceInteractive forces the survey prompt to appear
	ForceInteractive bool
}

func (LoginFlow) LoginAction

func (lf LoginFlow) LoginAction(c *cli.Context) error

type Presigner added in v0.2.0

type Presigner struct {
	PresignClient *s3.PresignClient
}

func (Presigner) GetObject added in v0.2.0

func (presigner Presigner) GetObject(
	bucketName string, objectKey string, lifetimeSecs int64) (*v4.PresignedHTTPRequest, error)

GetObject makes a presigned request that can be used to get an object from a bucket. The presigned request is valid for the specified number of seconds.

type SSMKeyOpts added in v0.2.0

type SSMKeyOpts struct {
	HandlerID    string
	Key          string
	Publisher    string
	ProviderName string
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL