Skip to content

chore!: send modules archive over the proto messages#21398

Merged
Emyrk merged 1 commit intomainfrom
stevenmasley/modules_over_the_wire
Jan 9, 2026
Merged

chore!: send modules archive over the proto messages#21398
Emyrk merged 1 commit intomainfrom
stevenmasley/modules_over_the_wire

Conversation

@Emyrk
Copy link
Member

@Emyrk Emyrk commented Dec 29, 2025

What this does

Dynamic parameters caches the ./terraform/modules directory for parameter usage. What this PR does is send over this archive to the provisioner when building workspaces.

This allow terraform to skip downloading modules from their registries, a step that takes seconds.

Screenshot From 2025-12-29 12-57-52

Wire protocol

The wire protocol reuses the same mechanism used to download the modules provisoner -> coder. It splits up large archives into multiple protobuf messages so larger archives can be sent under the message size limit.

🚨 Behavior Change (Breaking Change) 🚨

Before this PR modules were downloaded on every workspace build. This means unpinned modules always fetched the latest version

After this PR modules are cached at template import time, and their versions are effectively pinned for all subsequent workspace builds.

Copy link
Member Author

Emyrk commented Dec 29, 2025

@Emyrk Emyrk force-pushed the stevenmasley/modules_over_the_wire branch 2 times, most recently from 3a1cf4a to f68e058 Compare December 29, 2025 15:29
@Emyrk Emyrk force-pushed the stevenmasley/remove_exp branch from 301568a to dea6d25 Compare December 30, 2025 14:43
@Emyrk Emyrk force-pushed the stevenmasley/modules_over_the_wire branch from d7c2bf0 to add8b5a Compare December 30, 2025 14:43
@Emyrk Emyrk force-pushed the stevenmasley/modules_over_the_wire branch from d35a9f1 to eae738d Compare January 6, 2026 16:16
@Emyrk Emyrk force-pushed the stevenmasley/remove_exp branch from fe1156c to 408096b Compare January 7, 2026 14:37
@Emyrk Emyrk force-pushed the stevenmasley/modules_over_the_wire branch from eae738d to b180036 Compare January 7, 2026 14:37
@Emyrk Emyrk changed the base branch from stevenmasley/remove_exp to graphite-base/21398 January 7, 2026 14:49
@Emyrk Emyrk force-pushed the stevenmasley/modules_over_the_wire branch from b180036 to f65837d Compare January 7, 2026 14:49
@Emyrk Emyrk changed the base branch from graphite-base/21398 to 01-07-chore_update_protobuf_to_reuse_file_request January 7, 2026 14:49
@Emyrk Emyrk force-pushed the stevenmasley/modules_over_the_wire branch from f65837d to b54a67a Compare January 7, 2026 14:51
@Emyrk Emyrk force-pushed the 01-07-chore_update_protobuf_to_reuse_file_request branch 2 times, most recently from 5d2e3d6 to a3c50b5 Compare January 7, 2026 15:02
@Emyrk Emyrk force-pushed the stevenmasley/modules_over_the_wire branch from b54a67a to 9ea352f Compare January 7, 2026 15:02
@Emyrk Emyrk force-pushed the 01-07-chore_update_protobuf_to_reuse_file_request branch from a3c50b5 to 1d3e35e Compare January 7, 2026 16:15
@Emyrk Emyrk force-pushed the stevenmasley/modules_over_the_wire branch from 9ea352f to 035f01e Compare January 7, 2026 16:15
@Emyrk Emyrk force-pushed the 01-07-chore_update_protobuf_to_reuse_file_request branch from 1d3e35e to 83c73d9 Compare January 7, 2026 16:48
@Emyrk Emyrk force-pushed the stevenmasley/modules_over_the_wire branch 2 times, most recently from 7529e06 to 8e1a315 Compare January 7, 2026 17:24
@Emyrk Emyrk force-pushed the 01-07-chore_update_protobuf_to_reuse_file_request branch from 83c73d9 to 6e60c5f Compare January 7, 2026 17:24
@Emyrk Emyrk force-pushed the stevenmasley/modules_over_the_wire branch from 8e1a315 to 5e7de71 Compare January 7, 2026 17:27
@Emyrk Emyrk force-pushed the 01-07-chore_update_protobuf_to_reuse_file_request branch 2 times, most recently from ecac30d to 2df9ca9 Compare January 7, 2026 17:44
@Emyrk Emyrk force-pushed the stevenmasley/modules_over_the_wire branch 3 times, most recently from f708df5 to 8af46fb Compare January 7, 2026 17:51
@Emyrk Emyrk force-pushed the stevenmasley/modules_over_the_wire branch from 9344fd5 to dc40539 Compare January 8, 2026 18:07
@Emyrk Emyrk force-pushed the 01-07-chore_update_protobuf_to_reuse_file_request branch from 259ab00 to 19a5a3b Compare January 8, 2026 18:07
@Emyrk Emyrk force-pushed the stevenmasley/modules_over_the_wire branch from dc40539 to 16d9aac Compare January 8, 2026 18:11
@Emyrk Emyrk force-pushed the 01-07-chore_update_protobuf_to_reuse_file_request branch 2 times, most recently from c2c8425 to 5b0df75 Compare January 8, 2026 19:17
@Emyrk Emyrk force-pushed the stevenmasley/modules_over_the_wire branch 2 times, most recently from 1aaa81b to 715e5a1 Compare January 8, 2026 19:33
@Emyrk Emyrk force-pushed the 01-07-chore_update_protobuf_to_reuse_file_request branch from 5b0df75 to ecf4457 Compare January 8, 2026 19:33
@Emyrk Emyrk marked this pull request as ready for review January 8, 2026 19:39
@Emyrk Emyrk requested a review from johnstcn January 8, 2026 19:40
Copy link
Member

