Skip to content

Commit 840c4e7

Browse files
johnstcnmtojek
authored andcommitted
feat(support): add template info to support bundle (#12451)
Adds workspace build parameters, template, template version, and zipped template source to support bundle.
1 parent a3a88fd commit 840c4e7

File tree

4 files changed

+116
-15
lines changed

4 files changed

+116
-15
lines changed

cli/support.go

+18-8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cli
33
import (
44
"archive/zip"
55
"bytes"
6+
"encoding/base64"
67
"encoding/json"
78
"fmt"
89
"os"
@@ -137,14 +138,17 @@ func findAgent(agentName string, haystack []codersdk.WorkspaceResource) (*coders
137138

138139
func writeBundle(src *support.Bundle, dest *zip.Writer) error {
139140
for k, v := range map[string]any{
140-
"deployment/buildinfo.json": src.Deployment.BuildInfo,
141-
"deployment/config.json": src.Deployment.Config,
142-
"deployment/experiments.json": src.Deployment.Experiments,
143-
"deployment/health.json": src.Deployment.HealthReport,
144-
"network/netcheck_local.json": src.Network.NetcheckLocal,
145-
"network/netcheck_remote.json": src.Network.NetcheckRemote,
146-
"workspace/workspace.json": src.Workspace.Workspace,
147-
"workspace/agent.json": src.Workspace.Agent,
141+
"deployment/buildinfo.json": src.Deployment.BuildInfo,
142+
"deployment/config.json": src.Deployment.Config,
143+
"deployment/experiments.json": src.Deployment.Experiments,
144+
"deployment/health.json": src.Deployment.HealthReport,
145+
"network/netcheck_local.json": src.Network.NetcheckLocal,
146+
"network/netcheck_remote.json": src.Network.NetcheckRemote,
147+
"workspace/workspace.json": src.Workspace.Workspace,
148+
"workspace/agent.json": src.Workspace.Agent,
149+
"workspace/template.json": src.Workspace.Template,
150+
"workspace/template_version.json": src.Workspace.TemplateVersion,
151+
"workspace/parameters.json": src.Workspace.Parameters,
148152
} {
149153
f, err := dest.Create(k)
150154
if err != nil {
@@ -157,11 +161,17 @@ func writeBundle(src *support.Bundle, dest *zip.Writer) error {
157161
}
158162
}
159163

164+
templateVersionBytes, err := base64.StdEncoding.DecodeString(src.Workspace.TemplateFileBase64)
165+
if err != nil {
166+
return xerrors.Errorf("decode template zip from base64")
167+
}
168+
160169
for k, v := range map[string]string{
161170
"network/coordinator_debug.html": src.Network.CoordinatorDebug,
162171
"network/tailnet_debug.html": src.Network.TailnetDebug,
163172
"workspace/build_logs.txt": humanizeBuildLogs(src.Workspace.BuildLogs),
164173
"workspace/agent_startup_logs.txt": humanizeAgentLogs(src.Workspace.AgentStartupLogs),
174+
"workspace/template_file.zip": string(templateVersionBytes),
165175
"logs.txt": strings.Join(src.Logs, "\n"),
166176
} {
167177
f, err := dest.Create(k)

cli/support_test.go

+16-2
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,6 @@ func assertBundleContents(t *testing.T, path string) {
114114
require.NoError(t, err, "open zip file")
115115
defer r.Close()
116116
for _, f := range r.File {
117-
require.NotZero(t, f.UncompressedSize64, "file %q should not be empty", f.Name)
118117
switch f.Name {
119118
case "deployment/buildinfo.json":
120119
var v codersdk.BuildInfoResponse
@@ -156,11 +155,26 @@ func assertBundleContents(t *testing.T, path string) {
156155
case "workspace/agent_startup_logs.txt":
157156
bs := readBytesFromZip(t, f)
158157
require.Contains(t, string(bs), "started up")
158+
case "workspace/template.json":
159+
var v codersdk.Template
160+
decodeJSONFromZip(t, f, &v)
161+
require.NotEmpty(t, v, "workspace template should not be empty")
162+
case "workspace/template_version.json":
163+
var v codersdk.TemplateVersion
164+
decodeJSONFromZip(t, f, &v)
165+
require.NotEmpty(t, v, "workspace template version should not be empty")
166+
case "workspace/parameters.json":
167+
var v []codersdk.WorkspaceBuildParameter
168+
decodeJSONFromZip(t, f, &v)
169+
require.NotNil(t, v, "workspace parameters should not be nil")
170+
case "workspace/template_file.zip":
171+
bs := readBytesFromZip(t, f)
172+
require.NotNil(t, bs, "template file should not be nil")
159173
case "logs.txt":
160174
bs := readBytesFromZip(t, f)
161175
require.NotEmpty(t, bs, "logs should not be empty")
162176
default:
163-
require.Fail(t, "unexpected file in bundle", f.Name)
177+
require.Failf(t, "unexpected file in bundle: %q", f.Name)
164178
}
165179
}
166180
}

support/support.go

+59-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package support
22

33
import (
44
"context"
5+
"encoding/base64"
56
"io"
67
"net/http"
78
"strings"
@@ -42,10 +43,14 @@ type Network struct {
4243
}
4344

4445
type Workspace struct {
45-
Workspace codersdk.Workspace `json:"workspace"`
46-
BuildLogs []codersdk.ProvisionerJobLog `json:"build_logs"`
47-
Agent codersdk.WorkspaceAgent `json:"agent"`
48-
AgentStartupLogs []codersdk.WorkspaceAgentLog `json:"startup_logs"`
46+
Workspace codersdk.Workspace `json:"workspace"`
47+
Parameters []codersdk.WorkspaceBuildParameter `json:"parameters"`
48+
Template codersdk.Template `json:"template"`
49+
TemplateVersion codersdk.TemplateVersion `json:"template_version"`
50+
TemplateFileBase64 string `json:"template_file_base64"`
51+
BuildLogs []codersdk.ProvisionerJobLog `json:"build_logs"`
52+
Agent codersdk.WorkspaceAgent `json:"agent"`
53+
AgentStartupLogs []codersdk.WorkspaceAgentLog `json:"startup_logs"`
4954
}
5055

5156
// Deps is a set of dependencies for discovering information
@@ -229,6 +234,56 @@ func WorkspaceInfo(ctx context.Context, client *codersdk.Client, log slog.Logger
229234
return nil
230235
})
231236

237+
eg.Go(func() error {
238+
if w.Workspace.TemplateActiveVersionID == uuid.Nil {
239+
return xerrors.Errorf("workspace has nil template active version id")
240+
}
241+
tv, err := client.TemplateVersion(ctx, w.Workspace.TemplateActiveVersionID)
242+
if err != nil {
243+
return xerrors.Errorf("fetch template active version id")
244+
}
245+
w.TemplateVersion = tv
246+
247+
if tv.Job.FileID == uuid.Nil {
248+
return xerrors.Errorf("template file id is nil")
249+
}
250+
raw, ctype, err := client.DownloadWithFormat(ctx, tv.Job.FileID, codersdk.FormatZip)
251+
if err != nil {
252+
return err
253+
}
254+
if ctype != codersdk.ContentTypeZip {
255+
return xerrors.Errorf("expected content-type %s, got %s", codersdk.ContentTypeZip, ctype)
256+
}
257+
258+
b64encoded := base64.StdEncoding.EncodeToString(raw)
259+
w.TemplateFileBase64 = b64encoded
260+
return nil
261+
})
262+
263+
eg.Go(func() error {
264+
if w.Workspace.TemplateID == uuid.Nil {
265+
return xerrors.Errorf("workspace has nil version id")
266+
}
267+
tpl, err := client.Template(ctx, w.Workspace.TemplateID)
268+
if err != nil {
269+
return xerrors.Errorf("fetch template")
270+
}
271+
w.Template = tpl
272+
return nil
273+
})
274+
275+
eg.Go(func() error {
276+
if ws.LatestBuild.ID == uuid.Nil {
277+
return xerrors.Errorf("workspace has nil latest build id")
278+
}
279+
params, err := client.WorkspaceBuildParameters(ctx, ws.LatestBuild.ID)
280+
if err != nil {
281+
return xerrors.Errorf("fetch workspace build parameters: %w", err)
282+
}
283+
w.Parameters = params
284+
return nil
285+
})
286+
232287
if err := eg.Wait(); err != nil {
233288
log.Error(ctx, "fetch workspace information", slog.Error(err))
234289
}

support/support_test.go

+23-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package support_test
22

33
import (
4+
"bytes"
45
"context"
56
"io"
67
"net/http"
@@ -59,6 +60,10 @@ func TestRun(t *testing.T) {
5960
require.NotEmpty(t, bun.Workspace.BuildLogs)
6061
require.NotNil(t, bun.Workspace.Agent)
6162
require.NotEmpty(t, bun.Workspace.AgentStartupLogs)
63+
require.NotEmpty(t, bun.Workspace.Template)
64+
require.NotEmpty(t, bun.Workspace.TemplateVersion)
65+
require.NotEmpty(t, bun.Workspace.TemplateFileBase64)
66+
require.NotNil(t, bun.Workspace.Parameters)
6267
require.NotEmpty(t, bun.Logs)
6368
})
6469

@@ -136,10 +141,27 @@ func assertSanitizedDeploymentConfig(t *testing.T, dc *codersdk.DeploymentConfig
136141
}
137142

138143
func setupWorkspaceAndAgent(ctx context.Context, t *testing.T, client *codersdk.Client, db database.Store, user codersdk.CreateFirstUserResponse) (codersdk.Workspace, codersdk.WorkspaceAgent) {
144+
// This is a valid zip file
145+
zipBytes := make([]byte, 22)
146+
zipBytes[0] = 80
147+
zipBytes[1] = 75
148+
zipBytes[2] = 0o5
149+
zipBytes[3] = 0o6
150+
uploadRes, err := client.Upload(ctx, codersdk.ContentTypeZip, bytes.NewReader(zipBytes))
151+
require.NoError(t, err)
152+
153+
tv := dbfake.TemplateVersion(t, db).
154+
FileID(uploadRes.ID).
155+
Seed(database.TemplateVersion{
156+
OrganizationID: user.OrganizationID,
157+
CreatedBy: user.UserID,
158+
}).
159+
Do()
139160
wbr := dbfake.WorkspaceBuild(t, db, database.Workspace{
140161
OrganizationID: user.OrganizationID,
141162
OwnerID: user.UserID,
142-
}).WithAgent().Do()
163+
TemplateID: tv.Template.ID,
164+
}).Resource().WithAgent().Do()
143165
ws, err := client.Workspace(ctx, wbr.Workspace.ID)
144166
require.NoError(t, err)
145167
agt := ws.LatestBuild.Resources[0].Agents[0]

0 commit comments

Comments
 (0)