package cmd import ( "context" "fmt" "os" "github.com/AlecAivazis/survey/v2" "github.com/spf13/cobra" "knative.dev/func/pkg/config" fn "knative.dev/func/pkg/functions" "knative.dev/func/pkg/utils" ) func NewConfigLabelsCmd(loaderSaver functionLoaderSaver) *cobra.Command { var configLabelsCmd = &cobra.Command{ Use: "labels", Short: "List and manage configured labels for a function", Long: `List and manage configured labels for a function Prints configured labels for a function project present in the current directory or from the directory specified with --path. `, Aliases: []string{"label"}, SuggestFor: []string{"albels", "abels"}, PreRunE: bindEnv("path", "verbose"), RunE: func(cmd *cobra.Command, args []string) (err error) { function, err := initConfigCommand(loaderSaver) if err != nil { return } listLabels(function) return }, } var configLabelsAddCmd = &cobra.Command{ Use: "add", Short: "Add labels to the function configuration", Long: `Add labels to the function configuration Interactive prompt to add labels to the function project in the current directory or from the directory specified with --path. The label can be set directly from a value or from an environment variable on the local machine. `, SuggestFor: []string{"ad", "create", "insert", "append"}, PreRunE: bindEnv("path", "verbose"), RunE: func(cmd *cobra.Command, args []string) (err error) { function, err := initConfigCommand(loaderSaver) if err != nil { return } return runAddLabelsPrompt(cmd.Context(), function, loaderSaver) }, } var configLabelsRemoveCmd = &cobra.Command{ Use: "remove", Short: "Remove labels from the function configuration", Long: `Remove labels from the function configuration Interactive prompt to remove labels from the function project in the current directory or from the directory specified with --path. `, Aliases: []string{"rm"}, SuggestFor: []string{"del", "delete", "rmeove"}, PreRunE: bindEnv("path", "verbose"), RunE: func(cmd *cobra.Command, args []string) (err error) { function, err := initConfigCommand(loaderSaver) if err != nil { return } return runRemoveLabelsPrompt(function, loaderSaver) }, } cfg, err := config.NewDefault() if err != nil { fmt.Fprintf(configLabelsCmd.OutOrStdout(), "error loading config at '%v'. %v\n", config.File(), err) } addPathFlag(configLabelsCmd) addPathFlag(configLabelsAddCmd) addPathFlag(configLabelsRemoveCmd) addVerboseFlag(configLabelsCmd, cfg.Verbose) addVerboseFlag(configLabelsAddCmd, cfg.Verbose) addVerboseFlag(configLabelsRemoveCmd, cfg.Verbose) configLabelsCmd.AddCommand(configLabelsAddCmd) configLabelsCmd.AddCommand(configLabelsRemoveCmd) return configLabelsCmd } func listLabels(f fn.Function) { if len(f.Deploy.Labels) == 0 { fmt.Println("There aren't any configured labels") return } fmt.Println("Configured labels:") for _, e := range f.Deploy.Labels { fmt.Println(" - ", e.String()) } } func runAddLabelsPrompt(_ context.Context, f fn.Function, saver functionSaver) (err error) { insertToIndex := 0 // SECTION - if there are some labels already set, choose the position of the new entry if len(f.Deploy.Labels) > 0 { options := []string{} for _, e := range f.Deploy.Labels { options = append(options, fmt.Sprintf("Insert before: %s", e.String())) } options = append(options, "Insert here.") selectedLabel := "" prompt := &survey.Select{ Message: "Where do you want to add the label?", Options: options, Default: options[len(options)-1], } err = survey.AskOne(prompt, &selectedLabel) if err != nil { return } for i, option := range options { if option == selectedLabel { insertToIndex = i break } } } // SECTION - select the type of label to be added selectedOption := "" const ( optionLabelValue = "Label with a specified value" optionLabelLocal = "Value from a local environment variable" ) options := []string{optionLabelValue, optionLabelLocal} err = survey.AskOne(&survey.Select{ Message: "What type of label do you want to add?", Options: options, }, &selectedOption) if err != nil { return } newPair := fn.Label{} switch selectedOption { // SECTION - add new label with the specified value case optionLabelValue: qs := []*survey.Question{ { Name: "key", Prompt: &survey.Input{Message: "Please specify the label key:"}, Validate: func(val interface{}) error { return utils.ValidateLabelKey(val.(string)) }, }, { Name: "value", Prompt: &survey.Input{Message: "Please specify the label value:"}, Validate: func(val interface{}) error { return utils.ValidateLabelValue(val.(string)) }}, } answers := struct { Key string Value string }{} err = survey.Ask(qs, &answers) if err != nil { return } newPair.Key = &answers.Key newPair.Value = &answers.Value // SECTION - add new label with value from a local environment variable case optionLabelLocal: qs := []*survey.Question{ { Name: "key", Prompt: &survey.Input{Message: "Please specify the label key:"}, Validate: func(val interface{}) error { return utils.ValidateLabelKey(val.(string)) }, }, { Name: "value", Prompt: &survey.Input{Message: "Please specify the local environment variable:"}, Validate: func(val interface{}) error { return utils.ValidateLabelValue(val.(string)) }, }, } answers := struct { Key string Value string }{} err = survey.Ask(qs, &answers) if err != nil { return } if _, ok := os.LookupEnv(answers.Value); !ok { fmt.Printf("Warning: specified local environment variable %q is not set\n", answers.Value) } value := fmt.Sprintf("{{ env:%s }}", answers.Value) newPair.Key = &answers.Key newPair.Value = &value } // we have all necessary information -> let's insert the label to the selected position in the list if insertToIndex == len(f.Deploy.Labels) { f.Deploy.Labels = append(f.Deploy.Labels, newPair) } else { f.Deploy.Labels = append(f.Deploy.Labels[:insertToIndex+1], f.Deploy.Labels[insertToIndex:]...) f.Deploy.Labels[insertToIndex] = newPair } err = saver.Save(f) if err == nil { fmt.Println("Label entry was added to the function configuration") } return } func runRemoveLabelsPrompt(f fn.Function, saver functionSaver) (err error) { if len(f.Deploy.Labels) == 0 { fmt.Println("There aren't any configured labels") return } options := []string{} for _, e := range f.Deploy.Labels { options = append(options, e.String()) } selectedLabel := "" prompt := &survey.Select{ Message: "Which labels do you want to remove?", Options: options, } err = survey.AskOne(prompt, &selectedLabel) if err != nil { return } var newLabels []fn.Label removed := false for i, e := range f.Deploy.Labels { if e.String() == selectedLabel { newLabels = append(f.Deploy.Labels[:i], f.Deploy.Labels[i+1:]...) removed = true break } } if removed { f.Deploy.Labels = newLabels err = saver.Save(f) if err == nil { fmt.Println("Label was removed from the function configuration") } } return }