diff --git a/coderd/coderdtest/dynamicparameters.go b/coderd/coderdtest/dynamicparameters.go index 28e01885560ca..c6adb6c97e786 100644 --- a/coderd/coderdtest/dynamicparameters.go +++ b/coderd/coderdtest/dynamicparameters.go @@ -29,7 +29,8 @@ type DynamicParameterTemplateParams struct { // TemplateID is used to update an existing template instead of creating a new one. TemplateID uuid.UUID - Version func(request *codersdk.CreateTemplateVersionRequest) + Version func(request *codersdk.CreateTemplateVersionRequest) + Variables []codersdk.TemplateVersionVariable } func DynamicParameterTemplate(t *testing.T, client *codersdk.Client, org uuid.UUID, args DynamicParameterTemplateParams) (codersdk.Template, codersdk.TemplateVersion) { @@ -48,6 +49,32 @@ func DynamicParameterTemplate(t *testing.T, client *codersdk.Client, org uuid.UU }, }} + userVars := make([]codersdk.VariableValue, 0, len(args.Variables)) + parseVars := make([]*proto.TemplateVariable, 0, len(args.Variables)) + for _, argv := range args.Variables { + parseVars = append(parseVars, &proto.TemplateVariable{ + Name: argv.Name, + Description: argv.Description, + Type: argv.Type, + DefaultValue: argv.DefaultValue, + Required: argv.Required, + Sensitive: argv.Sensitive, + }) + + userVars = append(userVars, codersdk.VariableValue{ + Name: argv.Name, + Value: argv.Value, + }) + } + + files.Parse = []*proto.Response{{ + Type: &proto.Response_Parse{ + Parse: &proto.ParseComplete{ + TemplateVariables: parseVars, + }, + }, + }} + mime := codersdk.ContentTypeTar if args.Zip { mime = codersdk.ContentTypeZip @@ -59,6 +86,7 @@ func DynamicParameterTemplate(t *testing.T, client *codersdk.Client, org uuid.UU if args.Version != nil { args.Version(request) } + request.UserVariableValues = userVars }) AwaitTemplateVersionJobCompleted(t, client, version.ID) diff --git a/coderd/dynamicparameters/render.go b/coderd/dynamicparameters/render.go index 8a5a80cd25d22..7f0a98f18ce55 100644 --- a/coderd/dynamicparameters/render.go +++ b/coderd/dynamicparameters/render.go @@ -9,6 +9,7 @@ import ( "time" "github.com/google/uuid" + "github.com/zclconf/go-cty/cty" "golang.org/x/xerrors" "github.com/coder/coder/v2/apiversion" @@ -41,9 +42,10 @@ type loader struct { templateVersionID uuid.UUID // cache of objects - templateVersion *database.TemplateVersion - job *database.ProvisionerJob - terraformValues *database.TemplateVersionTerraformValue + templateVersion *database.TemplateVersion + job *database.ProvisionerJob + terraformValues *database.TemplateVersionTerraformValue + templateVariableValues *[]database.TemplateVersionVariable } // Prepare is the entrypoint for this package. It loads the necessary objects & @@ -61,6 +63,12 @@ func Prepare(ctx context.Context, db database.Store, cache files.FileAcquirer, v return l.Renderer(ctx, db, cache) } +func WithTemplateVariableValues(vals []database.TemplateVersionVariable) func(r *loader) { + return func(r *loader) { + r.templateVariableValues = &vals + } +} + func WithTemplateVersion(tv database.TemplateVersion) func(r *loader) { return func(r *loader) { if tv.ID == r.templateVersionID { @@ -127,6 +135,14 @@ func (r *loader) loadData(ctx context.Context, db database.Store) error { r.terraformValues = &values } + if r.templateVariableValues == nil { + vals, err := db.GetTemplateVersionVariables(ctx, r.templateVersion.ID) + if err != nil && !xerrors.Is(err, sql.ErrNoRows) { + return xerrors.Errorf("template version variables: %w", err) + } + r.templateVariableValues = &vals + } + return nil } @@ -160,13 +176,17 @@ func (r *loader) dynamicRenderer(ctx context.Context, db database.Store, cache * } }() + tfVarValues, err := VariableValues(*r.templateVariableValues) + if err != nil { + return nil, xerrors.Errorf("parse variable values: %w", err) + } + // If they can read the template version, then they can read the file for // parameter loading purposes. //nolint:gocritic fileCtx := dbauthz.AsFileReader(ctx) var templateFS fs.FS - var err error templateFS, err = cache.Acquire(fileCtx, db, r.job.FileID) if err != nil { @@ -189,6 +209,7 @@ func (r *loader) dynamicRenderer(ctx context.Context, db database.Store, cache * db: db, ownerErrors: make(map[uuid.UUID]error), close: cache.Close, + tfvarValues: tfVarValues, }, nil } @@ -199,6 +220,7 @@ type dynamicRenderer struct { ownerErrors map[uuid.UUID]error currentOwner *previewtypes.WorkspaceOwner + tfvarValues map[string]cty.Value once sync.Once close func() @@ -229,6 +251,7 @@ func (r *dynamicRenderer) Render(ctx context.Context, ownerID uuid.UUID, values PlanJSON: r.data.terraformValues.CachedPlan, ParameterValues: values, Owner: *r.currentOwner, + TFVars: r.tfvarValues, // Do not emit parser logs to coderd output logs. // TODO: Returning this logs in the output would benefit the caller. // Unsure how large the logs can be, so for now we just discard them. diff --git a/coderd/dynamicparameters/variablevalues.go b/coderd/dynamicparameters/variablevalues.go new file mode 100644 index 0000000000000..574039119c786 --- /dev/null +++ b/coderd/dynamicparameters/variablevalues.go @@ -0,0 +1,65 @@ +package dynamicparameters + +import ( + "strconv" + + "github.com/zclconf/go-cty/cty" + "github.com/zclconf/go-cty/cty/json" + "golang.org/x/xerrors" + + "github.com/coder/coder/v2/coderd/database" +) + +// VariableValues is a helper function that converts a slice of TemplateVersionVariable +// into a map of cty.Value for use in coder/preview. +func VariableValues(vals []database.TemplateVersionVariable) (map[string]cty.Value, error) { + ctyVals := make(map[string]cty.Value, len(vals)) + for _, v := range vals { + value := v.Value + if value == "" && v.DefaultValue != "" { + value = v.DefaultValue + } + + if value == "" { + // Empty strings are unsupported I guess? + continue // omit non-set vals + } + + var err error + switch v.Type { + // Defaulting the empty type to "string" + // TODO: This does not match the terraform behavior, however it is too late + // at this point in the code to determine this, as the database type stores all values + // as strings. The code needs to be fixed in the `Parse` step of the provisioner. + // That step should determine the type of the variable correctly and store it in the database. + case "string", "": + ctyVals[v.Name] = cty.StringVal(value) + case "number": + ctyVals[v.Name], err = cty.ParseNumberVal(value) + if err != nil { + return nil, xerrors.Errorf("parse variable %q: %w", v.Name, err) + } + case "bool": + parsed, err := strconv.ParseBool(value) + if err != nil { + return nil, xerrors.Errorf("parse variable %q: %w", v.Name, err) + } + ctyVals[v.Name] = cty.BoolVal(parsed) + default: + // If it is a complex type, let the cty json code give it a try. + // TODO: Ideally we parse `list` & `map` and build the type ourselves. + ty, err := json.ImpliedType([]byte(value)) + if err != nil { + return nil, xerrors.Errorf("implied type for variable %q: %w", v.Name, err) + } + + jv, err := json.Unmarshal([]byte(value), ty) + if err != nil { + return nil, xerrors.Errorf("unmarshal variable %q: %w", v.Name, err) + } + ctyVals[v.Name] = jv + } + } + + return ctyVals, nil +} diff --git a/coderd/parameters_test.go b/coderd/parameters_test.go index 855d95eb1de59..c00d6f9224bfb 100644 --- a/coderd/parameters_test.go +++ b/coderd/parameters_test.go @@ -343,6 +343,36 @@ func TestDynamicParametersWithTerraformValues(t *testing.T) { require.Len(t, preview.Diagnostics, 1) require.Equal(t, preview.Diagnostics[0].Extra.Code, "owner_not_found") }) + + t.Run("TemplateVariables", func(t *testing.T) { + t.Parallel() + + dynamicParametersTerraformSource, err := os.ReadFile("testdata/parameters/variables/main.tf") + require.NoError(t, err) + + setup := setupDynamicParamsTest(t, setupDynamicParamsTestParams{ + provisionerDaemonVersion: provProto.CurrentVersion.String(), + mainTF: dynamicParametersTerraformSource, + variables: []codersdk.TemplateVersionVariable{ + {Name: "one", Value: "austin", DefaultValue: "alice", Type: "string"}, + }, + plan: nil, + static: nil, + }) + + ctx := testutil.Context(t, testutil.WaitShort) + stream := setup.stream + previews := stream.Chan() + + // Should see the output of the module represented + preview := testutil.RequireReceive(ctx, t, previews) + require.Equal(t, -1, preview.ID) + require.Empty(t, preview.Diagnostics) + + require.Len(t, preview.Parameters, 1) + coderdtest.AssertParameter(t, "variable_values", preview.Parameters). + Exists().Value("austin") + }) } type setupDynamicParamsTestParams struct { @@ -355,6 +385,7 @@ type setupDynamicParamsTestParams struct { static []*proto.RichParameter expectWebsocketError bool + variables []codersdk.TemplateVersionVariable } type dynamicParamsTest struct { @@ -380,6 +411,7 @@ func setupDynamicParamsTest(t *testing.T, args setupDynamicParamsTestParams) dyn Plan: args.plan, ModulesArchive: args.modulesArchive, StaticParams: args.static, + Variables: args.variables, }) ctx := testutil.Context(t, testutil.WaitShort) diff --git a/coderd/templateversions.go b/coderd/templateversions.go index fa5a7ed1fe757..72b18a2c47e92 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -18,6 +18,7 @@ import ( "github.com/google/uuid" "github.com/moby/moby/pkg/namesgenerator" "github.com/sqlc-dev/pqtype" + "github.com/zclconf/go-cty/cty" "golang.org/x/xerrors" "cdr.dev/slog" @@ -1585,7 +1586,7 @@ func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *ht var parsedTags map[string]string var ok bool if dynamicTemplate { - parsedTags, ok = api.dynamicTemplateVersionTags(ctx, rw, organization.ID, apiKey.UserID, file) + parsedTags, ok = api.dynamicTemplateVersionTags(ctx, rw, organization.ID, apiKey.UserID, file, req.UserVariableValues) if !ok { return } @@ -1762,7 +1763,7 @@ func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *ht warnings)) } -func (api *API) dynamicTemplateVersionTags(ctx context.Context, rw http.ResponseWriter, orgID uuid.UUID, owner uuid.UUID, file database.File) (map[string]string, bool) { +func (api *API) dynamicTemplateVersionTags(ctx context.Context, rw http.ResponseWriter, orgID uuid.UUID, owner uuid.UUID, file database.File, templateVariables []codersdk.VariableValue) (map[string]string, bool) { ownerData, err := dynamicparameters.WorkspaceOwner(ctx, api.Database, orgID, owner) if err != nil { if httpapi.Is404Error(err) { @@ -1800,11 +1801,19 @@ func (api *API) dynamicTemplateVersionTags(ctx context.Context, rw http.Response return nil, false } + // Pass in any manually specified template variables as TFVars. + // TODO: Does this break if the type is not a string? + tfVarValues := make(map[string]cty.Value) + for _, variable := range templateVariables { + tfVarValues[variable.Name] = cty.StringVal(variable.Value) + } + output, diags := preview.Preview(ctx, preview.Input{ PlanJSON: nil, // Template versions are before `terraform plan` ParameterValues: nil, // No user-specified parameters Owner: *ownerData, Logger: stdslog.New(stdslog.DiscardHandler), + TFVars: tfVarValues, }, files) tagErr := dynamicparameters.CheckTags(output, diags) if tagErr != nil { diff --git a/coderd/testdata/parameters/variables/main.tf b/coderd/testdata/parameters/variables/main.tf new file mode 100644 index 0000000000000..684ee4505abe3 --- /dev/null +++ b/coderd/testdata/parameters/variables/main.tf @@ -0,0 +1,30 @@ +// Base case for workspace tags + parameters. +terraform { + required_providers { + coder = { + source = "coder/coder" + } + docker = { + source = "kreuzwerker/docker" + version = "3.0.2" + } + } +} + +variable "one" { + default = "alice" + type = string +} + + +data "coder_parameter" "variable_values" { + name = "variable_values" + description = "Just to show the variable values" + type = "string" + default = var.one + + option { + name = "one" + value = var.one + } +} diff --git a/coderd/wsbuilder/wsbuilder.go b/coderd/wsbuilder/wsbuilder.go index 90ea02e966a09..d608682c58eee 100644 --- a/coderd/wsbuilder/wsbuilder.go +++ b/coderd/wsbuilder/wsbuilder.go @@ -633,10 +633,16 @@ func (b *Builder) getDynamicParameterRenderer() (dynamicparameters.Renderer, err return nil, xerrors.Errorf("get template version terraform values: %w", err) } + variableValues, err := b.getTemplateVersionVariables() + if err != nil { + return nil, xerrors.Errorf("get template version variables: %w", err) + } + renderer, err := dynamicparameters.Prepare(b.ctx, b.store, b.fileCache, tv.ID, dynamicparameters.WithTemplateVersion(*tv), dynamicparameters.WithProvisionerJob(*job), dynamicparameters.WithTerraformValues(*tfVals), + dynamicparameters.WithTemplateVariableValues(variableValues), ) if err != nil { return nil, xerrors.Errorf("get template version renderer: %w", err) diff --git a/enterprise/coderd/workspaces_test.go b/enterprise/coderd/workspaces_test.go index 1030536f2111d..d622748899aa0 100644 --- a/enterprise/coderd/workspaces_test.go +++ b/enterprise/coderd/workspaces_test.go @@ -2627,6 +2627,21 @@ func TestWorkspaceTemplateParamsChange(t *testing.T) { require.Equal(t, codersdk.WorkspaceStatusDeleted, build.Status) } +type testWorkspaceTagsTerraformCase struct { + name string + // tags to apply to the external provisioner + provisionerTags map[string]string + // tags to apply to the create template version request + createTemplateVersionRequestTags map[string]string + // the coder_workspace_tags bit of main.tf. + // you can add more stuff here if you need + tfWorkspaceTags string + templateImportUserVariableValues []codersdk.VariableValue + // if we need to set parameters on workspace build + workspaceBuildParameters []codersdk.WorkspaceBuildParameter + skipCreateWorkspace bool +} + // TestWorkspaceTagsTerraform tests that a workspace can be created with tags. // This is an end-to-end-style test, meaning that we actually run the // real Terraform provisioner and validate that the workspace is created @@ -2636,7 +2651,7 @@ func TestWorkspaceTemplateParamsChange(t *testing.T) { // config file so that we only reference those // nolint:paralleltest // t.Setenv func TestWorkspaceTagsTerraform(t *testing.T) { - mainTfTemplate := ` + coderProviderTemplate := ` terraform { required_providers { coder = { @@ -2644,33 +2659,11 @@ func TestWorkspaceTagsTerraform(t *testing.T) { } } } - provider "coder" {} - data "coder_workspace" "me" {} - data "coder_workspace_owner" "me" {} - data "coder_parameter" "unrelated" { - name = "unrelated" - type = "list(string)" - default = jsonencode(["a", "b"]) - } - %s ` - tfCliConfigPath := downloadProviders(t, fmt.Sprintf(mainTfTemplate, "")) + tfCliConfigPath := downloadProviders(t, coderProviderTemplate) t.Setenv("TF_CLI_CONFIG_FILE", tfCliConfigPath) - for _, tc := range []struct { - name string - // tags to apply to the external provisioner - provisionerTags map[string]string - // tags to apply to the create template version request - createTemplateVersionRequestTags map[string]string - // the coder_workspace_tags bit of main.tf. - // you can add more stuff here if you need - tfWorkspaceTags string - templateImportUserVariableValues []codersdk.VariableValue - // if we need to set parameters on workspace build - workspaceBuildParameters []codersdk.WorkspaceBuildParameter - skipCreateWorkspace bool - }{ + for _, tc := range []testWorkspaceTagsTerraformCase{ { name: "no tags", tfWorkspaceTags: ``, @@ -2803,56 +2796,114 @@ func TestWorkspaceTagsTerraform(t *testing.T) { }, } { t.Run(tc.name, func(t *testing.T) { - client, owner := coderdenttest.New(t, &coderdenttest.Options{ - Options: &coderdtest.Options{ - // We intentionally do not run a built-in provisioner daemon here. - IncludeProvisionerDaemon: false, - }, - LicenseOptions: &coderdenttest.LicenseOptions{ - Features: license.Features{ - codersdk.FeatureExternalProvisionerDaemons: 1, - }, - }, + t.Run("dynamic", func(t *testing.T) { + workspaceTagsTerraform(t, tc, true) }) - templateAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleTemplateAdmin()) - member, memberUser := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) - - _ = coderdenttest.NewExternalProvisionerDaemonTerraform(t, client, owner.OrganizationID, tc.provisionerTags) - - // This can take a while, so set a relatively long timeout. - ctx := testutil.Context(t, 2*testutil.WaitSuperLong) - - // Creating a template as a template admin must succeed - templateFiles := map[string]string{"main.tf": fmt.Sprintf(mainTfTemplate, tc.tfWorkspaceTags)} - tarBytes := testutil.CreateTar(t, templateFiles) - fi, err := templateAdmin.Upload(ctx, "application/x-tar", bytes.NewReader(tarBytes)) - require.NoError(t, err, "failed to upload file") - tv, err := templateAdmin.CreateTemplateVersion(ctx, owner.OrganizationID, codersdk.CreateTemplateVersionRequest{ - Name: testutil.GetRandomName(t), - FileID: fi.ID, - StorageMethod: codersdk.ProvisionerStorageMethodFile, - Provisioner: codersdk.ProvisionerTypeTerraform, - ProvisionerTags: tc.createTemplateVersionRequestTags, - UserVariableValues: tc.templateImportUserVariableValues, + + // classic uses tfparse for tags. This sub test can be + // removed when tf parse is removed. + t.Run("classic", func(t *testing.T) { + workspaceTagsTerraform(t, tc, false) }) - require.NoError(t, err, "failed to create template version") - coderdtest.AwaitTemplateVersionJobCompleted(t, templateAdmin, tv.ID) - tpl := coderdtest.CreateTemplate(t, templateAdmin, owner.OrganizationID, tv.ID) - - if !tc.skipCreateWorkspace { - // Creating a workspace as a non-privileged user must succeed - ws, err := member.CreateUserWorkspace(ctx, memberUser.Username, codersdk.CreateWorkspaceRequest{ - TemplateID: tpl.ID, - Name: coderdtest.RandomUsername(t), - RichParameterValues: tc.workspaceBuildParameters, - }) - require.NoError(t, err, "failed to create workspace") - coderdtest.AwaitWorkspaceBuildJobCompleted(t, member, ws.LatestBuild.ID) - } }) } } +func workspaceTagsTerraform(t *testing.T, tc testWorkspaceTagsTerraformCase, dynamic bool) { + mainTfTemplate := ` + terraform { + required_providers { + coder = { + source = "coder/coder" + } + } + } + + provider "coder" {} + data "coder_workspace" "me" {} + data "coder_workspace_owner" "me" {} + data "coder_parameter" "unrelated" { + name = "unrelated" + type = "list(string)" + default = jsonencode(["a", "b"]) + } + %s + ` + + client, owner := coderdenttest.New(t, &coderdenttest.Options{ + Options: &coderdtest.Options{ + // We intentionally do not run a built-in provisioner daemon here. + IncludeProvisionerDaemon: false, + }, + LicenseOptions: &coderdenttest.LicenseOptions{ + Features: license.Features{ + codersdk.FeatureExternalProvisionerDaemons: 1, + }, + }, + }) + templateAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleTemplateAdmin()) + member, memberUser := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) + + // This can take a while, so set a relatively long timeout. + ctx := testutil.Context(t, 2*testutil.WaitSuperLong) + + emptyTar := testutil.CreateTar(t, map[string]string{"main.tf": ""}) + emptyFi, err := templateAdmin.Upload(ctx, "application/x-tar", bytes.NewReader(emptyTar)) + require.NoError(t, err) + + // This template version does not need to succeed in being created. + // It will be in pending forever. We just need it to create a template. + emptyTv, err := templateAdmin.CreateTemplateVersion(ctx, owner.OrganizationID, codersdk.CreateTemplateVersionRequest{ + Name: testutil.GetRandomName(t), + FileID: emptyFi.ID, + StorageMethod: codersdk.ProvisionerStorageMethodFile, + Provisioner: codersdk.ProvisionerTypeTerraform, + }) + require.NoError(t, err) + + tpl := coderdtest.CreateTemplate(t, templateAdmin, owner.OrganizationID, emptyTv.ID, func(request *codersdk.CreateTemplateRequest) { + request.UseClassicParameterFlow = ptr.Ref(!dynamic) + }) + + // The provisioner for the next template version + _ = coderdenttest.NewExternalProvisionerDaemonTerraform(t, client, owner.OrganizationID, tc.provisionerTags) + + // Creating a template as a template admin must succeed + templateFiles := map[string]string{"main.tf": fmt.Sprintf(mainTfTemplate, tc.tfWorkspaceTags)} + tarBytes := testutil.CreateTar(t, templateFiles) + fi, err := templateAdmin.Upload(ctx, "application/x-tar", bytes.NewReader(tarBytes)) + require.NoError(t, err, "failed to upload file") + tv, err := templateAdmin.CreateTemplateVersion(ctx, owner.OrganizationID, codersdk.CreateTemplateVersionRequest{ + Name: testutil.GetRandomName(t), + FileID: fi.ID, + StorageMethod: codersdk.ProvisionerStorageMethodFile, + Provisioner: codersdk.ProvisionerTypeTerraform, + ProvisionerTags: tc.createTemplateVersionRequestTags, + UserVariableValues: tc.templateImportUserVariableValues, + TemplateID: tpl.ID, + }) + require.NoError(t, err, "failed to create template version") + coderdtest.AwaitTemplateVersionJobCompleted(t, templateAdmin, tv.ID) + + err = templateAdmin.UpdateActiveTemplateVersion(ctx, tpl.ID, codersdk.UpdateActiveTemplateVersion{ + ID: tv.ID, + }) + require.NoError(t, err, "set to active template version") + + if !tc.skipCreateWorkspace { + // Creating a workspace as a non-privileged user must succeed + ws, err := member.CreateUserWorkspace(ctx, memberUser.Username, codersdk.CreateWorkspaceRequest{ + TemplateID: tpl.ID, + Name: coderdtest.RandomUsername(t), + RichParameterValues: tc.workspaceBuildParameters, + }) + require.NoError(t, err, "failed to create workspace") + tagJSON, _ := json.Marshal(ws.LatestBuild.Job.Tags) + t.Logf("Created workspace build [%s] with tags: %s", ws.LatestBuild.Job.Type, tagJSON) + coderdtest.AwaitWorkspaceBuildJobCompleted(t, member, ws.LatestBuild.ID) + } +} + // downloadProviders is a test helper that creates a temporary file and writes a // terraform CLI config file with a provider_installation stanza for coder/coder // using dev_overrides. It also fetches the latest provider release from GitHub @@ -3124,7 +3175,7 @@ func TestWorkspaceLock(t *testing.T) { require.NotNil(t, workspace.DeletingAt) require.NotNil(t, workspace.DormantAt) require.Equal(t, workspace.DormantAt.Add(dormantTTL), *workspace.DeletingAt) - require.WithinRange(t, *workspace.DormantAt, time.Now().Add(-time.Second*10), time.Now()) + require.WithinRange(t, *workspace.DormantAt, time.Now().Add(-time.Second), time.Now()) // Locking a workspace shouldn't update the last_used_at. require.Equal(t, lastUsedAt, workspace.LastUsedAt) diff --git a/go.mod b/go.mod index fa91932ceaecf..f2b12cbd6387c 100644 --- a/go.mod +++ b/go.mod @@ -483,7 +483,7 @@ require ( require ( github.com/coder/agentapi-sdk-go v0.0.0-20250505131810-560d1d88d225 github.com/coder/aisdk-go v0.0.9 - github.com/coder/preview v1.0.3-0.20250701142654-c3d6e86b9393 + github.com/coder/preview v1.0.3-0.20250714153828-a737d4750448 github.com/fsnotify/fsnotify v1.9.0 github.com/mark3labs/mcp-go v0.33.0 ) diff --git a/go.sum b/go.sum index e46a4eb61a477..1d6ae833a5dbe 100644 --- a/go.sum +++ b/go.sum @@ -916,8 +916,8 @@ github.com/coder/pq v1.10.5-0.20250630052411-a259f96b6102 h1:ahTJlTRmTogsubgRVGO github.com/coder/pq v1.10.5-0.20250630052411-a259f96b6102/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0 h1:3A0ES21Ke+FxEM8CXx9n47SZOKOpgSE1bbJzlE4qPVs= github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0/go.mod h1:5UuS2Ts+nTToAMeOjNlnHFkPahrtDkmpydBen/3wgZc= -github.com/coder/preview v1.0.3-0.20250701142654-c3d6e86b9393 h1:l+m2liikn8JoEv6C22QIV4qseolUfvNsyUNA6JJsD6Y= -github.com/coder/preview v1.0.3-0.20250701142654-c3d6e86b9393/go.mod h1:efDWGlO/PZPrvdt5QiDhMtTUTkPxejXo9c0wmYYLLjM= +github.com/coder/preview v1.0.3-0.20250714153828-a737d4750448 h1:S86sFp4Dr4dUn++fXOMOTu6ClnEZ/NrGCYv7bxZjYYc= +github.com/coder/preview v1.0.3-0.20250714153828-a737d4750448/go.mod h1:hQtBEqOFMJ3SHl9Q9pVvDA9CpeCEXBwbONNK29+3MLk= github.com/coder/quartz v0.2.1 h1:QgQ2Vc1+mvzewg2uD/nj8MJ9p9gE+QhGJm+Z+NGnrSE= github.com/coder/quartz v0.2.1/go.mod h1:vsiCc+AHViMKH2CQpGIpFgdHIEQsxwm8yCscqKmzbRA= github.com/coder/retry v1.5.1 h1:iWu8YnD8YqHs3XwqrqsjoBTAVqT9ml6z9ViJ2wlMiqc= diff --git a/provisioner/terraform/parse.go b/provisioner/terraform/parse.go index 7aa78e401c503..d5b59df327f65 100644 --- a/provisioner/terraform/parse.go +++ b/provisioner/terraform/parse.go @@ -15,6 +15,10 @@ import ( ) // Parse extracts Terraform variables from source-code. +// TODO: This Parse is incomplete. It uses tfparse instead of terraform. +// The inputs are incomplete, as values such as the user context, parameters, +// etc are all important to the parsing process. This should be replaced with +// preview and have all inputs. func (s *server) Parse(sess *provisionersdk.Session, _ *proto.ParseRequest, _ <-chan struct{}) *proto.ParseComplete { ctx := sess.Context() _, span := s.startTrace(ctx, tracing.FuncName())