Repository navigation
Conversation
`versions[*].directory` was the only way to give a template version its contents, which meant the contents had to exist on the filesystem of the machine running Terraform. Templates rendered with `templatefile()`, fetched from another repo or an artifact store, or applied from HCP Terraform can't be expressed that way, and the `local_file` workaround depends on plan-time/apply-time ordering that isn't guaranteed. A version can now take its contents from one of three mutually exclusive attributes, enforced with `stringvalidator.ExactlyOneOf`: - `directory`, unchanged. - `files`, a map of relative path to file content. The files are written out and archived exactly like a directory, so hidden files are excluded and `terraform.tfvars`/`*.auto.tfvars` entries are read as variable values. - `archive_base64`, a base64-encoded tar archive, uploaded as-is. Variable values aren't discovered from it, so `tf_vars` is the only way to set them. `directory_hash` is now computed from whichever source is set, and is unknown (rather than an error) while the source is unknown, so contents produced by another resource plan as a new version. Bad base64, a non-tar archive, an oversized archive, and a `files` map without a root `.tf` file all fail during the plan instead of the apply. Closes coder#439
The example template's `${var.name}` was interpolated by the test's own
configuration instead of reaching the provider, so the `files` acceptance
test failed to plan with "Reference to undeclared input variable". The
plan-only test now uses the same contents, so the mock-server test catches
this without a Coder deployment.
`archive_base64` closed the gap for contents that live in the configuration, but an archive that already exists as a file still had to be read into the configuration with `filebase64()`, which is wasteful for anything of size and awkward when the archive is produced by a data source such as `archive_file`. A version can now point `archive_file` at an archive on disk instead. It is the fourth mutually exclusive content attribute, hashed while planning like `directory` is, so the archive has to exist at plan time. Both archive attributes now accept an uncompressed tar, a gzipped tar, or a zip archive, detected from the contents rather than a file extension: `archive_file` is otherwise unusable with the `archive_file` data source, which produces zip or tar.gz only. Tar and zip are uploaded as-is since the API takes both, gzip is decompressed first, and decompression is bounded by the archive size limit so a compression bomb can't be expanded into an unbounded amount of memory. Refs coder#439
…ad helper `uploadTemplateArchive` returned a `[]codersdk.VariableValue` that was always nil, to match the shape of `uploadVersionContents`. `unparam` rejects that, failing the build job's linters, so the helper now returns just the file ID and its callers supply the nil themselves.
…o's style The comments added for `files`, `archive_base64`, and `archive_file` explained why the code is the way it is. The rest of the package states what a function does and leaves the reasoning to the pull request, so these now do the same.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
versions[*].directoryis the only way to give acoderd_templateversion its contents, so the contents must exist as a directory on the filesystem of the machine running Terraform, at plan time. That rules out the cases described in #439:templatefile()(thelocal_file+depends_onworkaround relies on plan-time/apply-time ordering that isn't guaranteed),http/vault, which arrive as strings or base64,data "archive_file".Fix
A version now takes its contents from exactly one of four attributes, enforced at validate time with
stringvalidator.ExactlyOneOf:directory(unchanged) — a path to a directory on disk.files—map(string)of relative path to file content:The files are written out and then archived through
provisionersdk.Tarlike any other directory, so a version built fromfilesbehaves identically to the same contents on disk: hidden files are excluded,terraform.tfvars/*.auto.tfvarsentries are read as variable values rather than uploaded, and the archive size limit applies. Paths must be relative and must not escape the root.archive_base64— a base64-encoded archive, uploaded as-is, covering binary contents and prebuilt tarballs.archive_file— a path to an archive file, uploaded as-is, for an archive that already exists on disk and would otherwise have to be slurped into the configuration withfilebase64():Both archive attributes accept an uncompressed tar, a gzipped tar, or a zip archive, detected from the contents rather than from a file extension. Tar and zip are uploaded as-is, since the Coder API accepts both and expands a zip server-side; a gzipped tar is decompressed first, buffering at most
provisionersdk.TemplateArchiveLimitso a compression bomb can't be expanded into an unbounded amount of memory. Accepting more than uncompressed tar matters forarchive_filein particular:data "archive_file"only produces zip and tar.gz, so tar-only support would have left the most common producer unusable.Variable values can't be discovered from an archive without extracting it, so
tf_varsis the only way to set them for the two archive sources.directory_hash— the value that decides whether a new template version is created — is now computed from whichever source is set (fileshashes sorted path + content, so a rename is a change; the archive sources hash the normalized archive bytes, so re-gzipping identical contents is not a change). When the configured source isn't known yet, the hash plans as unknown instead of erroring, so contents produced by another resource plan as a new version and are resolved during apply.Following "fail at plan time, not apply time": invalid base64, a missing or unreadable
archive_file, an archive that is none of the three formats, an oversized archive, more than one source set (whichExactlyOneOfdefers on when one of the four is unknown), and afilesmap with no root.tf/.tf.jsonfile all fail the plan.Notes
directorybecomesOptionalinstead ofRequired; existing configurations are unaffected.directory_hashkeeps its name (it's in state for every existing template) but now documents itself as the hash of the version contents.directoryto the equivalentfilesor archive creates one new template version, since the hashes are computed differently per source.archive_fileis read while planning, likedirectoryis, so the archive has to exist at plan time — a data source fits it, a resource that writes the file during apply does not.go.mod/go.sumare unchanged, andarchive/zip/compress/gzipare standard library packages already linked into the binary transitively.Closes #439