@johnstcn johnstcn left a comment

Choose a reason for hiding this comment

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

This does make template versions lock a specific set of module versions. Which is a behavior change for the better.

So if I understand correctly, this means that a template version that references one or more modules will always refer to the same versions of those modules that were resolved at template import time? This seems like a pretty big behavioural change that merits being called out in the release notes.

Comment on lines +1526 to +1529
// This check is not perfect. If these conditions are not true, then the file is not a modules file.
if file.CreatedBy != uuid.Nil || file.Mimetype != tarMimeType {
return fail(xerrors.Errorf("file %s is not a modules file", fid))
}
Copy link
Member

Choose a reason for hiding this comment

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

Is there a particular set of files for which we could check the presence that would indicate that it is, in fact, a modules archive?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, but that would require reading over the archive, which is however large. I do not think it's worth it.

Ideally maybe we could have some mimetype for our archives that indicate what kind of archive it is?

type/subtype;parameter=value

We could maybe do like application/x-tar;coder_type=modules or something?

Copy link
Member

Choose a reason for hiding this comment

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

Oh nice; I forgot about the parameter values you could stuff into mimetypes. That could work nicely.

Copy link
Member Author

Choose a reason for hiding this comment

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

If I do that, then it would not be backwards compatible. So all previous templates would not use this, since the mime types would be wrong

Copy link
Member Author

Choose a reason for hiding this comment

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

We also do not parse mime types right now, we just do direct string comparisons:

coder/coderd/files/cache.go

Lines 306 to 316 in e54f92b

var files fs.FS
switch file.Mimetype {
case "application/zip", "application/x-zip-compressed":
files, err = archivefs.FromZipReader(bytes.NewReader(file.Data), int64(len(file.Data)))
if err != nil {
return CacheEntryValue{}, xerrors.Errorf("failed to read zip file: %w", err)
}
default:
// Assume '"application/x-tar"' as the default mimetype.
files = archivefs.FromTarReader(bytes.NewBuffer(file.Data))
}

I do think it is a good idea, I just think we need to fix mime type handling in our codebase

Copy link
Member

Choose a reason for hiding this comment

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

That's fair, would be a good follow-up for later.


