package main import ( "fmt" "strings" "github.com/influxdata/influx-cli/v2/clients/stacks" "github.com/influxdata/influx-cli/v2/pkg/cli/middleware" "github.com/influxdata/influx-cli/v2/pkg/template" "github.com/urfave/cli" ) func newStacksCmd() cli.Command { var params stacks.ListParams return cli.Command{ Name: "stacks", Usage: "List stack(s) and associated templates. Subcommands manage stacks.", Description: `List stack(s) and associated templates. Subcommands manage stacks. Examples: # list all known stacks influx stacks # list stacks filtered by stack name # output here are stacks that have match at least 1 name provided influx stacks --stack-name=$STACK_NAME_1 --stack-name=$STACK_NAME_2 # list stacks filtered by stack id # output here are stacks that have match at least 1 ids provided influx stacks --stack-id=$STACK_ID_1 --stack-id=$STACK_ID_2 # list stacks filtered by stack id or stack name # output here are stacks that have match the id provided or # matches of the name provided influx stacks --stack-id=$STACK_ID --stack-name=$STACK_NAME For information about Stacks and how they integrate with InfluxDB templates, see https://docs.influxdata.com/influxdb/latest/reference/cli/influx/stacks/`, Before: middleware.WithBeforeFns(withCli(), withApi(true), middleware.NoArgs), Flags: append( append(commonFlags(), getOrgFlags(¶ms.OrgParams)...), &cli.StringSliceFlag{ Name: "stack-id", Usage: "Stack ID to filter by", }, &cli.StringSliceFlag{ Name: "stack-name", Usage: "Stack name to filter by", }, ), Subcommands: []cli.Command{ newStacksInitCmd(), newStacksRemoveCmd(), newStacksUpdateCmd(), }, Action: func(ctx *cli.Context) error { if err := checkOrgFlags(¶ms.OrgParams); err != nil { return err } params.StackIds = ctx.StringSlice("stack-id") params.StackNames = ctx.StringSlice("stack-name") api := getAPI(ctx) client := stacks.Client{ CLI: getCLI(ctx), StacksApi: api.StacksApi, OrganizationsApi: api.OrganizationsApi, } return client.List(getContext(ctx), ¶ms) }, } } func newStacksInitCmd() cli.Command { var params stacks.InitParams return cli.Command{ Name: "init", Usage: "Initialize a stack", Description: `The stack init command creates a new stack to associated templates with. A stack is used to make applying templates idempotent. When you apply a template and associate it with a stack, the stack can manage the created/updated resources from the template back to the platform. This enables a multitude of useful features. Any associated template urls will be applied when applying templates via a stack. Examples: # Initialize a stack with a name and description influx stacks init -n $STACK_NAME -d $STACK_DESCRIPTION # Initialize a stack with a name and urls to associate with stack. influx stacks init -n $STACK_NAME -u $PATH_TO_TEMPLATE For information about how stacks work with InfluxDB templates, see https://docs.influxdata.com/influxdb/latest/reference/cli/influx/stacks/ and https://docs.influxdata.com/influxdb/latest/reference/cli/influx/stacks/init/`, Before: middleware.WithBeforeFns(withCli(), withApi(true)), Flags: append( append(commonFlags(), getOrgFlags(¶ms.OrgParams)...), &cli.StringFlag{ Name: "stack-name, n", Usage: "Name given to created stack", Destination: ¶ms.Name, }, &cli.StringFlag{ Name: "stack-description, d", Usage: "Description given to created stack", Destination: ¶ms.Description, }, &cli.StringSliceFlag{ Name: "template-url, u", Usage: "Template urls to associate with new stack", }, ), Action: func(ctx *cli.Context) error { if err := checkOrgFlags(¶ms.OrgParams); err != nil { return err } params.URLs = ctx.StringSlice("template-url") api := getAPI(ctx) client := stacks.Client{ CLI: getCLI(ctx), StacksApi: api.StacksApi, OrganizationsApi: api.OrganizationsApi, } return client.Init(getContext(ctx), ¶ms) }, } } func newStacksRemoveCmd() cli.Command { var params stacks.RemoveParams return cli.Command{ Name: "rm", Aliases: []string{"remove", "uninstall"}, Usage: "Remove a stack(s) and all associated resources", Before: middleware.WithBeforeFns(withCli(), withApi(true)), Flags: append( append(commonFlags(), getOrgFlags(¶ms.OrgParams)...), &cli.StringSliceFlag{ Name: "stack-id", Usage: "Stack IDs to be removed", Required: true, }, &cli.BoolFlag{ Name: "force", Usage: "Remove stack without confirmation prompt", Destination: ¶ms.Force, }, ), Action: func(ctx *cli.Context) error { if err := checkOrgFlags(¶ms.OrgParams); err != nil { return err } params.Ids = ctx.StringSlice("stack-id") api := getAPI(ctx) client := stacks.Client{ CLI: getCLI(ctx), StacksApi: api.StacksApi, OrganizationsApi: api.OrganizationsApi, } return client.Remove(getContext(ctx), ¶ms) }, } } func newStacksUpdateCmd() cli.Command { var params stacks.UpdateParams return cli.Command{ Name: "update", Usage: "Update a stack", Description: `The stack update command updates a stack. Examples: # Update a stack with a name and description influx stacks update -i $STACK_ID -n $STACK_NAME -d $STACK_DESCRIPTION # Update a stack with a name and urls to associate with stack. influx stacks update --stack-id $STACK_ID --stack-name $STACK_NAME --template-url $PATH_TO_TEMPLATE # Update stack with new resources to manage influx stacks update \ --stack-id $STACK_ID \ --addResource=Bucket=$BUCKET_ID \ --addResource=Dashboard=$DASH_ID # Update stack with new resources to manage and export stack # as a template influx stacks update \ --stack-id $STACK_ID \ --addResource=Bucket=$BUCKET_ID \ --export-file /path/to/file.yml For information about how stacks work with InfluxDB templates, see https://docs.influxdata.com/influxdb/latest/reference/cli/influx/stacks/ and https://docs.influxdata.com/influxdb/latest/reference/cli/influx/stacks/update/ `, Before: middleware.WithBeforeFns(withCli(), withApi(true)), Flags: append( commonFlags(), &cli.StringFlag{ Name: "stack-id, i", Usage: "ID of stack", Destination: ¶ms.Id, Required: true, }, cli.StringFlag{ Name: "stack-name, n", Usage: "New name for the stack", }, cli.StringFlag{ Name: "stack-description, d", Usage: "New description for the stack", }, cli.StringSliceFlag{ Name: "template-url, u", Usage: "New template URLs to associate with the stack", }, cli.StringSliceFlag{ Name: "addResource", Usage: "Additional resources to associate with the stack", }, cli.StringFlag{ Name: "export-file, f", Usage: "Destination for exported template", TakesFile: true, }, ), Action: func(ctx *cli.Context) error { if ctx.IsSet("stack-name") { name := ctx.String("stack-name") params.Name = &name } if ctx.IsSet("stack-description") { desc := ctx.String("stack-description") params.Description = &desc } if ctx.IsSet("template-url") { urls := ctx.StringSlice("template-url") params.URLs = urls } rawResources := ctx.StringSlice("addResource") for _, res := range rawResources { pieces := strings.Split(res, "=") if len(pieces) != 2 { return fmt.Errorf("invalid resource specification %q, must have format `KIND=ID`", res) } params.AddedResources = append(params.AddedResources, stacks.AddedResource{ Kind: pieces[0], Id: pieces[1], }) } cli := getCLI(ctx) outParams, closer, err := template.ParseOutParams(ctx.String("export-file"), cli.StdIO) if closer != nil { defer closer() } if err != nil { return err } params.OutParams = outParams api := getAPI(ctx) client := stacks.Client{ CLI: cli, StacksApi: api.StacksApi, TemplatesApi: api.TemplatesApi, } return client.Update(getContext(ctx), ¶ms) }, } }