Skip to content

feat(support): add template info to support bundle #12451

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 18 additions & 8 deletions cli/support.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cli
import (
"archive/zip"
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"os"
Expand Down Expand Up @@ -137,14 +138,17 @@ func findAgent(agentName string, haystack []codersdk.WorkspaceResource) (*coders

func writeBundle(src *support.Bundle, dest *zip.Writer) error {
for k, v := range map[string]any{
"deployment/buildinfo.json": src.Deployment.BuildInfo,
"deployment/config.json": src.Deployment.Config,
"deployment/experiments.json": src.Deployment.Experiments,
"deployment/health.json": src.Deployment.HealthReport,
"network/netcheck_local.json": src.Network.NetcheckLocal,
"network/netcheck_remote.json": src.Network.NetcheckRemote,
"workspace/workspace.json": src.Workspace.Workspace,
"workspace/agent.json": src.Workspace.Agent,
"deployment/buildinfo.json": src.Deployment.BuildInfo,
"deployment/config.json": src.Deployment.Config,
"deployment/experiments.json": src.Deployment.Experiments,
"deployment/health.json": src.Deployment.HealthReport,
"network/netcheck_local.json": src.Network.NetcheckLocal,
"network/netcheck_remote.json": src.Network.NetcheckRemote,
"workspace/workspace.json": src.Workspace.Workspace,
"workspace/agent.json": src.Workspace.Agent,
"workspace/template.json": src.Workspace.Template,
"workspace/template_version.json": src.Workspace.TemplateVersion,
"workspace/parameters.json": src.Workspace.Parameters,
} {
f, err := dest.Create(k)
if err != nil {
Expand All @@ -157,11 +161,17 @@ func writeBundle(src *support.Bundle, dest *zip.Writer) error {
}
}

templateVersionBytes, err := base64.StdEncoding.DecodeString(src.Workspace.TemplateFileBase64)
if err != nil {
return xerrors.Errorf("decode template zip from base64")
}

for k, v := range map[string]string{
"network/coordinator_debug.html": src.Network.CoordinatorDebug,
"network/tailnet_debug.html": src.Network.TailnetDebug,
"workspace/build_logs.txt": humanizeBuildLogs(src.Workspace.BuildLogs),
"workspace/agent_startup_logs.txt": humanizeAgentLogs(src.Workspace.AgentStartupLogs),
"workspace/template_file.zip": string(templateVersionBytes),
"logs.txt": strings.Join(src.Logs, "\n"),
} {
f, err := dest.Create(k)
Expand Down
18 changes: 16 additions & 2 deletions cli/support_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@ func assertBundleContents(t *testing.T, path string) {
require.NoError(t, err, "open zip file")
defer r.Close()
for _, f := range r.File {
require.NotZero(t, f.UncompressedSize64, "file %q should not be empty", f.Name)
Copy link
Member Author

Choose a reason for hiding this comment

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

note: removing this as it's redundant with other checks

switch f.Name {
case "deployment/buildinfo.json":
var v codersdk.BuildInfoResponse
Expand Down Expand Up @@ -156,11 +155,26 @@ func assertBundleContents(t *testing.T, path string) {
case "workspace/agent_startup_logs.txt":
bs := readBytesFromZip(t, f)
require.Contains(t, string(bs), "started up")
case "workspace/template.json":
var v codersdk.Template
decodeJSONFromZip(t, f, &v)
require.NotEmpty(t, v, "workspace template should not be empty")
case "workspace/template_version.json":
var v codersdk.TemplateVersion
decodeJSONFromZip(t, f, &v)
require.NotEmpty(t, v, "workspace template version should not be empty")
case "workspace/parameters.json":
var v []codersdk.WorkspaceBuildParameter
decodeJSONFromZip(t, f, &v)
require.NotNil(t, v, "workspace parameters should not be nil")
case "workspace/template_file.zip":
bs := readBytesFromZip(t, f)
require.NotNil(t, bs, "template file should not be nil")
case "logs.txt":
bs := readBytesFromZip(t, f)
require.NotEmpty(t, bs, "logs should not be empty")
default:
require.Fail(t, "unexpected file in bundle", f.Name)
require.Failf(t, "unexpected file in bundle: %q", f.Name)
}
}
}
Expand Down
63 changes: 59 additions & 4 deletions support/support.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package support

import (
"context"
"encoding/base64"
"io"
"net/http"
"strings"
Expand Down Expand Up @@ -42,10 +43,14 @@ type Network struct {
}

type Workspace struct {
Workspace codersdk.Workspace `json:"workspace"`
BuildLogs []codersdk.ProvisionerJobLog `json:"build_logs"`
Agent codersdk.WorkspaceAgent `json:"agent"`
AgentStartupLogs []codersdk.WorkspaceAgentLog `json:"startup_logs"`
Workspace codersdk.Workspace `json:"workspace"`
Parameters []codersdk.WorkspaceBuildParameter `json:"parameters"`
Template codersdk.Template `json:"template"`
TemplateVersion codersdk.TemplateVersion `json:"template_version"`
TemplateFileBase64 string `json:"template_file_base64"`
BuildLogs []codersdk.ProvisionerJobLog `json:"build_logs"`
Agent codersdk.WorkspaceAgent `json:"agent"`
AgentStartupLogs []codersdk.WorkspaceAgentLog `json:"startup_logs"`
}

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

eg.Go(func() error {
if w.Workspace.TemplateActiveVersionID == uuid.Nil {
return xerrors.Errorf("workspace has nil template active version id")
}
tv, err := client.TemplateVersion(ctx, w.Workspace.TemplateActiveVersionID)
if err != nil {
return xerrors.Errorf("fetch template active version id")
}
w.TemplateVersion = tv

if tv.Job.FileID == uuid.Nil {
return xerrors.Errorf("template file id is nil")
}
raw, ctype, err := client.DownloadWithFormat(ctx, tv.Job.FileID, codersdk.FormatZip)
if err != nil {
return err
}
if ctype != codersdk.ContentTypeZip {
return xerrors.Errorf("expected content-type %s, got %s", codersdk.ContentTypeZip, ctype)
}

b64encoded := base64.StdEncoding.EncodeToString(raw)
w.TemplateFileBase64 = b64encoded
return nil
})

eg.Go(func() error {
if w.Workspace.TemplateID == uuid.Nil {
return xerrors.Errorf("workspace has nil version id")
}
tpl, err := client.Template(ctx, w.Workspace.TemplateID)
if err != nil {
return xerrors.Errorf("fetch template")
}
w.Template = tpl
return nil
})

eg.Go(func() error {
if ws.LatestBuild.ID == uuid.Nil {
return xerrors.Errorf("workspace has nil latest build id")
}
params, err := client.WorkspaceBuildParameters(ctx, ws.LatestBuild.ID)
if err != nil {
return xerrors.Errorf("fetch workspace build parameters: %w", err)
}
w.Parameters = params
return nil
})

if err := eg.Wait(); err != nil {
log.Error(ctx, "fetch workspace information", slog.Error(err))
}
Expand Down
24 changes: 23 additions & 1 deletion support/support_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package support_test

import (
"bytes"
"context"
"io"
"net/http"
Expand Down Expand Up @@ -59,6 +60,10 @@ func TestRun(t *testing.T) {
require.NotEmpty(t, bun.Workspace.BuildLogs)
require.NotNil(t, bun.Workspace.Agent)
require.NotEmpty(t, bun.Workspace.AgentStartupLogs)
require.NotEmpty(t, bun.Workspace.Template)
require.NotEmpty(t, bun.Workspace.TemplateVersion)
require.NotEmpty(t, bun.Workspace.TemplateFileBase64)
require.NotNil(t, bun.Workspace.Parameters)
require.NotEmpty(t, bun.Logs)
})

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

func setupWorkspaceAndAgent(ctx context.Context, t *testing.T, client *codersdk.Client, db database.Store, user codersdk.CreateFirstUserResponse) (codersdk.Workspace, codersdk.WorkspaceAgent) {
// This is a valid zip file
zipBytes := make([]byte, 22)
zipBytes[0] = 80
zipBytes[1] = 75
zipBytes[2] = 0o5
zipBytes[3] = 0o6
Comment on lines +148 to +149
Copy link
Member Author

@johnstcn johnstcn Mar 7, 2024

Choose a reason for hiding this comment

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

note: the formatter made these octal of its own volition; likely due to the leading 0.

uploadRes, err := client.Upload(ctx, codersdk.ContentTypeZip, bytes.NewReader(zipBytes))
require.NoError(t, err)

tv := dbfake.TemplateVersion(t, db).
FileID(uploadRes.ID).
Seed(database.TemplateVersion{
OrganizationID: user.OrganizationID,
CreatedBy: user.UserID,
}).
Do()
wbr := dbfake.WorkspaceBuild(t, db, database.Workspace{
OrganizationID: user.OrganizationID,
OwnerID: user.UserID,
}).WithAgent().Do()
TemplateID: tv.Template.ID,
}).Resource().WithAgent().Do()
ws, err := client.Workspace(ctx, wbr.Workspace.ID)
require.NoError(t, err)
agt := ws.LatestBuild.Resources[0].Agents[0]
Expand Down