mirror of
https://github.com/coder/coder.git
synced 2025-07-15 22:20:27 +00:00
feat(cli): provide parameter values via command line (#8898)
This commit is contained in:
151
cli/create.go
151
cli/create.go
@ -18,11 +18,12 @@ import (
|
||||
|
||||
func (r *RootCmd) create() *clibase.Cmd {
|
||||
var (
|
||||
richParameterFile string
|
||||
templateName string
|
||||
startAt string
|
||||
stopAfter time.Duration
|
||||
workspaceName string
|
||||
templateName string
|
||||
startAt string
|
||||
stopAfter time.Duration
|
||||
workspaceName string
|
||||
|
||||
parameterFlags workspaceParameterFlags
|
||||
)
|
||||
client := new(codersdk.Client)
|
||||
cmd := &clibase.Cmd{
|
||||
@ -129,10 +130,18 @@ func (r *RootCmd) create() *clibase.Cmd {
|
||||
schedSpec = ptr.Ref(sched.String())
|
||||
}
|
||||
|
||||
buildParams, err := prepWorkspaceBuild(inv, client, prepWorkspaceBuildArgs{
|
||||
Template: template,
|
||||
RichParameterFile: richParameterFile,
|
||||
NewWorkspaceName: workspaceName,
|
||||
cliRichParameters, err := asWorkspaceBuildParameters(parameterFlags.richParameters)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("can't parse given parameter values: %w", err)
|
||||
}
|
||||
|
||||
richParameters, err := prepWorkspaceBuild(inv, client, prepWorkspaceBuildArgs{
|
||||
Action: WorkspaceCreate,
|
||||
Template: template,
|
||||
NewWorkspaceName: workspaceName,
|
||||
|
||||
RichParameterFile: parameterFlags.richParameterFile,
|
||||
RichParameters: cliRichParameters,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("prepare build: %w", err)
|
||||
@ -156,7 +165,7 @@ func (r *RootCmd) create() *clibase.Cmd {
|
||||
Name: workspaceName,
|
||||
AutostartSchedule: schedSpec,
|
||||
TTLMillis: ttlMillis,
|
||||
RichParameterValues: buildParams.richParameters,
|
||||
RichParameterValues: richParameters,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("create workspace: %w", err)
|
||||
@ -179,12 +188,6 @@ func (r *RootCmd) create() *clibase.Cmd {
|
||||
Description: "Specify a template name.",
|
||||
Value: clibase.StringOf(&templateName),
|
||||
},
|
||||
clibase.Option{
|
||||
Flag: "rich-parameter-file",
|
||||
Env: "CODER_RICH_PARAMETER_FILE",
|
||||
Description: "Specify a file path with values for rich parameters defined in the template.",
|
||||
Value: clibase.StringOf(&richParameterFile),
|
||||
},
|
||||
clibase.Option{
|
||||
Flag: "start-at",
|
||||
Env: "CODER_WORKSPACE_START_AT",
|
||||
@ -199,99 +202,59 @@ func (r *RootCmd) create() *clibase.Cmd {
|
||||
},
|
||||
cliui.SkipPromptOption(),
|
||||
)
|
||||
cmd.Options = append(cmd.Options, parameterFlags.cliParameters()...)
|
||||
return cmd
|
||||
}
|
||||
|
||||
type prepWorkspaceBuildArgs struct {
|
||||
Template codersdk.Template
|
||||
ExistingRichParams []codersdk.WorkspaceBuildParameter
|
||||
RichParameterFile string
|
||||
NewWorkspaceName string
|
||||
Action WorkspaceCLIAction
|
||||
Template codersdk.Template
|
||||
NewWorkspaceName string
|
||||
WorkspaceID uuid.UUID
|
||||
|
||||
UpdateWorkspace bool
|
||||
BuildOptions bool
|
||||
WorkspaceID uuid.UUID
|
||||
}
|
||||
LastBuildParameters []codersdk.WorkspaceBuildParameter
|
||||
|
||||
type buildParameters struct {
|
||||
// Rich parameters stores values for build parameters annotated with description, icon, type, etc.
|
||||
richParameters []codersdk.WorkspaceBuildParameter
|
||||
PromptBuildOptions bool
|
||||
BuildOptions []codersdk.WorkspaceBuildParameter
|
||||
|
||||
PromptRichParameters bool
|
||||
RichParameters []codersdk.WorkspaceBuildParameter
|
||||
RichParameterFile string
|
||||
}
|
||||
|
||||
// prepWorkspaceBuild will ensure a workspace build will succeed on the latest template version.
|
||||
// Any missing params will be prompted to the user. It supports legacy and rich parameters.
|
||||
func prepWorkspaceBuild(inv *clibase.Invocation, client *codersdk.Client, args prepWorkspaceBuildArgs) (*buildParameters, error) {
|
||||
// Any missing params will be prompted to the user. It supports rich parameters.
|
||||
func prepWorkspaceBuild(inv *clibase.Invocation, client *codersdk.Client, args prepWorkspaceBuildArgs) ([]codersdk.WorkspaceBuildParameter, error) {
|
||||
ctx := inv.Context()
|
||||
|
||||
templateVersion, err := client.TemplateVersion(ctx, args.Template.ActiveVersionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, xerrors.Errorf("get template version: %w", err)
|
||||
}
|
||||
|
||||
// Rich parameters
|
||||
templateVersionParameters, err := client.TemplateVersionRichParameters(inv.Context(), templateVersion.ID)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("get template version rich parameters: %w", err)
|
||||
}
|
||||
|
||||
parameterMapFromFile := map[string]string{}
|
||||
useParamFile := false
|
||||
parameterFile := map[string]string{}
|
||||
if args.RichParameterFile != "" {
|
||||
useParamFile = true
|
||||
_, _ = fmt.Fprintln(inv.Stdout, cliui.DefaultStyles.Paragraph.Render("Attempting to read the variables from the rich parameter file.")+"\r\n")
|
||||
parameterMapFromFile, err = createParameterMapFromFile(args.RichParameterFile)
|
||||
parameterFile, err = parseParameterMapFile(args.RichParameterFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, xerrors.Errorf("can't parse parameter map file: %w", err)
|
||||
}
|
||||
}
|
||||
disclaimerPrinted := false
|
||||
richParameters := make([]codersdk.WorkspaceBuildParameter, 0)
|
||||
PromptRichParamLoop:
|
||||
for _, templateVersionParameter := range templateVersionParameters {
|
||||
if !args.BuildOptions && templateVersionParameter.Ephemeral {
|
||||
continue
|
||||
}
|
||||
|
||||
if !disclaimerPrinted {
|
||||
_, _ = fmt.Fprintln(inv.Stdout, cliui.DefaultStyles.Paragraph.Render("This template has customizable parameters. Values can be changed after create, but may have unintended side effects (like data loss).")+"\r\n")
|
||||
disclaimerPrinted = true
|
||||
}
|
||||
|
||||
// Param file is all or nothing
|
||||
if !useParamFile && !templateVersionParameter.Ephemeral {
|
||||
for _, e := range args.ExistingRichParams {
|
||||
if e.Name == templateVersionParameter.Name {
|
||||
// If the param already exists, we do not need to prompt it again.
|
||||
// The workspace scope will reuse params for each build.
|
||||
continue PromptRichParamLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if args.UpdateWorkspace && !templateVersionParameter.Mutable {
|
||||
// Check if the immutable parameter was used in the previous build. If so, then it isn't a fresh one
|
||||
// and the user should be warned.
|
||||
exists, err := workspaceBuildParameterExists(ctx, client, args.WorkspaceID, templateVersionParameter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if exists {
|
||||
_, _ = fmt.Fprintln(inv.Stdout, cliui.DefaultStyles.Warn.Render(fmt.Sprintf(`Parameter %q is not mutable, so can't be customized after workspace creation.`, templateVersionParameter.Name)))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
parameterValue, err := getWorkspaceBuildParameterValueFromMapOrInput(inv, parameterMapFromFile, templateVersionParameter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
richParameters = append(richParameters, *parameterValue)
|
||||
}
|
||||
|
||||
if disclaimerPrinted {
|
||||
_, _ = fmt.Fprintln(inv.Stdout)
|
||||
resolver := new(ParameterResolver).
|
||||
WithLastBuildParameters(args.LastBuildParameters).
|
||||
WithPromptBuildOptions(args.PromptBuildOptions).
|
||||
WithBuildOptions(args.BuildOptions).
|
||||
WithPromptRichParameters(args.PromptRichParameters).
|
||||
WithRichParameters(args.RichParameters).
|
||||
WithRichParametersFile(parameterFile)
|
||||
buildParameters, err := resolver.Resolve(inv, args.Action, templateVersionParameters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = cliui.GitAuth(ctx, inv.Stdout, cliui.GitAuthOptions{
|
||||
@ -306,7 +269,7 @@ PromptRichParamLoop:
|
||||
// Run a dry-run with the given parameters to check correctness
|
||||
dryRun, err := client.CreateTemplateVersionDryRun(inv.Context(), templateVersion.ID, codersdk.CreateTemplateVersionDryRunRequest{
|
||||
WorkspaceName: args.NewWorkspaceName,
|
||||
RichParameterValues: richParameters,
|
||||
RichParameterValues: buildParameters,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("begin workspace dry-run: %w", err)
|
||||
@ -346,21 +309,5 @@ PromptRichParamLoop:
|
||||
return nil, xerrors.Errorf("get resources: %w", err)
|
||||
}
|
||||
|
||||
return &buildParameters{
|
||||
richParameters: richParameters,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func workspaceBuildParameterExists(ctx context.Context, client *codersdk.Client, workspaceID uuid.UUID, templateVersionParameter codersdk.TemplateVersionParameter) (bool, error) {
|
||||
lastBuildParameters, err := client.WorkspaceBuildParameters(ctx, workspaceID)
|
||||
if err != nil {
|
||||
return false, xerrors.Errorf("can't fetch last workspace build parameters: %w", err)
|
||||
}
|
||||
|
||||
for _, p := range lastBuildParameters {
|
||||
if p.Name == templateVersionParameter.Name {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
return buildParameters, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user