Thanks to visit codestin.com
Credit goes to github.com

Skip to content

chore: add dynamic parameter warning if missing metadata #17809

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
May 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions coderd/coderd.go
Original file line number Diff line number Diff line change
Expand Up @@ -1774,6 +1774,7 @@ func (api *API) CreateInMemoryTaggedProvisionerDaemon(dialCtx context.Context, n
logger := api.Logger.Named(fmt.Sprintf("inmem-provisionerd-%s", name))
srv, err := provisionerdserver.NewServer(
api.ctx, // use the same ctx as the API
daemon.APIVersion,
api.AccessURL,
daemon.ID,
defaultOrg.ID,
Expand Down
10 changes: 6 additions & 4 deletions coderd/database/dbgen/dbgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/cryptorand"
"github.com/coder/coder/v2/provisionerd/proto"
"github.com/coder/coder/v2/testutil"
)

Expand Down Expand Up @@ -1000,10 +1001,11 @@ func TemplateVersionTerraformValues(t testing.TB, db database.Store, orig databa
t.Helper()

params := database.InsertTemplateVersionTerraformValuesByJobIDParams{
JobID: takeFirst(orig.JobID, uuid.New()),
CachedPlan: takeFirstSlice(orig.CachedPlan, []byte("{}")),
CachedModuleFiles: orig.CachedModuleFiles,
UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()),
JobID: takeFirst(orig.JobID, uuid.New()),
CachedPlan: takeFirstSlice(orig.CachedPlan, []byte("{}")),
CachedModuleFiles: orig.CachedModuleFiles,
UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()),
ProvisionerdVersion: takeFirst(orig.ProvisionerdVersion, proto.CurrentVersion.String()),
}

err := db.InsertTemplateVersionTerraformValuesByJobID(genCtx, params)
Expand Down
9 changes: 5 additions & 4 deletions coderd/database/dbmem/dbmem.go
Original file line number Diff line number Diff line change
Expand Up @@ -9343,10 +9343,11 @@ func (q *FakeQuerier) InsertTemplateVersionTerraformValuesByJobID(_ context.Cont

// Insert the new row
row := database.TemplateVersionTerraformValue{
TemplateVersionID: templateVersion.ID,
CachedPlan: arg.CachedPlan,
CachedModuleFiles: arg.CachedModuleFiles,
UpdatedAt: arg.UpdatedAt,
TemplateVersionID: templateVersion.ID,
UpdatedAt: arg.UpdatedAt,
CachedPlan: arg.CachedPlan,
CachedModuleFiles: arg.CachedModuleFiles,
ProvisionerdVersion: arg.ProvisionerdVersion,
}
q.templateVersionTerraformValues = append(q.templateVersionTerraformValues, row)
return nil
Expand Down
5 changes: 4 additions & 1 deletion coderd/database/dump.sql

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE template_version_terraform_values DROP COLUMN provisionerd_version;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
ALTER TABLE template_version_terraform_values ADD COLUMN IF NOT EXISTS provisionerd_version TEXT NOT NULL DEFAULT '';

COMMENT ON COLUMN template_version_terraform_values.provisionerd_version IS
'What version of the provisioning engine was used to generate the cached plan and module files.';
2 changes: 2 additions & 0 deletions coderd/database/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 12 additions & 7 deletions coderd/database/queries.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions coderd/database/queries/templateversionterraformvalues.sql
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ INSERT INTO
template_version_id,
cached_plan,
cached_module_files,
updated_at
updated_at,
provisionerd_version
)
VALUES
(
(select id from template_versions where job_id = @job_id),
@cached_plan,
@cached_module_files,
@updated_at
@updated_at,
@provisionerd_version
);
37 changes: 35 additions & 2 deletions coderd/parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import (
"time"

"github.com/google/uuid"
"github.com/hashicorp/hcl/v2"
"golang.org/x/sync/errgroup"
"golang.org/x/xerrors"

"github.com/coder/coder/v2/apiversion"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/dbauthz"
"github.com/coder/coder/v2/coderd/files"
Expand Down Expand Up @@ -114,6 +116,9 @@ func (api *API) templateVersionDynamicParameters(rw http.ResponseWriter, r *http
return
}

// If the err is sql.ErrNoRows, an empty terraform values struct is correct.
staticDiagnostics := parameterProvisionerVersionDiagnostic(tf)

owner, err := api.getWorkspaceOwnerData(ctx, user, templateVersion.OrganizationID)
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Expand Down Expand Up @@ -148,7 +153,7 @@ func (api *API) templateVersionDynamicParameters(rw http.ResponseWriter, r *http
result, diagnostics := preview.Preview(ctx, input, templateFS)
response := codersdk.DynamicParametersResponse{
ID: -1,
Diagnostics: previewtypes.Diagnostics(diagnostics),
Diagnostics: previewtypes.Diagnostics(diagnostics.Extend(staticDiagnostics)),
}
if result != nil {
response.Parameters = result.Parameters
Expand Down Expand Up @@ -176,7 +181,7 @@ func (api *API) templateVersionDynamicParameters(rw http.ResponseWriter, r *http
result, diagnostics := preview.Preview(ctx, input, templateFS)
response := codersdk.DynamicParametersResponse{
ID: update.ID,
Diagnostics: previewtypes.Diagnostics(diagnostics),
Diagnostics: previewtypes.Diagnostics(diagnostics.Extend(staticDiagnostics)),
}
if result != nil {
response.Parameters = result.Parameters
Expand Down Expand Up @@ -269,3 +274,31 @@ func (api *API) getWorkspaceOwnerData(
Groups: groupNames,
}, nil
}

// parameterProvisionerVersionDiagnostic checks the version of the provisioner
// used to create the template version. If the version is less than 1.5, it
// returns a warning diagnostic. Only versions 1.5+ return the module & plan data
// required.
func parameterProvisionerVersionDiagnostic(tf database.TemplateVersionTerraformValue) hcl.Diagnostics {
missingMetadata := hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "This template version is missing required metadata to support dynamic parameters. Go back to the classic creation flow.",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm. I don't like this. I would much prefer if we could have a way to expose this naturally to the frontend, forcing it over to the classic form. why make everyone click the "dynamic parameters suck" button? that's a super bad first impression

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, it is unfortunate. I want to get this in first, just to get an error up if someone toggles on dev and get this detection in. I will build on this experience.

We just need something now, otherwise it fails silently.

Detail: "To restore full functionality, please re-import the terraform as a new template version.",
}

if tf.ProvisionerdVersion == "" {
return hcl.Diagnostics{&missingMetadata}
}

major, minor, err := apiversion.Parse(tf.ProvisionerdVersion)
if err != nil || tf.ProvisionerdVersion == "" {
return hcl.Diagnostics{&missingMetadata}
} else if major < 1 || (major == 1 && minor < 5) {
missingMetadata.Detail = "This template version does not support dynamic parameters. " +
"Some options may be missing or incorrect. " +
"Please contact an administrator to update the provisioner and re-import the template version."
return hcl.Diagnostics{&missingMetadata}
}

return nil
}
77 changes: 77 additions & 0 deletions coderd/parameters_internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package coderd

import (
"testing"

"github.com/stretchr/testify/require"

"github.com/coder/coder/v2/coderd/database"
)

func Test_parameterProvisionerVersionDiagnostic(t *testing.T) {
t.Parallel()

testCases := []struct {
version string
warning bool
}{
{
version: "",
warning: true,
},
{
version: "invalid",
warning: true,
},
{
version: "0.4",
warning: true,
},
{
version: "0.5",
warning: true,
},
{
version: "0.6",
warning: true,
},
{
version: "1.4",
warning: true,
},
{
version: "1.5",
warning: false,
},
{
version: "1.6",
warning: false,
},
{
version: "2.0",
warning: false,
},
{
version: "2.5",
warning: false,
},
{
version: "2.6",
warning: false,
},
}

for _, tc := range testCases {
t.Run("Version_"+tc.version, func(t *testing.T) {
t.Parallel()
diags := parameterProvisionerVersionDiagnostic(database.TemplateVersionTerraformValue{
ProvisionerdVersion: tc.version,
})
if tc.warning {
require.Len(t, diags, 1, "expected warning")
} else {
require.Len(t, diags, 0, "expected no warning")
}
})
}
}
15 changes: 10 additions & 5 deletions coderd/provisionerdserver/provisionerdserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ type Options struct {
}

type server struct {
apiVersion string
// lifecycleCtx must be tied to the API server's lifecycle
// as when the API server shuts down, we want to cancel any
// long-running operations.
Expand Down Expand Up @@ -153,7 +154,9 @@ func (t Tags) Valid() error {
return nil
}

func NewServer(lifecycleCtx context.Context,
func NewServer(
lifecycleCtx context.Context,
apiVersion string,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel like coderd should be telling the provisioner what it's version is. This would break on deployments with external provisioners that are on an older version. and I know that's not really a thing we prioritize supporting, but this masks over the mismatched versions in a bad way.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coderd does not tell the provisioner.

External provisioners call coderd via provisionerDaemonServe

In that, they add a query param of their version:

apiVersion := "1.0"
if qv := r.URL.Query().Get("version"); qv != "" {
apiVersion = qv
}

Which is saved to the DB, and what we show on the provisioners page:

APIVersion: apiVersion,

And in that serve function, that apiVersion is sent to the go routine that manages the gRPC connection:

So the apiVersion in that NewServer is the Coderd side of the provisioner. And that version is sourced from the external provisionerd

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The daemon client sends the version here:

query.Add("version", proto.CurrentVersion.String())

Idk why it sets it twice

accessURL *url.URL,
id uuid.UUID,
organizationID uuid.UUID,
Expand Down Expand Up @@ -214,6 +217,7 @@ func NewServer(lifecycleCtx context.Context,

s := &server{
lifecycleCtx: lifecycleCtx,
apiVersion: apiVersion,
AccessURL: accessURL,
ID: id,
OrganizationID: organizationID,
Expand Down Expand Up @@ -1536,10 +1540,11 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
}

err = s.Database.InsertTemplateVersionTerraformValuesByJobID(ctx, database.InsertTemplateVersionTerraformValuesByJobIDParams{
JobID: jobID,
UpdatedAt: now,
CachedPlan: plan,
CachedModuleFiles: fileID,
JobID: jobID,
UpdatedAt: now,
CachedPlan: plan,
CachedModuleFiles: fileID,
ProvisionerdVersion: s.apiVersion,
})
if err != nil {
return nil, xerrors.Errorf("insert template version terraform data: %w", err)
Expand Down
1 change: 1 addition & 0 deletions coderd/provisionerdserver/provisionerdserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2993,6 +2993,7 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi

srv, err := provisionerdserver.NewServer(
ov.ctx,
proto.CurrentVersion.String(),
&url.URL{},
daemon.ID,
defOrg.ID,
Expand Down
1 change: 1 addition & 0 deletions enterprise/coderd/provisionerdaemons.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ func (api *API) provisionerDaemonServe(rw http.ResponseWriter, r *http.Request)
logger.Info(ctx, "starting external provisioner daemon")
srv, err := provisionerdserver.NewServer(
srvCtx,
daemon.APIVersion,
api.AccessURL,
daemon.ID,
authRes.orgID,
Expand Down
Loading