diff --git a/cmd/containerd/command/main.go b/cmd/containerd/command/main.go index 3db453303..de5070423 100644 --- a/cmd/containerd/command/main.go +++ b/cmd/containerd/command/main.go @@ -227,6 +227,10 @@ can be used and modified as necessary as a custom configuration.` } serve(ctx, l, server.ServeGRPC) + if err := notifyReady(ctx); err != nil { + log.G(ctx).WithError(err).Warn("notify ready failed") + } + log.G(ctx).Infof("containerd successfully booted in %fs", time.Since(start).Seconds()) <-done return nil diff --git a/cmd/containerd/command/main_unix.go b/cmd/containerd/command/main_unix.go index c9081eeef..7bc9b1cf9 100644 --- a/cmd/containerd/command/main_unix.go +++ b/cmd/containerd/command/main_unix.go @@ -52,6 +52,10 @@ func handleSignals(ctx context.Context, signals chan os.Signal, serverC chan *se case unix.SIGPIPE: continue default: + if err := notifyStopping(ctx); err != nil { + log.G(ctx).WithError(err).Error("notify stopping failed") + } + if server == nil { close(done) return diff --git a/cmd/containerd/command/main_windows.go b/cmd/containerd/command/main_windows.go index 2f7cf7471..7e3430bb6 100644 --- a/cmd/containerd/command/main_windows.go +++ b/cmd/containerd/command/main_windows.go @@ -50,6 +50,11 @@ func handleSignals(ctx context.Context, signals chan os.Signal, serverC chan *se server = s case s := <-signals: log.G(ctx).WithField("signal", s).Debug("received signal") + + if err := notifyStopping(ctx); err != nil { + log.G(ctx).WithError(err).Error("notify stopping failed") + } + if server == nil { close(done) return diff --git a/cmd/containerd/command/notify_linux.go b/cmd/containerd/command/notify_linux.go new file mode 100644 index 000000000..eafc401b6 --- /dev/null +++ b/cmd/containerd/command/notify_linux.go @@ -0,0 +1,52 @@ +// +build linux + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package command + +import ( + "context" + "os" + + sd "github.com/coreos/go-systemd/v22/daemon" + + "github.com/containerd/containerd/log" +) + +// notifyReady notifies systemd that the daemon is ready to serve requests +func notifyReady(ctx context.Context) error { + return sdNotify(ctx, sd.SdNotifyReady) +} + +// notifyStopping notifies systemd that the daemon is about to be stopped +func notifyStopping(ctx context.Context) error { + return sdNotify(ctx, sd.SdNotifyStopping) +} + +func sdNotify(ctx context.Context, state string) error { + if os.Getenv("NOTIFY_SOCKET") != "" { + notified, err := sd.SdNotify(false, state) + log.G(ctx). + WithError(err). + WithField("notified", notified). + WithField("state", state). + Debug("sd notification") + return err + } + + return nil +} diff --git a/cmd/containerd/command/notify_unsupported.go b/cmd/containerd/command/notify_unsupported.go new file mode 100644 index 000000000..3a0513739 --- /dev/null +++ b/cmd/containerd/command/notify_unsupported.go @@ -0,0 +1,31 @@ +// +build !linux + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package command + +import ( + "context" +) + +func notifyReady(ctx context.Context) error { + return nil +} + +func notifyStopping(ctx context.Context) error { + return nil +} diff --git a/containerd.service b/containerd.service index 5452ea1f6..cae30ec7c 100644 --- a/containerd.service +++ b/containerd.service @@ -7,6 +7,7 @@ After=network.target local-fs.target ExecStartPre=-/sbin/modprobe overlay ExecStart=/usr/local/bin/containerd +Type=notify Delegate=yes KillMode=process Restart=always diff --git a/vendor/github.com/coreos/go-systemd/v22/daemon/sdnotify.go b/vendor/github.com/coreos/go-systemd/v22/daemon/sdnotify.go new file mode 100644 index 000000000..ba4ae31f1 --- /dev/null +++ b/vendor/github.com/coreos/go-systemd/v22/daemon/sdnotify.go @@ -0,0 +1,84 @@ +// Copyright 2014 Docker, Inc. +// Copyright 2015-2018 CoreOS, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Package daemon provides a Go implementation of the sd_notify protocol. +// It can be used to inform systemd of service start-up completion, watchdog +// events, and other status changes. +// +// https://www.freedesktop.org/software/systemd/man/sd_notify.html#Description +package daemon + +import ( + "net" + "os" +) + +const ( + // SdNotifyReady tells the service manager that service startup is finished + // or the service finished loading its configuration. + SdNotifyReady = "READY=1" + + // SdNotifyStopping tells the service manager that the service is beginning + // its shutdown. + SdNotifyStopping = "STOPPING=1" + + // SdNotifyReloading tells the service manager that this service is + // reloading its configuration. Note that you must call SdNotifyReady when + // it completed reloading. + SdNotifyReloading = "RELOADING=1" + + // SdNotifyWatchdog tells the service manager to update the watchdog + // timestamp for the service. + SdNotifyWatchdog = "WATCHDOG=1" +) + +// SdNotify sends a message to the init daemon. It is common to ignore the error. +// If `unsetEnvironment` is true, the environment variable `NOTIFY_SOCKET` +// will be unconditionally unset. +// +// It returns one of the following: +// (false, nil) - notification not supported (i.e. NOTIFY_SOCKET is unset) +// (false, err) - notification supported, but failure happened (e.g. error connecting to NOTIFY_SOCKET or while sending data) +// (true, nil) - notification supported, data has been sent +func SdNotify(unsetEnvironment bool, state string) (bool, error) { + socketAddr := &net.UnixAddr{ + Name: os.Getenv("NOTIFY_SOCKET"), + Net: "unixgram", + } + + // NOTIFY_SOCKET not set + if socketAddr.Name == "" { + return false, nil + } + + if unsetEnvironment { + if err := os.Unsetenv("NOTIFY_SOCKET"); err != nil { + return false, err + } + } + + conn, err := net.DialUnix(socketAddr.Net, nil, socketAddr) + // Error connecting to NOTIFY_SOCKET + if err != nil { + return false, err + } + defer conn.Close() + + if _, err = conn.Write([]byte(state)); err != nil { + return false, err + } + return true, nil +} diff --git a/vendor/github.com/coreos/go-systemd/v22/daemon/watchdog.go b/vendor/github.com/coreos/go-systemd/v22/daemon/watchdog.go new file mode 100644 index 000000000..7a0e0d3a5 --- /dev/null +++ b/vendor/github.com/coreos/go-systemd/v22/daemon/watchdog.go @@ -0,0 +1,73 @@ +// Copyright 2016 CoreOS, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package daemon + +import ( + "fmt" + "os" + "strconv" + "time" +) + +// SdWatchdogEnabled returns watchdog information for a service. +// Processes should call daemon.SdNotify(false, daemon.SdNotifyWatchdog) every +// time / 2. +// If `unsetEnvironment` is true, the environment variables `WATCHDOG_USEC` and +// `WATCHDOG_PID` will be unconditionally unset. +// +// It returns one of the following: +// (0, nil) - watchdog isn't enabled or we aren't the watched PID. +// (0, err) - an error happened (e.g. error converting time). +// (time, nil) - watchdog is enabled and we can send ping. +// time is delay before inactive service will be killed. +func SdWatchdogEnabled(unsetEnvironment bool) (time.Duration, error) { + wusec := os.Getenv("WATCHDOG_USEC") + wpid := os.Getenv("WATCHDOG_PID") + if unsetEnvironment { + wusecErr := os.Unsetenv("WATCHDOG_USEC") + wpidErr := os.Unsetenv("WATCHDOG_PID") + if wusecErr != nil { + return 0, wusecErr + } + if wpidErr != nil { + return 0, wpidErr + } + } + + if wusec == "" { + return 0, nil + } + s, err := strconv.Atoi(wusec) + if err != nil { + return 0, fmt.Errorf("error converting WATCHDOG_USEC: %s", err) + } + if s <= 0 { + return 0, fmt.Errorf("error WATCHDOG_USEC must be a positive number") + } + interval := time.Duration(s) * time.Microsecond + + if wpid == "" { + return interval, nil + } + p, err := strconv.Atoi(wpid) + if err != nil { + return 0, fmt.Errorf("error converting WATCHDOG_PID: %s", err) + } + if os.Getpid() != p { + return 0, nil + } + + return interval, nil +}