chore: use static params when dynamic param metadata is missing (#17836)

Existing template versions do not have the metadata (modules + plan) in
the db. So revert to using static parameter information from the
original template import.

This data will still be served over the websocket.
This commit is contained in:
Steven Masley
2025-05-16 11:47:59 -05:00
committed by GitHub
parent fb0e3d64db
commit f36fb67f57
14 changed files with 559 additions and 252 deletions

View File

@ -0,0 +1,101 @@
package coderd_test
import (
"os"
"testing"
"github.com/stretchr/testify/require"
"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/enterprise/coderd/coderdenttest"
"github.com/coder/coder/v2/enterprise/coderd/license"
"github.com/coder/coder/v2/provisioner/echo"
"github.com/coder/coder/v2/provisionersdk/proto"
"github.com/coder/coder/v2/testutil"
"github.com/coder/websocket"
)
func TestDynamicParametersOwnerGroups(t *testing.T) {
t.Parallel()
cfg := coderdtest.DeploymentValues(t)
cfg.Experiments = []string{string(codersdk.ExperimentDynamicParameters)}
ownerClient, owner := coderdenttest.New(t,
&coderdenttest.Options{
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
codersdk.FeatureTemplateRBAC: 1,
},
},
Options: &coderdtest.Options{IncludeProvisionerDaemon: true, DeploymentValues: cfg},
},
)
templateAdmin, templateAdminUser := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID, rbac.RoleTemplateAdmin())
// Create the group to be asserted
group := coderdtest.CreateGroup(t, ownerClient, owner.OrganizationID, "bloob", templateAdminUser)
dynamicParametersTerraformSource, err := os.ReadFile("testdata/parameters/groups/main.tf")
require.NoError(t, err)
dynamicParametersTerraformPlan, err := os.ReadFile("testdata/parameters/groups/plan.json")
require.NoError(t, err)
files := echo.WithExtraFiles(map[string][]byte{
"main.tf": dynamicParametersTerraformSource,
})
files.ProvisionPlan = []*proto.Response{{
Type: &proto.Response_Plan{
Plan: &proto.PlanComplete{
Plan: dynamicParametersTerraformPlan,
},
},
}}
version := coderdtest.CreateTemplateVersion(t, templateAdmin, owner.OrganizationID, files)
coderdtest.AwaitTemplateVersionJobCompleted(t, templateAdmin, version.ID)
_ = coderdtest.CreateTemplate(t, templateAdmin, owner.OrganizationID, version.ID)
ctx := testutil.Context(t, testutil.WaitShort)
stream, err := templateAdmin.TemplateVersionDynamicParameters(ctx, templateAdminUser.ID, version.ID)
require.NoError(t, err)
defer stream.Close(websocket.StatusGoingAway)
previews := stream.Chan()
// Should automatically send a form state with all defaulted/empty values
preview := testutil.RequireReceive(ctx, t, previews)
require.Equal(t, -1, preview.ID)
require.Empty(t, preview.Diagnostics)
require.Equal(t, "group", preview.Parameters[0].Name)
require.True(t, preview.Parameters[0].Value.Valid())
require.Equal(t, database.EveryoneGroup, preview.Parameters[0].Value.Value.AsString())
// Send a new value, and see it reflected
err = stream.Send(codersdk.DynamicParametersRequest{
ID: 1,
Inputs: map[string]string{"group": group.Name},
})
require.NoError(t, err)
preview = testutil.RequireReceive(ctx, t, previews)
require.Equal(t, 1, preview.ID)
require.Empty(t, preview.Diagnostics)
require.Equal(t, "group", preview.Parameters[0].Name)
require.True(t, preview.Parameters[0].Value.Valid())
require.Equal(t, group.Name, preview.Parameters[0].Value.Value.AsString())
// Back to default
err = stream.Send(codersdk.DynamicParametersRequest{
ID: 3,
Inputs: map[string]string{},
})
require.NoError(t, err)
preview = testutil.RequireReceive(ctx, t, previews)
require.Equal(t, 3, preview.ID)
require.Empty(t, preview.Diagnostics)
require.Equal(t, "group", preview.Parameters[0].Name)
require.True(t, preview.Parameters[0].Value.Valid())
require.Equal(t, database.EveryoneGroup, preview.Parameters[0].Value.Value.AsString())
}

View File

@ -0,0 +1,21 @@
terraform {
required_providers {
coder = {
source = "coder/coder"
}
}
}
data "coder_workspace_owner" "me" {}
data "coder_parameter" "group" {
name = "group"
default = try(data.coder_workspace_owner.me.groups[0], "")
dynamic "option" {
for_each = data.coder_workspace_owner.me.groups
content {
name = option.value
value = option.value
}
}
}

View File

@ -0,0 +1,80 @@
{
"terraform_version": "1.11.2",
"format_version": "1.2",
"checks": [],
"complete": true,
"timestamp": "2025-04-02T01:29:59Z",
"variables": {},
"prior_state": {
"values": {
"root_module": {
"resources": [
{
"mode": "data",
"name": "me",
"type": "coder_workspace_owner",
"address": "data.coder_workspace_owner.me",
"provider_name": "registry.terraform.io/coder/coder",
"schema_version": 0,
"values": {
"id": "",
"name": "",
"email": "",
"groups": [],
"full_name": "",
"login_type": "",
"rbac_roles": [],
"session_token": "",
"ssh_public_key": "",
"ssh_private_key": "",
"oidc_access_token": ""
},
"sensitive_values": {
"groups": [],
"rbac_roles": [],
"ssh_private_key": true
}
}
],
"child_modules": []
}
},
"format_version": "1.0",
"terraform_version": "1.11.2"
},
"configuration": {
"root_module": {
"resources": [
{
"mode": "data",
"name": "me",
"type": "coder_workspace_owner",
"address": "data.coder_workspace_owner.me",
"schema_version": 0,
"provider_config_key": "coder"
}
],
"variables": {},
"module_calls": {}
},
"provider_config": {
"coder": {
"name": "coder",
"full_name": "registry.terraform.io/coder/coder"
}
}
},
"planned_values": {
"root_module": {
"resources": [],
"child_modules": []
}
},
"resource_changes": [],
"relevant_attributes": [
{
"resource": "data.coder_workspace_owner.me",
"attribute": ["groups"]
}
]
}