Skip to content

Commit 765d99c

Browse files
stirbyjohnstcnericpaulsenSasSwart
authored
chore: cherry-pick commits for 2.18.1 (coder#15885)
Co-authored-by: Cian Johnston <cian@coder.com> Co-authored-by: Eric Paulsen <ericpaulsen@coder.com> Co-authored-by: Sas Swart <sas.swart.cdk@gmail.com>
1 parent c5e8769 commit 765d99c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+2209
-347
lines changed

cli/cliutil/provisionerwarn.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package cliutil
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"io"
7+
"strings"
8+
9+
"github.com/coder/coder/v2/cli/cliui"
10+
"github.com/coder/coder/v2/codersdk"
11+
)
12+
13+
var (
14+
warnNoMatchedProvisioners = `Your build has been enqueued, but there are no provisioners that accept the required tags. Once a compatible provisioner becomes available, your build will continue. Please contact your administrator.
15+
Details:
16+
Provisioner job ID : %s
17+
Requested tags : %s
18+
`
19+
warnNoAvailableProvisioners = `Provisioners that accept the required tags have not responded for longer than expected. This may delay your build. Please contact your administrator if your build does not complete.
20+
Details:
21+
Provisioner job ID : %s
22+
Requested tags : %s
23+
Most recently seen : %s
24+
`
25+
)
26+
27+
// WarnMatchedProvisioners warns the user if there are no provisioners that
28+
// match the requested tags for a given provisioner job.
29+
// If the job is not pending, it is ignored.
30+
func WarnMatchedProvisioners(w io.Writer, mp *codersdk.MatchedProvisioners, job codersdk.ProvisionerJob) {
31+
if mp == nil {
32+
// Nothing in the response, nothing to do here!
33+
return
34+
}
35+
if job.Status != codersdk.ProvisionerJobPending {
36+
// Only warn if the job is pending.
37+
return
38+
}
39+
var tagsJSON strings.Builder
40+
if err := json.NewEncoder(&tagsJSON).Encode(job.Tags); err != nil {
41+
// Fall back to the less-pretty string representation.
42+
tagsJSON.Reset()
43+
_, _ = tagsJSON.WriteString(fmt.Sprintf("%v", job.Tags))
44+
}
45+
if mp.Count == 0 {
46+
cliui.Warnf(w, warnNoMatchedProvisioners, job.ID, tagsJSON.String())
47+
return
48+
}
49+
if mp.Available == 0 {
50+
cliui.Warnf(w, warnNoAvailableProvisioners, job.ID, strings.TrimSpace(tagsJSON.String()), mp.MostRecentlySeen.Time)
51+
return
52+
}
53+
}

cli/cliutil/provisionerwarn_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package cliutil_test
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
9+
"github.com/coder/coder/v2/cli/cliutil"
10+
"github.com/coder/coder/v2/codersdk"
11+
)
12+
13+
func TestWarnMatchedProvisioners(t *testing.T) {
14+
t.Parallel()
15+
16+
for _, tt := range []struct {
17+
name string
18+
mp *codersdk.MatchedProvisioners
19+
job codersdk.ProvisionerJob
20+
expect string
21+
}{
22+
{
23+
name: "no_match",
24+
mp: &codersdk.MatchedProvisioners{
25+
Count: 0,
26+
Available: 0,
27+
},
28+
job: codersdk.ProvisionerJob{
29+
Status: codersdk.ProvisionerJobPending,
30+
},
31+
expect: `there are no provisioners that accept the required tags`,
32+
},
33+
{
34+
name: "no_available",
35+
mp: &codersdk.MatchedProvisioners{
36+
Count: 1,
37+
Available: 0,
38+
},
39+
job: codersdk.ProvisionerJob{
40+
Status: codersdk.ProvisionerJobPending,
41+
},
42+
expect: `Provisioners that accept the required tags have not responded for longer than expected`,
43+
},
44+
{
45+
name: "match",
46+
mp: &codersdk.MatchedProvisioners{
47+
Count: 1,
48+
Available: 1,
49+
},
50+
job: codersdk.ProvisionerJob{
51+
Status: codersdk.ProvisionerJobPending,
52+
},
53+
},
54+
{
55+
name: "not_pending",
56+
mp: &codersdk.MatchedProvisioners{},
57+
job: codersdk.ProvisionerJob{
58+
Status: codersdk.ProvisionerJobRunning,
59+
},
60+
},
61+
} {
62+
tt := tt
63+
t.Run(tt.name, func(t *testing.T) {
64+
t.Parallel()
65+
var w strings.Builder
66+
cliutil.WarnMatchedProvisioners(&w, tt.mp, tt.job)
67+
if tt.expect != "" {
68+
require.Contains(t, w.String(), tt.expect)
69+
} else {
70+
require.Empty(t, w.String())
71+
}
72+
})
73+
}
74+
}

