Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit2b57dcc

Browse files
feat(coderd): add matched provisioner daemons information to more places (#15688)
- Refactors `checkProvisioners` into `db2sdk.MatchedProvisioners`- Adds a separate RBAC subject just for reading provisioner daemons- Adds matched provisioners information to additional endpoints relating to workspace builds and templates-Updates existing unit tests for above endpoints-Adds API endpoint for matched provisioners of template dry-run job-Updates CLI to show warning when creating/starting/stopping/deleting workspaces for which no provisoners are available---------Co-authored-by: Danny Kopping <danny@coder.com>
1 parent7e1ac2e commit2b57dcc

30 files changed

+1058
-166
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+
funcWarnMatchedProvisioners(w io.Writer,mp*codersdk.MatchedProvisioners,job codersdk.ProvisionerJob) {
31+
ifmp==nil {
32+
// Nothing in the response, nothing to do here!
33+
return
34+
}
35+
ifjob.Status!=codersdk.ProvisionerJobPending {
36+
// Only warn if the job is pending.
37+
return
38+
}
39+
vartagsJSON strings.Builder
40+
iferr:=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+
ifmp.Count==0 {
46+
cliui.Warnf(w,warnNoMatchedProvisioners,job.ID,tagsJSON.String())
47+
return
48+
}
49+
ifmp.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+
funcTestWarnMatchedProvisioners(t*testing.T) {
14+
t.Parallel()
15+
16+
for_,tt:=range []struct {
17+
namestring
18+
mp*codersdk.MatchedProvisioners
19+
job codersdk.ProvisionerJob
20+
expectstring
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+
varw strings.Builder
66+
cliutil.WarnMatchedProvisioners(&w,tt.mp,tt.job)
67+
iftt.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
returnxerrors.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
iferr!=nil {
306309
returnxerrors.Errorf("watch build: %w",err)
@@ -433,6 +436,12 @@ func prepWorkspaceBuild(inv *serpent.Invocation, client *codersdk.Client, args p
433436
iferr!=nil {
434437
returnnil,xerrors.Errorf("begin workspace dry-run: %w",err)
435438
}
439+
440+
matchedProvisioners,err:=client.TemplateVersionDryRunMatchedProvisioners(inv.Context(),templateVersion.ID,dryRun.ID)
441+
iferr!=nil {
442+
returnnil,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
iferr!=nil {
5657
returnerr
5758
}
59+
cliutil.WarnMatchedProvisioners(inv.Stdout,build.MatchedProvisioners,build.Job)
5860

5961
err=cliui.WorkspaceBuild(inv.Context(),inv.Stdout,client,build.ID)
6062
iferr!=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(chanstruct{})
201+
ctx,cancel:=context.WithTimeout(context.Background(),testutil.WaitLong)
202+
defercancel()
203+
gofunc() {
204+
deferclose(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
varbuild codersdk.WorkspaceBuild
3738
switchworkspace.LatestBuild.Status {
39+
casecodersdk.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+
returnerr
55+
}
3856
casecodersdk.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
iferr!=nil {
160178
return codersdk.WorkspaceBuild{},xerrors.Errorf("create workspace build: %w",err)
161179
}
180+
cliutil.WarnMatchedProvisioners(inv.Stderr,build.MatchedProvisioners,build.Job)
162181

163182
returnbuild,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
iferr!=nil {
3738
returnerr
3839
}
40+
ifworkspace.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+
returnerr
52+
}
53+
}
54+
3955
wbr:= codersdk.CreateWorkspaceBuildRequest{
4056
Transition:codersdk.WorkspaceTransitionStop,
4157
}
@@ -46,6 +62,7 @@ func (r *RootCmd) stop() *serpent.Command {
4662
iferr!=nil {
4763
returnerr
4864
}
65+
cliutil.WarnMatchedProvisioners(inv.Stderr,build.MatchedProvisioners,build.Job)
4966

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

‎cli/templatepush.go

Lines changed: 2 additions & 37 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,7 +416,7 @@ func createValidTemplateVersion(inv *serpent.Invocation, args createValidTemplat
416416
iferr!=nil {
417417
returnnil,err
418418
}
419-
WarnMatchedProvisioners(inv,version)
419+
cliutil.WarnMatchedProvisioners(inv.Stderr,version.MatchedProvisioners,version.Job)
420420
err=cliui.ProvisionerJob(inv.Context(),inv.Stdout, cliui.ProvisionerJobOptions{
421421
Fetch:func() (codersdk.ProvisionerJob,error) {
422422
version,err:=client.TemplateVersion(inv.Context(),version.ID)
@@ -482,41 +482,6 @@ func ParseProvisionerTags(rawTags []string) (map[string]string, error) {
482482
returntags,nil
483483
}
484484

485-
var (
486-
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.
487-
Details:
488-
Provisioner job ID : %s
489-
Requested tags : %s
490-
`
491-
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.
492-
Details:
493-
Provisioner job ID : %s
494-
Requested tags : %s
495-
Most recently seen : %s
496-
`
497-
)
498-
499-
funcWarnMatchedProvisioners(inv*serpent.Invocation,tv codersdk.TemplateVersion) {
500-
iftv.MatchedProvisioners==nil {
501-
// Nothing in the response, nothing to do here!
502-
return
503-
}
504-
vartagsJSON strings.Builder
505-
iferr:=json.NewEncoder(&tagsJSON).Encode(tv.Job.Tags);err!=nil {
506-
// Fall back to the less-pretty string representation.
507-
tagsJSON.Reset()
508-
_,_=tagsJSON.WriteString(fmt.Sprintf("%v",tv.Job.Tags))
509-
}
510-
iftv.MatchedProvisioners.Count==0 {
511-
cliui.Warnf(inv.Stderr,warnNoMatchedProvisioners,tv.Job.ID,tagsJSON.String())
512-
return
513-
}
514-
iftv.MatchedProvisioners.Available==0 {
515-
cliui.Warnf(inv.Stderr,warnNoAvailableProvisioners,tv.Job.ID,strings.TrimSpace(tagsJSON.String()),tv.MatchedProvisioners.MostRecentlySeen.Time)
516-
return
517-
}
518-
}
519-
520485
// prettyDirectoryPath returns a prettified path when inside the users
521486
// home directory. Falls back to dir if the users home directory cannot
522487
// discerned. This function calls filepath.Clean on the result.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp