From 74abe6ef48115f72f0b0ae619debae19fdcb2f66 Mon Sep 17 00:00:00 2001 From: Drew Bailey <2614075+drewbailey@users.noreply.github.com> Date: Tue, 21 Apr 2020 14:55:31 -0400 Subject: [PATCH 1/6] license cli commands cli changes, formatting --- api/operator.go | 87 ++++++++++++++++++++++++- command/commands.go | 15 +++++ command/license.go | 99 +++++++++++++++++++++++++++++ command/license_get.go | 63 +++++++++++++++++++ command/license_put.go | 122 ++++++++++++++++++++++++++++++++++++ command/license_put_test.go | 13 ++++ 6 files changed, 398 insertions(+), 1 deletion(-) create mode 100644 command/license.go create mode 100644 command/license_get.go create mode 100644 command/license_put.go create mode 100644 command/license_put_test.go diff --git a/api/operator.go b/api/operator.go index 72d8fbdd2..6f20c1982 100644 --- a/api/operator.go +++ b/api/operator.go @@ -1,6 +1,9 @@ package api -import "strconv" +import ( + "strconv" + "time" +) // Operator can be used to perform low-level operator tasks for Nomad. type Operator struct { @@ -176,3 +179,85 @@ func (op *Operator) SchedulerCASConfiguration(conf *SchedulerConfiguration, q *W return &out, wm, nil } + +type License struct { + // The unique identifier of the license + LicenseID string `json:"license_id"` + + // The customer ID associated with the license + CustomerID string `json:"customer_id"` + + // If set, an identifier that should be used to lock the license to a + // particular site, cluster, etc. + InstallationID string `json:"installation_id"` + + // The time at which the license was issued + IssueTime time.Time `json:"issue_time"` + + // The time at which the license starts being valid + StartTime time.Time `json:"start_time"` + + // The time after which the license expires + ExpirationTime time.Time `json:"expiration_time"` + + // The time at which the license ceases to function and can + // no longer be used in any capacity + TerminationTime time.Time `json:"termination_time"` + + // The product the license is valid for + Product string `json:"product"` + + // License Specific Flags + Flags map[string]interface{} `json:"flags"` + + // Modules is a list of the licensed enterprise modules + Modules []string `json:"modules"` + + // List of features enabled by the license + Features []string `json:"features"` +} + +type LicenseReply struct { + Valid bool + License *License + Warnings []string + QueryMeta +} + +func (op *Operator) LicensePut(license string, q *WriteOptions) (*LicenseReply, *WriteMeta, error) { + var resp LicenseReply + wm, err := op.c.write("/v1/operator/license", license, &resp, q) + if err != nil { + return nil, nil, err + } + return &resp, wm, nil +} + +func (op *Operator) LicenseGet(q *QueryOptions) (*LicenseReply, *QueryMeta, error) { + var reply LicenseReply + qm, err := op.c.query("/v1/operator/license", &reply, q) + if err != nil { + return nil, nil, err + } + return &reply, qm, nil +} + +func (op *Operator) LicenseGetSigned(q *QueryOptions) (string, *QueryMeta, error) { + var reply string + qm, err := op.c.query("/v1/operator/license?signed=true", &reply, q) + if err != nil { + return "", nil, err + } + return reply, qm, nil +} + +// LicenseReset will reset the license to the builtin one if it is still valid. +// If the builtin license is invalid, the current license stays active +func (op *Operator) LicenseReset(q *WriteOptions) (*LicenseReply, *WriteMeta, error) { + var reply LicenseReply + wm, err := op.c.delete("/v1/operator/license", &reply, q) + if err != nil { + return nil, nil, err + } + return &reply, wm, nil +} diff --git a/command/commands.go b/command/commands.go index dbbcec006..922a07e8c 100644 --- a/command/commands.go +++ b/command/commands.go @@ -361,6 +361,21 @@ func Commands(metaPtr *Meta, agentUi cli.Ui) map[string]cli.CommandFactory { Meta: meta, }, nil }, + "license": func() (cli.Command, error) { + return &LicenseCommand{ + Meta: meta, + }, nil + }, + "license get": func() (cli.Command, error) { + return &LicenseGetCommand{ + Meta: meta, + }, nil + }, + "license put": func() (cli.Command, error) { + return &LicensePutCommand{ + Meta: meta, + }, nil + }, "logs": func() (cli.Command, error) { return &AllocLogsCommand{ Meta: meta, diff --git a/command/license.go b/command/license.go new file mode 100644 index 000000000..e3ec7a1a7 --- /dev/null +++ b/command/license.go @@ -0,0 +1,99 @@ +package command + +import ( + "fmt" + "strings" + "time" + + "github.com/hashicorp/nomad/api" + "github.com/mitchellh/cli" +) + +type LicenseCommand struct { + Meta +} + +func (l *LicenseCommand) Help() string { + helpText := ` +Usage: nomad license [options] [args] + +This command has subcommands for managing the Nomad Enterprise license. +For more detailed examples see: +https://www.nomadproject.io/docs/commands/license/ + +Install a new license from a file: + $ nomad license put @nomad.license + +Install a new license from stdin: + $ nomad license put - + +Install a new license from a string: + $ nomad license put "" + +Retrieve the current license: + + $ nomad license get + +Reset the current license: + $ nomad license reset + ` + return strings.TrimSpace(helpText) +} + +func (l *LicenseCommand) Synopsis() string { + return "Interact with Nomad Enterprise License" +} + +func (l *LicenseCommand) Name() string { return "license" } + +func (l *LicenseCommand) Run(args []string) int { + return cli.RunResultHelp +} + +func OutputLicenseReply(ui cli.Ui, resp *api.LicenseReply) int { + if resp.Valid { + ui.Output("License is valid") + outputLicenseInfo(ui, resp.License, false) + return 0 + } else if resp.License != nil { + now := time.Now() + if resp.License.ExpirationTime.Before(now) { + ui.Output("License has expired!") + outputLicenseInfo(ui, resp.License, true) + } else { + ui.Output("License is invalid!") + for _, warn := range resp.Warnings { + ui.Output(fmt.Sprintf(" %s", warn)) + } + outputLicenseInfo(ui, resp.License, false) + } + return 1 + } else { + // TODO - remove the expired message here in the future + // once the go-licensing library is updated post 1.1 + ui.Output("Nomad is unlicensed or the license has expired") + return 0 + } +} + +func outputLicenseInfo(ui cli.Ui, lic *api.License, expired bool) { + ui.Output(fmt.Sprintf("License ID: %s", lic.LicenseID)) + ui.Output(fmt.Sprintf("Customer ID: %s", lic.CustomerID)) + if expired { + ui.Output(fmt.Sprintf("Expired At: %s", lic.ExpirationTime.String())) + } else { + ui.Output(fmt.Sprintf("Expires At: %s", lic.ExpirationTime.String())) + } + ui.Output(fmt.Sprintf("Terminates At: %s", lic.TerminationTime.String())) + ui.Output(fmt.Sprintf("Datacenter: %s", lic.InstallationID)) + if len(lic.Modules) > 0 { + ui.Output("Modules:") + for _, mod := range lic.Modules { + ui.Output(fmt.Sprintf("\t%v", mod)) + } + } + ui.Output("Licensed Features:") + for _, f := range lic.Features { + ui.Output(fmt.Sprintf("\t%s", f)) + } +} diff --git a/command/license_get.go b/command/license_get.go new file mode 100644 index 000000000..64fd06789 --- /dev/null +++ b/command/license_get.go @@ -0,0 +1,63 @@ +package command + +import ( + "fmt" +) + +type LicenseGetCommand struct { + Meta +} + +func (c *LicenseGetCommand) Help() string { + helpText := ` +Usage: nomad license put [options] + +Gets a new license in Servers and Clients +General Options: + + ` + generalOptionsUsage() + ` + +Get Options: + + -signed + Determines if the returned license should be a signed blob instead of a + parsed license. + + ` + return helpText +} + +func (c *LicenseGetCommand) Synopsis() string { + return "Install a new Nomad Enterprise License" +} + +func (c *LicenseGetCommand) Name() string { return "license get" } + +func (c *LicenseGetCommand) Run(args []string) int { + var signed bool + + flags := c.Meta.FlagSet(c.Name(), FlagSetClient) + flags.Usage = func() { c.Ui.Output(c.Help()) } + flags.BoolVar(&signed, "signed", false, "Gets the signed license blob instead of a parsed license") + + client, err := c.Meta.Client() + if err != nil { + c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err)) + } + + if signed { + resp, _, err := client.Operator().LicenseGetSigned(nil) + if err != nil { + c.Ui.Error(fmt.Sprintf("Error getting signed license: %v", err)) + } + c.Ui.Output(resp) + return 0 + } + + resp, _, err := client.Operator().LicenseGet(nil) + if err != nil { + c.Ui.Error(fmt.Sprintf("Error putting license: %v", err)) + } + + return OutputLicenseReply(c.Ui, resp) +} diff --git a/command/license_put.go b/command/license_put.go new file mode 100644 index 000000000..c5ac4cd74 --- /dev/null +++ b/command/license_put.go @@ -0,0 +1,122 @@ +package command + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + + "github.com/pkg/errors" +) + +type LicensePutCommand struct { + Meta + + testStdin io.Reader +} + +func (c *LicensePutCommand) Help() string { + helpText := ` +Usage: nomad license put [options] + +Puts a new license in Servers and Clients +General Options: + ` + generalOptionsUsage() + ` +Install a new license from a file: + $ nomad license put @nomad.license +Install a new license from stdin: + $ nomad license put - +Install a new license from a string: + $ nomad license put "" + ` + return helpText +} + +func (c *LicensePutCommand) Synopsis() string { + return "Install a new Nomad Enterprise License" +} + +func (c *LicensePutCommand) Name() string { return "license put" } + +func (c *LicensePutCommand) Run(args []string) int { + flags := c.Meta.FlagSet(c.Name(), FlagSetClient) + flags.Usage = func() { c.Ui.Output(c.Help()) } + + if err := flags.Parse(args); err != nil { + c.Ui.Error(fmt.Sprintf("Error parsing flags: %s", err)) + return 1 + } + + args = flags.Args() + data, err := c.dataFromArgs(args) + if err != nil { + c.Ui.Error(errors.Wrap(err, "Error parsing arguments").Error()) + return 1 + } + + client, err := c.Meta.Client() + if err != nil { + c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err)) + return 1 + } + + resp, _, err := client.Operator().LicensePut(data, nil) + if err != nil { + c.Ui.Error(fmt.Sprintf("Error putting license: %v", err)) + return 1 + } + + return OutputLicenseReply(c.Ui, resp) +} + +func (c *LicensePutCommand) dataFromArgs(args []string) (string, error) { + switch len(args) { + case 0: + return "", fmt.Errorf("Missing LICENSE argument") + case 1: + return LoadDataSource(args[0], c.testStdin) + default: + return "", fmt.Errorf("Too many arguments, exptected 1, got %d", len(args)) + } +} + +func loadFromFile(path string) (string, error) { + data, err := ioutil.ReadFile(path) + if err != nil { + return "", fmt.Errorf("Failed to read file: %v", err) + } + return string(data), nil +} + +func loadFromStdin(testStdin io.Reader) (string, error) { + var stdin io.Reader = os.Stdin + if testStdin != nil { + stdin = testStdin + } + + var b bytes.Buffer + if _, err := io.Copy(&b, stdin); err != nil { + return "", fmt.Errorf("Failed to read stdin: %v", err) + } + return b.String(), nil +} + +func LoadDataSource(data string, testStdin io.Reader) (string, error) { + // Handle empty quoted shell parameters + if len(data) == 0 { + return "", nil + } + + switch data[0] { + case '@': + return loadFromFile(data[1:]) + case '-': + if len(data) > 1 { + return data, nil + } + return loadFromStdin(testStdin) + default: + return data, nil + } +} diff --git a/command/license_put_test.go b/command/license_put_test.go new file mode 100644 index 000000000..ed47ca978 --- /dev/null +++ b/command/license_put_test.go @@ -0,0 +1,13 @@ +package command + +import ( + "testing" + + "github.com/mitchellh/cli" +) + +var _ cli.Command = &LicensePutCommand{} + +func TestCommand_LicensePut(t *testing.T) { + // TODO create test once http endpoints are configured +} From 59b76f90e872fc358be414f68ce10519ca3db9dc Mon Sep 17 00:00:00 2001 From: Drew Bailey <2614075+drewbailey@users.noreply.github.com> Date: Tue, 21 Apr 2020 15:59:39 -0400 Subject: [PATCH 2/6] hcl fmt from editor license cli formatting, license endpoints ent only test oss error type assertions --- command/agent/http_oss.go | 2 ++ command/license.go | 2 ++ command/license_get.go | 6 ++++++ command/license_put.go | 10 +++++++++- command/license_put_test.go | 19 +++++++++++++++++-- 5 files changed, 36 insertions(+), 3 deletions(-) diff --git a/command/agent/http_oss.go b/command/agent/http_oss.go index a323bda1b..61de29347 100644 --- a/command/agent/http_oss.go +++ b/command/agent/http_oss.go @@ -19,6 +19,8 @@ func (s *HTTPServer) registerEnterpriseHandlers() { s.mux.HandleFunc("/v1/quota-usages", s.wrap(s.entOnly)) s.mux.HandleFunc("/v1/quota/", s.wrap(s.entOnly)) s.mux.HandleFunc("/v1/quota", s.wrap(s.entOnly)) + + s.mux.HandleFunc("/v1/operator/license", s.wrap(s.entOnly)) } func (s *HTTPServer) entOnly(resp http.ResponseWriter, req *http.Request) (interface{}, error) { diff --git a/command/license.go b/command/license.go index e3ec7a1a7..92a02b4c9 100644 --- a/command/license.go +++ b/command/license.go @@ -9,6 +9,8 @@ import ( "github.com/mitchellh/cli" ) +var _ cli.Command = &LicenseCommand{} + type LicenseCommand struct { Meta } diff --git a/command/license_get.go b/command/license_get.go index 64fd06789..a5a7d7e51 100644 --- a/command/license_get.go +++ b/command/license_get.go @@ -2,8 +2,12 @@ package command import ( "fmt" + + "github.com/mitchellh/cli" ) +var _ cli.Command = &LicenseGetCommand{} + type LicenseGetCommand struct { Meta } @@ -43,6 +47,7 @@ func (c *LicenseGetCommand) Run(args []string) int { client, err := c.Meta.Client() if err != nil { c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err)) + return 0 } if signed { @@ -57,6 +62,7 @@ func (c *LicenseGetCommand) Run(args []string) int { resp, _, err := client.Operator().LicenseGet(nil) if err != nil { c.Ui.Error(fmt.Sprintf("Error putting license: %v", err)) + return 0 } return OutputLicenseReply(c.Ui, resp) diff --git a/command/license_put.go b/command/license_put.go index c5ac4cd74..2434f7297 100644 --- a/command/license_put.go +++ b/command/license_put.go @@ -21,13 +21,21 @@ func (c *LicensePutCommand) Help() string { Usage: nomad license put [options] Puts a new license in Servers and Clients + General Options: - ` + generalOptionsUsage() + ` + + ` + generalOptionsUsage() + ` + Install a new license from a file: + $ nomad license put @nomad.license + Install a new license from stdin: + $ nomad license put - + Install a new license from a string: + $ nomad license put "" ` return helpText diff --git a/command/license_put_test.go b/command/license_put_test.go index ed47ca978..42b4a6758 100644 --- a/command/license_put_test.go +++ b/command/license_put_test.go @@ -1,13 +1,28 @@ package command import ( + "strings" "testing" "github.com/mitchellh/cli" + "github.com/stretchr/testify/require" ) var _ cli.Command = &LicensePutCommand{} -func TestCommand_LicensePut(t *testing.T) { - // TODO create test once http endpoints are configured +func TestCommand_LicensePut_OSSErr(t *testing.T) { + t.Parallel() + + srv, _, url := testServer(t, false, nil) + defer srv.Shutdown() + + ui := new(cli.MockUi) + cmd := &LicensePutCommand{Meta: Meta{Ui: ui}, testStdin: strings.NewReader("testlicenseblob")} + + if code := cmd.Run([]string{"-address=" + url, "-"}); code != 1 { + require.Equal(t, code, 1) + } + + require.Contains(t, ui.ErrorWriter.String(), "Nomad Enterprise only endpoint") + } From a266284f6008890572cfb90831a4ce1996d4f538 Mon Sep 17 00:00:00 2001 From: Drew Bailey <2614075+drewbailey@users.noreply.github.com> Date: Wed, 22 Apr 2020 09:42:25 -0400 Subject: [PATCH 3/6] test all commands oss err --- command/license_get.go | 18 ++++++------ command/license_get_test.go | 27 ++++++++++++++++++ command/license_reset.go | 54 +++++++++++++++++++++++++++++++++++ command/license_reset_test.go | 27 ++++++++++++++++++ 4 files changed, 118 insertions(+), 8 deletions(-) create mode 100644 command/license_get_test.go create mode 100644 command/license_reset.go create mode 100644 command/license_reset_test.go diff --git a/command/license_get.go b/command/license_get.go index a5a7d7e51..8dcc7abeb 100644 --- a/command/license_get.go +++ b/command/license_get.go @@ -2,19 +2,15 @@ package command import ( "fmt" - - "github.com/mitchellh/cli" ) -var _ cli.Command = &LicenseGetCommand{} - type LicenseGetCommand struct { Meta } func (c *LicenseGetCommand) Help() string { helpText := ` -Usage: nomad license put [options] +Usage: nomad license get [options] Gets a new license in Servers and Clients General Options: @@ -44,16 +40,22 @@ func (c *LicenseGetCommand) Run(args []string) int { flags.Usage = func() { c.Ui.Output(c.Help()) } flags.BoolVar(&signed, "signed", false, "Gets the signed license blob instead of a parsed license") + if err := flags.Parse(args); err != nil { + c.Ui.Error(fmt.Sprintf("Error parsing flags: %s", err)) + return 1 + } + client, err := c.Meta.Client() if err != nil { c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err)) - return 0 + return 1 } if signed { resp, _, err := client.Operator().LicenseGetSigned(nil) if err != nil { c.Ui.Error(fmt.Sprintf("Error getting signed license: %v", err)) + return 1 } c.Ui.Output(resp) return 0 @@ -61,8 +63,8 @@ func (c *LicenseGetCommand) Run(args []string) int { resp, _, err := client.Operator().LicenseGet(nil) if err != nil { - c.Ui.Error(fmt.Sprintf("Error putting license: %v", err)) - return 0 + c.Ui.Error(fmt.Sprintf("Error getting license: %v", err)) + return 1 } return OutputLicenseReply(c.Ui, resp) diff --git a/command/license_get_test.go b/command/license_get_test.go new file mode 100644 index 000000000..ae3a67c82 --- /dev/null +++ b/command/license_get_test.go @@ -0,0 +1,27 @@ +package command + +import ( + "testing" + + "github.com/mitchellh/cli" + "github.com/stretchr/testify/require" +) + +var _ cli.Command = &LicenseGetCommand{} + +func TestCommand_LicenseGet_OSSErr(t *testing.T) { + t.Parallel() + + srv, _, url := testServer(t, false, nil) + defer srv.Shutdown() + + ui := new(cli.MockUi) + cmd := &LicenseGetCommand{Meta: Meta{Ui: ui}} + + if code := cmd.Run([]string{"-address=" + url}); code != 1 { + require.Equal(t, 1, code) + } + + require.Contains(t, ui.ErrorWriter.String(), "Nomad Enterprise only endpoint") + +} diff --git a/command/license_reset.go b/command/license_reset.go new file mode 100644 index 000000000..57ce1f96f --- /dev/null +++ b/command/license_reset.go @@ -0,0 +1,54 @@ +package command + +import ( + "fmt" +) + +type LicenseResetCommand struct { + Meta +} + +func (c *LicenseResetCommand) Help() string { + helpText := ` +Usage: nomad license reset [options] + +Resets the Nomad Server and Clients Enterprise license to the builtin one if +it is still valid. IF the builtin license is invalid, the current one stays +active. + +General Options: + + ` + generalOptionsUsage() + + return helpText +} + +func (c *LicenseResetCommand) Synopsis() string { + return "Install a new Nomad Enterprise License" +} + +func (c *LicenseResetCommand) Name() string { return "license reset" } + +func (c *LicenseResetCommand) Run(args []string) int { + flags := c.Meta.FlagSet(c.Name(), FlagSetClient) + flags.Usage = func() { c.Ui.Output(c.Help()) } + + if err := flags.Parse(args); err != nil { + c.Ui.Error(fmt.Sprintf("Error parsing flags: %s", err)) + return 1 + } + + client, err := c.Meta.Client() + if err != nil { + c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err)) + return 1 + } + + resp, _, err := client.Operator().LicenseReset(nil) + if err != nil { + c.Ui.Error(fmt.Sprintf("Error resetting license: %v", err)) + return 1 + } + + return OutputLicenseReply(c.Ui, resp) +} diff --git a/command/license_reset_test.go b/command/license_reset_test.go new file mode 100644 index 000000000..8b20aa0b4 --- /dev/null +++ b/command/license_reset_test.go @@ -0,0 +1,27 @@ +package command + +import ( + "testing" + + "github.com/mitchellh/cli" + "github.com/stretchr/testify/require" +) + +var _ cli.Command = &LicenseResetCommand{} + +func TestCommand_LicenseReset_OSSErr(t *testing.T) { + t.Parallel() + + srv, _, url := testServer(t, false, nil) + defer srv.Shutdown() + + ui := new(cli.MockUi) + cmd := &LicenseResetCommand{Meta: Meta{Ui: ui}} + + if code := cmd.Run([]string{"-address=" + url}); code != 1 { + require.Equal(t, 1, code) + } + + require.Contains(t, ui.ErrorWriter.String(), "Nomad Enterprise only endpoint") + +} From acacecc67b9099a5412ee100dd12a8e74f5af7c8 Mon Sep 17 00:00:00 2001 From: Drew Bailey <2614075+drewbailey@users.noreply.github.com> Date: Wed, 22 Apr 2020 10:19:51 -0400 Subject: [PATCH 4/6] add license reset command to commands help text formatting remove reset no signed option --- api/operator.go | 20 ------------- command/license_get.go | 21 +------------- command/license_put.go | 3 +- command/license_reset.go | 54 ----------------------------------- command/license_reset_test.go | 27 ------------------ 5 files changed, 3 insertions(+), 122 deletions(-) delete mode 100644 command/license_reset.go delete mode 100644 command/license_reset_test.go diff --git a/api/operator.go b/api/operator.go index 6f20c1982..d215326f4 100644 --- a/api/operator.go +++ b/api/operator.go @@ -241,23 +241,3 @@ func (op *Operator) LicenseGet(q *QueryOptions) (*LicenseReply, *QueryMeta, erro } return &reply, qm, nil } - -func (op *Operator) LicenseGetSigned(q *QueryOptions) (string, *QueryMeta, error) { - var reply string - qm, err := op.c.query("/v1/operator/license?signed=true", &reply, q) - if err != nil { - return "", nil, err - } - return reply, qm, nil -} - -// LicenseReset will reset the license to the builtin one if it is still valid. -// If the builtin license is invalid, the current license stays active -func (op *Operator) LicenseReset(q *WriteOptions) (*LicenseReply, *WriteMeta, error) { - var reply LicenseReply - wm, err := op.c.delete("/v1/operator/license", &reply, q) - if err != nil { - return nil, nil, err - } - return &reply, wm, nil -} diff --git a/command/license_get.go b/command/license_get.go index 8dcc7abeb..e46d56b46 100644 --- a/command/license_get.go +++ b/command/license_get.go @@ -15,15 +15,8 @@ Usage: nomad license get [options] Gets a new license in Servers and Clients General Options: - ` + generalOptionsUsage() + ` + ` + generalOptionsUsage() -Get Options: - - -signed - Determines if the returned license should be a signed blob instead of a - parsed license. - - ` return helpText } @@ -34,11 +27,9 @@ func (c *LicenseGetCommand) Synopsis() string { func (c *LicenseGetCommand) Name() string { return "license get" } func (c *LicenseGetCommand) Run(args []string) int { - var signed bool flags := c.Meta.FlagSet(c.Name(), FlagSetClient) flags.Usage = func() { c.Ui.Output(c.Help()) } - flags.BoolVar(&signed, "signed", false, "Gets the signed license blob instead of a parsed license") if err := flags.Parse(args); err != nil { c.Ui.Error(fmt.Sprintf("Error parsing flags: %s", err)) @@ -51,16 +42,6 @@ func (c *LicenseGetCommand) Run(args []string) int { return 1 } - if signed { - resp, _, err := client.Operator().LicenseGetSigned(nil) - if err != nil { - c.Ui.Error(fmt.Sprintf("Error getting signed license: %v", err)) - return 1 - } - c.Ui.Output(resp) - return 0 - } - resp, _, err := client.Operator().LicenseGet(nil) if err != nil { c.Ui.Error(fmt.Sprintf("Error getting license: %v", err)) diff --git a/command/license_put.go b/command/license_put.go index 2434f7297..d110c39c6 100644 --- a/command/license_put.go +++ b/command/license_put.go @@ -6,6 +6,7 @@ import ( "io" "io/ioutil" "os" + "strings" "github.com/pkg/errors" ) @@ -38,7 +39,7 @@ Install a new license from a string: $ nomad license put "" ` - return helpText + return strings.TrimSpace(helpText) } func (c *LicensePutCommand) Synopsis() string { diff --git a/command/license_reset.go b/command/license_reset.go deleted file mode 100644 index 57ce1f96f..000000000 --- a/command/license_reset.go +++ /dev/null @@ -1,54 +0,0 @@ -package command - -import ( - "fmt" -) - -type LicenseResetCommand struct { - Meta -} - -func (c *LicenseResetCommand) Help() string { - helpText := ` -Usage: nomad license reset [options] - -Resets the Nomad Server and Clients Enterprise license to the builtin one if -it is still valid. IF the builtin license is invalid, the current one stays -active. - -General Options: - - ` + generalOptionsUsage() - - return helpText -} - -func (c *LicenseResetCommand) Synopsis() string { - return "Install a new Nomad Enterprise License" -} - -func (c *LicenseResetCommand) Name() string { return "license reset" } - -func (c *LicenseResetCommand) Run(args []string) int { - flags := c.Meta.FlagSet(c.Name(), FlagSetClient) - flags.Usage = func() { c.Ui.Output(c.Help()) } - - if err := flags.Parse(args); err != nil { - c.Ui.Error(fmt.Sprintf("Error parsing flags: %s", err)) - return 1 - } - - client, err := c.Meta.Client() - if err != nil { - c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err)) - return 1 - } - - resp, _, err := client.Operator().LicenseReset(nil) - if err != nil { - c.Ui.Error(fmt.Sprintf("Error resetting license: %v", err)) - return 1 - } - - return OutputLicenseReply(c.Ui, resp) -} diff --git a/command/license_reset_test.go b/command/license_reset_test.go deleted file mode 100644 index 8b20aa0b4..000000000 --- a/command/license_reset_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package command - -import ( - "testing" - - "github.com/mitchellh/cli" - "github.com/stretchr/testify/require" -) - -var _ cli.Command = &LicenseResetCommand{} - -func TestCommand_LicenseReset_OSSErr(t *testing.T) { - t.Parallel() - - srv, _, url := testServer(t, false, nil) - defer srv.Shutdown() - - ui := new(cli.MockUi) - cmd := &LicenseResetCommand{Meta: Meta{Ui: ui}} - - if code := cmd.Run([]string{"-address=" + url}); code != 1 { - require.Equal(t, 1, code) - } - - require.Contains(t, ui.ErrorWriter.String(), "Nomad Enterprise only endpoint") - -} From 42075ef30e77059d2f8b36bfbc01c977298e7fc9 Mon Sep 17 00:00:00 2001 From: Drew Bailey <2614075+drewbailey@users.noreply.github.com> Date: Tue, 28 Apr 2020 15:28:35 -0400 Subject: [PATCH 5/6] allow test to check if server is enterprise --- command/agent/testagent.go | 4 ++++ command/agent/testagent_oss.go | 8 ++++++++ command/license.go | 24 ++++++++++++++++++------ command/license_put.go | 19 ++++++++----------- command/license_put_test.go | 8 ++++++-- 5 files changed, 44 insertions(+), 19 deletions(-) create mode 100644 command/agent/testagent_oss.go diff --git a/command/agent/testagent.go b/command/agent/testagent.go index 64fb3193d..183598110 100644 --- a/command/agent/testagent.go +++ b/command/agent/testagent.go @@ -81,6 +81,9 @@ type TestAgent struct { // ports that are reserved through freeport that must be returned at // the end of a test, done when Shutdown() is called. ports []int + + // Enterprise specifies if the agent is enterprise or not + Enterprise bool } // NewTestAgent returns a started agent with the given name and @@ -91,6 +94,7 @@ func NewTestAgent(t testing.T, name string, configCallback func(*Config)) *TestA T: t, Name: name, ConfigCallback: configCallback, + Enterprise: EnterpriseTestAgent, } a.Start() diff --git a/command/agent/testagent_oss.go b/command/agent/testagent_oss.go new file mode 100644 index 000000000..37e18ee41 --- /dev/null +++ b/command/agent/testagent_oss.go @@ -0,0 +1,8 @@ +// +build !ent + +package agent + +const ( + // EnterpriseTestAgent is used to configure a TestAgent's Enterprise flag + EnterpriseTestAgent = false +) diff --git a/command/license.go b/command/license.go index 92a02b4c9..724b3e92a 100644 --- a/command/license.go +++ b/command/license.go @@ -79,15 +79,27 @@ func OutputLicenseReply(ui cli.Ui, resp *api.LicenseReply) int { } func outputLicenseInfo(ui cli.Ui, lic *api.License, expired bool) { - ui.Output(fmt.Sprintf("License ID: %s", lic.LicenseID)) - ui.Output(fmt.Sprintf("Customer ID: %s", lic.CustomerID)) + expStr := "" if expired { - ui.Output(fmt.Sprintf("Expired At: %s", lic.ExpirationTime.String())) + expStr = fmt.Sprintf("Expired At: %s", lic.ExpirationTime.String()) } else { - ui.Output(fmt.Sprintf("Expires At: %s", lic.ExpirationTime.String())) + expStr = fmt.Sprintf("Expires At: %s", lic.ExpirationTime.String()) } - ui.Output(fmt.Sprintf("Terminates At: %s", lic.TerminationTime.String())) - ui.Output(fmt.Sprintf("Datacenter: %s", lic.InstallationID)) + + output := []string{ + fmt.Sprintf("License ID: %s", lic.LicenseID), + fmt.Sprintf("Customer ID: %s", lic.CustomerID), + expStr, + fmt.Sprintf("License ID: %s", lic.LicenseID), + fmt.Sprintf("Customer ID: %s", lic.CustomerID), + fmt.Sprintf("Terminates At: %s", lic.TerminationTime.String()), + fmt.Sprintf("Datacenter: %s", lic.InstallationID), + } + ui.Output(formatKV(output)) + // ui.Output(fmt.Sprintf("License ID: %s", lic.LicenseID)) + // ui.Output(fmt.Sprintf("Customer ID: %s", lic.CustomerID)) + // ui.Output(fmt.Sprintf("Terminates At: %s", lic.TerminationTime.String())) + // ui.Output(fmt.Sprintf("Datacenter: %s", lic.InstallationID)) if len(lic.Modules) > 0 { ui.Output("Modules:") for _, mod := range lic.Modules { diff --git a/command/license_put.go b/command/license_put.go index d110c39c6..4a1549a9c 100644 --- a/command/license_put.go +++ b/command/license_put.go @@ -29,7 +29,7 @@ General Options: Install a new license from a file: - $ nomad license put @nomad.license + $ nomad license put Install a new license from stdin: @@ -111,21 +111,18 @@ func loadFromStdin(testStdin io.Reader) (string, error) { return b.String(), nil } -func LoadDataSource(data string, testStdin io.Reader) (string, error) { +func LoadDataSource(file string, testStdin io.Reader) (string, error) { // Handle empty quoted shell parameters - if len(data) == 0 { + if len(file) == 0 { return "", nil } - switch data[0] { - case '@': - return loadFromFile(data[1:]) - case '-': - if len(data) > 1 { - return data, nil + if file == "-" { + if len(file) > 1 { + return file, nil } return loadFromStdin(testStdin) - default: - return data, nil } + + return loadFromFile(file) } diff --git a/command/license_put_test.go b/command/license_put_test.go index 42b4a6758..71858c7ef 100644 --- a/command/license_put_test.go +++ b/command/license_put_test.go @@ -10,7 +10,7 @@ import ( var _ cli.Command = &LicensePutCommand{} -func TestCommand_LicensePut_OSSErr(t *testing.T) { +func TestCommand_LicensePut_Err(t *testing.T) { t.Parallel() srv, _, url := testServer(t, false, nil) @@ -23,6 +23,10 @@ func TestCommand_LicensePut_OSSErr(t *testing.T) { require.Equal(t, code, 1) } - require.Contains(t, ui.ErrorWriter.String(), "Nomad Enterprise only endpoint") + if srv.Enterprise { + require.Contains(t, ui.ErrorWriter.String(), "error validating license") + } else { + require.Contains(t, ui.ErrorWriter.String(), "Nomad Enterprise only endpoint") + } } From 41c7d49eb76c4097b880f7d0e3f76c7f8a533811 Mon Sep 17 00:00:00 2001 From: Drew Bailey <2614075+drewbailey@users.noreply.github.com> Date: Thu, 30 Apr 2020 08:56:40 -0400 Subject: [PATCH 6/6] properly format license output --- command/license.go | 44 ++++++++++++++++--------------------- command/license_get_test.go | 11 ++++++---- command/license_put.go | 3 --- 3 files changed, 26 insertions(+), 32 deletions(-) diff --git a/command/license.go b/command/license.go index 724b3e92a..101191469 100644 --- a/command/license.go +++ b/command/license.go @@ -24,20 +24,15 @@ For more detailed examples see: https://www.nomadproject.io/docs/commands/license/ Install a new license from a file: - $ nomad license put @nomad.license + $ nomad license put Install a new license from stdin: $ nomad license put - -Install a new license from a string: - $ nomad license put "" - Retrieve the current license: $ nomad license get -Reset the current license: - $ nomad license reset ` return strings.TrimSpace(helpText) } @@ -53,21 +48,22 @@ func (l *LicenseCommand) Run(args []string) int { } func OutputLicenseReply(ui cli.Ui, resp *api.LicenseReply) int { + var validity string if resp.Valid { - ui.Output("License is valid") - outputLicenseInfo(ui, resp.License, false) + validity = "valid" + outputLicenseInfo(ui, resp.License, false, validity) return 0 } else if resp.License != nil { now := time.Now() if resp.License.ExpirationTime.Before(now) { - ui.Output("License has expired!") - outputLicenseInfo(ui, resp.License, true) + validity = "expired!" + outputLicenseInfo(ui, resp.License, true, validity) } else { - ui.Output("License is invalid!") + validity = "invalid!" for _, warn := range resp.Warnings { ui.Output(fmt.Sprintf(" %s", warn)) } - outputLicenseInfo(ui, resp.License, false) + outputLicenseInfo(ui, resp.License, false, validity) } return 1 } else { @@ -78,28 +74,26 @@ func OutputLicenseReply(ui cli.Ui, resp *api.LicenseReply) int { } } -func outputLicenseInfo(ui cli.Ui, lic *api.License, expired bool) { +func outputLicenseInfo(ui cli.Ui, lic *api.License, expired bool, validity string) { expStr := "" if expired { - expStr = fmt.Sprintf("Expired At: %s", lic.ExpirationTime.String()) + expStr = fmt.Sprintf("Expired At|%s", lic.ExpirationTime.String()) } else { - expStr = fmt.Sprintf("Expires At: %s", lic.ExpirationTime.String()) + expStr = fmt.Sprintf("Expires At|%s", lic.ExpirationTime.String()) } output := []string{ - fmt.Sprintf("License ID: %s", lic.LicenseID), - fmt.Sprintf("Customer ID: %s", lic.CustomerID), + fmt.Sprintf("License Status|%s", validity), + fmt.Sprintf("License ID|%s", lic.LicenseID), + fmt.Sprintf("Customer ID|%s", lic.CustomerID), expStr, - fmt.Sprintf("License ID: %s", lic.LicenseID), - fmt.Sprintf("Customer ID: %s", lic.CustomerID), - fmt.Sprintf("Terminates At: %s", lic.TerminationTime.String()), - fmt.Sprintf("Datacenter: %s", lic.InstallationID), + fmt.Sprintf("License ID|%s", lic.LicenseID), + fmt.Sprintf("Customer ID|%s", lic.CustomerID), + fmt.Sprintf("Terminates At|%s", lic.TerminationTime.String()), + fmt.Sprintf("Datacenter|%s", lic.InstallationID), } ui.Output(formatKV(output)) - // ui.Output(fmt.Sprintf("License ID: %s", lic.LicenseID)) - // ui.Output(fmt.Sprintf("Customer ID: %s", lic.CustomerID)) - // ui.Output(fmt.Sprintf("Terminates At: %s", lic.TerminationTime.String())) - // ui.Output(fmt.Sprintf("Datacenter: %s", lic.InstallationID)) + if len(lic.Modules) > 0 { ui.Output("Modules:") for _, mod := range lic.Modules { diff --git a/command/license_get_test.go b/command/license_get_test.go index ae3a67c82..39f25bc3f 100644 --- a/command/license_get_test.go +++ b/command/license_get_test.go @@ -18,10 +18,13 @@ func TestCommand_LicenseGet_OSSErr(t *testing.T) { ui := new(cli.MockUi) cmd := &LicenseGetCommand{Meta: Meta{Ui: ui}} - if code := cmd.Run([]string{"-address=" + url}); code != 1 { - require.Equal(t, 1, code) + code := cmd.Run([]string{"-address=" + url}) + require.Equal(t, 1, code) + + if srv.Enterprise { + require.Contains(t, ui.OutputWriter.String(), "License Status") + } else { + require.Contains(t, ui.ErrorWriter.String(), "Nomad Enterprise only endpoint") } - require.Contains(t, ui.ErrorWriter.String(), "Nomad Enterprise only endpoint") - } diff --git a/command/license_put.go b/command/license_put.go index 4a1549a9c..f7148ab17 100644 --- a/command/license_put.go +++ b/command/license_put.go @@ -35,9 +35,6 @@ Install a new license from stdin: $ nomad license put - -Install a new license from a string: - - $ nomad license put "" ` return strings.TrimSpace(helpText) }