forked from hashicorp/nomad
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
node pools: implement
node pool init
command (hashicorp#17479)
Implement a `nomad node pool init` command that generates an example spec file in either HCL or JSON format.
- Loading branch information
Showing
8 changed files
with
334 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
node_pool "example" { | ||
|
||
description = "Example node pool" | ||
|
||
# meta is optional metadata on the node pool, defined as key-value pairs. | ||
# The scheduler does not use node pool metadata as part of scheduling. | ||
meta { | ||
environment = "prod" | ||
owner = "sre" | ||
} | ||
|
||
# The scheduler configuration options specific to this node pool. This block | ||
# supports a subset of the fields supported in the global scheduler | ||
# configuration as described at: | ||
# https://developer.hashicorp.com/nomad/docs/commands/operator/scheduler/set-config | ||
# | ||
# * scheduler_algorithm is the scheduling algorithm to use for the pool. | ||
# If not defined, the global cluster scheduling algorithm is used. | ||
# | ||
# Available only in Nomad Enterprise. | ||
|
||
# scheduler_configuration { | ||
# scheduler_algorithm = "spread" | ||
# } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"Name": "example", | ||
"Description": "Example node pool", | ||
"Meta": { | ||
"environment": "prod", | ||
"owner": "sre" | ||
}, | ||
"SchedulerConfiguration": { | ||
"SchedulerAlgorithm": "spread" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package command | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"io/fs" | ||
"os" | ||
"strings" | ||
|
||
"github.com/hashicorp/nomad/command/asset" | ||
"github.com/posener/complete" | ||
) | ||
|
||
const ( | ||
// DefaultHclNodePoolInitName is the default name we use when initializing | ||
// the example node pool spec file in HCL format | ||
DefaultHclNodePoolInitName = "pool.nomad.hcl" | ||
|
||
// DefaultJsonNodePoolInitName is the default name we use when initializing | ||
// the example node pool spec in JSON format | ||
DefaultJsonNodePoolInitName = "pool.nomad.json" | ||
) | ||
|
||
// NodePoolInitCommand generates a new variable specification | ||
type NodePoolInitCommand struct { | ||
Meta | ||
} | ||
|
||
func (c *NodePoolInitCommand) Help() string { | ||
helpText := ` | ||
Usage: nomad node pool init <filename> | ||
Creates an example node pool specification file that can be used as a starting | ||
point to customize further. When no filename is supplied, a default filename | ||
of "pool.nomad.hcl" or "pool.nomad.json" will be used depending on the output | ||
format. | ||
Init Options: | ||
-out (hcl | json) | ||
Format of generated node pool specification. Defaults to "hcl". | ||
-quiet | ||
Do not print success message. | ||
` | ||
return strings.TrimSpace(helpText) | ||
} | ||
|
||
func (c *NodePoolInitCommand) Synopsis() string { | ||
return "Create an example node pool specification file" | ||
} | ||
|
||
func (c *NodePoolInitCommand) AutocompleteFlags() complete.Flags { | ||
return complete.Flags{ | ||
"-out": complete.PredictSet("hcl", "json"), | ||
"-quiet": complete.PredictNothing, | ||
} | ||
} | ||
|
||
func (c *NodePoolInitCommand) AutocompleteArgs() complete.Predictor { | ||
return complete.PredictNothing | ||
} | ||
|
||
func (c *NodePoolInitCommand) Name() string { return "node pool init" } | ||
|
||
func (c *NodePoolInitCommand) Run(args []string) int { | ||
var outFmt string | ||
var quiet bool | ||
|
||
flags := c.Meta.FlagSet(c.Name(), FlagSetClient) | ||
flags.Usage = func() { c.Ui.Output(c.Help()) } | ||
flags.StringVar(&outFmt, "out", "hcl", "") | ||
flags.BoolVar(&quiet, "quiet", false, "") | ||
|
||
if err := flags.Parse(args); err != nil { | ||
return 1 | ||
} | ||
|
||
// Check that we get no arguments | ||
args = flags.Args() | ||
if l := len(args); l > 1 { | ||
c.Ui.Error("This command takes no arguments or one: <filename>") | ||
c.Ui.Error(commandErrorText(c)) | ||
return 1 | ||
} | ||
var fileName string | ||
var fileContent []byte | ||
switch outFmt { | ||
case "hcl": | ||
fileName = DefaultHclNodePoolInitName | ||
fileContent = asset.NodePoolSpec | ||
case "json": | ||
fileName = DefaultJsonNodePoolInitName | ||
fileContent = asset.NodePoolSpecJSON | ||
} | ||
|
||
if len(args) == 1 { | ||
fileName = args[0] | ||
} | ||
|
||
// Check if the file already exists | ||
_, err := os.Stat(fileName) | ||
if err == nil { | ||
c.Ui.Error(fmt.Sprintf("File %q already exists", fileName)) | ||
return 1 | ||
} | ||
if err != nil && !errors.Is(err, fs.ErrNotExist) { | ||
c.Ui.Error(fmt.Sprintf("Failed to stat %q: %v", fileName, err)) | ||
return 1 | ||
} | ||
|
||
// Write out the example | ||
err = os.WriteFile(fileName, fileContent, 0660) | ||
if err != nil { | ||
c.Ui.Error(fmt.Sprintf("Failed to write %q: %v", fileName, err)) | ||
return 1 | ||
} | ||
|
||
// Success | ||
if !quiet { | ||
c.Ui.Output(fmt.Sprintf("Example node pool specification written to %s", fileName)) | ||
} | ||
return 0 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package command | ||
|
||
import ( | ||
"os" | ||
"path" | ||
"testing" | ||
|
||
"github.com/mitchellh/cli" | ||
"github.com/shoenig/test/must" | ||
|
||
"github.com/hashicorp/nomad/ci" | ||
"github.com/hashicorp/nomad/command/asset" | ||
) | ||
|
||
func TestNodePoolInitCommand_Implements(t *testing.T) { | ||
ci.Parallel(t) | ||
var _ cli.Command = &NodePoolInitCommand{} | ||
} | ||
|
||
func TestNodePoolInitCommand_Run(t *testing.T) { | ||
ci.Parallel(t) | ||
dir := t.TempDir() | ||
origDir, err := os.Getwd() | ||
must.NoError(t, err) | ||
err = os.Chdir(dir) | ||
must.NoError(t, err) | ||
t.Cleanup(func() { os.Chdir(origDir) }) | ||
|
||
t.Run("hcl", func(t *testing.T) { | ||
ci.Parallel(t) | ||
dir := dir | ||
ui := cli.NewMockUi() | ||
cmd := &NodePoolInitCommand{Meta: Meta{Ui: ui}} | ||
|
||
// Fails on misuse | ||
ec := cmd.Run([]string{"some", "bad", "args"}) | ||
must.Eq(t, 1, ec) | ||
must.StrContains(t, ui.ErrorWriter.String(), commandErrorText(cmd)) | ||
must.Eq(t, "", ui.OutputWriter.String()) | ||
reset(ui) | ||
|
||
// Works if the file doesn't exist | ||
ec = cmd.Run([]string{"-out", "hcl"}) | ||
must.Eq(t, "", ui.ErrorWriter.String()) | ||
must.Eq(t, "Example node pool specification written to pool.nomad.hcl\n", ui.OutputWriter.String()) | ||
must.Zero(t, ec) | ||
reset(ui) | ||
t.Cleanup(func() { os.Remove(path.Join(dir, "pool.nomad.hcl")) }) | ||
|
||
content, err := os.ReadFile(DefaultHclNodePoolInitName) | ||
must.NoError(t, err) | ||
must.Eq(t, asset.NodePoolSpec, content) | ||
|
||
// Fails if the file exists | ||
ec = cmd.Run([]string{"-out", "hcl"}) | ||
must.StrContains(t, ui.ErrorWriter.String(), "exists") | ||
must.Eq(t, "", ui.OutputWriter.String()) | ||
must.Eq(t, 1, ec) | ||
reset(ui) | ||
|
||
// Works if file is passed | ||
ec = cmd.Run([]string{"-out", "hcl", "myTest.hcl"}) | ||
must.Eq(t, "", ui.ErrorWriter.String()) | ||
must.Eq(t, "Example node pool specification written to myTest.hcl\n", ui.OutputWriter.String()) | ||
must.Zero(t, ec) | ||
reset(ui) | ||
|
||
t.Cleanup(func() { os.Remove(path.Join(dir, "myTest.hcl")) }) | ||
content, err = os.ReadFile("myTest.hcl") | ||
must.NoError(t, err) | ||
must.Eq(t, asset.NodePoolSpec, content) | ||
}) | ||
|
||
t.Run("json", func(t *testing.T) { | ||
ci.Parallel(t) | ||
dir := dir | ||
ui := cli.NewMockUi() | ||
cmd := &NodePoolInitCommand{Meta: Meta{Ui: ui}} | ||
|
||
// Fails on misuse | ||
code := cmd.Run([]string{"some", "bad", "args"}) | ||
must.Eq(t, 1, code) | ||
must.StrContains(t, ui.ErrorWriter.String(), "This command takes no arguments or one") | ||
must.Eq(t, "", ui.OutputWriter.String()) | ||
reset(ui) | ||
|
||
// Works if the file doesn't exist | ||
code = cmd.Run([]string{"-out", "json"}) | ||
must.StrContains(t, ui.OutputWriter.String(), "Example node pool specification written to pool.nomad.json\n") | ||
must.Zero(t, code) | ||
reset(ui) | ||
|
||
t.Cleanup(func() { os.Remove(path.Join(dir, "pool.nomad.json")) }) | ||
content, err := os.ReadFile(DefaultJsonNodePoolInitName) | ||
must.NoError(t, err) | ||
must.Eq(t, asset.NodePoolSpecJSON, content) | ||
|
||
// Fails if the file exists | ||
code = cmd.Run([]string{"-out", "json"}) | ||
must.StrContains(t, ui.ErrorWriter.String(), "exists") | ||
must.Eq(t, "", ui.OutputWriter.String()) | ||
must.Eq(t, 1, code) | ||
reset(ui) | ||
|
||
// Works if file is passed | ||
code = cmd.Run([]string{"-out", "json", "myTest.json"}) | ||
must.StrContains(t, ui.OutputWriter.String(), "Example node pool specification written to myTest.json\n") | ||
must.Zero(t, code) | ||
reset(ui) | ||
|
||
t.Cleanup(func() { os.Remove(path.Join(dir, "myTest.json")) }) | ||
content, err = os.ReadFile("myTest.json") | ||
must.NoError(t, err) | ||
must.Eq(t, asset.NodePoolSpecJSON, content) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
--- | ||
layout: docs | ||
page_title: 'Commands: node pool init' | ||
description: | | ||
Generate an example node pool specification. | ||
--- | ||
|
||
# Command: node pool init | ||
|
||
The `node pool init` creates an example node pool specification file that can be | ||
used as a starting point to customize further. | ||
|
||
## Usage | ||
|
||
```plaintext | ||
nomad node pool init <filename> | ||
``` | ||
|
||
When no filename is supplied, a default filename of "pool.nomad.hcl" or | ||
"pool.nomad.json" will be used depending on the output format. | ||
|
||
## Init Options | ||
|
||
- `-out` `(enum: hcl | json)`: Format of generated node pool | ||
specification. Defaults to `hcl`. | ||
|
||
- `-quiet`: Do not print success message. | ||
|
||
## Examples | ||
|
||
Create an example node pool specification: | ||
|
||
```shell-session | ||
$ nomad node pool init | ||
Example node pool specification written to pool.nomad.hcl | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters