Files
linuxkit/pkg/containerd/cmd/service/start.go
Justin Cormack cb2ca4ef66 Switch runc and containerd startup to be entirely Go
At present they use a small shared function called "prepare"
that does the read-write remounts, that I will switch to doing overlay
mounts soon.

Signed-off-by: Justin Cormack <justin.cormack@docker.com>
2017-07-26 16:32:09 +01:00

129 lines
3.1 KiB
Go

package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
log "github.com/Sirupsen/logrus"
"github.com/containerd/containerd"
"github.com/containerd/containerd/namespaces"
specs "github.com/opencontainers/runtime-spec/specs-go"
)
func startCmd(args []string) {
invoked := filepath.Base(os.Args[0])
flags := flag.NewFlagSet("start", flag.ExitOnError)
flags.Usage = func() {
fmt.Printf("USAGE: %s start [service]\n\n", invoked)
fmt.Printf("Options:\n")
flags.PrintDefaults()
}
sock := flags.String("sock", defaultSocket, "Path to containerd socket")
path := flags.String("path", defaultPath, "Path to service configs")
dumpSpec := flags.String("dump-spec", "", "Dump container spec to file before start")
if err := flags.Parse(args); err != nil {
log.Fatal("Unable to parse args")
}
args = flags.Args()
if len(args) != 1 {
fmt.Println("Please specify the service")
flags.Usage()
os.Exit(1)
}
service := args[0]
log.Infof("Starting service: %q", service)
log := log.WithFields(log.Fields{
"service": service,
})
id, pid, msg, err := start(service, *sock, *path, *dumpSpec)
if err != nil {
log.WithError(err).Fatal(msg)
}
log.Debugf("Started %s pid %d", id, pid)
}
func start(service, sock, path, dumpSpec string) (string, uint32, string, error) {
rootfs := filepath.Join(path, service, "rootfs")
if err := prepare(filepath.Join(path, service)); err != nil {
return "", 0, "preparing rootfs", err
}
client, err := containerd.New(sock)
if err != nil {
return "", 0, "creating containerd client", err
}
ctx := namespaces.WithNamespace(context.Background(), "default")
var spec *specs.Spec
specf, err := os.Open(filepath.Join(path, service, "config.json"))
if err != nil {
return "", 0, "failed to read service spec", err
}
if err := json.NewDecoder(specf).Decode(&spec); err != nil {
return "", 0, "failed to parse service spec", err
}
spec.Root.Path = rootfs
if dumpSpec != "" {
d, err := os.Create(dumpSpec)
if err != nil {
return "", 0, "failed to open file for spec dump", err
}
enc := json.NewEncoder(d)
enc.SetIndent("", " ")
if err := enc.Encode(&spec); err != nil {
return "", 0, "failed to write spec dump", err
}
}
ctr, err := client.NewContainer(ctx, service, containerd.WithSpec(spec))
if err != nil {
return "", 0, "failed to create container", err
}
io := func() (*containerd.IO, error) {
logfile := filepath.Join("/var/log", service+".log")
// We just need this to exist.
if err := ioutil.WriteFile(logfile, []byte{}, 0600); err != nil {
// if we cannot write to log, discard output
logfile = "/dev/null"
}
return &containerd.IO{
Stdin: "/dev/null",
Stdout: logfile,
Stderr: logfile,
Terminal: false,
}, nil
}
task, err := ctr.NewTask(ctx, io)
if err != nil {
// Don't bother to destroy the container here.
return "", 0, "failed to create task", err
}
if err := task.Start(ctx); err != nil {
// Don't destroy the container here so it can be inspected for debugging.
return "", 0, "failed to start task", err
}
return ctr.ID(), task.Pid(), "", nil
}