cli/create.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/coder/pretty"
1515

1616
"github.com/coder/coder/v2/cli/cliui"
17+
"github.com/coder/coder/v2/cli/cliutil"
1718
"github.com/coder/coder/v2/coderd/util/ptr"
1819
"github.com/coder/coder/v2/coderd/util/slice"
1920
"github.com/coder/coder/v2/codersdk"
@@ -289,7 +290,7 @@ func (r *RootCmd) create() *serpent.Command {
289290
ttlMillis = ptr.Ref(stopAfter.Milliseconds())
290291
}
291292

292-
workspace, err := client.CreateWorkspace(inv.Context(), template.OrganizationID, workspaceOwner, codersdk.CreateWorkspaceRequest{
293+
workspace, err := client.CreateUserWorkspace(inv.Context(), workspaceOwner, codersdk.CreateWorkspaceRequest{
293294
TemplateVersionID: templateVersionID,
294295
Name: workspaceName,
295296
AutostartSchedule: schedSpec,
@@ -301,6 +302,8 @@ func (r *RootCmd) create() *serpent.Command {
301302
return xerrors.Errorf("create workspace: %w", err)
302303
}
303304

305+
cliutil.WarnMatchedProvisioners(inv.Stderr, workspace.LatestBuild.MatchedProvisioners, workspace.LatestBuild.Job)
306+
304307
err = cliui.WorkspaceBuild(inv.Context(), inv.Stdout, client, workspace.LatestBuild.ID)
305308
if err != nil {
306309
return xerrors.Errorf("watch build: %w", err)
@@ -433,6 +436,12 @@ func prepWorkspaceBuild(inv *serpent.Invocation, client *codersdk.Client, args p
433436
if err != nil {
434437
return nil, xerrors.Errorf("begin workspace dry-run: %w", err)
435438
}
439+
440+
matchedProvisioners, err := client.TemplateVersionDryRunMatchedProvisioners(inv.Context(), templateVersion.ID, dryRun.ID)
441+
if err != nil {
442+
return nil, xerrors.Errorf("get matched provisioners: %w", err)
443+
}
444+
cliutil.WarnMatchedProvisioners(inv.Stdout, &matchedProvisioners, dryRun)
436445
_, _ = fmt.Fprintln(inv.Stdout, "Planning workspace...")
437446
err = cliui.ProvisionerJob(inv.Context(), inv.Stdout, cliui.ProvisionerJobOptions{
438447
Fetch: func() (codersdk.ProvisionerJob, error) {

cli/delete.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"time"
66

77
"github.com/coder/coder/v2/cli/cliui"
8+
"github.com/coder/coder/v2/cli/cliutil"
89
"github.com/coder/coder/v2/codersdk"
910
"github.com/coder/serpent"
1011
)
@@ -55,6 +56,7 @@ func (r *RootCmd) deleteWorkspace() *serpent.Command {
5556
if err != nil {
5657
return err
5758
}
59+
cliutil.WarnMatchedProvisioners(inv.Stdout, build.MatchedProvisioners, build.Job)
5860

5961
err = cliui.WorkspaceBuild(inv.Context(), inv.Stdout, client, build.ID)
6062
if err != nil {

cli/delete_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
"github.com/coder/coder/v2/cli/clitest"
1313
"github.com/coder/coder/v2/coderd/coderdtest"
1414
"github.com/coder/coder/v2/coderd/database/dbauthz"
15+
"github.com/coder/coder/v2/coderd/database/dbtestutil"
16+
"github.com/coder/coder/v2/coderd/rbac"
1517
"github.com/coder/coder/v2/codersdk"
1618
"github.com/coder/coder/v2/pty/ptytest"
1719
"github.com/coder/coder/v2/testutil"
@@ -164,4 +166,46 @@ func TestDelete(t *testing.T) {
164166
}()
165167
<-doneChan
166168
})
169+
170+
t.Run("WarnNoProvisioners", func(t *testing.T) {
171+
t.Parallel()
172+
if !dbtestutil.WillUsePostgres() {
173+
t.Skip("this test requires postgres")
174+
}
175+
176+
store, ps, db := dbtestutil.NewDBWithSQLDB(t)
177+
client, closeDaemon := coderdtest.NewWithProvisionerCloser(t, &coderdtest.Options{
178+
Database: store,
179+
Pubsub: ps,
180+
IncludeProvisionerDaemon: true,
181+
})
182+
183+
// Given: a user, template, and workspace
184+
user := coderdtest.CreateFirstUser(t, client)
185+
templateAdmin, _ := coderdtest.CreateAnotherUser(t, client, user.OrganizationID, rbac.RoleTemplateAdmin())
186+
version := coderdtest.CreateTemplateVersion(t, templateAdmin, user.OrganizationID, nil)
187+
template := coderdtest.CreateTemplate(t, templateAdmin, user.OrganizationID, version.ID)
188+
workspace := coderdtest.CreateWorkspace(t, templateAdmin, template.ID)
189+
coderdtest.AwaitWorkspaceBuildJobCompleted(t, templateAdmin, workspace.LatestBuild.ID)
190+
191+
// When: all provisioner daemons disappear
192+
require.NoError(t, closeDaemon.Close())
193+
_, err := db.Exec("DELETE FROM provisioner_daemons;")
194+
require.NoError(t, err)
195+
196+
// Then: the workspace deletion should warn about no provisioners
197+
inv, root := clitest.New(t, "delete", workspace.Name, "-y")
198+
pty := ptytest.New(t).Attach(inv)
199+
clitest.SetupConfig(t, templateAdmin, root)
200+
doneChan := make(chan struct{})
201+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
202+
defer cancel()
203+
go func() {
204+
defer close(doneChan)
205+
_ = inv.WithContext(ctx).Run()
206+
}()
207+
pty.ExpectMatch("there are no provisioners that accept the required tags")
208+
cancel()
209+
<-doneChan
210+
})
167211
}

cli/start.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"golang.org/x/xerrors"
99

1010
"github.com/coder/coder/v2/cli/cliui"
11+
"github.com/coder/coder/v2/cli/cliutil"
1112
"github.com/coder/coder/v2/codersdk"
1213
"github.com/coder/serpent"
1314
)
@@ -35,6 +36,23 @@ func (r *RootCmd) start() *serpent.Command {
3536
}
3637
var build codersdk.WorkspaceBuild
3738
switch workspace.LatestBuild.Status {
39+
case codersdk.WorkspaceStatusPending:
40+
// The above check is technically duplicated in cliutil.WarnmatchedProvisioners
41+
// but we still want to avoid users spamming multiple builds that will
42+
// not be picked up.
43+
_, _ = fmt.Fprintf(
44+
inv.Stdout,
45+
"\nThe %s workspace is waiting to start!\n",
46+
cliui.Keyword(workspace.Name),
47+
)
48+
cliutil.WarnMatchedProvisioners(inv.Stderr, workspace.LatestBuild.MatchedProvisioners, workspace.LatestBuild.Job)
49+
if _, err := cliui.Prompt(inv, cliui.PromptOptions{
50+
Text: "Enqueue another start?",
51+
IsConfirm: true,
52+
Default: cliui.ConfirmNo,
53+
}); err != nil {
54+
return err
55+
}
3856
case codersdk.WorkspaceStatusRunning:
3957
_, _ = fmt.Fprintf(
4058
inv.Stdout, "\nThe %s workspace is already running!\n",
@@ -159,6 +177,7 @@ func startWorkspace(inv *serpent.Invocation, client *codersdk.Client, workspace
159177
if err != nil {
160178
return codersdk.WorkspaceBuild{}, xerrors.Errorf("create workspace build: %w", err)
161179
}
180+
cliutil.WarnMatchedProvisioners(inv.Stderr, build.MatchedProvisioners, build.Job)
162181

163182
return build, nil
164183
}

cli/stop.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"time"
66

77
"github.com/coder/coder/v2/cli/cliui"
8+
"github.com/coder/coder/v2/cli/cliutil"
89
"github.com/coder/coder/v2/codersdk"
910
"github.com/coder/serpent"
1011
)
@@ -36,6 +37,21 @@ func (r *RootCmd) stop() *serpent.Command {
3637
if err != nil {
3738
return err
3839
}
40+
if workspace.LatestBuild.Job.Status == codersdk.ProvisionerJobPending {
41+
// cliutil.WarnMatchedProvisioners also checks if the job is pending
42+
// but we still want to avoid users spamming multiple builds that will
43+
// not be picked up.
44+
cliui.Warn(inv.Stderr, "The workspace is already stopping!")
45+
cliutil.WarnMatchedProvisioners(inv.Stderr, workspace.LatestBuild.MatchedProvisioners, workspace.LatestBuild.Job)
46+
if _, err := cliui.Prompt(inv, cliui.PromptOptions{
47+
Text: "Enqueue another stop?",
48+
IsConfirm: true,
49+
Default: cliui.ConfirmNo,
50+
}); err != nil {
51+
return err
52+
}
53+
}
54+
3955
wbr := codersdk.CreateWorkspaceBuildRequest{
4056
Transition: codersdk.WorkspaceTransitionStop,
4157
}
@@ -46,6 +62,7 @@ func (r *RootCmd) stop() *serpent.Command {
4662
if err != nil {
4763
return err
4864
}
65+
cliutil.WarnMatchedProvisioners(inv.Stderr, build.MatchedProvisioners, build.Job)
4966

5067
err = cliui.WorkspaceBuild(inv.Context(), inv.Stdout, client, build.ID)
5168
if err != nil {

cli/templatepush.go

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package cli
22

33
import (
44
"bufio"
5-
"encoding/json"
65
"errors"
76
"fmt"
87
"io"
@@ -17,6 +16,7 @@ import (
1716
"golang.org/x/xerrors"
1817

1918
"github.com/coder/coder/v2/cli/cliui"
19+
"github.com/coder/coder/v2/cli/cliutil"
2020
"github.com/coder/coder/v2/codersdk"
2121
"github.com/coder/coder/v2/provisionersdk"
2222
"github.com/coder/pretty"
@@ -416,30 +416,7 @@ func createValidTemplateVersion(inv *serpent.Invocation, args createValidTemplat
416416
if err != nil {
417417
return nil, err
418418
}
419-
var tagsJSON strings.Builder
420-
if err := json.NewEncoder(&tagsJSON).Encode(version.Job.Tags); err != nil {
421-
// Fall back to the less-pretty string representation.
422-
tagsJSON.Reset()
423-
_, _ = tagsJSON.WriteString(fmt.Sprintf("%v", version.Job.Tags))
424-
}
425-
if version.MatchedProvisioners.Count == 0 {
426-
cliui.Warnf(inv.Stderr, `No provisioners are available to handle the job!
427-
Please contact your deployment administrator for assistance.
428-
Details:
429-
Provisioner job ID : %s
430-
Requested tags : %s
431-
`, version.Job.ID, tagsJSON.String())
432-
} else if version.MatchedProvisioners.Available == 0 {
433-
cliui.Warnf(inv.Stderr, `All available provisioner daemons have been silent for a while.
434-
Your build will proceed once they become available.
435-
If this persists, please contact your deployment administrator for assistance.
436-
Details:
437-
Provisioner job ID : %s
438-
Requested tags : %s
439-
Most recently seen : %s
440-
`, version.Job.ID, strings.TrimSpace(tagsJSON.String()), version.MatchedProvisioners.MostRecentlySeen.Time)
441-
}
442-
419+
cliutil.WarnMatchedProvisioners(inv.Stderr, version.MatchedProvisioners, version.Job)
443420
err = cliui.ProvisionerJob(inv.Context(), inv.Stdout, cliui.ProvisionerJobOptions{
444421
Fetch: func() (codersdk.ProvisionerJob, error) {
445422
version, err := client.TemplateVersion(inv.Context(), version.ID)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy