mirror of https://github.com/coder/coder.git
feat(support): fetch agent network info over tailnet (#12577)
Adds agent-related information to support bundle command.
This commit is contained in:
parent
653ddccd8e
commit
2abc1cd2b7
|
@ -137,15 +137,19 @@ func findAgent(agentName string, haystack []codersdk.WorkspaceResource) (*coders
|
|||
}
|
||||
|
||||
func writeBundle(src *support.Bundle, dest *zip.Writer) error {
|
||||
// We JSON-encode the following:
|
||||
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,
|
||||
"network/netcheck.json": src.Network.Netcheck,
|
||||
"workspace/workspace.json": src.Workspace.Workspace,
|
||||
"workspace/agent.json": src.Workspace.Agent,
|
||||
"agent/agent.json": src.Agent.Agent,
|
||||
"agent/listening_ports.json": src.Agent.ListeningPorts,
|
||||
"agent/manifest.json": src.Agent.Manifest,
|
||||
"agent/peer_diagnostics.json": src.Agent.PeerDiagnostics,
|
||||
"agent/ping_result.json": src.Agent.PingResult,
|
||||
"workspace/template.json": src.Workspace.Template,
|
||||
"workspace/template_version.json": src.Workspace.TemplateVersion,
|
||||
"workspace/parameters.json": src.Workspace.Parameters,
|
||||
|
@ -166,13 +170,16 @@ func writeBundle(src *support.Bundle, dest *zip.Writer) error {
|
|||
return xerrors.Errorf("decode template zip from base64")
|
||||
}
|
||||
|
||||
// The below we just write as we have them:
|
||||
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"),
|
||||
"network/coordinator_debug.html": src.Network.CoordinatorDebug,
|
||||
"network/tailnet_debug.html": src.Network.TailnetDebug,
|
||||
"workspace/build_logs.txt": humanizeBuildLogs(src.Workspace.BuildLogs),
|
||||
"agent/logs.txt": string(src.Agent.Logs),
|
||||
"agent/magicsock.html": string(src.Agent.MagicsockHTML),
|
||||
"agent/startup_logs.txt": humanizeAgentLogs(src.Agent.StartupLogs),
|
||||
"workspace/template_file.zip": string(templateVersionBytes),
|
||||
"logs.txt": strings.Join(src.Logs, "\n"),
|
||||
} {
|
||||
f, err := dest.Create(k)
|
||||
if err != nil {
|
||||
|
|
|
@ -4,19 +4,26 @@ import (
|
|||
"archive/zip"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"tailscale.com/ipn/ipnstate"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/v2/agent"
|
||||
"github.com/coder/coder/v2/agent/agenttest"
|
||||
"github.com/coder/coder/v2/cli/clitest"
|
||||
"github.com/coder/coder/v2/coderd/coderdtest"
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/dbfake"
|
||||
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/codersdk/agentsdk"
|
||||
"github.com/coder/coder/v2/tailnet"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
)
|
||||
|
||||
|
@ -37,7 +44,14 @@ func TestSupportBundle(t *testing.T) {
|
|||
}).WithAgent().Do()
|
||||
ws, err := client.Workspace(ctx, r.Workspace.ID)
|
||||
require.NoError(t, err)
|
||||
agt := ws.LatestBuild.Resources[0].Agents[0]
|
||||
tempDir := t.TempDir()
|
||||
logPath := filepath.Join(tempDir, "coder-agent.log")
|
||||
require.NoError(t, os.WriteFile(logPath, []byte("hello from the agent"), 0o600))
|
||||
agt := agenttest.New(t, client.URL, r.AgentToken, func(o *agent.Options) {
|
||||
o.LogDir = tempDir
|
||||
})
|
||||
defer agt.Close()
|
||||
coderdtest.NewWorkspaceAgentWaiter(t, client, r.Workspace.ID).Wait()
|
||||
|
||||
// Insert a provisioner job log
|
||||
_, err = db.InsertProvisionerJobLogs(ctx, database.InsertProvisionerJobLogsParams{
|
||||
|
@ -51,7 +65,7 @@ func TestSupportBundle(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
// Insert an agent log
|
||||
_, err = db.InsertWorkspaceAgentLogs(ctx, database.InsertWorkspaceAgentLogsParams{
|
||||
AgentID: agt.ID,
|
||||
AgentID: ws.LatestBuild.Resources[0].Agents[0].ID,
|
||||
CreatedAt: dbtime.Now(),
|
||||
Output: []string{"started up"},
|
||||
Level: []database.LogLevel{database.LogLevelInfo},
|
||||
|
@ -141,10 +155,10 @@ func assertBundleContents(t *testing.T, path string) {
|
|||
case "network/tailnet_debug.html":
|
||||
bs := readBytesFromZip(t, f)
|
||||
require.NotEmpty(t, bs, "tailnet debug should not be empty")
|
||||
case "network/netcheck_local.json", "network/netcheck_remote.json":
|
||||
// TODO: setup fake agent?
|
||||
bs := readBytesFromZip(t, f)
|
||||
require.NotEmpty(t, bs, "netcheck should not be empty")
|
||||
case "network/netcheck.json":
|
||||
var v codersdk.WorkspaceAgentConnectionInfo
|
||||
decodeJSONFromZip(t, f, &v)
|
||||
require.NotEmpty(t, v, "connection info should not be empty")
|
||||
case "workspace/workspace.json":
|
||||
var v codersdk.Workspace
|
||||
decodeJSONFromZip(t, f, &v)
|
||||
|
@ -152,11 +166,33 @@ func assertBundleContents(t *testing.T, path string) {
|
|||
case "workspace/build_logs.txt":
|
||||
bs := readBytesFromZip(t, f)
|
||||
require.Contains(t, string(bs), "provision done")
|
||||
case "workspace/agent.json":
|
||||
case "agent/agent.json":
|
||||
var v codersdk.WorkspaceAgent
|
||||
decodeJSONFromZip(t, f, &v)
|
||||
require.NotEmpty(t, v, "agent should not be empty")
|
||||
case "workspace/agent_startup_logs.txt":
|
||||
case "agent/listening_ports.json":
|
||||
var v codersdk.WorkspaceAgentListeningPortsResponse
|
||||
decodeJSONFromZip(t, f, &v)
|
||||
require.NotEmpty(t, v, "agent listening ports should not be empty")
|
||||
case "agent/logs.txt":
|
||||
bs := readBytesFromZip(t, f)
|
||||
require.NotEmpty(t, bs, "logs should not be empty")
|
||||
case "agent/magicsock.html":
|
||||
bs := readBytesFromZip(t, f)
|
||||
require.NotEmpty(t, bs, "agent magicsock should not be empty")
|
||||
case "agent/manifest.json":
|
||||
var v agentsdk.Manifest
|
||||
decodeJSONFromZip(t, f, &v)
|
||||
require.NotEmpty(t, v, "agent manifest should not be empty")
|
||||
case "agent/peer_diagnostics.json":
|
||||
var v *tailnet.PeerDiagnostics
|
||||
decodeJSONFromZip(t, f, &v)
|
||||
require.NotEmpty(t, v, "peer diagnostics should not be empty")
|
||||
case "agent/ping_result.json":
|
||||
var v *ipnstate.PingResult
|
||||
decodeJSONFromZip(t, f, &v)
|
||||
require.NotEmpty(t, v, "ping result should not be empty")
|
||||
case "agent/startup_logs.txt":
|
||||
bs := readBytesFromZip(t, f)
|
||||
require.Contains(t, string(bs), "started up")
|
||||
case "workspace/template.json":
|
||||
|
@ -178,7 +214,7 @@ func assertBundleContents(t *testing.T, path string) {
|
|||
bs := readBytesFromZip(t, f)
|
||||
require.NotEmpty(t, bs, "logs should not be empty")
|
||||
default:
|
||||
require.Failf(t, "unexpected file in bundle: %q", f.Name)
|
||||
require.Failf(t, "unexpected file in bundle", f.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
package support
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
"golang.org/x/xerrors"
|
||||
"tailscale.com/ipn/ipnstate"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
|
@ -16,6 +19,8 @@ import (
|
|||
"cdr.dev/slog/sloggers/sloghuman"
|
||||
"github.com/coder/coder/v2/coderd/rbac"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/codersdk/agentsdk"
|
||||
"github.com/coder/coder/v2/tailnet"
|
||||
)
|
||||
|
||||
// Bundle is a set of information discovered about a deployment.
|
||||
|
@ -25,6 +30,7 @@ type Bundle struct {
|
|||
Deployment Deployment `json:"deployment"`
|
||||
Network Network `json:"network"`
|
||||
Workspace Workspace `json:"workspace"`
|
||||
Agent Agent `json:"agent"`
|
||||
Logs []string `json:"logs"`
|
||||
}
|
||||
|
||||
|
@ -38,8 +44,7 @@ type Deployment struct {
|
|||
type Network struct {
|
||||
CoordinatorDebug string `json:"coordinator_debug"`
|
||||
TailnetDebug string `json:"tailnet_debug"`
|
||||
NetcheckLocal *codersdk.WorkspaceAgentConnectionInfo `json:"netcheck_local"`
|
||||
NetcheckRemote *codersdk.WorkspaceAgentConnectionInfo `json:"netcheck_remote"`
|
||||
Netcheck *codersdk.WorkspaceAgentConnectionInfo `json:"netcheck"`
|
||||
}
|
||||
|
||||
type Workspace struct {
|
||||
|
@ -49,8 +54,17 @@ type Workspace struct {
|
|||
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"`
|
||||
}
|
||||
|
||||
type Agent struct {
|
||||
Agent *codersdk.WorkspaceAgent `json:"agent"`
|
||||
ListeningPorts *codersdk.WorkspaceAgentListeningPortsResponse `json:"listening_ports"`
|
||||
Logs []byte `json:"logs"`
|
||||
MagicsockHTML []byte `json:"magicsock_html"`
|
||||
Manifest *agentsdk.Manifest `json:"manifest"`
|
||||
PeerDiagnostics *tailnet.PeerDiagnostics `json:"peer_diagnostics"`
|
||||
PingResult *ipnstate.PingResult `json:"ping_result"`
|
||||
StartupLogs []codersdk.WorkspaceAgentLog `json:"startup_logs"`
|
||||
}
|
||||
|
||||
// Deps is a set of dependencies for discovering information
|
||||
|
@ -159,7 +173,7 @@ func NetworkInfo(ctx context.Context, client *codersdk.Client, log slog.Logger,
|
|||
if err != nil {
|
||||
return xerrors.Errorf("fetch agent conn info: %w", err)
|
||||
}
|
||||
n.NetcheckLocal = &connInfo
|
||||
n.Netcheck = &connInfo
|
||||
return nil
|
||||
})
|
||||
|
||||
|
@ -170,7 +184,7 @@ func NetworkInfo(ctx context.Context, client *codersdk.Client, log slog.Logger,
|
|||
return n
|
||||
}
|
||||
|
||||
func WorkspaceInfo(ctx context.Context, client *codersdk.Client, log slog.Logger, workspaceID, agentID uuid.UUID) Workspace {
|
||||
func WorkspaceInfo(ctx context.Context, client *codersdk.Client, log slog.Logger, workspaceID uuid.UUID) Workspace {
|
||||
var (
|
||||
w Workspace
|
||||
eg errgroup.Group
|
||||
|
@ -181,10 +195,6 @@ func WorkspaceInfo(ctx context.Context, client *codersdk.Client, log slog.Logger
|
|||
return w
|
||||
}
|
||||
|
||||
if agentID == uuid.Nil {
|
||||
log.Error(ctx, "no agent id specified")
|
||||
}
|
||||
|
||||
// dependency, cannot fetch concurrently
|
||||
ws, err := client.Workspace(ctx, workspaceID)
|
||||
if err != nil {
|
||||
|
@ -198,15 +208,6 @@ func WorkspaceInfo(ctx context.Context, client *codersdk.Client, log slog.Logger
|
|||
}
|
||||
w.Workspace = ws
|
||||
|
||||
eg.Go(func() error {
|
||||
agt, err := client.WorkspaceAgent(ctx, agentID)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("fetch workspace agent: %w", err)
|
||||
}
|
||||
w.Agent = agt
|
||||
return nil
|
||||
})
|
||||
|
||||
eg.Go(func() error {
|
||||
buildLogCh, closer, err := client.WorkspaceBuildLogsAfter(ctx, ws.LatestBuild.ID, 0)
|
||||
if err != nil {
|
||||
|
@ -221,24 +222,6 @@ func WorkspaceInfo(ctx context.Context, client *codersdk.Client, log slog.Logger
|
|||
return nil
|
||||
})
|
||||
|
||||
eg.Go(func() error {
|
||||
if len(w.Workspace.LatestBuild.Resources) == 0 {
|
||||
log.Warn(ctx, "workspace build has no resources")
|
||||
return nil
|
||||
}
|
||||
agentLogCh, closer, err := client.WorkspaceAgentLogsAfter(ctx, agentID, 0, false)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("fetch agent startup logs: %w", err)
|
||||
}
|
||||
defer closer.Close()
|
||||
var logs []codersdk.WorkspaceAgentLog
|
||||
for logChunk := range agentLogCh {
|
||||
logs = append(w.AgentStartupLogs, logChunk...)
|
||||
}
|
||||
w.AgentStartupLogs = logs
|
||||
return nil
|
||||
})
|
||||
|
||||
eg.Go(func() error {
|
||||
if w.Workspace.TemplateActiveVersionID == uuid.Nil {
|
||||
return xerrors.Errorf("workspace has nil template active version id")
|
||||
|
@ -296,6 +279,119 @@ func WorkspaceInfo(ctx context.Context, client *codersdk.Client, log slog.Logger
|
|||
return w
|
||||
}
|
||||
|
||||
func AgentInfo(ctx context.Context, client *codersdk.Client, log slog.Logger, agentID uuid.UUID) Agent {
|
||||
var (
|
||||
a Agent
|
||||
eg errgroup.Group
|
||||
)
|
||||
|
||||
if agentID == uuid.Nil {
|
||||
log.Error(ctx, "no agent id specified")
|
||||
return a
|
||||
}
|
||||
|
||||
eg.Go(func() error {
|
||||
agt, err := client.WorkspaceAgent(ctx, agentID)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("fetch workspace agent: %w", err)
|
||||
}
|
||||
a.Agent = &agt
|
||||
return nil
|
||||
})
|
||||
|
||||
eg.Go(func() error {
|
||||
agentLogCh, closer, err := client.WorkspaceAgentLogsAfter(ctx, agentID, 0, false)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("fetch agent startup logs: %w", err)
|
||||
}
|
||||
defer closer.Close()
|
||||
var logs []codersdk.WorkspaceAgentLog
|
||||
for logChunk := range agentLogCh {
|
||||
logs = append(logs, logChunk...)
|
||||
}
|
||||
a.StartupLogs = logs
|
||||
return nil
|
||||
})
|
||||
|
||||
conn, err := client.DialWorkspaceAgent(ctx, agentID, &codersdk.DialWorkspaceAgentOptions{
|
||||
Logger: log.Named("dial-agent"),
|
||||
BlockEndpoints: false,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error(ctx, "dial agent", slog.Error(err))
|
||||
} else {
|
||||
defer func() {
|
||||
if err := conn.Close(); err != nil {
|
||||
log.Error(ctx, "failed to close agent connection", slog.Error(err))
|
||||
}
|
||||
<-conn.Closed()
|
||||
}()
|
||||
if !conn.AwaitReachable(ctx) {
|
||||
log.Error(ctx, "timed out waiting for agent")
|
||||
} else {
|
||||
eg.Go(func() error {
|
||||
_, _, pingRes, err := conn.Ping(ctx)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("ping agent: %w", err)
|
||||
}
|
||||
a.PingResult = pingRes
|
||||
return nil
|
||||
})
|
||||
|
||||
eg.Go(func() error {
|
||||
pds := conn.GetPeerDiagnostics()
|
||||
a.PeerDiagnostics = &pds
|
||||
return nil
|
||||
})
|
||||
|
||||
eg.Go(func() error {
|
||||
msBytes, err := conn.DebugMagicsock(ctx)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("get agent magicsock page: %w", err)
|
||||
}
|
||||
a.MagicsockHTML = msBytes
|
||||
return nil
|
||||
})
|
||||
|
||||
eg.Go(func() error {
|
||||
manifestRes, err := conn.DebugManifest(ctx)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("fetch manifest: %w", err)
|
||||
}
|
||||
if err := json.NewDecoder(bytes.NewReader(manifestRes)).Decode(&a.Manifest); err != nil {
|
||||
return xerrors.Errorf("decode agent manifest: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
eg.Go(func() error {
|
||||
logBytes, err := conn.DebugLogs(ctx)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("fetch coder agent logs: %w", err)
|
||||
}
|
||||
a.Logs = logBytes
|
||||
return nil
|
||||
})
|
||||
|
||||
eg.Go(func() error {
|
||||
lps, err := conn.ListeningPorts(ctx)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("get listening ports: %w", err)
|
||||
}
|
||||
a.ListeningPorts = &lps
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if err := eg.Wait(); err != nil {
|
||||
log.Error(ctx, "fetch agent information", slog.Error(err))
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// Run generates a support bundle with the given dependencies.
|
||||
func Run(ctx context.Context, d *Deps) (*Bundle, error) {
|
||||
var b Bundle
|
||||
|
@ -337,7 +433,7 @@ func Run(ctx context.Context, d *Deps) (*Bundle, error) {
|
|||
return nil
|
||||
})
|
||||
eg.Go(func() error {
|
||||
wi := WorkspaceInfo(ctx, d.Client, d.Log, d.WorkspaceID, d.AgentID)
|
||||
wi := WorkspaceInfo(ctx, d.Client, d.Log, d.WorkspaceID)
|
||||
b.Workspace = wi
|
||||
return nil
|
||||
})
|
||||
|
@ -346,6 +442,11 @@ func Run(ctx context.Context, d *Deps) (*Bundle, error) {
|
|||
b.Network = ni
|
||||
return nil
|
||||
})
|
||||
eg.Go(func() error {
|
||||
ai := AgentInfo(ctx, d.Client, d.Log, d.AgentID)
|
||||
b.Agent = ai
|
||||
return nil
|
||||
})
|
||||
|
||||
_ = eg.Wait()
|
||||
|
||||
|
|
|
@ -5,15 +5,20 @@ import (
|
|||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/goleak"
|
||||
|
||||
"cdr.dev/slog"
|
||||
"cdr.dev/slog/sloggers/sloghuman"
|
||||
"cdr.dev/slog/sloggers/slogtest"
|
||||
"github.com/coder/coder/v2/agent"
|
||||
"github.com/coder/coder/v2/agent/agenttest"
|
||||
"github.com/coder/coder/v2/coderd/coderdtest"
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/dbfake"
|
||||
|
@ -24,6 +29,10 @@ import (
|
|||
"github.com/coder/coder/v2/testutil"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
goleak.VerifyTestMain(m)
|
||||
}
|
||||
|
||||
func TestRun(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -31,7 +40,7 @@ func TestRun(t *testing.T) {
|
|||
t.Parallel()
|
||||
cfg := coderdtest.DeploymentValues(t)
|
||||
cfg.Experiments = []string{"foo"}
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
client, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{
|
||||
DeploymentValues: cfg,
|
||||
Logger: ptr.Ref(slog.Make(sloghuman.Sink(io.Discard))),
|
||||
|
@ -46,33 +55,38 @@ func TestRun(t *testing.T) {
|
|||
AgentID: agt.ID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, bun)
|
||||
require.NotEmpty(t, bun.Deployment.BuildInfo)
|
||||
require.NotEmpty(t, bun.Deployment.Config)
|
||||
require.NotEmpty(t, bun.Deployment.Config.Options)
|
||||
assertNotNilNotEmpty(t, bun, "bundle should be present")
|
||||
assertNotNilNotEmpty(t, bun.Deployment.BuildInfo, "deployment build info should be present")
|
||||
assertNotNilNotEmpty(t, bun.Deployment.Config, "deployment config should be present")
|
||||
assertNotNilNotEmpty(t, bun.Deployment.Config.Options, "deployment config should be present")
|
||||
assertSanitizedDeploymentConfig(t, bun.Deployment.Config)
|
||||
require.NotEmpty(t, bun.Deployment.HealthReport)
|
||||
require.NotEmpty(t, bun.Deployment.Experiments)
|
||||
require.NotEmpty(t, bun.Network.CoordinatorDebug)
|
||||
require.NotEmpty(t, bun.Network.TailnetDebug)
|
||||
require.NotNil(t, bun.Network.NetcheckLocal)
|
||||
require.NotNil(t, bun.Workspace.Workspace)
|
||||
assertNotNilNotEmpty(t, bun.Deployment.HealthReport, "deployment health report should be present")
|
||||
assertNotNilNotEmpty(t, bun.Deployment.Experiments, "deployment experiments should be present")
|
||||
assertNotNilNotEmpty(t, bun.Network.CoordinatorDebug, "network coordinator debug should be present")
|
||||
assertNotNilNotEmpty(t, bun.Network.TailnetDebug, "network tailnet debug should be present")
|
||||
assertNotNilNotEmpty(t, bun.Network.Netcheck, "network netcheck should be present")
|
||||
assertNotNilNotEmpty(t, bun.Workspace.Workspace, "workspace should be present")
|
||||
assertSanitizedWorkspace(t, bun.Workspace.Workspace)
|
||||
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)
|
||||
assertNotNilNotEmpty(t, bun.Workspace.BuildLogs, "workspace build logs should be present")
|
||||
assertNotNilNotEmpty(t, bun.Workspace.Template, "workspace template should be present")
|
||||
assertNotNilNotEmpty(t, bun.Workspace.TemplateVersion, "workspace template version should be present")
|
||||
assertNotNilNotEmpty(t, bun.Workspace.TemplateFileBase64, "workspace template file should be present")
|
||||
require.NotNil(t, bun.Workspace.Parameters, "workspace parameters should be present")
|
||||
assertNotNilNotEmpty(t, bun.Agent.Agent, "agent should be present")
|
||||
assertNotNilNotEmpty(t, bun.Agent.ListeningPorts, "agent listening ports should be present")
|
||||
assertNotNilNotEmpty(t, bun.Agent.Logs, "agent logs should be present")
|
||||
assertNotNilNotEmpty(t, bun.Agent.MagicsockHTML, "agent magicsock should be present")
|
||||
assertNotNilNotEmpty(t, bun.Agent.PeerDiagnostics, "agent peer diagnostics should be present")
|
||||
assertNotNilNotEmpty(t, bun.Agent.PingResult, "agent ping result should be present")
|
||||
assertNotNilNotEmpty(t, bun.Agent.StartupLogs, "agent startup logs should be present")
|
||||
assertNotNilNotEmpty(t, bun.Logs, "bundle logs should be present")
|
||||
})
|
||||
|
||||
t.Run("OK_NoAgent", func(t *testing.T) {
|
||||
t.Run("OK_NoWorkspace", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
cfg := coderdtest.DeploymentValues(t)
|
||||
cfg.Experiments = []string{"foo"}
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
client := coderdtest.New(t, &coderdtest.Options{
|
||||
DeploymentValues: cfg,
|
||||
Logger: ptr.Ref(slog.Make(sloghuman.Sink(io.Discard))),
|
||||
|
@ -83,23 +97,24 @@ func TestRun(t *testing.T) {
|
|||
Log: slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Named("bundle").Leveled(slog.LevelDebug),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, bun)
|
||||
require.NotEmpty(t, bun.Deployment.BuildInfo)
|
||||
require.NotEmpty(t, bun.Deployment.Config)
|
||||
require.NotEmpty(t, bun.Deployment.Config.Options)
|
||||
assertNotNilNotEmpty(t, bun, "bundle should be present")
|
||||
assertNotNilNotEmpty(t, bun.Deployment.BuildInfo, "deployment build info should be present")
|
||||
assertNotNilNotEmpty(t, bun.Deployment.Config, "deployment config should be present")
|
||||
assertNotNilNotEmpty(t, bun.Deployment.Config.Options, "deployment config should be present")
|
||||
assertSanitizedDeploymentConfig(t, bun.Deployment.Config)
|
||||
require.NotEmpty(t, bun.Deployment.HealthReport)
|
||||
require.NotEmpty(t, bun.Deployment.Experiments)
|
||||
require.NotEmpty(t, bun.Network.CoordinatorDebug)
|
||||
require.NotEmpty(t, bun.Network.TailnetDebug)
|
||||
require.NotNil(t, bun.Workspace)
|
||||
assertSanitizedWorkspace(t, bun.Workspace.Workspace)
|
||||
require.NotEmpty(t, bun.Logs)
|
||||
assertNotNilNotEmpty(t, bun.Deployment.HealthReport, "deployment health report should be present")
|
||||
assertNotNilNotEmpty(t, bun.Deployment.Experiments, "deployment experiments should be present")
|
||||
assertNotNilNotEmpty(t, bun.Network.CoordinatorDebug, "network coordinator debug should be present")
|
||||
assertNotNilNotEmpty(t, bun.Network.TailnetDebug, "network tailnet debug should be present")
|
||||
assert.Empty(t, bun.Network.Netcheck, "did not expect netcheck to be present")
|
||||
assert.Empty(t, bun.Workspace.Workspace, "did not expect workspace to be present")
|
||||
assert.Empty(t, bun.Agent, "did not expect agent to be present")
|
||||
assertNotNilNotEmpty(t, bun.Logs, "bundle logs should be present")
|
||||
})
|
||||
|
||||
t.Run("NoAuth", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
client := coderdtest.New(t, &coderdtest.Options{
|
||||
Logger: ptr.Ref(slog.Make(sloghuman.Sink(io.Discard))),
|
||||
})
|
||||
|
@ -117,7 +132,7 @@ func TestRun(t *testing.T) {
|
|||
|
||||
t.Run("MissingPrivilege", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
client := coderdtest.New(t, &coderdtest.Options{
|
||||
Logger: ptr.Ref(slog.Make(sloghuman.Sink(io.Discard))),
|
||||
})
|
||||
|
@ -200,5 +215,21 @@ func setupWorkspaceAndAgent(ctx context.Context, t *testing.T, client *codersdk.
|
|||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
tempDir := t.TempDir()
|
||||
logPath := filepath.Join(tempDir, "coder-agent.log")
|
||||
require.NoError(t, os.WriteFile(logPath, []byte("hello from the agent"), 0o600))
|
||||
_ = agenttest.New(t, client.URL, wbr.AgentToken, func(o *agent.Options) {
|
||||
o.LogDir = tempDir
|
||||
})
|
||||
coderdtest.NewWorkspaceAgentWaiter(t, client, wbr.Workspace.ID).Wait()
|
||||
|
||||
return ws, agt
|
||||
}
|
||||
|
||||
func assertNotNilNotEmpty[T any](t *testing.T, v T, msg string) {
|
||||
t.Helper()
|
||||
|
||||
if assert.NotNil(t, v, msg+" but was nil") {
|
||||
assert.NotEmpty(t, v, msg+" but was empty")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue