package cli import ( "context" "flag" "fmt" "strings" "github.com/peterbourgon/ff/v3/ffcli" alertmgr "edge-infra.dev/pkg/lib/gcp/monitoring/alertmanager" ) type updateArgsT struct { addUserLabels string delUserLabels string name string filter string rename string enable bool disable bool templatePath string } var updateArgs updateArgsT var updateFlagSet = newUpdateFlagSet(&updateArgs) func newUpdateFlagSet(updateArgs *updateArgsT) *flag.FlagSet { updatef := newFlagSet("update") updatef.StringVar(&updateArgs.name, "name", "", "AlertPolicy Name or Display Name to update. (required)\nNOTE: If a Display Name is used and multiple AlertPolicies exist with the same name, only the first match will be used.") updatef.StringVar(&updateArgs.addUserLabels, "add-userlabels", "", "UserLabel(s) to append to the specified AlertPolicy. (optional)\nNOTE: Use ':' to separate each userlabel Key/Value pair and ',' if specifying multiple Key/Value pairs.\n No empty Key/Value allowed.") updatef.StringVar(&updateArgs.delUserLabels, "del-userlabels", "", "UserLabel(s) to remove from the specified AlertPolicy. (optional)\nNOTE: Use ':' to separate each userlabel Key/Value pair and ',' if specifying multiple Key/Value pairs.\n No empty Key/Value allowed.") updatef.StringVar(&updateArgs.filter, "filter", "", "Updates AlertPolicy(ies) matching filter criteria. (optional)\nNOTE: The filter syntax consists of a simple expression language. Negation, conjunction, and disjunction are written using NOT, AND, and OR keywords.\n Fields can be compared with literal values by use of : (containment), = (equality), > (greater), < (less than), >= (greater than or equal to), <= (less than or equal to), and != (inequality) operators.") updatef.StringVar(&updateArgs.rename, "new-name", "", "Rename's the AlertPolicy with the new name provided. (optional)") updatef.StringVar(&updateArgs.templatePath, "path", "", "File path to the AlertPolicy (*.json) file.\nNOTE: Only supports updating a single AlertPolicy at a time. Recommend using the AlertPolicy 'Name' instead of 'DisplayName' to identify a unique match") updatef.BoolVar(&updateArgs.enable, "enable", false, "Enable the AlertPolicy if not enabled. (optional)") updatef.BoolVar(&updateArgs.disable, "disable", false, "Disable the AlertPolicy if enabled. (optional)") return updatef } var updateCmd = &ffcli.Command{ Name: "update", ShortUsage: "update [flags]", ShortHelp: "Update running alert policy", LongHelp: strings.TrimSpace(` Updates the AlertPolicy instance configuration in the project. `), FlagSet: withGlobalFlags(updateFlagSet), Exec: runUpdate, } func runUpdate(_ context.Context, args []string) error { if len(args) > 0 { Fatalf("too many non-flag arguments: %q", args) } if !checkUpdateFlags() { Println() return flag.ErrHelp } // Enable AlertPolicy. if updateArgs.enable { err := alertmgr.ActivateAlertPolicy(projectID, updateArgs.enable, updateArgs.disable, updateArgs.name, updateArgs.filter) if strings.Contains(err.Error(), "error") { return err } } // Disable AlertPolicy. if updateArgs.disable { err := alertmgr.ActivateAlertPolicy(projectID, updateArgs.enable, updateArgs.disable, updateArgs.name, updateArgs.filter) if strings.Contains(err.Error(), "error") { return err } } // add AlertPolicy UserLabels. if len(updateArgs.addUserLabels) > 0 { callFlag := true userlabels, err := strArray(updateArgs.addUserLabels, callFlag) if err != nil { logger.Info("Please update the [add-userlabel] to the required format") return err } err = alertmgr.UpdateAlertPolicyUserLabels(projectID, userlabels, updateArgs.name, updateArgs.filter, callFlag) if strings.Contains(err.Error(), "error") { return err } } // remove AlertPolicy UserLabels. if len(updateArgs.delUserLabels) > 0 { callFlag := false userlabels, err := strArray(updateArgs.delUserLabels, callFlag) if err != nil { logger.Info("Please update the [remove-userlabel] to the required format") return err } err = alertmgr.UpdateAlertPolicyUserLabels(projectID, userlabels, updateArgs.name, updateArgs.filter, callFlag) if strings.Contains(err.Error(), "error") { return err } } // rename AlertPolicy. if len(updateArgs.rename) > 0 { err := alertmgr.RenameAlertPolicy(projectID, updateArgs.rename, updateArgs.name) if err != nil { return err } } // Update AlertPolicy from template. if len(updateArgs.templatePath) > 0 { var flag bool // Get the AlertPolicy configuration template(s) from the path. sourceAlertPolicy, merr := alertmgr.ReadAlertPolicyFromPath(updateArgs.templatePath) if merr != nil { return Errorf("failed to read AlertPolicy configuration from file: %w", merr) } // Get the running AlertPolicy configuration. matchedAlertPolicy, serr := alertmgr.GetAlertPolicies(projectID, updateArgs.name, "") if serr == nil && len(matchedAlertPolicy) == 0 { return fmt.Errorf("%s AlertPolicy doesn't exists in project %s and should be created instead", updateArgs.name, projectID) } // Validate alertPolicy doesn't have any duplicates. If yes return an error and exit. switch { case len(sourceAlertPolicy) > 1: return Errorf("please provide the filepath to the AlertPolicy (*.json) template not the directory path: %s", updateArgs.templatePath) case len(matchedAlertPolicy) > 1: return fmt.Errorf("'%s' AlertPolicy has multiple matches in project %s a unique match is required for this update method. Recommend using the AlertPolicy 'Name' instead of 'DisplayName' in the cli", updateArgs.name, projectID) } for i := 0; i < len(sourceAlertPolicy); i++ { err := alertmgr.UpdateAlertPolicyFromTemplate(projectID, flag, sourceAlertPolicy[i].AlertPolicy, matchedAlertPolicy) if err != nil { return err } } } return nil } // validates the required update subcommand flags have been provided. func checkUpdateFlags() bool { if len(projectID) == 0 { logger.Error(nil, "Error: no value specified for [project] - a valid project-id is required") return false } if len(updateArgs.name) == 0 && len(updateArgs.filter) == 0 { logger.Error(nil, "Error: no value specified for [name] or [filter] - a AlertPolicy (name / display name) or a filter critieria is required") return false } if len(updateArgs.name) > 0 && len(updateArgs.filter) > 0 { logger.Error(nil, "Error: cannot use both [name] and [filter] for update - Please use only one option") return false } if len(updateArgs.rename) > 0 && len(updateArgs.filter) > 0 { logger.Error(nil, "Error: cannot rename multiple AlertPolicies - Please use [name] insted of [filter] flag") return false } if len(updateArgs.templatePath) > 0 && len(updateArgs.filter) > 0 { logger.Error(nil, "Error: cannot update multiple AlertPolicies from Path - Please use [name] insted of [filter] flag") return false } if len(updateArgs.addUserLabels) == 0 && len(updateArgs.delUserLabels) == 0 && len(updateArgs.rename) == 0 && !updateArgs.enable && !updateArgs.disable && len(updateArgs.templatePath) == 0 { logger.Error(nil, "Error: no values specified for [add-userlabels], [remove-labels], [new-name], [path] or [enable]/[disable] - please provide at least one parameter to perform an update action on the AlertPolicy") return false } if strings.ContainsAny(updateArgs.addUserLabels, "(){}[]_;.'") { logger.Error(nil, "Error: no special characters '(' ')' '{' '}' '[' ']' '_' ';' '.' allowed in [add-userlabels] - please update the command") return false } if strings.ContainsAny(updateArgs.delUserLabels, "(){}[]_;.'") { logger.Error(nil, "Error: no special characters '(' ')' '{' '}' '[' ']' '_' ';' '.' allowed in [del-userlabels] - please update the command") return false } if !checkPath(updateArgs.templatePath, false) { logger.Error(nil, fmt.Sprintf("Error: invalid AlertPolicy [path] specified - %s\n", updateArgs.templatePath)) return false } return true }