Skip to content

Commit 658f5b2

Browse files
committed
WIP agent tests
1 parent 592b8a5 commit 658f5b2

File tree

4 files changed

+99
-58
lines changed

4 files changed

+99
-58
lines changed

agent/agent.go

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -207,11 +207,15 @@ func (a *agent) runLoop(ctx context.Context) {
207207
}
208208
}
209209

210-
func (a *agent) collectMetadata(ctx context.Context, md agentsdk.Metadata) agentsdk.MetadataResult {
210+
func (*agent) collectMetadata(ctx context.Context, md agentsdk.Metadata) agentsdk.MetadataResult {
211+
ctx, cancel := context.WithDeadline(ctx, time.Now().Add(md.Interval))
212+
defer cancel()
213+
211214
collectedAt := time.Now()
212215

213216
var out bytes.Buffer
214217

218+
//nolint:gosec
215219
cmd := exec.CommandContext(ctx, md.Cmd[0], md.Cmd[1:]...)
216220
cmd.Stdout = &out
217221
cmd.Stderr = &out
@@ -237,14 +241,15 @@ func (a *agent) collectMetadata(ctx context.Context, md agentsdk.Metadata) agent
237241
}
238242

239243
func (a *agent) reportMetadataLoop(ctx context.Context) {
240-
// In production, the minimum report interval is one second.
241-
ticker := time.Second
244+
// In production, the minimum report interval is one second because
245+
// `coder_agent.metadata` accepts `interval` in integer seconds.
246+
baseInterval := time.Second
242247
if flag.Lookup("test.v") != nil {
243-
ticker = time.Millisecond * 100
248+
baseInterval = time.Millisecond * 100
244249
}
245250

246251
var (
247-
baseTicker = time.NewTicker(ticker)
252+
baseTicker = time.NewTicker(baseInterval)
248253
lastCollectedAts = make(map[string]time.Time)
249254
metadataResults = make(chan agentsdk.MetadataResult, 16)
250255
)
@@ -279,12 +284,19 @@ func (a *agent) reportMetadataLoop(ctx context.Context) {
279284
// to synchronize the results and avoid messy mutex logic.
280285
for _, md := range manifest.Metadata {
281286
collectedAt, ok := lastCollectedAts[md.Key]
282-
if ok && collectedAt.Add(md.Interval).After(time.Now()) {
283-
continue
287+
if ok {
288+
// If the interval is zero, we assume the user just wants
289+
// a single collection at startup, not a spinning loop.
290+
if md.Interval == 0 {
291+
continue
292+
}
293+
if collectedAt.Add(md.Interval).After(time.Now()) {
294+
continue
295+
}
284296
}
285297
if len(metadataResults) > cap(metadataResults)/2 {
286298
// If we're backpressured on sending back results, we risk
287-
// runaway goroutine growth.
299+
// runaway goroutine growth or overloading coderd.
288300
continue
289301
}
290302
go func(md agentsdk.Metadata) {
@@ -396,10 +408,10 @@ func (a *agent) run(ctx context.Context) error {
396408
return xerrors.Errorf("update workspace agent version: %w", err)
397409
}
398410

399-
oldMetadata := a.manifest.Swap(&manifest)
411+
oldManifest := a.manifest.Swap(&manifest)
400412

401413
// The startup script should only execute on the first run!
402-
if oldMetadata == nil {
414+
if oldManifest == nil {
403415
a.setLifecycle(ctx, codersdk.WorkspaceAgentLifecycleStarting)
404416

405417
// Perform overrides early so that Git auth can work even if users

agent/agent_test.go

Lines changed: 77 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -776,31 +776,81 @@ func TestAgent_StartupScript(t *testing.T) {
776776
func TestAgent_Metadata(t *testing.T) {
777777
t.Parallel()
778778

779-
//nolint:dogsled
780-
_, client, _, _, _ := setupAgent(t, agentsdk.Manifest{
781-
Metadata: []agentsdk.Metadata{
782-
{
783-
Key: "greeting",
784-
Interval: time.Millisecond * 100,
785-
Cmd: []string{"echo", "hello"},
779+
t.Run("Basic", func(t *testing.T) {
780+
t.Parallel()
781+
//nolint:dogsled
782+
dir := t.TempDir()
783+
greetingPath := filepath.Join(dir, "greeting")
784+
_, client, _, _, _ := setupAgent(t, agentsdk.Manifest{
785+
Metadata: []agentsdk.Metadata{
786+
{
787+
Key: "greeting",
788+
Interval: time.Millisecond * 100,
789+
Cmd: []string{"sh", "-c", "echo hello | tee " + greetingPath},
790+
},
791+
{
792+
Key: "bad",
793+
Interval: time.Millisecond * 100,
794+
Cmd: []string{"sh", "-c", "exit 1"},
795+
},
786796
},
787-
{
788-
Key: "bad",
789-
Interval: time.Millisecond * 100,
790-
Cmd: []string{"sh", "-c", "exit 1"},
797+
}, 0)
798+
799+
start := time.Now()
800+
801+
require.Eventually(t, func() bool {
802+
return len(client.getMetadata()) == 2
803+
}, testutil.WaitShort, testutil.IntervalMedium)
804+
805+
for start := time.Now(); time.Since(start) < testutil.WaitShort; time.Sleep(testutil.IntervalMedium) {
806+
md := client.getMetadata()
807+
if len(md) != 2 {
808+
panic("unexpected number of metadata entries")
809+
}
810+
811+
require.Equal(t, "hello", md["greeting"].Value)
812+
require.Equal(t, "exit status 1", md["bad"].Error)
813+
814+
greetingByt, err := os.ReadFile(greetingPath)
815+
require.NoError(t, err)
816+
817+
var (
818+
numGreetings = bytes.Count(greetingByt, []byte("hello"))
819+
idealNumGreetings = time.Since(start) / (time.Millisecond * 100)
820+
)
821+
822+
}
823+
})
824+
825+
t.Run("CollectOnce", func(t *testing.T) {
826+
t.Parallel()
827+
//nolint:dogsled
828+
_, client, _, _, _ := setupAgent(t, agentsdk.Manifest{
829+
Metadata: []agentsdk.Metadata{
830+
{
831+
Key: "greeting",
832+
Interval: 0,
833+
Cmd: []string{"echo", "-n", "hello"},
834+
},
791835
},
792-
},
793-
}, 0)
836+
}, 0)
794837

795-
var gotMd agentsdk.PostMetadataRequest
796-
require.Eventually(t, func() bool {
797-
gotMd = client.getMetadata()
798-
return len(gotMd) == 2
799-
}, testutil.WaitShort, testutil.IntervalMedium)
838+
var gotMd map[string]agentsdk.PostMetadataRequest
839+
require.Eventually(t, func() bool {
840+
gotMd = client.getMetadata()
841+
return len(gotMd) == 1
842+
}, testutil.WaitShort, testutil.IntervalMedium)
843+
844+
collectedAt := gotMd["greeting"].CollectedAt
800845

801-
require.Equal(t, "hello", gotMd["greeting"].Value)
802-
require.Empty(t, gotMd["bad"].Value)
803-
require.Equal(t, "exit status 1", gotMd["bad"].Error)
846+
require.Never(t, func() bool {
847+
gotMd = client.getMetadata()
848+
if len(gotMd) != 1 {
849+
panic("unexpected number of metadata")
850+
}
851+
return !gotMd["greeting"].CollectedAt.Equal(collectedAt)
852+
}, testutil.WaitShort, testutil.IntervalMedium)
853+
})
804854
}
805855

806856
func TestAgent_Lifecycle(t *testing.T) {
@@ -1523,7 +1573,7 @@ type client struct {
15231573
t *testing.T
15241574
agentID uuid.UUID
15251575
manifest agentsdk.Manifest
1526-
metadata agentsdk.PostMetadataRequest
1576+
metadata map[string]agentsdk.PostMetadataRequest
15271577
statsChan chan *agentsdk.Stats
15281578
coordinator tailnet.Coordinator
15291579
lastWorkspaceAgent func()
@@ -1608,7 +1658,7 @@ func (c *client) getStartup() agentsdk.PostStartupRequest {
16081658
return c.startup
16091659
}
16101660

1611-
func (c *client) getMetadata() agentsdk.PostMetadataRequest {
1661+
func (c *client) getMetadata() map[string]agentsdk.PostMetadataRequest {
16121662
c.mu.Lock()
16131663
defer c.mu.Unlock()
16141664
return maps.Clone(c.metadata)
@@ -1617,7 +1667,10 @@ func (c *client) getMetadata() agentsdk.PostMetadataRequest {
16171667
func (c *client) PostMetadata(_ context.Context, req agentsdk.PostMetadataRequest) error {
16181668
c.mu.Lock()
16191669
defer c.mu.Unlock()
1620-
c.metadata = req
1670+
if c.metadata == nil {
1671+
c.metadata = make(map[string]agentsdk.PostMetadataRequest)
1672+
}
1673+
c.metadata[req.Key] = req
16211674
return nil
16221675
}
16231676

go.mod

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,17 +183,13 @@ require (
183183
github.com/dustin/go-humanize v1.0.1 // indirect
184184
github.com/google/flatbuffers v23.1.21+incompatible // indirect
185185
github.com/h2non/filetype v1.1.3 // indirect
186-
github.com/hashicorp/go-plugin v1.4.4 // indirect
187-
github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c // indirect
188-
github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 // indirect
189186
github.com/json-iterator/go v1.1.12 // indirect
190187
github.com/juju/errors v1.0.0 // indirect
191188
github.com/mattn/go-localereader v0.0.1 // indirect
192189
github.com/mattn/go-sqlite3 v1.14.15 // indirect
193190
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
194191
github.com/modern-go/reflect2 v1.0.2 // indirect
195192
github.com/muesli/cancelreader v0.2.2 // indirect
196-
github.com/oklog/run v1.0.0 // indirect
197193
)
198194

199195
require (

go.sum

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -376,22 +376,8 @@ github.com/coder/retry v1.3.1-0.20230210155434-e90a2e1e091d h1:09JG37IgTB6n3ouX9
376376
github.com/coder/retry v1.3.1-0.20230210155434-e90a2e1e091d/go.mod h1:r+1J5i/989wt6CUeNSuvFKKA9hHuKKPMxdzDbTuvwwk=
377377
github.com/coder/ssh v0.0.0-20220811105153-fcea99919338 h1:tN5GKFT68YLVzJoA8AHuiMNJ0qlhoD3pGN3JY9gxSko=
378378
github.com/coder/ssh v0.0.0-20220811105153-fcea99919338/go.mod h1:ZSS+CUoKHDrqVakTfTWUlKSr9MtMFkC4UvtQKD7O914=
379-
github.com/coder/tailscale v1.1.1-0.20230307022319-1e5e724a3949 h1:8WfMfRTDaEpnmhCJWfFQ7JHz19GyP+EgFgLGu5ngdek=
380-
github.com/coder/tailscale v1.1.1-0.20230307022319-1e5e724a3949/go.mod h1:jpg+77g19FpXL43U1VoIqoSg1K/Vh5CVxycGldQ8KhA=
381-
github.com/coder/tailscale v1.1.1-0.20230313184322-307d4b9ef4e1 h1:kOh+1rBcbahdodSiNBioO4g47m3Ef8CagNEX8U7/RyY=
382-
github.com/coder/tailscale v1.1.1-0.20230313184322-307d4b9ef4e1/go.mod h1:jpg+77g19FpXL43U1VoIqoSg1K/Vh5CVxycGldQ8KhA=
383-
github.com/coder/tailscale v1.1.1-0.20230313192237-8d691a009b20 h1:FFPv3xLsAT+n8chkePxB/VwqFwn4NL8GV8Lk97efUP0=
384-
github.com/coder/tailscale v1.1.1-0.20230313192237-8d691a009b20/go.mod h1:jpg+77g19FpXL43U1VoIqoSg1K/Vh5CVxycGldQ8KhA=
385-
github.com/coder/tailscale v1.1.1-0.20230313194848-e281f646c460 h1:WYnHzxQ1DsgDYyyS6i8tgdpjiwS1LrXJjwXcOVkU/3g=
386-
github.com/coder/tailscale v1.1.1-0.20230313194848-e281f646c460/go.mod h1:jpg+77g19FpXL43U1VoIqoSg1K/Vh5CVxycGldQ8KhA=
387-
github.com/coder/tailscale v1.1.1-0.20230313195101-c6534848756b h1:3xWbBCpQ+CqVP2Myh1YhCw8cJbMi6mjkDl4/x6YkiUw=
388-
github.com/coder/tailscale v1.1.1-0.20230313195101-c6534848756b/go.mod h1:jpg+77g19FpXL43U1VoIqoSg1K/Vh5CVxycGldQ8KhA=
389-
github.com/coder/tailscale v1.1.1-0.20230314021518-0cda9db154be h1:b0ZTDPpV/fAdkuS/V59cawnq+5vcXzkvXPMG/eZcRx0=
390-
github.com/coder/tailscale v1.1.1-0.20230314021518-0cda9db154be/go.mod h1:jpg+77g19FpXL43U1VoIqoSg1K/Vh5CVxycGldQ8KhA=
391379
github.com/coder/tailscale v1.1.1-0.20230314023417-d9efcc0ac972 h1:193YGsJz8hc4yxqAclE36paKl+9CQ6KGLgdleIguCVE=
392380
github.com/coder/tailscale v1.1.1-0.20230314023417-d9efcc0ac972/go.mod h1:jpg+77g19FpXL43U1VoIqoSg1K/Vh5CVxycGldQ8KhA=
393-
github.com/coder/terraform-provider-coder v0.6.15 h1:Llvh4RwxSQ/goy7ToTOeHf3tdEz+79qbyOh61hNnJs0=
394-
github.com/coder/terraform-provider-coder v0.6.15/go.mod h1:UIfU3bYNeSzJJvHyJ30tEKjD6Z9utloI+HUM/7n94CY=
395381
github.com/coder/terraform-provider-coder v0.6.17 h1:YvjM5nQx5RO+gXsYIv++CkiWCuJueQdJaPrsjnkZ4XQ=
396382
github.com/coder/terraform-provider-coder v0.6.17/go.mod h1:UIfU3bYNeSzJJvHyJ30tEKjD6Z9utloI+HUM/7n94CY=
397383
github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE=
@@ -1039,7 +1025,6 @@ github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+
10391025
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
10401026
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
10411027
github.com/hashicorp/go-plugin v1.4.4 h1:NVdrSdFRt3SkZtNckJ6tog7gbpRrcbOjQi/rgF7JYWQ=
1042-
github.com/hashicorp/go-plugin v1.4.4/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s=
10431028
github.com/hashicorp/go-reap v0.0.0-20170704170343-bf58d8a43e7b h1:3GrpnZQBxcMj1gCXQLelfjCT1D5MPGTuGMKHVzSIH6A=
10441029
github.com/hashicorp/go-reap v0.0.0-20170704170343-bf58d8a43e7b/go.mod h1:qIFzeFcJU3OIFk/7JreWXcUjFmcCaeHTH9KoNyHYVCs=
10451030
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
@@ -1050,7 +1035,6 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
10501035
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
10511036
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
10521037
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
1053-
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
10541038
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
10551039
github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
10561040
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
@@ -1087,9 +1071,7 @@ github.com/hashicorp/terraform-plugin-log v0.7.0/go.mod h1:p4R1jWBXRTvL4odmEkFfD
10871071
github.com/hashicorp/terraform-plugin-sdk/v2 v2.20.0 h1:+KxZULPsbjpAVoP0WNj/8aVW6EqpcX5JcUcQ5wl7Da4=
10881072
github.com/hashicorp/terraform-plugin-sdk/v2 v2.20.0/go.mod h1:DwGJG3KNxIPluVk6hexvDfYR/MS/eKGpiztJoT3Bbbw=
10891073
github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c h1:D8aRO6+mTqHfLsK/BC3j5OAoogv1WLRWzY1AaTo3rBg=
1090-
github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c/go.mod h1:Wn3Na71knbXc1G8Lh+yu/dQWWJeFQEpDeJMtWMtlmNI=
10911074
github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 h1:HKLsbzeOsfXmKNpr3GiT18XAblV0BjCbzL8KQAMZGa0=
1092-
github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg=
10931075
github.com/hashicorp/yamux v0.0.0-20220718163420-dd80a7ee44ce h1:7FO+LmZwiG/eDsBWo50ZeqV5PoH0gwiM1mxFajXAkas=
10941076
github.com/hashicorp/yamux v0.0.0-20220718163420-dd80a7ee44ce/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
10951077
github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3 h1:aSVUgRRRtOrZOC1fYmY9gV0e9z/Iu+xNVSASWjsuyGU=
@@ -1513,7 +1495,6 @@ github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH
15131495
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
15141496
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
15151497
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
1516-
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
15171498
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
15181499
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
15191500
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
@@ -2182,7 +2163,6 @@ golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLL
21822163
golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
21832164
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
21842165
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
2185-
golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
21862166
golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
21872167
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
21882168
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=

0 commit comments

Comments
 (0)