Skip to content

Commit 09f00c0

Browse files
authored
chore: shutdown provisioner should stop waiting on client (coder#13118)
* chore: shutdown provisioner should stop waiting on client * chore: add unit test that replicates failed client conn
1 parent 94a3e3a commit 09f00c0

File tree

3 files changed

+41
-0
lines changed

3 files changed

+41
-0
lines changed

enterprise/cli/provisionerdaemons.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,12 @@ func (r *RootCmd) provisionerDaemonStart() *serpent.Command {
239239
return xerrors.Errorf("shutdown: %w", err)
240240
}
241241

242+
// Shutdown does not call close. Must call it manually.
243+
err = srv.Close()
244+
if err != nil {
245+
return xerrors.Errorf("close server: %w", err)
246+
}
247+
242248
cancel()
243249
if xerrors.Is(exitErr, context.Canceled) {
244250
return nil

provisionerd/provisionerd.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,9 @@ func (p *Server) client() (proto.DRPCProvisionerDaemonClient, bool) {
236236
select {
237237
case <-p.closeContext.Done():
238238
return nil, false
239+
case <-p.shuttingDownCh:
240+
// Shutting down should return a nil client and unblock
241+
return nil, false
239242
case client := <-p.clientCh:
240243
return client, true
241244
}

provisionerd/provisionerd_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,38 @@ func TestProvisionerd(t *testing.T) {
597597
assert.True(t, didFail.Load(), "should fail the job")
598598
})
599599

600+
// Simulates when there is no coderd to connect to. So the client connection
601+
// will never be established.
602+
t.Run("ShutdownNoCoderd", func(t *testing.T) {
603+
t.Parallel()
604+
done := make(chan struct{})
605+
t.Cleanup(func() {
606+
close(done)
607+
})
608+
609+
connectAttemptedClose := sync.Once{}
610+
connectAttempted := make(chan struct{})
611+
server := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
612+
// This is the dial out to Coderd, which in this unit test will always fail.
613+
connectAttemptedClose.Do(func() { close(connectAttempted) })
614+
return nil, fmt.Errorf("client connection always fails")
615+
}, provisionerd.LocalProvisioners{
616+
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{}),
617+
})
618+
619+
// Wait for at least 1 attempt to connect to ensure the connect go routine
620+
// is running.
621+
require.Condition(t, closedWithin(connectAttempted, testutil.WaitShort))
622+
623+
// The test is ensuring this Shutdown call does not block indefinitely.
624+
// If it does, the context will return with an error, and the test will
625+
// fail.
626+
shutdownCtx := testutil.Context(t, testutil.WaitShort)
627+
err := server.Shutdown(shutdownCtx, true)
628+
require.NoError(t, err, "shutdown did not unblock. Failed to close the server gracefully.")
629+
require.NoError(t, server.Close())
630+
})
631+
600632
t.Run("Shutdown", func(t *testing.T) {
601633
t.Parallel()
602634
done := make(chan struct{})

0 commit comments

Comments
 (0)