SV CLI: var init (#13820)
* Nomad dep: add museli/reflow * SV CLI: var init
This commit is contained in:
parent
81cac313c5
commit
dba6b39815
|
@ -911,6 +911,11 @@ func Commands(metaPtr *Meta, agentUi cli.Ui) map[string]cli.CommandFactory {
|
|||
Meta: meta,
|
||||
}, nil
|
||||
},
|
||||
"var init": func() (cli.Command, error) {
|
||||
return &VarInitCommand{
|
||||
Meta: meta,
|
||||
}, nil
|
||||
},
|
||||
"version": func() (cli.Command, error) {
|
||||
return &VersionCommand{
|
||||
Version: version.GetVersion(),
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/muesli/reflow/wordwrap"
|
||||
"github.com/posener/complete"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultHclVarInitName is the default name we use when initializing the
|
||||
// example var file in HCL format
|
||||
DefaultHclVarInitName = "spec.nsv.hcl"
|
||||
|
||||
// DefaultHclVarInitName is the default name we use when initializing the
|
||||
// example var file in JSON format
|
||||
DefaultJsonVarInitName = "spec.nsv.json"
|
||||
)
|
||||
|
||||
// VarInitCommand generates a new secure variable specification
|
||||
type VarInitCommand struct {
|
||||
Meta
|
||||
}
|
||||
|
||||
func (c *VarInitCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: nomad var init <filename>
|
||||
|
||||
Creates an example secure variable specification file that can be used as a
|
||||
starting point to customize further. If no filename is given, the default of
|
||||
"spec.nsv.hcl" or "spec.nsv.json" will be used.
|
||||
|
||||
Init Options:
|
||||
|
||||
-json
|
||||
Create an example JSON secure variable specification.
|
||||
|
||||
-q
|
||||
Suppress non-error output
|
||||
`
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
||||
func (c *VarInitCommand) Synopsis() string {
|
||||
return "Create an example secure variable specification file"
|
||||
}
|
||||
|
||||
func (c *VarInitCommand) AutocompleteFlags() complete.Flags {
|
||||
return complete.Flags{
|
||||
"-json": complete.PredictNothing,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *VarInitCommand) AutocompleteArgs() complete.Predictor {
|
||||
return complete.PredictNothing
|
||||
}
|
||||
|
||||
func (c *VarInitCommand) Name() string { return "var init" }
|
||||
|
||||
func (c *VarInitCommand) Run(args []string) int {
|
||||
var jsonOutput bool
|
||||
var quiet bool
|
||||
|
||||
flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
|
||||
flags.Usage = func() { c.Ui.Output(c.Help()) }
|
||||
flags.BoolVar(&jsonOutput, "json", false, "")
|
||||
flags.BoolVar(&quiet, "q", 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
|
||||
}
|
||||
|
||||
fileName := DefaultHclVarInitName
|
||||
fileContent := defaultHclVarSpec
|
||||
if jsonOutput {
|
||||
fileName = DefaultJsonVarInitName
|
||||
fileContent = defaultJsonVarSpec
|
||||
}
|
||||
if len(args) == 1 {
|
||||
fileName = args[0]
|
||||
}
|
||||
|
||||
// Check if the file already exists
|
||||
_, err := os.Stat(fileName)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to stat %q: %v", fileName, err))
|
||||
return 1
|
||||
}
|
||||
if !os.IsNotExist(err) {
|
||||
c.Ui.Error(fmt.Sprintf("File %q already exists", fileName))
|
||||
return 1
|
||||
}
|
||||
|
||||
// Write out the example
|
||||
err = ioutil.WriteFile(fileName, []byte(fileContent), 0660)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to write %q: %v", fileName, err))
|
||||
return 1
|
||||
}
|
||||
|
||||
// Success
|
||||
if !quiet {
|
||||
c.Ui.Warn(WrapAndPrepend(TidyRawString(msgWarnKeys), 70, ""))
|
||||
c.Ui.Output(fmt.Sprintf("Example secure variable specification written to %s", fileName))
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
const (
|
||||
msgWarnKeys = `
|
||||
REMINDER: While keys in the items map can contain dots, using them in
|
||||
templates is easier when they do not. As a best practice, avoid dotted
|
||||
keys when possible.`
|
||||
)
|
||||
|
||||
var defaultHclVarSpec = strings.TrimSpace(`
|
||||
# A secure variable path can be specified in the specification file
|
||||
# and will be used when writing the variable without specifying a
|
||||
# path in the command or when writing JSON directly to the `+"`/var/`"+`
|
||||
# HTTP API endpoint
|
||||
# path = "path/to/variable"
|
||||
|
||||
# The Namespace to write the variable can be included in the specification
|
||||
# and is the highest precedence way to set the namespace value.
|
||||
# namespace = "default"
|
||||
|
||||
# The items map is the only strictly required part of a secure variable
|
||||
# specification, since path and namespace can be set via other means. It
|
||||
# contains the sensitive material to encrypt and store as a Nomad secure
|
||||
# variable. The entire items map is encrypted and decrypted as a single unit.
|
||||
|
||||
`+warnInHCLFile()+`
|
||||
items {
|
||||
key1 = "value 1"
|
||||
key2 = "value 2"
|
||||
}
|
||||
`) + "\n"
|
||||
|
||||
var defaultJsonVarSpec = strings.TrimSpace(`
|
||||
{
|
||||
"Items": {
|
||||
"key1": "value 1",
|
||||
"key2": "value 2"
|
||||
}
|
||||
}
|
||||
`) + "\n"
|
||||
|
||||
func warnInHCLFile() string {
|
||||
return WrapAndPrepend(TidyRawString(msgWarnKeys), 70, "# ")
|
||||
}
|
||||
|
||||
// WrapString is a convienience func to abstract away the word wrapping
|
||||
// implementation
|
||||
func WrapString(input string, lineLen int) string {
|
||||
return wordwrap.String(input, lineLen)
|
||||
}
|
||||
|
||||
// WrapAndPrepend will word wrap the input string to lineLen characters and
|
||||
// prepend the provided prefix to every line. The total length of each returned
|
||||
// line will be at most len(input[line])+len(prefix)
|
||||
func WrapAndPrepend(input string, lineLen int, prefix string) string {
|
||||
ss := strings.Split(wordwrap.String(input, lineLen), "\n")
|
||||
prefixStringList(ss, prefix)
|
||||
return strings.Join(ss, "\n")
|
||||
}
|
||||
|
||||
// TidyRawString will convert a wrapped and indented raw string into a single
|
||||
// long string suitable for rewrapping with another tool. It trims leading and
|
||||
// trailing whitespace and then consume groups of tabs, newlines, and spaces
|
||||
// replacing them with a single space
|
||||
func TidyRawString(raw string) string {
|
||||
re := regexp.MustCompile("[\t\n ]+")
|
||||
return re.ReplaceAllString(strings.TrimSpace(raw), " ")
|
||||
}
|
||||
|
||||
func prefixStringList(ss []string, prefix string) []string {
|
||||
for i, s := range ss {
|
||||
ss[i] = prefix + s
|
||||
}
|
||||
return ss
|
||||
}
|
5
go.mod
5
go.mod
|
@ -223,13 +223,14 @@ require (
|
|||
github.com/linode/linodego v0.7.1 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.7 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.12 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||
github.com/mitchellh/pointerstructure v1.2.1 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/mrunalp/fileutils v0.5.0 // indirect
|
||||
github.com/muesli/reflow v0.3.0
|
||||
github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 // indirect
|
||||
github.com/oklog/run v1.1.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
|
@ -278,3 +279,5 @@ require (
|
|||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
require github.com/rivo/uniseg v0.2.0 // indirect
|
||||
|
|
8
go.sum
8
go.sum
|
@ -937,8 +937,9 @@ github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9
|
|||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
|
||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.12 h1:Y41i/hVW3Pgwr8gV+J23B9YEY0zxjptBuCWEaxmAOow=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
||||
github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
|
@ -1017,6 +1018,8 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
|||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/mrunalp/fileutils v0.5.0 h1:NKzVxiH7eSk+OQ4M+ZYW1K6h27RUV3MI6NUTsHhU6Z4=
|
||||
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
|
||||
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
|
@ -1164,6 +1167,9 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T
|
|||
github.com/rboyer/safeio v0.2.1/go.mod h1:Cq/cEPK+YXFn622lsQ0K4KsPZSPtaptHHEldsy7Fmig=
|
||||
github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 h1:Wdi9nwnhFNAlseAOekn6B5G/+GMtks9UKbvRU/CMM/o=
|
||||
github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03/go.mod h1:gRAiPF5C5Nd0eyyRdqIu9qTiFSoZzpTq727b5B8fkkU=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
|
|
Loading…
Reference in New Issue