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

Commit46e04c6

Browse files
authored
feat(provisioner): add support for presets to coder provisioners (#16574)
This pull request adds support for presets to coder provisioners.If a template defines presets using a compatible version of theprovider, then this PR will allow those presets to be persisted to thecontrol plane database for use in workspace creation.
1 parenta845370 commit46e04c6

File tree

20 files changed

+2252
-760
lines changed

20 files changed

+2252
-760
lines changed

‎coderd/presets_test.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ import (
1515
)
1616

1717
funcTestTemplateVersionPresets(t*testing.T) {
18-
// TODO (sasswart): Test case: what if a user tries to read presets or preset parameters from a different org?
19-
2018
t.Parallel()
2119

2220
givenPreset:= codersdk.Preset{

‎coderd/provisionerdserver/provisionerdserver.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,6 +1340,11 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
13401340
}
13411341
}
13421342

1343+
err=InsertWorkspacePresetsAndParameters(ctx,s.Logger,s.Database,jobID,input.TemplateVersionID,jobType.TemplateImport.Presets,s.timeNow())
1344+
iferr!=nil {
1345+
returnnil,xerrors.Errorf("insert workspace presets and parameters: %w",err)
1346+
}
1347+
13431348
varcompletedError sql.NullString
13441349

13451350
for_,externalAuthProvider:=rangejobType.TemplateImport.ExternalAuthProviders {
@@ -1809,6 +1814,52 @@ func InsertWorkspaceModule(ctx context.Context, db database.Store, jobID uuid.UU
18091814
returnnil
18101815
}
18111816

1817+
funcInsertWorkspacePresetsAndParameters(ctx context.Context,logger slog.Logger,db database.Store,jobID uuid.UUID,templateVersionID uuid.UUID,protoPresets []*sdkproto.Preset,t time.Time)error {
1818+
for_,preset:=rangeprotoPresets {
1819+
logger.Info(ctx,"inserting template import job preset",
1820+
slog.F("job_id",jobID.String()),
1821+
slog.F("preset_name",preset.Name),
1822+
)
1823+
iferr:=InsertWorkspacePresetAndParameters(ctx,db,templateVersionID,preset,t);err!=nil {
1824+
returnxerrors.Errorf("insert workspace preset: %w",err)
1825+
}
1826+
}
1827+
returnnil
1828+
}
1829+
1830+
funcInsertWorkspacePresetAndParameters(ctx context.Context,db database.Store,templateVersionID uuid.UUID,protoPreset*sdkproto.Preset,t time.Time)error {
1831+
err:=db.InTx(func(tx database.Store)error {
1832+
dbPreset,err:=tx.InsertPreset(ctx, database.InsertPresetParams{
1833+
TemplateVersionID:templateVersionID,
1834+
Name:protoPreset.Name,
1835+
CreatedAt:t,
1836+
})
1837+
iferr!=nil {
1838+
returnxerrors.Errorf("insert preset: %w",err)
1839+
}
1840+
1841+
varpresetParameterNames []string
1842+
varpresetParameterValues []string
1843+
for_,parameter:=rangeprotoPreset.Parameters {
1844+
presetParameterNames=append(presetParameterNames,parameter.Name)
1845+
presetParameterValues=append(presetParameterValues,parameter.Value)
1846+
}
1847+
_,err=tx.InsertPresetParameters(ctx, database.InsertPresetParametersParams{
1848+
TemplateVersionPresetID:dbPreset.ID,
1849+
Names:presetParameterNames,
1850+
Values:presetParameterValues,
1851+
})
1852+
iferr!=nil {
1853+
returnxerrors.Errorf("insert preset parameters: %w",err)
1854+
}
1855+
returnnil
1856+
},nil)
1857+
iferr!=nil {
1858+
returnxerrors.Errorf("insert preset and parameters: %w",err)
1859+
}
1860+
returnnil
1861+
}
1862+
18121863
funcInsertWorkspaceResource(ctx context.Context,db database.Store,jobID uuid.UUID,transition database.WorkspaceTransition,protoResource*sdkproto.Resource,snapshot*telemetry.Snapshot)error {
18131864
resource,err:=db.InsertWorkspaceResource(ctx, database.InsertWorkspaceResourceParams{
18141865
ID:uuid.New(),

‎coderd/provisionerdserver/provisionerdserver_test.go

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"github.com/coder/coder/v2/coderd/database"
3131
"github.com/coder/coder/v2/coderd/database/dbgen"
3232
"github.com/coder/coder/v2/coderd/database/dbmem"
33+
"github.com/coder/coder/v2/coderd/database/dbtestutil"
3334
"github.com/coder/coder/v2/coderd/database/dbtime"
3435
"github.com/coder/coder/v2/coderd/database/pubsub"
3536
"github.com/coder/coder/v2/coderd/externalauth"
@@ -1708,6 +1709,155 @@ func TestCompleteJob(t *testing.T) {
17081709
})
17091710
}
17101711

1712+
funcTestInsertWorkspacePresetsAndParameters(t*testing.T) {
1713+
t.Parallel()
1714+
1715+
typetestCasestruct {
1716+
namestring
1717+
givenPresets []*sdkproto.Preset
1718+
}
1719+
1720+
testCases:= []testCase{
1721+
{
1722+
name:"no presets",
1723+
},
1724+
{
1725+
name:"one preset with no parameters",
1726+
givenPresets: []*sdkproto.Preset{
1727+
{
1728+
Name:"preset1",
1729+
},
1730+
},
1731+
},
1732+
{
1733+
name:"one preset with multiple parameters",
1734+
givenPresets: []*sdkproto.Preset{
1735+
{
1736+
Name:"preset1",
1737+
Parameters: []*sdkproto.PresetParameter{
1738+
{
1739+
Name:"param1",
1740+
Value:"value1",
1741+
},
1742+
{
1743+
Name:"param2",
1744+
Value:"value2",
1745+
},
1746+
},
1747+
},
1748+
},
1749+
},
1750+
{
1751+
name:"multiple presets with parameters",
1752+
givenPresets: []*sdkproto.Preset{
1753+
{
1754+
Name:"preset1",
1755+
Parameters: []*sdkproto.PresetParameter{
1756+
{
1757+
Name:"param1",
1758+
Value:"value1",
1759+
},
1760+
{
1761+
Name:"param2",
1762+
Value:"value2",
1763+
},
1764+
},
1765+
},
1766+
{
1767+
Name:"preset2",
1768+
Parameters: []*sdkproto.PresetParameter{
1769+
{
1770+
Name:"param3",
1771+
Value:"value3",
1772+
},
1773+
{
1774+
Name:"param4",
1775+
Value:"value4",
1776+
},
1777+
},
1778+
},
1779+
},
1780+
},
1781+
}
1782+
1783+
for_,c:=rangetestCases {
1784+
c:=c
1785+
t.Run(c.name,func(t*testing.T) {
1786+
t.Parallel()
1787+
1788+
ctx:=testutil.Context(t,testutil.WaitLong)
1789+
logger:=testutil.Logger(t)
1790+
db,ps:=dbtestutil.NewDB(t)
1791+
org:=dbgen.Organization(t,db, database.Organization{})
1792+
user:=dbgen.User(t,db, database.User{})
1793+
job:=dbgen.ProvisionerJob(t,db,ps, database.ProvisionerJob{
1794+
Type:database.ProvisionerJobTypeWorkspaceBuild,
1795+
OrganizationID:org.ID,
1796+
})
1797+
templateVersion:=dbgen.TemplateVersion(t,db, database.TemplateVersion{
1798+
JobID:job.ID,
1799+
OrganizationID:org.ID,
1800+
CreatedBy:user.ID,
1801+
})
1802+
1803+
err:=provisionerdserver.InsertWorkspacePresetsAndParameters(
1804+
ctx,
1805+
logger,
1806+
db,
1807+
job.ID,
1808+
templateVersion.ID,
1809+
c.givenPresets,
1810+
time.Now(),
1811+
)
1812+
require.NoError(t,err)
1813+
1814+
gotPresets,err:=db.GetPresetsByTemplateVersionID(ctx,templateVersion.ID)
1815+
require.NoError(t,err)
1816+
require.Len(t,gotPresets,len(c.givenPresets))
1817+
1818+
for_,givenPreset:=rangec.givenPresets {
1819+
foundMatch:=false
1820+
for_,gotPreset:=rangegotPresets {
1821+
ifgivenPreset.Name==gotPreset.Name {
1822+
foundMatch=true
1823+
break
1824+
}
1825+
}
1826+
require.True(t,foundMatch,"preset %s not found in parameters",givenPreset.Name)
1827+
}
1828+
1829+
gotPresetParameters,err:=db.GetPresetParametersByTemplateVersionID(ctx,templateVersion.ID)
1830+
require.NoError(t,err)
1831+
1832+
for_,givenPreset:=rangec.givenPresets {
1833+
for_,givenParameter:=rangegivenPreset.Parameters {
1834+
foundMatch:=false
1835+
for_,gotParameter:=rangegotPresetParameters {
1836+
nameMatches:=givenParameter.Name==gotParameter.Name
1837+
valueMatches:=givenParameter.Value==gotParameter.Value
1838+
1839+
// ensure that preset parameters are matched to the correct preset:
1840+
vargotPreset database.TemplateVersionPreset
1841+
for_,preset:=rangegotPresets {
1842+
ifpreset.ID==gotParameter.TemplateVersionPresetID {
1843+
gotPreset=preset
1844+
break
1845+
}
1846+
}
1847+
presetMatches:=gotPreset.Name==givenPreset.Name
1848+
1849+
ifnameMatches&&valueMatches&&presetMatches {
1850+
foundMatch=true
1851+
break
1852+
}
1853+
}
1854+
require.True(t,foundMatch,"preset parameter %s not found in presets",givenParameter.Name)
1855+
}
1856+
}
1857+
})
1858+
}
1859+
}
1860+
17111861
funcTestInsertWorkspaceResource(t*testing.T) {
17121862
t.Parallel()
17131863
ctx:=context.Background()

‎provisioner/terraform/executor.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr l
308308
Resources:state.Resources,
309309
ExternalAuthProviders:state.ExternalAuthProviders,
310310
Timings:append(e.timings.aggregate(),graphTimings.aggregate()...),
311+
Presets:state.Presets,
311312
},nil
312313
}
313314

‎provisioner/terraform/resources.go

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ type resourceMetadataItem struct {
149149
typeStatestruct {
150150
Resources []*proto.Resource
151151
Parameters []*proto.RichParameter
152+
Presets []*proto.Preset
152153
ExternalAuthProviders []*proto.ExternalAuthProviderResource
153154
}
154155

@@ -176,7 +177,7 @@ func ConvertState(ctx context.Context, modules []*tfjson.StateModule, rawGraph s
176177

177178
// Extra array to preserve the order of rich parameters.
178179
tfResourcesRichParameters:=make([]*tfjson.StateResource,0)
179-
180+
tfResourcesPresets:=make([]*tfjson.StateResource,0)
180181
varfindTerraformResourcesfunc(mod*tfjson.StateModule)
181182
findTerraformResources=func(mod*tfjson.StateModule) {
182183
for_,module:=rangemod.ChildModules {
@@ -186,6 +187,9 @@ func ConvertState(ctx context.Context, modules []*tfjson.StateModule, rawGraph s
186187
ifresource.Type=="coder_parameter" {
187188
tfResourcesRichParameters=append(tfResourcesRichParameters,resource)
188189
}
190+
ifresource.Type=="coder_workspace_preset" {
191+
tfResourcesPresets=append(tfResourcesPresets,resource)
192+
}
189193

190194
label:=convertAddressToLabel(resource.Address)
191195
iftfResourcesByLabel[label]==nil {
@@ -775,6 +779,78 @@ func ConvertState(ctx context.Context, modules []*tfjson.StateModule, rawGraph s
775779
)
776780
}
777781

782+
varduplicatedPresetNames []string
783+
presets:=make([]*proto.Preset,0)
784+
for_,resource:=rangetfResourcesPresets {
785+
varpreset provider.WorkspacePreset
786+
err=mapstructure.Decode(resource.AttributeValues,&preset)
787+
iferr!=nil {
788+
returnnil,xerrors.Errorf("decode preset attributes: %w",err)
789+
}
790+
791+
varduplicatedPresetParameterNames []string
792+
varnonExistentParameters []string
793+
varpresetParameters []*proto.PresetParameter
794+
forname,value:=rangepreset.Parameters {
795+
presetParameter:=&proto.PresetParameter{
796+
Name:name,
797+
Value:value,
798+
}
799+
800+
formattedName:=fmt.Sprintf("%q",name)
801+
if!slice.Contains(duplicatedPresetParameterNames,formattedName)&&
802+
slice.ContainsCompare(presetParameters,presetParameter,func(a,b*proto.PresetParameter)bool {
803+
returna.Name==b.Name
804+
}) {
805+
duplicatedPresetParameterNames=append(duplicatedPresetParameterNames,formattedName)
806+
}
807+
if!slice.ContainsCompare(parameters,&proto.RichParameter{Name:name},func(a,b*proto.RichParameter)bool {
808+
returna.Name==b.Name
809+
}) {
810+
nonExistentParameters=append(nonExistentParameters,name)
811+
}
812+
813+
presetParameters=append(presetParameters,presetParameter)
814+
}
815+
816+
iflen(duplicatedPresetParameterNames)>0 {
817+
s:=""
818+
iflen(duplicatedPresetParameterNames)==1 {
819+
s="s"
820+
}
821+
returnnil,xerrors.Errorf(
822+
"coder_workspace_preset parameters must be unique but %s appear%s multiple times",stringutil.JoinWithConjunction(duplicatedPresetParameterNames),s,
823+
)
824+
}
825+
826+
iflen(nonExistentParameters)>0 {
827+
logger.Warn(
828+
ctx,
829+
"coder_workspace_preset defines preset values for at least one parameter that is not defined by the template",
830+
slog.F("parameters",stringutil.JoinWithConjunction(nonExistentParameters)),
831+
)
832+
}
833+
834+
protoPreset:=&proto.Preset{
835+
Name:preset.Name,
836+
Parameters:presetParameters,
837+
}
838+
ifslice.Contains(duplicatedPresetNames,preset.Name) {
839+
duplicatedPresetNames=append(duplicatedPresetNames,preset.Name)
840+
}
841+
presets=append(presets,protoPreset)
842+
}
843+
iflen(duplicatedPresetNames)>0 {
844+
s:=""
845+
iflen(duplicatedPresetNames)==1 {
846+
s="s"
847+
}
848+
returnnil,xerrors.Errorf(
849+
"coder_workspace_preset names must be unique but %s appear%s multiple times",
850+
stringutil.JoinWithConjunction(duplicatedPresetNames),s,
851+
)
852+
}
853+
778854
// A map is used to ensure we don't have duplicates!
779855
externalAuthProvidersMap:=map[string]*proto.ExternalAuthProviderResource{}
780856
for_,tfResources:=rangetfResourcesByLabel {
@@ -808,6 +884,7 @@ func ConvertState(ctx context.Context, modules []*tfjson.StateModule, rawGraph s
808884
return&State{
809885
Resources:resources,
810886
Parameters:parameters,
887+
Presets:presets,
811888
ExternalAuthProviders:externalAuthProviders,
812889
},nil
813890
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp