Skip to content

Commit 083fc89

Browse files
authored
feat: accept immutable parameters when used first time (#7000)
* Backend fixes * CLI: adjust update flow
1 parent e84061e commit 083fc89

File tree

4 files changed

+220
-7
lines changed

4 files changed

+220
-7
lines changed

cli/create.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"io"
77
"time"
88

9+
"github.com/google/uuid"
910
"golang.org/x/exp/slices"
1011
"golang.org/x/xerrors"
1112

@@ -213,6 +214,7 @@ type prepWorkspaceBuildArgs struct {
213214
NewWorkspaceName string
214215

215216
UpdateWorkspace bool
217+
WorkspaceID uuid.UUID
216218
}
217219

218220
type buildParameters struct {
@@ -340,8 +342,17 @@ PromptRichParamLoop:
340342
}
341343

342344
if args.UpdateWorkspace && !templateVersionParameter.Mutable {
343-
_, _ = fmt.Fprintln(inv.Stdout, cliui.Styles.Warn.Render(fmt.Sprintf(`Parameter %q is not mutable, so can't be customized after workspace creation.`, templateVersionParameter.Name)))
344-
continue
345+
// Check if the immutable parameter was used in the previous build. If so, then it isn't a fresh one
346+
// and the user should be warned.
347+
exists, err := workspaceBuildParameterExists(ctx, client, args.WorkspaceID, templateVersionParameter)
348+
if err != nil {
349+
return nil, err
350+
}
351+
352+
if exists {
353+
_, _ = fmt.Fprintln(inv.Stdout, cliui.Styles.Warn.Render(fmt.Sprintf(`Parameter %q is not mutable, so can't be customized after workspace creation.`, templateVersionParameter.Name)))
354+
continue
355+
}
345356
}
346357

347358
parameterValue, err := getWorkspaceBuildParameterValueFromMapOrInput(inv, parameterMapFromFile, templateVersionParameter)
@@ -414,3 +425,17 @@ PromptRichParamLoop:
414425
richParameters: richParameters,
415426
}, nil
416427
}
428+
429+
func workspaceBuildParameterExists(ctx context.Context, client *codersdk.Client, workspaceID uuid.UUID, templateVersionParameter codersdk.TemplateVersionParameter) (bool, error) {
430+
lastBuildParameters, err := client.WorkspaceBuildParameters(ctx, workspaceID)
431+
if err != nil {
432+
return false, xerrors.Errorf("can't fetch last workspace build parameters: %w", err)
433+
}
434+
435+
for _, p := range lastBuildParameters {
436+
if p.Name == templateVersionParameter.Name {
437+
return true, nil
438+
}
439+
}
440+
return false, nil
441+
}

cli/update.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@ func (r *RootCmd) update() *clibase.Cmd {
5959
ExistingRichParams: existingRichParams,
6060
RichParameterFile: richParameterFile,
6161
NewWorkspaceName: workspace.Name,
62-
UpdateWorkspace: true,
62+
63+
UpdateWorkspace: true,
64+
WorkspaceID: workspace.LatestBuild.ID,
6365
})
6466
if err != nil {
6567
return nil

coderd/workspacebuilds.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -530,10 +530,12 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
530530
// Check if parameter value is in request
531531
if buildParameter, found := findWorkspaceBuildParameter(createBuild.RichParameterValues, templateVersionParameter.Name); found {
532532
if !templateVersionParameter.Mutable {
533-
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
534-
Message: fmt.Sprintf("Parameter %q is not mutable, so it can't be updated after creating a workspace.", templateVersionParameter.Name),
535-
})
536-
return
533+
if _, found := findWorkspaceBuildParameter(apiLastBuildParameters, templateVersionParameter.Name); found {
534+
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
535+
Message: fmt.Sprintf("Parameter %q is not mutable, so it can't be updated after creating a workspace.", templateVersionParameter.Name),
536+
})
537+
return
538+
}
537539
}
538540
parameters = append(parameters, *buildParameter)
539541
continue

