mirror of https://github.com/coder/coder.git
feat(support): add template info to support bundle (#12451)
Adds workspace build parameters, template, template version, and zipped template source to support bundle.
This commit is contained in:
parent
db02c72ac6
commit
17caf58b5e
|
@ -3,6 +3,7 @@ package cli
|
|||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
switch f.Name {
|
||||
case "deployment/buildinfo.json":
|
||||
var v codersdk.BuildInfoResponse
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package support
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package support_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
|
@ -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)
|
||||
})
|
||||
|
||||
|
@ -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
|
||||
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]
|
||||
|
|
Loading…
Reference in New Issue