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

Commita099a8a

Browse files
authored
feat: use preview to compute workspace tags from terraform (#18720)
If using dynamic parameters, workspace tags are extracted using`coder/preview`.
1 parent4607e51 commita099a8a

File tree

14 files changed

+1185
-78
lines changed

14 files changed

+1185
-78
lines changed

‎archive/fs/zip.go‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package archivefs
2+
3+
import (
4+
"archive/zip"
5+
"io"
6+
"io/fs"
7+
8+
"github.com/spf13/afero"
9+
"github.com/spf13/afero/zipfs"
10+
)
11+
12+
// FromZipReader creates a read-only in-memory FS
13+
funcFromZipReader(r io.ReaderAt,sizeint64) (fs.FS,error) {
14+
zr,err:=zip.NewReader(r,size)
15+
iferr!=nil {
16+
returnnil,err
17+
}
18+
returnafero.NewIOFS(zipfs.New(zr)),nil
19+
}

‎coderd/coderdtest/dynamicparameters.go‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ type DynamicParameterTemplateParams struct {
2525

2626
// TemplateID is used to update an existing template instead of creating a new one.
2727
TemplateID uuid.UUID
28+
29+
Versionfunc(request*codersdk.CreateTemplateVersionRequest)
2830
}
2931

3032
funcDynamicParameterTemplate(t*testing.T,client*codersdk.Client,org uuid.UUID,argsDynamicParameterTemplateParams) (codersdk.Template, codersdk.TemplateVersion) {
@@ -47,6 +49,9 @@ func DynamicParameterTemplate(t *testing.T, client *codersdk.Client, org uuid.UU
4749
ifargs.TemplateID!=uuid.Nil {
4850
request.TemplateID=args.TemplateID
4951
}
52+
ifargs.Version!=nil {
53+
args.Version(request)
54+
}
5055
})
5156
AwaitTemplateVersionJobCompleted(t,client,version.ID)
5257

‎coderd/dynamicparameters/error.go‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,17 @@ import (
1010
"github.com/coder/coder/v2/codersdk"
1111
)
1212

13-
funcParameterValidationError(diags hcl.Diagnostics)*DiagnosticError {
13+
funcparameterValidationError(diags hcl.Diagnostics)*DiagnosticError {
1414
return&DiagnosticError{
1515
Message:"Unable to validate parameters",
1616
Diagnostics:diags,
1717
KeyedDiagnostics:make(map[string]hcl.Diagnostics),
1818
}
1919
}
2020

21-
funcTagValidationError(diags hcl.Diagnostics)*DiagnosticError {
21+
functagValidationError(diags hcl.Diagnostics)*DiagnosticError {
2222
return&DiagnosticError{
23-
Message:"Failed to parse workspace tags",
23+
Message:"Unable to parse workspace tags",
2424
Diagnostics:diags,
2525
KeyedDiagnostics:make(map[string]hcl.Diagnostics),
2626
}

‎coderd/dynamicparameters/render.go‎

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,28 @@ func (r *dynamicRenderer) getWorkspaceOwnerData(ctx context.Context, ownerID uui
243243
returnnil// already fetched
244244
}
245245

246-
user,err:=r.db.GetUserByID(ctx,ownerID)
246+
owner,err:=WorkspaceOwner(ctx,r.db,r.data.templateVersion.OrganizationID,ownerID)
247+
iferr!=nil {
248+
returnerr
249+
}
250+
251+
r.currentOwner=owner
252+
returnnil
253+
}
254+
255+
func (r*dynamicRenderer)Close() {
256+
r.once.Do(r.close)
257+
}
258+
259+
funcProvisionerVersionSupportsDynamicParameters(versionstring)bool {
260+
major,minor,err:=apiversion.Parse(version)
261+
// If the api version is not valid or less than 1.6, we need to use the static parameters
262+
useStaticParams:=err!=nil||major<1|| (major==1&&minor<6)
263+
return!useStaticParams
264+
}
265+
266+
funcWorkspaceOwner(ctx context.Context,db database.Store,org uuid.UUID,ownerID uuid.UUID) (*previewtypes.WorkspaceOwner,error) {
267+
user,err:=db.GetUserByID(ctx,ownerID)
247268
iferr!=nil {
248269
// If the user failed to read, we also try to read the user from their
249270
// organization member. You only need to be able to read the organization member
@@ -252,37 +273,37 @@ func (r *dynamicRenderer) getWorkspaceOwnerData(ctx context.Context, ownerID uui
252273
// Only the terraform files can therefore leak more information than the
253274
// caller should have access to. All this info should be public assuming you can
254275
// read the user though.
255-
mem,err:=database.ExpectOne(r.db.OrganizationMembers(ctx, database.OrganizationMembersParams{
256-
OrganizationID:r.data.templateVersion.OrganizationID,
276+
mem,err:=database.ExpectOne(db.OrganizationMembers(ctx, database.OrganizationMembersParams{
277+
OrganizationID:org,
257278
UserID:ownerID,
258279
IncludeSystem:true,
259280
}))
260281
iferr!=nil {
261-
returnxerrors.Errorf("fetch user: %w",err)
282+
returnnil,xerrors.Errorf("fetch user: %w",err)
262283
}
263284

264285
// Org member fetched, so use the provisioner context to fetch the user.
265286
//nolint:gocritic // Has the correct permissions, and matches the provisioning flow.
266-
user,err=r.db.GetUserByID(dbauthz.AsProvisionerd(ctx),mem.OrganizationMember.UserID)
287+
user,err=db.GetUserByID(dbauthz.AsProvisionerd(ctx),mem.OrganizationMember.UserID)
267288
iferr!=nil {
268-
returnxerrors.Errorf("fetch user: %w",err)
289+
returnnil,xerrors.Errorf("fetch user: %w",err)
269290
}
270291
}
271292

272293
// nolint:gocritic // This is kind of the wrong query to use here, but it
273294
// matches how the provisioner currently works. We should figure out
274295
// something that needs less escalation but has the correct behavior.
275-
row,err:=r.db.GetAuthorizationUserRoles(dbauthz.AsProvisionerd(ctx),ownerID)
296+
row,err:=db.GetAuthorizationUserRoles(dbauthz.AsProvisionerd(ctx),ownerID)
276297
iferr!=nil {
277-
returnxerrors.Errorf("user roles: %w",err)
298+
returnnil,xerrors.Errorf("user roles: %w",err)
278299
}
279300
roles,err:=row.RoleNames()
280301
iferr!=nil {
281-
returnxerrors.Errorf("expand roles: %w",err)
302+
returnnil,xerrors.Errorf("expand roles: %w",err)
282303
}
283304
ownerRoles:=make([]previewtypes.WorkspaceOwnerRBACRole,0,len(roles))
284305
for_,it:=rangeroles {
285-
ifit.OrganizationID!=uuid.Nil&&it.OrganizationID!=r.data.templateVersion.OrganizationID {
306+
ifit.OrganizationID!=uuid.Nil&&it.OrganizationID!=org {
286307
continue
287308
}
288309
varorgIDstring
@@ -298,28 +319,28 @@ func (r *dynamicRenderer) getWorkspaceOwnerData(ctx context.Context, ownerID uui
298319
// The correct public key has to be sent. This will not be leaked
299320
// unless the template leaks it.
300321
// nolint:gocritic
301-
key,err:=r.db.GetGitSSHKey(dbauthz.AsProvisionerd(ctx),ownerID)
322+
key,err:=db.GetGitSSHKey(dbauthz.AsProvisionerd(ctx),ownerID)
302323
iferr!=nil&&!xerrors.Is(err,sql.ErrNoRows) {
303-
returnxerrors.Errorf("ssh key: %w",err)
324+
returnnil,xerrors.Errorf("ssh key: %w",err)
304325
}
305326

306327
// The groups need to be sent to preview. These groups are not exposed to the
307328
// user, unless the template does it through the parameters. Regardless, we need
308329
// the correct groups, and a user might not have read access.
309330
// nolint:gocritic
310-
groups,err:=r.db.GetGroups(dbauthz.AsProvisionerd(ctx), database.GetGroupsParams{
311-
OrganizationID:r.data.templateVersion.OrganizationID,
331+
groups,err:=db.GetGroups(dbauthz.AsProvisionerd(ctx), database.GetGroupsParams{
332+
OrganizationID:org,
312333
HasMemberID:ownerID,
313334
})
314335
iferr!=nil {
315-
returnxerrors.Errorf("groups: %w",err)
336+
returnnil,xerrors.Errorf("groups: %w",err)
316337
}
317338
groupNames:=make([]string,0,len(groups))
318339
for_,it:=rangegroups {
319340
groupNames=append(groupNames,it.Group.Name)
320341
}
321342

322-
r.currentOwner=&previewtypes.WorkspaceOwner{
343+
return&previewtypes.WorkspaceOwner{
323344
ID:user.ID.String(),
324345
Name:user.Username,
325346
FullName:user.Name,
@@ -328,17 +349,5 @@ func (r *dynamicRenderer) getWorkspaceOwnerData(ctx context.Context, ownerID uui
328349
RBACRoles:ownerRoles,
329350
SSHPublicKey:key.PublicKey,
330351
Groups:groupNames,
331-
}
332-
returnnil
333-
}
334-
335-
func (r*dynamicRenderer)Close() {
336-
r.once.Do(r.close)
337-
}
338-
339-
funcProvisionerVersionSupportsDynamicParameters(versionstring)bool {
340-
major,minor,err:=apiversion.Parse(version)
341-
// If the api version is not valid or less than 1.6, we need to use the static parameters
342-
useStaticParams:=err!=nil||major<1|| (major==1&&minor<6)
343-
return!useStaticParams
352+
},nil
344353
}

‎coderd/dynamicparameters/resolver.go‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func ResolveParameters(
7373
// always be valid. If there is a case where this is not true, then this has to
7474
// be changed to allow the build to continue with a different set of values.
7575

76-
returnnil,ParameterValidationError(diags)
76+
returnnil,parameterValidationError(diags)
7777
}
7878

7979
// The user's input now needs to be validated against the parameters.
@@ -113,13 +113,13 @@ func ResolveParameters(
113113
// are fatal. Additional validation for immutability has to be done manually.
114114
output,diags=renderer.Render(ctx,ownerID,values.ValuesMap())
115115
ifdiags.HasErrors() {
116-
returnnil,ParameterValidationError(diags)
116+
returnnil,parameterValidationError(diags)
117117
}
118118

119119
// parameterNames is going to be used to remove any excess values that were left
120120
// around without a parameter.
121121
parameterNames:=make(map[string]struct{},len(output.Parameters))
122-
parameterError:=ParameterValidationError(nil)
122+
parameterError:=parameterValidationError(nil)
123123
for_,parameter:=rangeoutput.Parameters {
124124
parameterNames[parameter.Name]=struct{}{}
125125

‎coderd/dynamicparameters/tags.go‎

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package dynamicparameters
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/hashicorp/hcl/v2"
7+
8+
"github.com/coder/preview"
9+
previewtypes"github.com/coder/preview/types"
10+
)
11+
12+
funcCheckTags(output*preview.Output,diags hcl.Diagnostics)*DiagnosticError {
13+
de:=tagValidationError(diags)
14+
failedTags:=output.WorkspaceTags.UnusableTags()
15+
iflen(failedTags)==0&&!de.HasError() {
16+
returnnil// No errors, all is good!
17+
}
18+
19+
for_,tag:=rangefailedTags {
20+
name:=tag.KeyString()
21+
ifname==previewtypes.UnknownStringValue {
22+
name="unknown"// Best effort to get a name for the tag
23+
}
24+
de.Extend(name,failedTagDiagnostic(tag))
25+
}
26+
returnde
27+
}
28+
29+
// failedTagDiagnostic is a helper function that takes an invalid tag and
30+
// returns an appropriate hcl diagnostic for it.
31+
funcfailedTagDiagnostic(tag previewtypes.Tag) hcl.Diagnostics {
32+
const (
33+
key="key"
34+
value="value"
35+
)
36+
37+
diags:= hcl.Diagnostics{}
38+
39+
// TODO: It would be really nice to pull out the variable references to help identify the source of
40+
// the unknown or invalid tag.
41+
unknownErr:="Tag %s is not known, it likely refers to a variable that is not set or has no default."
42+
invalidErr:="Tag %s is not valid, it must be a non-null string value."
43+
44+
if!tag.Key.Value.IsWhollyKnown() {
45+
diags=diags.Append(&hcl.Diagnostic{
46+
Severity:hcl.DiagError,
47+
Summary:fmt.Sprintf(unknownErr,key),
48+
})
49+
}elseif!tag.Key.Valid() {
50+
diags=diags.Append(&hcl.Diagnostic{
51+
Severity:hcl.DiagError,
52+
Summary:fmt.Sprintf(invalidErr,key),
53+
})
54+
}
55+
56+
if!tag.Value.Value.IsWhollyKnown() {
57+
diags=diags.Append(&hcl.Diagnostic{
58+
Severity:hcl.DiagError,
59+
Summary:fmt.Sprintf(unknownErr,value),
60+
})
61+
}elseif!tag.Value.Valid() {
62+
diags=diags.Append(&hcl.Diagnostic{
63+
Severity:hcl.DiagError,
64+
Summary:fmt.Sprintf(invalidErr,value),
65+
})
66+
}
67+
68+
ifdiags.HasErrors() {
69+
// Stop here if there are diags, as the diags manually created above are more
70+
// informative than the original tag's diagnostics.
71+
returndiags
72+
}
73+
74+
// If we reach here, decorate the original tag's diagnostics
75+
diagErr:="Tag %s: %s"
76+
iftag.Key.ValueDiags.HasErrors() {
77+
// add 'Tag key' prefix to each diagnostic
78+
for_,d:=rangetag.Key.ValueDiags {
79+
d.Summary=fmt.Sprintf(diagErr,key,d.Summary)
80+
}
81+
}
82+
diags=diags.Extend(tag.Key.ValueDiags)
83+
84+
iftag.Value.ValueDiags.HasErrors() {
85+
// add 'Tag value' prefix to each diagnostic
86+
for_,d:=rangetag.Value.ValueDiags {
87+
d.Summary=fmt.Sprintf(diagErr,value,d.Summary)
88+
}
89+
}
90+
diags=diags.Extend(tag.Value.ValueDiags)
91+
92+
if!diags.HasErrors() {
93+
diags=diags.Append(&hcl.Diagnostic{
94+
Severity:hcl.DiagError,
95+
Summary:"Tag is invalid for some unknown reason. Please check the tag's value and key.",
96+
})
97+
}
98+
99+
returndiags
100+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp