mirror of
https://github.com/coder/coder.git
synced 2025-07-08 11:39:50 +00:00
feat: Add CLI support for workspace build parameters (#5768)
* WIP * WIP * CLI: handle workspace build parameters * fix: golintci * Fix: dry run * fix * CLI: is mutable * coderd: mutable * fix: golanci * fix: richParameterFile * CLI: create unit tests * CLI: update test * Fix * fix: order * fix
This commit is contained in:
6
coderd/apidoc/docs.go
generated
6
coderd/apidoc/docs.go
generated
@ -5519,6 +5519,12 @@ const docTemplate = `{
|
||||
"$ref": "#/definitions/codersdk.CreateParameterRequest"
|
||||
}
|
||||
},
|
||||
"rich_parameter_values": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceBuildParameter"
|
||||
}
|
||||
},
|
||||
"workspace_name": {
|
||||
"type": "string"
|
||||
}
|
||||
|
6
coderd/apidoc/swagger.json
generated
6
coderd/apidoc/swagger.json
generated
@ -4889,6 +4889,12 @@
|
||||
"$ref": "#/definitions/codersdk.CreateParameterRequest"
|
||||
}
|
||||
},
|
||||
"rich_parameter_values": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceBuildParameter"
|
||||
}
|
||||
},
|
||||
"workspace_name": {
|
||||
"type": "string"
|
||||
}
|
||||
|
@ -235,7 +235,8 @@ func (server *Server) AcquireJob(ctx context.Context, _ *proto.Empty) (*proto.Ac
|
||||
|
||||
protoJob.Type = &proto.AcquiredJob_TemplateDryRun_{
|
||||
TemplateDryRun: &proto.AcquiredJob_TemplateDryRun{
|
||||
ParameterValues: protoParameters,
|
||||
ParameterValues: protoParameters,
|
||||
RichParameterValues: convertRichParameterValues(input.RichParameterValues),
|
||||
Metadata: &sdkproto.Provision_Metadata{
|
||||
CoderUrl: server.AccessURL.String(),
|
||||
WorkspaceName: input.WorkspaceName,
|
||||
@ -1175,9 +1176,10 @@ type WorkspaceProvisionJob struct {
|
||||
|
||||
// TemplateVersionDryRunJob is the payload for the "template_version_dry_run" job type.
|
||||
type TemplateVersionDryRunJob struct {
|
||||
TemplateVersionID uuid.UUID `json:"template_version_id"`
|
||||
WorkspaceName string `json:"workspace_name"`
|
||||
ParameterValues []database.ParameterValue `json:"parameter_values"`
|
||||
TemplateVersionID uuid.UUID `json:"template_version_id"`
|
||||
WorkspaceName string `json:"workspace_name"`
|
||||
ParameterValues []database.ParameterValue `json:"parameter_values"`
|
||||
RichParameterValues []database.WorkspaceBuildParameter `json:"rich_parameter_values"`
|
||||
}
|
||||
|
||||
// ProvisionerJobLogsNotifyMessage is the payload published on
|
||||
|
@ -362,12 +362,22 @@ func (api *API) postTemplateVersionDryRun(rw http.ResponseWriter, r *http.Reques
|
||||
}
|
||||
}
|
||||
|
||||
richParameterValues := make([]database.WorkspaceBuildParameter, len(req.RichParameterValues))
|
||||
for i, v := range req.RichParameterValues {
|
||||
richParameterValues[i] = database.WorkspaceBuildParameter{
|
||||
WorkspaceBuildID: uuid.Nil,
|
||||
Name: v.Name,
|
||||
Value: v.Value,
|
||||
}
|
||||
}
|
||||
|
||||
// Marshal template version dry-run job with the parameters from the
|
||||
// request.
|
||||
input, err := json.Marshal(provisionerdserver.TemplateVersionDryRunJob{
|
||||
TemplateVersionID: templateVersion.ID,
|
||||
WorkspaceName: req.WorkspaceName,
|
||||
ParameterValues: parameterValues,
|
||||
TemplateVersionID: templateVersion.ID,
|
||||
WorkspaceName: req.WorkspaceName,
|
||||
ParameterValues: parameterValues,
|
||||
RichParameterValues: richParameterValues,
|
||||
})
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
|
@ -377,11 +377,6 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
|
||||
state = createBuild.ProvisionerState
|
||||
}
|
||||
|
||||
var parameters []codersdk.WorkspaceBuildParameter
|
||||
if createBuild.RichParameterValues != nil {
|
||||
parameters = createBuild.RichParameterValues
|
||||
}
|
||||
|
||||
if createBuild.Orphan {
|
||||
if createBuild.Transition != codersdk.WorkspaceTransitionDelete {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
@ -454,21 +449,42 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
|
||||
state = priorHistory.ProvisionerState
|
||||
}
|
||||
|
||||
if parameters == nil {
|
||||
buildParameters, err := api.Database.GetWorkspaceBuildParameters(ctx, priorHistory.ID)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error fetching prior workspace build parameters.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
templateVersionParameters, err := api.Database.GetTemplateVersionParameters(ctx, createBuild.TemplateVersionID)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error fetching template version parameters.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
lastBuildParameters, err := api.Database.GetWorkspaceBuildParameters(ctx, priorHistory.ID)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error fetching prior workspace build parameters.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
apiLastBuildParameters := convertWorkspaceBuildParameters(lastBuildParameters)
|
||||
|
||||
var parameters []codersdk.WorkspaceBuildParameter
|
||||
for _, templateVersionParameter := range templateVersionParameters {
|
||||
// Check if parameter value is in request
|
||||
if buildParameter, found := findWorkspaceBuildParameter(createBuild.RichParameterValues, templateVersionParameter.Name); found {
|
||||
if !templateVersionParameter.Mutable {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: fmt.Sprintf("Parameter %q is mutable, so it can't be updated after creating workspace.", templateVersionParameter.Name),
|
||||
})
|
||||
return
|
||||
}
|
||||
parameters = append(parameters, *buildParameter)
|
||||
continue
|
||||
}
|
||||
parameters = make([]codersdk.WorkspaceBuildParameter, 0, len(buildParameters))
|
||||
for _, param := range buildParameters {
|
||||
parameters = append(parameters, codersdk.WorkspaceBuildParameter{
|
||||
Name: param.Name,
|
||||
Value: param.Value,
|
||||
})
|
||||
|
||||
// Check if parameter is defined in previous build
|
||||
if buildParameter, found := findWorkspaceBuildParameter(apiLastBuildParameters, templateVersionParameter.Name); found {
|
||||
parameters = append(parameters, *buildParameter)
|
||||
}
|
||||
}
|
||||
|
||||
@ -567,7 +583,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
|
||||
Value: values,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("insert workspace build parameter: %w", err)
|
||||
return xerrors.Errorf("insert workspace build parameters: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -1171,3 +1187,12 @@ func convertWorkspaceBuildParameters(parameters []database.WorkspaceBuildParamet
|
||||
}
|
||||
return apiParameters
|
||||
}
|
||||
|
||||
func findWorkspaceBuildParameter(params []codersdk.WorkspaceBuildParameter, parameterName string) (*codersdk.WorkspaceBuildParameter, bool) {
|
||||
for _, p := range params {
|
||||
if p.Name == parameterName {
|
||||
return &p, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
@ -644,11 +644,16 @@ func TestWorkspaceBuildWithRichParameters(t *testing.T) {
|
||||
secondParameterName = "second_parameter"
|
||||
secondParameterDescription = "This is second parameter"
|
||||
secondParameterValue = "2"
|
||||
|
||||
immutableParameterName = "immutable_parameter"
|
||||
immutableParameterDescription = "This is immutable parameter"
|
||||
immutableParameterValue = "3"
|
||||
)
|
||||
|
||||
initialBuildParameters := []codersdk.WorkspaceBuildParameter{
|
||||
{Name: firstParameterName, Value: firstParameterValue},
|
||||
{Name: secondParameterName, Value: secondParameterValue},
|
||||
{Name: immutableParameterName, Value: immutableParameterValue},
|
||||
}
|
||||
|
||||
echoResponses := &echo.Responses{
|
||||
@ -658,8 +663,9 @@ func TestWorkspaceBuildWithRichParameters(t *testing.T) {
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Parameters: []*proto.RichParameter{
|
||||
{Name: firstParameterName, Description: firstParameterDescription},
|
||||
{Name: secondParameterName, Description: secondParameterDescription},
|
||||
{Name: firstParameterName, Description: firstParameterDescription, Mutable: true},
|
||||
{Name: secondParameterName, Description: secondParameterDescription, Mutable: true},
|
||||
{Name: immutableParameterName, Description: immutableParameterDescription, Mutable: false},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -705,7 +711,12 @@ func TestWorkspaceBuildWithRichParameters(t *testing.T) {
|
||||
|
||||
workspaceBuildParameters, err := client.WorkspaceBuildParameters(ctx, nextWorkspaceBuild.ID)
|
||||
require.NoError(t, err)
|
||||
require.ElementsMatch(t, nextBuildParameters, workspaceBuildParameters)
|
||||
|
||||
expected := append(nextBuildParameters, codersdk.WorkspaceBuildParameter{
|
||||
Name: immutableParameterName,
|
||||
Value: immutableParameterValue,
|
||||
})
|
||||
require.ElementsMatch(t, expected, workspaceBuildParameters)
|
||||
})
|
||||
t.Run("UsePreviousParameterValues", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
@ -738,4 +749,34 @@ func TestWorkspaceBuildWithRichParameters(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.ElementsMatch(t, initialBuildParameters, workspaceBuildParameters)
|
||||
})
|
||||
|
||||
t.Run("DoNotModifyImmutables", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, echoResponses)
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID, func(cwr *codersdk.CreateWorkspaceRequest) {
|
||||
cwr.RichParameterValues = initialBuildParameters
|
||||
})
|
||||
|
||||
workspaceBuild := coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
|
||||
require.Equal(t, codersdk.WorkspaceStatusRunning, workspaceBuild.Status)
|
||||
|
||||
// Update build parameters
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
|
||||
nextBuildParameters := []codersdk.WorkspaceBuildParameter{
|
||||
{Name: immutableParameterName, Value: "BAD"},
|
||||
}
|
||||
_, err := client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
|
||||
Transition: codersdk.WorkspaceTransitionStart,
|
||||
RichParameterValues: nextBuildParameters,
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
Reference in New Issue
Block a user