Skip to content

Commit 779c446

Browse files
authored
cli prints license warnings (coder#3716)
* cli prints license warnings Signed-off-by: Spike Curtis <spike@coder.com> * Satisfy the linter Signed-off-by: Spike Curtis <spike@coder.com> Signed-off-by: Spike Curtis <spike@coder.com>
1 parent 62f686c commit 779c446

File tree

2 files changed

+69
-35
lines changed

2 files changed

+69
-35
lines changed

cli/root.go

Lines changed: 48 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,20 @@ var (
3535
)
3636

3737
const (
38-
varURL = "url"
39-
varToken = "token"
40-
varAgentToken = "agent-token"
41-
varAgentURL = "agent-url"
42-
varGlobalConfig = "global-config"
43-
varNoOpen = "no-open"
44-
varNoVersionCheck = "no-version-warning"
45-
varForceTty = "force-tty"
46-
varVerbose = "verbose"
47-
notLoggedInMessage = "You are not logged in. Try logging in using 'coder login <url>'."
48-
49-
envNoVersionCheck = "CODER_NO_VERSION_WARNING"
38+
varURL = "url"
39+
varToken = "token"
40+
varAgentToken = "agent-token"
41+
varAgentURL = "agent-url"
42+
varGlobalConfig = "global-config"
43+
varNoOpen = "no-open"
44+
varNoVersionCheck = "no-version-warning"
45+
varNoFeatureWarning = "no-feature-warning"
46+
varForceTty = "force-tty"
47+
varVerbose = "verbose"
48+
notLoggedInMessage = "You are not logged in. Try logging in using 'coder login <url>'."
49+
50+
envNoVersionCheck = "CODER_NO_VERSION_WARNING"
51+
envNoFeatureWarning = "CODER_NO_FEATURE_WARNING"
5052
)
5153

5254
var (
@@ -103,36 +105,33 @@ func Root(subcommands []*cobra.Command) *cobra.Command {
103105
Long: `Coder — A tool for provisioning self-hosted development environments.
104106
`,
105107
PersistentPreRun: func(cmd *cobra.Command, args []string) {
106-
err := func() error {
107-
if cliflag.IsSetBool(cmd, varNoVersionCheck) {
108-
return nil
109-
}
110-
111-
// Login handles checking the versions itself since it
112-
// has a handle to an unauthenticated client.
113-
// Server is skipped for obvious reasons.
114-
if cmd.Name() == "login" || cmd.Name() == "server" || cmd.Name() == "gitssh" {
115-
return nil
116-
}
117-
118-
client, err := CreateClient(cmd)
119-
// If the client is unauthenticated we can ignore the check.
120-
// The child commands should handle an unauthenticated client.
121-
if xerrors.Is(err, errUnauthenticated) {
122-
return nil
123-
}
124-
if err != nil {
125-
return xerrors.Errorf("create client: %w", err)
126-
}
127-
return checkVersions(cmd, client)
128-
}()
108+
if cliflag.IsSetBool(cmd, varNoVersionCheck) &&
109+
cliflag.IsSetBool(cmd, varNoFeatureWarning) {
110+
return
111+
}
112+
113+
// Login handles checking the versions itself since it
114+
// has a handle to an unauthenticated client.
115+
// Server is skipped for obvious reasons.
116+
if cmd.Name() == "login" || cmd.Name() == "server" || cmd.Name() == "gitssh" {
117+
return
118+
}
119+
120+
client, err := CreateClient(cmd)
121+
// If we are unable to create a client, presumably the subcommand will fail as well
122+
// so we can bail out here.
123+
if err != nil {
124+
return
125+
}
126+
err = checkVersions(cmd, client)
129127
if err != nil {
130128
// Just log the error here. We never want to fail a command
131129
// due to a pre-run.
132130
_, _ = fmt.Fprintf(cmd.ErrOrStderr(),
133131
cliui.Styles.Warn.Render("check versions error: %s"), err)
134132
_, _ = fmt.Fprintln(cmd.ErrOrStderr())
135133
}
134+
checkWarnings(cmd, client)
136135
},
137136
Example: formatExamples(
138137
example{
@@ -152,6 +151,7 @@ func Root(subcommands []*cobra.Command) *cobra.Command {
152151

153152
cmd.PersistentFlags().String(varURL, "", "Specify the URL to your deployment.")
154153
cliflag.Bool(cmd.PersistentFlags(), varNoVersionCheck, "", envNoVersionCheck, false, "Suppress warning when client and server versions do not match.")
154+
cliflag.Bool(cmd.PersistentFlags(), varNoFeatureWarning, "", envNoFeatureWarning, false, "Suppress warnings about unlicensed features.")
155155
cliflag.String(cmd.PersistentFlags(), varToken, "", envSessionToken, "", fmt.Sprintf("Specify an authentication token. For security reasons setting %s is preferred.", envSessionToken))
156156
cliflag.String(cmd.PersistentFlags(), varAgentToken, "", "CODER_AGENT_TOKEN", "", "Specify an agent authentication token.")
157157
_ = cmd.PersistentFlags().MarkHidden(varAgentToken)
@@ -493,3 +493,16 @@ download the server version with: 'curl -L https://coder.com/install.sh | sh -s
493493

494494
return nil
495495
}
496+
497+
func checkWarnings(cmd *cobra.Command, client *codersdk.Client) {
498+
if cliflag.IsSetBool(cmd, varNoFeatureWarning) {
499+
return
500+
}
501+
entitlements, err := client.Entitlements(cmd.Context())
502+
if err != nil {
503+
return
504+
}
505+
for _, w := range entitlements.Warnings {
506+
_, _ = fmt.Fprintln(cmd.ErrOrStderr(), cliui.Styles.Warn.Render(w))
507+
}
508+
}

enterprise/cli/licenses_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020

2121
"github.com/coder/coder/cli/clitest"
2222
"github.com/coder/coder/coderd/coderdtest"
23+
"github.com/coder/coder/coderd/httpapi"
2324
"github.com/coder/coder/codersdk"
2425
"github.com/coder/coder/enterprise/cli"
2526
"github.com/coder/coder/enterprise/coderd"
@@ -28,6 +29,7 @@ import (
2829
)
2930

3031
const fakeLicenseJWT = "test.jwt.sig"
32+
const testWarning = "This is a test warning"
3133

3234
func TestLicensesAddFake(t *testing.T) {
3335
t.Parallel()
@@ -179,6 +181,8 @@ func TestLicensesListReal(t *testing.T) {
179181
"licenses", "list")
180182
stdout := new(bytes.Buffer)
181183
cmd.SetOut(stdout)
184+
stderr := new(bytes.Buffer)
185+
cmd.SetErr(stderr)
182186
clitest.SetupConfig(t, client, root)
183187
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
184188
defer cancel()
@@ -188,6 +192,7 @@ func TestLicensesListReal(t *testing.T) {
188192
}()
189193
require.NoError(t, <-errC)
190194
assert.Equal(t, "[]\n", stdout.String())
195+
assert.Contains(t, testWarning, stderr.String())
191196
})
192197
}
193198

@@ -260,6 +265,7 @@ func newFakeLicenseAPI(t *testing.T) http.Handler {
260265
r.Get("/api/v2/licenses", a.licenses)
261266
r.Get("/api/v2/buildinfo", a.noop)
262267
r.Delete("/api/v2/licenses/{id}", a.deleteLicense)
268+
r.Get("/api/v2/entitlements", a.entitlements)
263269
return r
264270
}
265271

@@ -330,3 +336,18 @@ func (s *fakeLicenseAPI) deleteLicense(rw http.ResponseWriter, r *http.Request)
330336
assert.Equal(s.t, "55", chi.URLParam(r, "id"))
331337
rw.WriteHeader(200)
332338
}
339+
340+
func (*fakeLicenseAPI) entitlements(rw http.ResponseWriter, _ *http.Request) {
341+
features := make(map[string]codersdk.Feature)
342+
for _, f := range codersdk.FeatureNames {
343+
features[f] = codersdk.Feature{
344+
Entitlement: codersdk.EntitlementEntitled,
345+
Enabled: true,
346+
}
347+
}
348+
httpapi.Write(rw, http.StatusOK, codersdk.Entitlements{
349+
Features: features,
350+
Warnings: []string{testWarning},
351+
HasLicense: true,
352+
})
353+
}

0 commit comments

Comments
 (0)