// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 package command import ( "fmt" "strings" "github.com/hashicorp/nomad/api" flagHelper "github.com/hashicorp/nomad/helper/flags" "github.com/mitchellh/cli" "github.com/posener/complete" ) // Ensure OperatorSchedulerSetConfig satisfies the cli.Command interface. var _ cli.Command = &OperatorSchedulerSetConfig{} type OperatorSchedulerSetConfig struct { Meta // The scheduler configuration flags allow us to tell whether the user set // a value or not. This means we can safely merge the current configuration // with user supplied, selective updates. checkIndex string schedulerAlgorithm string memoryOversubscription flagHelper.BoolValue rejectJobRegistration flagHelper.BoolValue pauseEvalBroker flagHelper.BoolValue preemptBatchScheduler flagHelper.BoolValue preemptServiceScheduler flagHelper.BoolValue preemptSysBatchScheduler flagHelper.BoolValue preemptSystemScheduler flagHelper.BoolValue } func (o *OperatorSchedulerSetConfig) AutocompleteFlags() complete.Flags { return mergeAutocompleteFlags(o.Meta.AutocompleteFlags(FlagSetClient), complete.Flags{ "-check-index": complete.PredictAnything, "-scheduler-algorithm": complete.PredictSet( string(api.SchedulerAlgorithmBinpack), string(api.SchedulerAlgorithmSpread), ), "-memory-oversubscription": complete.PredictSet("true", "false"), "-reject-job-registration": complete.PredictSet("true", "false"), "-pause-eval-broker": complete.PredictSet("true", "false"), "-preempt-batch-scheduler": complete.PredictSet("true", "false"), "-preempt-service-scheduler": complete.PredictSet("true", "false"), "-preempt-sysbatch-scheduler": complete.PredictSet("true", "false"), "-preempt-system-scheduler": complete.PredictSet("true", "false"), }, ) } func (o *OperatorSchedulerSetConfig) AutocompleteArgs() complete.Predictor { return complete.PredictNothing } func (o *OperatorSchedulerSetConfig) Name() string { return "operator scheduler set-config" } func (o *OperatorSchedulerSetConfig) Run(args []string) int { flags := o.Meta.FlagSet("set-config", FlagSetClient) flags.Usage = func() { o.Ui.Output(o.Help()) } flags.StringVar(&o.checkIndex, "check-index", "", "") flags.StringVar(&o.schedulerAlgorithm, "scheduler-algorithm", "", "") flags.Var(&o.memoryOversubscription, "memory-oversubscription", "") flags.Var(&o.rejectJobRegistration, "reject-job-registration", "") flags.Var(&o.pauseEvalBroker, "pause-eval-broker", "") flags.Var(&o.preemptBatchScheduler, "preempt-batch-scheduler", "") flags.Var(&o.preemptServiceScheduler, "preempt-service-scheduler", "") flags.Var(&o.preemptSysBatchScheduler, "preempt-sysbatch-scheduler", "") flags.Var(&o.preemptSystemScheduler, "preempt-system-scheduler", "") if err := flags.Parse(args); err != nil { return 1 } // Set up a client. client, err := o.Meta.Client() if err != nil { o.Ui.Error(fmt.Sprintf("Error initializing client: %s", err)) return 1 } // Check that we got no arguments. args = flags.Args() if l := len(args); l != 0 { o.Ui.Error("This command takes no arguments") o.Ui.Error(commandErrorText(o)) return 1 } // Convert the check index string and handle any errors before adding this // to our request. This parsing handles empty values correctly. checkIndex, _, err := parseCheckIndex(o.checkIndex) if err != nil { o.Ui.Error(fmt.Sprintf("Error parsing check-index value %q: %v", o.checkIndex, err)) return 1 } // Fetch the current configuration. This will be used as a base to merge // user configuration onto. resp, _, err := client.Operator().SchedulerGetConfiguration(nil) if err != nil { o.Ui.Error(fmt.Sprintf("Error querying for scheduler configuration: %s", err)) return 1 } if checkIndex > 0 && resp.SchedulerConfig.ModifyIndex != checkIndex { errMsg := fmt.Sprintf("check-index %v does not match does not match current state value %v", checkIndex, resp.SchedulerConfig.ModifyIndex) o.Ui.Error(fmt.Sprintf("Error performing check index set: %s", errMsg)) return 1 } schedulerConfig := resp.SchedulerConfig // Overwrite the modification index if the user supplied one, otherwise we // use what was included within the read response. if checkIndex > 0 { schedulerConfig.ModifyIndex = checkIndex } // Merge the current configuration with any values set by the operator. if o.schedulerAlgorithm != "" { schedulerConfig.SchedulerAlgorithm = api.SchedulerAlgorithm(o.schedulerAlgorithm) } o.memoryOversubscription.Merge(&schedulerConfig.MemoryOversubscriptionEnabled) o.rejectJobRegistration.Merge(&schedulerConfig.RejectJobRegistration) o.pauseEvalBroker.Merge(&schedulerConfig.PauseEvalBroker) o.preemptBatchScheduler.Merge(&schedulerConfig.PreemptionConfig.BatchSchedulerEnabled) o.preemptServiceScheduler.Merge(&schedulerConfig.PreemptionConfig.ServiceSchedulerEnabled) o.preemptSysBatchScheduler.Merge(&schedulerConfig.PreemptionConfig.SysBatchSchedulerEnabled) o.preemptSystemScheduler.Merge(&schedulerConfig.PreemptionConfig.SystemSchedulerEnabled) // Check-and-set the new configuration. result, _, err := client.Operator().SchedulerCASConfiguration(schedulerConfig, nil) if err != nil { o.Ui.Error(fmt.Sprintf("Error setting scheduler configuration: %s", err)) return 1 } if result.Updated { o.Ui.Output("Scheduler configuration updated!") return 0 } o.Ui.Output("Scheduler configuration could not be atomically updated, please try again") return 1 } func (o *OperatorSchedulerSetConfig) Synopsis() string { return "Modify the current scheduler configuration" } func (o *OperatorSchedulerSetConfig) Help() string { helpText := ` Usage: nomad operator scheduler set-config [options] Modifies the current scheduler configuration. If ACLs are enabled, this command requires a token with the 'operator:write' capability. General Options: ` + generalOptionsUsage(usageOptsDefault|usageOptsNoNamespace) + ` Scheduler Set Config Options: -check-index If set, the scheduler config is only updated if the passed modify index matches the current server side version. If a non-zero value is passed, it ensures that the scheduler config is being updated from a known state. -scheduler-algorithm=["binpack"|"spread"] Specifies whether scheduler binpacks or spreads allocations on available nodes. -memory-oversubscription=[true|false] When true, tasks may exceed their reserved memory limit, if the client has excess memory capacity. Tasks must specify memory_max to take advantage of memory oversubscription. -reject-job-registration=[true|false] When true, the server will return permission denied errors for job registration, job dispatch, and job scale APIs, unless the ACL token for the request is a management token. If ACLs are disabled, no user will be able to register jobs. This allows operators to shed load from automated processes during incident response. -pause-eval-broker=[true|false] When set to true, the eval broker which usually runs on the leader will be disabled. This will prevent the scheduler workers from receiving new work. -preempt-batch-scheduler=[true|false] Specifies whether preemption for batch jobs is enabled. Note that if this is set to true, then batch jobs can preempt any other jobs. -preempt-service-scheduler=[true|false] Specifies whether preemption for service jobs is enabled. Note that if this is set to true, then service jobs can preempt any other jobs. -preempt-sysbatch-scheduler=[true|false] Specifies whether preemption for system batch jobs is enabled. Note that if this is set to true, then system batch jobs can preempt any other jobs. -preempt-system-scheduler=[true|false] Specifies whether preemption for system jobs is enabled. Note that if this is set to true, then system jobs can preempt any other jobs. ` return strings.TrimSpace(helpText) }