// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 package command import ( "encoding/json" "fmt" "io" "os" "strings" "github.com/hashicorp/hcl/v2/hclsimple" "github.com/hashicorp/nomad/api" "github.com/posener/complete" ) type NodePoolApplyCommand struct { Meta } func (c *NodePoolApplyCommand) Name() string { return "node pool apply" } func (c *NodePoolApplyCommand) Synopsis() string { return "Create or update a node pool" } func (c *NodePoolApplyCommand) Help() string { helpText := ` Usage: nomad node pool apply [options] Apply is used to create or update a node pool. The specification file is read from stdin by specifying "-", otherwise a path to the file is expected. If ACLs are enabled, this command requires a token with the 'write' capability in a 'node_pool' policy that matches the node pool being targeted. In federated clusters, the node pool will be created in the authoritative region and will be replicated to all federated regions. General Options: ` + generalOptionsUsage(usageOptsDefault) + ` Apply Options: -json Parse the input as a JSON node pool specification. ` return strings.TrimSpace(helpText) } func (c *NodePoolApplyCommand) AutocompleteFlags() complete.Flags { return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient), complete.Flags{ "-json": complete.PredictNothing, }) } func (c *NodePoolApplyCommand) AutocompleteArgs() complete.Predictor { return complete.PredictOr( complete.PredictFiles("*.hcl"), complete.PredictFiles("*.json"), ) } func (c *NodePoolApplyCommand) Run(args []string) int { var jsonInput bool flags := c.Meta.FlagSet(c.Name(), FlagSetClient) flags.Usage = func() { c.Ui.Output(c.Help()) } flags.BoolVar(&jsonInput, "json", false, "") if err := flags.Parse(args); err != nil { return 1 } // Check that we only have one argument. args = flags.Args() if len(args) != 1 { c.Ui.Error("This command takes one argument: ") c.Ui.Error(commandErrorText(c)) return 1 } // Read input content. path := args[0] var content []byte var err error switch path { case "-": content, err = io.ReadAll(os.Stdin) if err != nil { c.Ui.Error(fmt.Sprintf("Failed to read stdin: %v", err)) return 1 } // Set .hcl extension so the decoder doesn't fail. if !jsonInput { path = "stdin.nomad.hcl" } default: content, err = os.ReadFile(path) if err != nil { c.Ui.Error(fmt.Sprintf("Failed to read file %q: %v", path, err)) return 1 } } // Parse input. var poolSpec nodePoolSpec if jsonInput { err = json.Unmarshal(content, &poolSpec.NodePool) } else { err = hclsimple.Decode(path, content, nil, &poolSpec) } if err != nil { c.Ui.Error(fmt.Sprintf("Failed to parse input content: %v", err)) return 1 } // Make API request. client, err := c.Meta.Client() if err != nil { c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err)) return 1 } _, err = client.NodePools().Register(poolSpec.NodePool, nil) if err != nil { c.Ui.Error(fmt.Sprintf("Error applying node pool: %s", err)) return 1 } c.Ui.Output(fmt.Sprintf("Successfully applied node pool %q!", poolSpec.NodePool.Name)) return 0 } type nodePoolSpec struct { NodePool *api.NodePool `hcl:"node_pool,block"` }