feat: run a terraform plan before creating workspaces with the given template parameters - #1732
Conversation
johnstcn
left a comment
There was a problem hiding this comment.
I haven't tested this out myself, but it looks solid to me.
| // We use the workspace RBAC check since we don't want to allow plans if the | ||
| // user can't create workspaces. |
There was a problem hiding this comment.
Technically correct - the best kind of correct.
| -- It's not possible to drop enum values from enum types, so the UP has "IF NOT | ||
| -- EXISTS". | ||
|
|
||
| -- Delete all jobs that use the new enum value. | ||
| DELETE FROM | ||
| provisioner_jobs | ||
| WHERE | ||
| provisioner_job_type = 'template_version_plan' | ||
| ; |
| ALTER TYPE provisioner_job_type | ||
| ADD VALUE IF NOT EXISTS 'template_version_plan'; |
| // TODO (Dean): reprompt for parameter values if we deem it to | ||
| // be a validation error |
| transition, err := convertWorkspaceTransition(workspaceBuild.Transition) | ||
| if err != nil { | ||
| return nil, failJob(fmt.Sprint("convert workspace transition: %w", err)) | ||
| return nil, failJob(fmt.Sprintf("convert workspace transition: %s", err)) |
There was a problem hiding this comment.
No wrapping? I guess this gets protobuffed in the end, right?
There was a problem hiding this comment.
Can't wrap with Sprintf, and these just get condensed to strings sadly :(
| var req codersdk.CreateTemplateVersionPlanRequest | ||
| if !httpapi.Read(rw, r, &req) { | ||
| return | ||
| } |
There was a problem hiding this comment.
As a user, I could spam this ~1000 times and take down a Coder deployment for the day. The same could be said for workspaces, but we have issues tracking quotas for those.
There was a problem hiding this comment.
OK so I'll just add details to those issues that whatever fix we do needs to apply to this endpoint too
There was a problem hiding this comment.
Could you link the issue? I can't seem to find it after searching "rate limit" and "quota"
There was a problem hiding this comment.
Ahh my bad for being slow here.
Can we limit the parallelization here? This is a remarkably easy attack vector that we don't seem to have a solid plan to fix.
There was a problem hiding this comment.
That seems out of scope for this PR because we have the same issue with regular workspace builds and template imports which also needs to be fixed. It'd make sense to come up with a good solution that covers all of these job types
There was a problem hiding this comment.
In that case, what's the mechanism we use to block multiple workspace builds so we can implement here
There was a problem hiding this comment.
Multiple builds cannot occur for the same workspace in parallel because they are stateful.
There was a problem hiding this comment.
Do we have any protection across multiple builds across multiple workspaces on the same user?
There was a problem hiding this comment.
Nope, but it's less of a problem because that'd be extremely easy to detect malice via our current API. An admin could list workspaces to easily detect who's the malicious actor.
There was a problem hiding this comment.
That's fair, but I still think that a global solution should be applied to all jobs simultaneously. You said earlier there was a ticket about this, could you link it so I can add context to it? I will try to tackle it before community MVP
| var input templateVersionPlanJob | ||
| err = json.Unmarshal(job.Input, &input) | ||
| if err != nil { | ||
| return nil, failJob(fmt.Sprintf("unmarshal job input %q: %s", job.Input, err)) | ||
| } |
There was a problem hiding this comment.
It scares me to add another job with such similar scope to the others, but I understand why.
It'd be helpful if you explained why we couldn't use the workspace_build job to do similarly. I believe it'd just require not being attached to a literal workspace build, which seems like a reasonable change anyways.
There was a problem hiding this comment.
We need to have a separate job for this because the job endpoints have different schemas and different endpoints to access the job details, not a unified /jobs endpoint. You've said in the past that you'd like to split these different job types apart more in the future (so they don't share DB tables for metadata etc.), so merging these two jobs together would be counterintuitive to that goal
There was a problem hiding this comment.
The inputs are slightly different, but the outputs are the same, right?
There was a problem hiding this comment.
It's probably worth opening a ticket considering merging those job types, or at least stating what the impl would look like to merge them. I'm always nervous about adding more job types.
| return err | ||
| return xerrors.Errorf("begin workspace dry-run: %w", err) | ||
| } | ||
| _, _ = fmt.Fprintln(cmd.OutOrStdout(), "Planning workspace...") |
There was a problem hiding this comment.
I kept the language "Planning workspace" because I couldn't come up with something that made sense with the word "dry-run"....
"Dry-running workspace"
"Running workspace dry-run"
etc.
…template parameters (#1732)
Adds a new provisioner job type
template_version_planwhich is for running a plan against a template version with custom parameters, but without having to make a workspace first. This will be used incoder createso we can verify that the workspace creation will start successfully and not fail quickly due to bad parameter values (leaving an inconsolable workspace state).Subtasks
template_version_planprovisioner job type to databasePOST /templateversions/:template_version_id/planendpoint and codersdk methodsTemplateVersionPlantemplate_version_planprovisioner jobs =>TemplateVersionPlanprotobuf jobsTemplateVersionPlanjobscoder createbefore creating the workspacecoder createplanFuture:
Screenshots
Successful validation:

Unsuccessful validation/plan:

Fixes #831