var cachedModulesTar []byte
// Download modules if cached in coderd
if r.job.GetWorkspaceBuild().Metadata.TemplateVersionModulesFile != "" {
Copy link
Member

Choose a reason for hiding this comment

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

(Not necessary in this PR, potential followup) I'm nervous about this unconditional calling of r.job.GetWorkspaceBuild() that potentially returns nil. Can we add a top-level check e.g.

jobWorkspaceBuild := r.job.GetWorkspaceBuild()
if jobWorkspaceBuild == nil {
  // whoopsiedoodle
}

Copy link
Member Author

Choose a reason for hiding this comment

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

That is a good point. Protobuf handling these union types is unfortunate.

@Emyrk Emyrk force-pushed the stevenmasley/modules_over_the_wire branch from 715e5a1 to e54f92b Compare January 9, 2026 14:23
@Emyrk Emyrk force-pushed the 01-07-chore_update_protobuf_to_reuse_file_request branch from ecf4457 to 9f5aab4 Compare January 9, 2026 14:23
@Emyrk
Copy link
Member Author

Emyrk commented Jan 9, 2026

So if I understand correctly, this means that a template version that references one or more modules will always refer to the same versions of those modules that were resolved at template import time? This seems like a pretty big behavioural change that merits being called out in the release notes.

I will mark this a breaking change and mention that 👍.

You do understand correctly. This is a positive change, because without this, a template can break from module changes.

@Emyrk Emyrk added the release/breaking This label is applied to PRs to detect breaking changes as part of the release process label Jan 9, 2026
@Emyrk Emyrk changed the title chore: send modules archive over the proto messages chore!: send modules archive over the proto messages Jan 9, 2026
@Emyrk Emyrk requested a review from johnstcn January 9, 2026 16:15
versionModulesFile := ""
tfvals, err := s.Database.GetTemplateVersionTerraformValues(ctx, templateVersion.ID)
if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
// Older templates (before dynamic parameters) will not have cached module files.
Copy link
Member

Choose a reason for hiding this comment

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

There shouldn't be any left though, right?

Copy link
Member Author

Choose a reason for hiding this comment

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

There are old template versions. They will be around forever.

Copy link
Member Author

Emyrk commented Jan 9, 2026

Merge activity

  • Jan 9, 5:12 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Jan 9, 5:25 PM UTC: Graphite rebased this pull request as part of a merge.
  • Jan 9, 5:33 PM UTC: @Emyrk merged this pull request with Graphite.

Emyrk added a commit that referenced this pull request Jan 9, 2026
Experiment is no longer required, the new method will be released without an experiment and without a toggle

Main PR is: #21398
@Emyrk Emyrk changed the base branch from 01-07-chore_update_protobuf_to_reuse_file_request to graphite-base/21398 January 9, 2026 17:14
Emyrk added a commit that referenced this pull request Jan 9, 2026
**This is just the protobuf changes for the PR #21398

Moved `UploadFileRequest` from `provisionerd.proto` -> `provisioner.proto`.

Renamed to `FileUpload` because it is now bi-directional.

This **is backwards compatible**. I tested it to confirm the payloads are identical.  Types were just renamed and moved around.

```golang
func TestTypeUpgrade(t *testing.T) {
	t.Parallel()

	x := &proto2.UploadFileRequest{
		Type: &proto2.UploadFileRequest_ChunkPiece{
			ChunkPiece: &proto.ChunkPiece{
				Data:         []byte("Hello World!"),
				FullDataHash: []byte("Foobar"),
				PieceIndex:   42,
			},
		},
	}

	data, err := protobuf.Marshal(x)
	require.NoError(t, err)

	// Exactly the same output
	// EhgKDEhlbGxvIFdvcmxkIRIGRm9vYmFyGCo= on `main`
	// EhgKDEhlbGxvIFdvcmxkIRIGRm9vYmFyGCo= on this branch
	fmt.Println(base64.StdEncoding.EncodeToString(data))
}
```


# What this does

This allows provisioner daemons to download files from `coderd`'s `files` table. This is used to send over cached module files and prevent the need of downloading these modules on each workspace build.
@Emyrk Emyrk changed the base branch from graphite-base/21398 to main January 9, 2026 17:23
@Emyrk Emyrk requested a review from spikecurtis as a code owner January 9, 2026 17:23
Serializing modules to disk prevents an external download
@Emyrk Emyrk force-pushed the stevenmasley/modules_over_the_wire branch from e54f92b to ced4ebd Compare January 9, 2026 17:24
@Emyrk Emyrk merged commit 60b3fd0 into main Jan 9, 2026
31 checks passed
@Emyrk Emyrk deleted the stevenmasley/modules_over_the_wire branch January 9, 2026 17:33
@github-actions github-actions bot locked and limited conversation to collaborators Jan 9, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release/breaking This label is applied to PRs to detect breaking changes as part of the release process

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants