Skip to content

Commit 67e23ff

Browse files
author
Timo Reimann
authored
Implement graceful shutdown (#238)
This change implements graceful shutdown so that a proper driver termination leads to an exit code of zero. Specifically, we handle signals, and let the HTTP and gRPC servers finish requests gracefully. We also remove the Stop() function in favor a context-based approach.
1 parent 06b7443 commit 67e23ff

File tree

4 files changed

+47
-17
lines changed

4 files changed

+47
-17
lines changed

CHANGELOG.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
[[GH-241]](https://github.com/digitalocean/csi-digitalocean/pull/241)
77
* Check all snapshots for existence
88
[[GH-240]](https://github.com/digitalocean/csi-digitalocean/pull/240)
9-
* Update sidecars
9+
* Implement graceful shutdown
1010
[[GH-238]](https://github.com/digitalocean/csi-digitalocean/pull/238)
11+
* Update sidecars
12+
[[GH-236]](https://github.com/digitalocean/csi-digitalocean/pull/236)
1113
* Support checkLimit for multiple pages
1214
[[GH-235]](https://github.com/digitalocean/csi-digitalocean/pull/235)
1315
* Return error when fetching the snapshot fails

cmd/do-csi-plugin/main.go

+14-1
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,13 @@ limitations under the License.
1717
package main
1818

1919
import (
20+
"context"
2021
"flag"
2122
"fmt"
2223
"log"
2324
"os"
25+
"os/signal"
26+
"syscall"
2427

2528
"github.com/digitalocean/csi-digitalocean/driver"
2629
)
@@ -47,7 +50,17 @@ func main() {
4750
log.Fatalln(err)
4851
}
4952

50-
if err := drv.Run(); err != nil {
53+
ctx, cancel := context.WithCancel(context.Background())
54+
defer cancel()
55+
56+
c := make(chan os.Signal, 1)
57+
signal.Notify(c, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
58+
go func() {
59+
<-c
60+
cancel()
61+
}()
62+
63+
if err := drv.Run(ctx); err != nil {
5164
log.Fatalln(err)
5265
}
5366
}

driver/driver.go

+18-12
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ func NewDriver(ep, token, url, doTag, driverName, address string) (*Driver, erro
164164
}
165165

166166
// Run starts the CSI plugin by communication over the given endpoint
167-
func (d *Driver) Run() error {
167+
func (d *Driver) Run(ctx context.Context) error {
168168
u, err := url.Parse(d.endpoint)
169169
if err != nil {
170170
return fmt.Errorf("unable to parse address: %q", err)
@@ -247,25 +247,31 @@ func (d *Driver) Run() error {
247247

248248
var eg errgroup.Group
249249
eg.Go(func() error {
250+
<-ctx.Done()
251+
return d.httpSrv.Shutdown(context.Background())
252+
})
253+
eg.Go(func() error {
254+
go func() {
255+
<-ctx.Done()
256+
d.log.Info("server stopped")
257+
d.readyMu.Lock()
258+
d.ready = false
259+
d.readyMu.Unlock()
260+
d.srv.GracefulStop()
261+
}()
250262
return d.srv.Serve(grpcListener)
251263
})
252264
eg.Go(func() error {
253-
return d.httpSrv.Serve(httpListener)
265+
err := d.httpSrv.Serve(httpListener)
266+
if err == http.ErrServerClosed {
267+
return nil
268+
}
269+
return err
254270
})
255271

256272
return eg.Wait()
257273
}
258274

259-
// Stop stops the plugin
260-
func (d *Driver) Stop() {
261-
d.readyMu.Lock()
262-
d.ready = false
263-
d.readyMu.Unlock()
264-
265-
d.log.Info("server stopped")
266-
d.srv.Stop()
267-
}
268-
269275
// When building any packages that import version, pass the build/install cmd
270276
// ldflags like so:
271277
// go build -ldflags "-X github.com/digitalocean/csi-digitalocean/driver.version=0.0.1"

driver/driver_test.go

+12-3
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/digitalocean/godo"
3030
"github.com/kubernetes-csi/csi-test/pkg/sanity"
3131
"github.com/sirupsen/logrus"
32+
"golang.org/x/sync/errgroup"
3233
)
3334

3435
func init() {
@@ -81,17 +82,25 @@ func TestDriverSuite(t *testing.T) {
8182
account: &fakeAccountDriver{},
8283
tags: &fakeTagsDriver{},
8384
}
84-
defer driver.Stop()
8585

86-
go driver.Run()
86+
ctx, cancel := context.WithCancel(context.Background())
87+
88+
var eg errgroup.Group
89+
eg.Go(func() error {
90+
return driver.Run(ctx)
91+
})
8792

8893
cfg := &sanity.Config{
8994
TargetPath: os.TempDir() + "/csi-target",
9095
StagingPath: os.TempDir() + "/csi-staging",
9196
Address: endpoint,
9297
}
93-
9498
sanity.Test(t, cfg)
99+
100+
cancel()
101+
if err := eg.Wait(); err != nil {
102+
t.Errorf("driver run failed: %s", err)
103+
}
95104
}
96105

97106
type fakeAccountDriver struct {

0 commit comments

Comments
 (0)