diff --git a/pkg/containerd/Makefile b/pkg/containerd/Makefile index 8538dd615..838de4d29 100644 --- a/pkg/containerd/Makefile +++ b/pkg/containerd/Makefile @@ -1,5 +1,5 @@ IMAGE=containerd NETWORK=1 -DEPS=$(wildcard etc/init.d/*) etc/containerd/config.toml +DEPS=$(wildcard etc/init.d/*) $(wildcard cmd/service/*.go) etc/containerd/config.toml include ../package.mk diff --git a/pkg/containerd/cmd/service/main.go b/pkg/containerd/cmd/service/main.go index 39ccf91f1..801c71078 100644 --- a/pkg/containerd/cmd/service/main.go +++ b/pkg/containerd/cmd/service/main.go @@ -29,6 +29,7 @@ func main() { flag.Usage = func() { fmt.Printf("USAGE: %s [options] COMMAND\n\n", filepath.Base(os.Args[0])) fmt.Printf("Commands:\n") + fmt.Printf(" system-init Prepare the system at start of day\n") fmt.Printf(" start Start a service\n") fmt.Printf(" help Print this message\n") fmt.Printf("\n") @@ -67,6 +68,8 @@ func main() { switch args[0] { case "start": start(args[1:]) + case "system-init": + systemInit(args[1:]) default: fmt.Printf("%q is not valid command.\n\n", args[0]) flag.Usage() diff --git a/pkg/containerd/cmd/service/system_init.go b/pkg/containerd/cmd/service/system_init.go new file mode 100644 index 000000000..b3aa8b840 --- /dev/null +++ b/pkg/containerd/cmd/service/system_init.go @@ -0,0 +1,101 @@ +package main + +import ( + "context" + "flag" + "fmt" + "os" + "path/filepath" + "syscall" + + log "github.com/Sirupsen/logrus" + "github.com/containerd/containerd" + "github.com/containerd/containerd/namespaces" + "github.com/pkg/errors" +) + +func cleanupTask(ctx context.Context, ctr containerd.Container) error { + task, err := ctr.Task(ctx, nil) + if err != nil { + if err == containerd.ErrNoRunningTask { + return nil + } + return errors.Wrap(err, "getting task") + } + + deleteErr := make(chan error, 1) + deleteCtx, deleteCancel := context.WithCancel(ctx) + defer deleteCancel() + + go func(ctx context.Context, ch chan error) { + _, err := task.Delete(ctx) + if err != nil { + ch <- errors.Wrap(err, "killing task") + } + ch <- nil + }(deleteCtx, deleteErr) + + sig := syscall.SIGKILL + if err := task.Kill(ctx, sig); err != nil && err != containerd.ErrProcessExited { + return errors.Wrapf(err, "killing task with %q", sig) + } + + select { + case err := <-deleteErr: + return err + case <-ctx.Done(): + return ctx.Err() + } +} + +func systemInit(args []string) { + invoked := filepath.Base(os.Args[0]) + flags := flag.NewFlagSet("system-init", flag.ExitOnError) + flags.Usage = func() { + fmt.Printf("USAGE: %s system-init\n\n", invoked) + fmt.Printf("Options:\n") + flags.PrintDefaults() + } + + sock := flags.String("sock", "/run/containerd/containerd.sock", "Path to containerd socket") + + if err := flags.Parse(args); err != nil { + log.Fatal("Unable to parse args") + } + args = flags.Args() + + if len(args) != 0 { + fmt.Println("Unexpected argument") + flags.Usage() + os.Exit(1) + } + + client, err := containerd.New(*sock) + if err != nil { + log.WithError(err).Fatal("creating containerd client") + } + + ctx := namespaces.WithNamespace(context.Background(), "default") + + ctrs, err := client.Containers(ctx) + if err != nil { + log.WithError(err).Fatal("listing containers") + } + + // None of the errors in this loop are fatal since we want to + // keep trying. + for _, ctr := range ctrs { + log.Infof("Cleaning up stale service: %q", ctr.ID()) + log := log.WithFields(log.Fields{ + "service": ctr.ID(), + }) + + if err := cleanupTask(ctx, ctr); err != nil { + log.WithError(err).Error("cleaning up task") + } + + if err := ctr.Delete(ctx); err != nil { + log.WithError(err).Error("deleting container") + } + } +} diff --git a/pkg/containerd/etc/init.d/020-containerd b/pkg/containerd/etc/init.d/020-containerd index c3d5d6339..af34e26da 100755 --- a/pkg/containerd/etc/init.d/020-containerd +++ b/pkg/containerd/etc/init.d/020-containerd @@ -12,6 +12,8 @@ done # start service containers +service system-init + if [ -d /containers/services ] then for f in $(find /containers/services -mindepth 1 -maxdepth 1 | sort)