coderd/workspacebuilds_test.go

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,190 @@ func TestWorkspaceBuildWithRichParameters(t *testing.T) {
781781
})
782782
require.Error(t, err)
783783
})
784+
785+
t.Run("NewImmutableRequiredParameterAdded", func(t *testing.T) {
786+
t.Parallel()
787+
788+
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
789+
user := coderdtest.CreateFirstUser(t, client)
790+
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, echoResponses)
791+
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
792+
793+
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
794+
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID, func(cwr *codersdk.CreateWorkspaceRequest) {
795+
cwr.RichParameterValues = initialBuildParameters
796+
})
797+
798+
workspaceBuild := coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
799+
require.Equal(t, codersdk.WorkspaceStatusRunning, workspaceBuild.Status)
800+
801+
// Push new template revision
802+
const newImmutableParameterName = "new_immutable_parameter"
803+
const newImmutableParameterDescription = "This is also an immutable parameter"
804+
version2 := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
805+
Parse: echo.ParseComplete,
806+
ProvisionPlan: []*proto.Provision_Response{
807+
{
808+
Type: &proto.Provision_Response_Complete{
809+
Complete: &proto.Provision_Complete{
810+
Parameters: []*proto.RichParameter{
811+
{Name: firstParameterName, Description: firstParameterDescription, Mutable: true},
812+
{Name: secondParameterName, Description: secondParameterDescription, Mutable: true},
813+
{Name: immutableParameterName, Description: immutableParameterDescription, Mutable: false},
814+
{Name: newImmutableParameterName, Description: newImmutableParameterDescription, Mutable: false, Required: true},
815+
},
816+
},
817+
},
818+
},
819+
},
820+
ProvisionApply: []*proto.Provision_Response{{
821+
Type: &proto.Provision_Response_Complete{
822+
Complete: &proto.Provision_Complete{},
823+
},
824+
}},
825+
}, template.ID)
826+
coderdtest.AwaitTemplateVersionJob(t, client, version2.ID)
827+
err := client.UpdateActiveTemplateVersion(context.Background(), template.ID, codersdk.UpdateActiveTemplateVersion{
828+
ID: version2.ID,
829+
})
830+
require.NoError(t, err)
831+
832+
// Update build parameters
833+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
834+
defer cancel()
835+
836+
nextBuildParameters := []codersdk.WorkspaceBuildParameter{
837+
{Name: newImmutableParameterName, Value: "good"},
838+
}
839+
_, err = client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
840+
TemplateVersionID: version2.ID,
841+
Transition: codersdk.WorkspaceTransitionStart,
842+
RichParameterValues: nextBuildParameters,
843+
})
844+
require.NoError(t, err)
845+
})
846+
847+
t.Run("NewImmutableOptionalParameterAdded", func(t *testing.T) {
848+
t.Parallel()
849+
850+
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
851+
user := coderdtest.CreateFirstUser(t, client)
852+
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, echoResponses)
853+
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
854+
855+
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
856+
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID, func(cwr *codersdk.CreateWorkspaceRequest) {
857+
cwr.RichParameterValues = initialBuildParameters
858+
})
859+
860+
workspaceBuild := coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
861+
require.Equal(t, codersdk.WorkspaceStatusRunning, workspaceBuild.Status)
862+
863+
// Push new template revision
864+
const newImmutableParameterName = "new_immutable_parameter"
865+
const newImmutableParameterDescription = "This is also an immutable parameter"
866+
version2 := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
867+
Parse: echo.ParseComplete,
868+
ProvisionPlan: []*proto.Provision_Response{
869+
{
870+
Type: &proto.Provision_Response_Complete{
871+
Complete: &proto.Provision_Complete{
872+
Parameters: []*proto.RichParameter{
873+
{Name: firstParameterName, Description: firstParameterDescription, Mutable: true},
874+
{Name: secondParameterName, Description: secondParameterDescription, Mutable: true},
875+
{Name: immutableParameterName, Description: immutableParameterDescription, Mutable: false},
876+
{Name: newImmutableParameterName, Description: newImmutableParameterDescription, Mutable: false, DefaultValue: "12345"},
877+
},
878+
},
879+
},
880+
},
881+
},
882+
ProvisionApply: []*proto.Provision_Response{{
883+
Type: &proto.Provision_Response_Complete{
884+
Complete: &proto.Provision_Complete{},
885+
},
886+
}},
887+
}, template.ID)
888+
coderdtest.AwaitTemplateVersionJob(t, client, version2.ID)
889+
err := client.UpdateActiveTemplateVersion(context.Background(), template.ID, codersdk.UpdateActiveTemplateVersion{
890+
ID: version2.ID,
891+
})
892+
require.NoError(t, err)
893+
894+
// Update build parameters
895+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
896+
defer cancel()
897+
898+
nextBuildParameters := []codersdk.WorkspaceBuildParameter{
899+
{Name: newImmutableParameterName, Value: "good"},
900+
}
901+
_, err = client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
902+
TemplateVersionID: version2.ID,
903+
Transition: codersdk.WorkspaceTransitionStart,
904+
RichParameterValues: nextBuildParameters,
905+
})
906+
require.NoError(t, err)
907+
})
908+
909+
t.Run("NewImmutableOptionalParameterUsesDefault", func(t *testing.T) {
910+
t.Parallel()
911+
912+
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
913+
user := coderdtest.CreateFirstUser(t, client)
914+
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, echoResponses)
915+
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
916+
917+
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
918+
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID, func(cwr *codersdk.CreateWorkspaceRequest) {
919+
cwr.RichParameterValues = initialBuildParameters
920+
})
921+
922+
workspaceBuild := coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
923+
require.Equal(t, codersdk.WorkspaceStatusRunning, workspaceBuild.Status)
924+
925+
// Push new template revision
926+
const newImmutableParameterName = "new_immutable_parameter"
927+
const newImmutableParameterDescription = "This is also an immutable parameter"
928+
version2 := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
929+
Parse: echo.ParseComplete,
930+
ProvisionPlan: []*proto.Provision_Response{
931+
{
932+
Type: &proto.Provision_Response_Complete{
933+
Complete: &proto.Provision_Complete{
934+
Parameters: []*proto.RichParameter{
935+
{Name: firstParameterName, Description: firstParameterDescription, Mutable: true},
936+
{Name: secondParameterName, Description: secondParameterDescription, Mutable: true},
937+
{Name: immutableParameterName, Description: immutableParameterDescription, Mutable: false},
938+
{Name: newImmutableParameterName, Description: newImmutableParameterDescription, Mutable: false, DefaultValue: "12345"},
939+
},
940+
},
941+
},
942+
},
943+
},
944+
ProvisionApply: []*proto.Provision_Response{{
945+
Type: &proto.Provision_Response_Complete{
946+
Complete: &proto.Provision_Complete{},
947+
},
948+
}},
949+
}, template.ID)
950+
coderdtest.AwaitTemplateVersionJob(t, client, version2.ID)
951+
err := client.UpdateActiveTemplateVersion(context.Background(), template.ID, codersdk.UpdateActiveTemplateVersion{
952+
ID: version2.ID,
953+
})
954+
require.NoError(t, err)
955+
956+
// Update build parameters
957+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
958+
defer cancel()
959+
960+
var nextBuildParameters []codersdk.WorkspaceBuildParameter
961+
_, err = client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
962+
TemplateVersionID: version2.ID,
963+
Transition: codersdk.WorkspaceTransitionStart,
964+
RichParameterValues: nextBuildParameters,
965+
})
966+
require.NoError(t, err)
967+
})
784968
}
785969

786970
func TestWorkspaceBuildValidateRichParameters(t *testing.T) {

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