mirror of
https://github.com/coder/coder.git
synced 2026-09-21 12:44:32 +08:00
fix(cli): allow new immutable parameters via --parameter flag during update (#22221)
## Problem When a template adds a new immutable parameter, `coder update --parameter param=value` fails with: ``` error: start workspace: parameter "machine_type" is immutable and cannot be updated ``` The interactive prompt handles this correctly (allows setting first-time immutable params), but the CLI `--parameter` flag path does not. ## Root Cause In `cli/parameterresolver.go`, `verifyConstraints()` runs before the interactive prompt and unconditionally rejects any immutable parameter during updates. It doesn't distinguish between **new** immutable parameters (first-time use, should be allowed) and **existing** ones (already set, should be blocked from changing). ## Fix Added an `isFirstTimeUse` check to the immutable parameter constraint, matching the logic already used by the interactive prompt path (line 323). New immutable parameters can now be set via `--parameter`, while existing immutable parameters are still blocked from being changed. ## Testing Added `TestUpdateValidateRichParameters/NewImmutableParameterViaFlag` which: 1. Creates a workspace with a mutable parameter 2. Updates the template to add a new immutable parameter 3. Runs `coder update --parameter immutable_param=value` 4. Verifies the update succeeds and the parameter is set correctly Fixes #22164
This commit is contained in:
@@ -297,7 +297,7 @@ func (pr *ParameterResolver) verifyConstraints(resolved []codersdk.WorkspaceBuil
|
||||
return xerrors.Errorf("ephemeral parameter %q can be used only with --prompt-ephemeral-parameters or --ephemeral-parameter flag", r.Name)
|
||||
}
|
||||
|
||||
if !tvp.Mutable && action != WorkspaceCreate {
|
||||
if !tvp.Mutable && action != WorkspaceCreate && !pr.isFirstTimeUse(r.Name) {
|
||||
return xerrors.Errorf("parameter %q is immutable and cannot be updated", r.Name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -990,4 +990,74 @@ func TestUpdateValidateRichParameters(t *testing.T) {
|
||||
|
||||
_ = testutil.TryReceive(ctx, t, doneChan)
|
||||
})
|
||||
|
||||
t.Run("NewImmutableParameterViaFlag", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create template and workspace with only a mutable parameter.
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||
owner := coderdtest.CreateFirstUser(t, client)
|
||||
member, memberUser := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
|
||||
|
||||
templateParameters := []*proto.RichParameter{
|
||||
{Name: stringParameterName, Type: "string", Mutable: true, Required: true, Options: []*proto.RichParameterOption{
|
||||
{Name: "First option", Description: "This is first option", Value: "1st"},
|
||||
{Name: "Second option", Description: "This is second option", Value: "2nd"},
|
||||
}},
|
||||
}
|
||||
version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, prepareEchoResponses(templateParameters))
|
||||
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
||||
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID)
|
||||
|
||||
inv, root := clitest.New(t, "create", "my-workspace", "--yes", "--template", template.Name, "--parameter", fmt.Sprintf("%s=%s", stringParameterName, "1st"))
|
||||
clitest.SetupConfig(t, member, root)
|
||||
err := inv.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Update template: add a new immutable parameter.
|
||||
updatedTemplateParameters := []*proto.RichParameter{
|
||||
templateParameters[0],
|
||||
{Name: immutableParameterName, Type: "string", Mutable: false, Required: true, Options: []*proto.RichParameterOption{
|
||||
{Name: "fir", Description: "First option for immutable parameter", Value: "I"},
|
||||
{Name: "sec", Description: "Second option for immutable parameter", Value: "II"},
|
||||
}},
|
||||
}
|
||||
|
||||
updatedVersion := coderdtest.UpdateTemplateVersion(t, client, owner.OrganizationID, prepareEchoResponses(updatedTemplateParameters), template.ID)
|
||||
coderdtest.AwaitTemplateVersionJobCompleted(t, client, updatedVersion.ID)
|
||||
err = client.UpdateActiveTemplateVersion(context.Background(), template.ID, codersdk.UpdateActiveTemplateVersion{
|
||||
ID: updatedVersion.ID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Update workspace, supplying the new immutable parameter via
|
||||
// the --parameter flag. This should succeed because it's the
|
||||
// first time this parameter is being set.
|
||||
inv, root = clitest.New(t, "update", "my-workspace",
|
||||
"--parameter", fmt.Sprintf("%s=%s", immutableParameterName, "II"))
|
||||
clitest.SetupConfig(t, member, root)
|
||||
|
||||
pty := ptytest.New(t).Attach(inv)
|
||||
doneChan := make(chan struct{})
|
||||
go func() {
|
||||
defer close(doneChan)
|
||||
err := inv.Run()
|
||||
assert.NoError(t, err)
|
||||
}()
|
||||
|
||||
pty.ExpectMatch("Planning workspace")
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
_ = testutil.TryReceive(ctx, t, doneChan)
|
||||
|
||||
// Verify the immutable parameter was set correctly.
|
||||
workspace, err := client.WorkspaceByOwnerAndName(ctx, memberUser.ID.String(), "my-workspace", codersdk.WorkspaceOptions{})
|
||||
require.NoError(t, err)
|
||||
actualParameters, err := client.WorkspaceBuildParameters(ctx, workspace.LatestBuild.ID)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, actualParameters, codersdk.WorkspaceBuildParameter{
|
||||
Name: immutableParameterName,
|
||||
Value: "II",
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user