diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 1a10766e..a44f6a49 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,6 +1,6 @@ { "ImportPath": "github.com/hyperhq/runv", - "GoVersion": "go1.7", + "GoVersion": "go1.8", "GodepVersion": "v79", "Packages": [ "./..." @@ -26,11 +26,6 @@ "Comment": "scotty_09012012-31-gdfaa4d4", "Rev": "dfaa4d491586af21e1ce06028df3926389a99654" }, - { - "ImportPath": "github.com/urfave/cli", - "Comment": "v1.19.1-36-gd70f47e", - "Rev": "d70f47eeca3afd795160003bc6e28b001d60c67c" - }, { "ImportPath": "github.com/docker/docker/integration-cli/checker", "Comment": "docs-v1.12.0-rc4-2016-07-15-5449-g65068ea", @@ -85,23 +80,27 @@ }, { "ImportPath": "github.com/golang/protobuf/proto", - "Rev": "c9c7427a2a70d2eb3bafa0ab2dc163e45f143317" + "Rev": "748d386b5c1ea99658fd69fe9f03991ce86a90c1" }, { "ImportPath": "github.com/golang/protobuf/ptypes", - "Rev": "c9c7427a2a70d2eb3bafa0ab2dc163e45f143317" + "Rev": "748d386b5c1ea99658fd69fe9f03991ce86a90c1" }, { "ImportPath": "github.com/golang/protobuf/ptypes/any", - "Rev": "c9c7427a2a70d2eb3bafa0ab2dc163e45f143317" + "Rev": "748d386b5c1ea99658fd69fe9f03991ce86a90c1" }, { "ImportPath": "github.com/golang/protobuf/ptypes/duration", - "Rev": "c9c7427a2a70d2eb3bafa0ab2dc163e45f143317" + "Rev": "748d386b5c1ea99658fd69fe9f03991ce86a90c1" + }, + { + "ImportPath": "github.com/golang/protobuf/ptypes/empty", + "Rev": "748d386b5c1ea99658fd69fe9f03991ce86a90c1" }, { "ImportPath": "github.com/golang/protobuf/ptypes/timestamp", - "Rev": "c9c7427a2a70d2eb3bafa0ab2dc163e45f143317" + "Rev": "748d386b5c1ea99658fd69fe9f03991ce86a90c1" }, { "ImportPath": "github.com/golang/snappy", @@ -148,6 +147,11 @@ "Comment": "v1.0-1-g362bfb3", "Rev": "362bfb3384d53ae4d5dd745983a4d70b6d23628c" }, + { + "ImportPath": "github.com/urfave/cli", + "Comment": "v1.19.1-36-gd70f47e", + "Rev": "d70f47eeca3afd795160003bc6e28b001d60c67c" + }, { "ImportPath": "github.com/vdemeester/shakers", "Comment": "v0.1.0", diff --git a/Makefile.am b/Makefile.am index b8544ee9..5d1bdda9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,7 +22,7 @@ install-exec-local: $(INSTALL_PROGRAM) runv $(bindir) build-runv: - go build -tags "static_build $(HYPER_BULD_TAGS)" -ldflags ${GOLDFLAGS} -o runv . + go build -tags "static_build $(HYPER_BULD_TAGS)" -ldflags ${GOLDFLAGS} -o runv ./cli/ test-integration: cd integration-test/test_data && make cd integration-test && go test -check.v -test.timeout=120m ${TESTFLAGS} . diff --git a/cli/container.go b/cli/container.go new file mode 100644 index 00000000..b75c1f4f --- /dev/null +++ b/cli/container.go @@ -0,0 +1,265 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "os/exec" + "path/filepath" + "syscall" + "time" + + "github.com/golang/glog" + "github.com/hyperhq/runv/api" + "github.com/hyperhq/runv/hypervisor" + "github.com/hyperhq/runv/lib/linuxsignal" + "github.com/opencontainers/runtime-spec/specs-go" +) + +func startContainer(vm *hypervisor.Vm, container string, spec *specs.Spec, state *specs.State) error { + err := vm.StartContainer(container) + if err != nil { + glog.V(1).Infof("Start Container fail: fail to start container with err: %#v\n", err) + return err + } + + err = syscall.Kill(state.Pid, syscall.SIGUSR1) + if err != nil { + glog.V(1).Infof("failed to notify the shim to work", err.Error()) + return err + } + + err = execPoststartHooks(spec, state) + if err != nil { + glog.V(1).Infof("execute Poststart hooks failed %s\n", err.Error()) + } + + return err +} + +func createContainer(options runvOptions, vm *hypervisor.Vm, container, bundle, stateRoot string, spec *specs.Spec) (shim *os.Process, err error) { + if err = setupContainerFs(vm, bundle, container, spec); err != nil { + return nil, err + } + defer func() { + if err != nil { + removeContainerFs(vm, container) + } + }() + + glog.V(3).Infof("vm.AddContainer()") + config := api.ContainerDescriptionFromOCF(container, spec) + r := vm.AddContainer(config) + if !r.IsSuccess() { + return nil, fmt.Errorf("add container %s failed: %s", container, r.Message()) + } + defer func() { + if err != nil { + vm.RemoveContainer(container) + } + }() + + // Prepare container state directory + stateDir := filepath.Join(stateRoot, container) + _, err = os.Stat(stateDir) + if err == nil { + glog.Errorf("Container %s exists\n", container) + return nil, fmt.Errorf("Container %s exists", container) + } + err = os.MkdirAll(stateDir, 0644) + if err != nil { + glog.V(1).Infof("%s\n", err.Error()) + return nil, err + } + defer func() { + if err != nil { + os.RemoveAll(stateDir) + } + }() + + // Create sandbox dir symbol link in container root dir + vmRootLinkPath := filepath.Join(stateDir, "sandbox") + vmRootPath := sandboxPath(vm) + if err = os.Symlink(vmRootPath, vmRootLinkPath); err != nil { + return nil, fmt.Errorf("failed to create symbol link %q: %v", vmRootLinkPath, err) + } + + // create shim and save the state + shim, err = createShim(options, container, "init") + if err != nil { + return nil, err + } + defer func() { + if err != nil { + shim.Kill() + } + }() + + state := &specs.State{ + Version: spec.Version, + ID: container, + Pid: shim.Pid, + Bundle: bundle, + } + glog.V(3).Infof("save state id %s, boundle %s", container, bundle) + if err = saveStateFile(filepath.Join(stateDir, "state.json"), state); err != nil { + return nil, err + } + + err = execPrestartHooks(spec, state) + if err != nil { + // cli refator todo stop container + glog.V(1).Infof("execute Prestart hooks failed, %s\n", err.Error()) + return nil, err + } + + // If runv is launched via docker/containerd, we start netlistener to watch/collect network changes. + // TODO: if runv is launched by cni compatible tools, the cni script can use `runv cni` cmdline to update the network. + // Create the listener process which will enters into the netns of the shim + options.withContainer = state + if err = startNsListener(options, vm); err != nil { + // cli refator todo stop container + glog.Errorf("start ns listener fail: %v", err) + return nil, err + } + + return shim, nil +} + +func deleteContainer(vm *hypervisor.Vm, root, container string, force bool, spec *specs.Spec, state *specs.State) error { + + // todo: check the container from vm.ContainerList() + // todo: check the process of state.Pid in case it is a new unrelated process + + // non-force killing can only be performed when at least one of the realProcess and shimProcess exited + exitedVM := vm.SignalProcess(container, "init", syscall.Signal(0)) != nil // todo: is this check reliable? + exitedHost := syscall.Kill(state.Pid, syscall.Signal(0)) != nil + if !exitedVM && !exitedHost && !force { + // don't perform deleting + return fmt.Errorf("the container %s is still alive, use -f to force kill it?", container) + } + + if !exitedVM { // force kill the real init process inside the vm + for i := 0; i < 100; i++ { + vm.SignalProcess(container, "init", linuxsignal.SIGKILL) + time.Sleep(100 * time.Millisecond) + if vm.SignalProcess(container, "init", syscall.Signal(0)) != nil { + break + } + } + } + + if !exitedHost { // force kill the shim process in the host + time.Sleep(200 * time.Millisecond) // the shim might be going to exit, wait it + for i := 0; i < 100; i++ { + syscall.Kill(state.Pid, syscall.SIGKILL) + time.Sleep(100 * time.Millisecond) + if syscall.Kill(state.Pid, syscall.Signal(0)) != nil { + break + } + } + } + + vm.RemoveContainer(container) + err := execPoststopHooks(spec, state) + if err != nil { + glog.V(1).Infof("execute Poststop hooks failed %s\n", err.Error()) + removeContainerFs(vm, container) + os.RemoveAll(filepath.Join(root, container)) + return err // return err of the hooks + } + + removeContainerFs(vm, container) + return os.RemoveAll(filepath.Join(root, container)) +} + +func addProcess(options runvOptions, vm *hypervisor.Vm, container, process string, spec *specs.Process) (shim *os.Process, err error) { + err = vm.AddProcess(&api.Process{ + Container: container, + Id: process, + Terminal: spec.Terminal, + Args: spec.Args, + Envs: spec.Env, + Workdir: spec.Cwd}, nil) + + if err != nil { + glog.V(1).Infof("add process to container failed: %v\n", err) + return nil, err + } + defer func() { + if err != nil { + vm.SignalProcess(container, process, linuxsignal.SIGKILL) + } + }() + + shim, err = createShim(options, container, process) + if err != nil { + return nil, err + } + defer func() { + if err != nil { + shim.Kill() + } + }() + + // cli refactor todo (for the purpose of 'runv ps` command) save to persist file. + + return shim, nil +} + +func execHook(hook specs.Hook, state *specs.State) error { + b, err := json.Marshal(state) + if err != nil { + return err + } + cmd := exec.Cmd{ + Path: hook.Path, + Args: hook.Args, + Env: hook.Env, + Stdin: bytes.NewReader(b), + } + return cmd.Run() +} + +func execPrestartHooks(rt *specs.Spec, state *specs.State) error { + if rt.Hooks == nil { + return nil + } + for _, hook := range rt.Hooks.Prestart { + err := execHook(hook, state) + if err != nil { + return err + } + } + + return nil +} + +func execPoststartHooks(rt *specs.Spec, state *specs.State) error { + if rt.Hooks == nil { + return nil + } + for _, hook := range rt.Hooks.Poststart { + err := execHook(hook, state) + if err != nil { + glog.V(1).Infof("exec Poststart hook %s failed %s", hook.Path, err.Error()) + } + } + + return nil +} + +func execPoststopHooks(rt *specs.Spec, state *specs.State) error { + if rt.Hooks == nil { + return nil + } + for _, hook := range rt.Hooks.Poststop { + err := execHook(hook, state) + if err != nil { + glog.V(1).Infof("exec Poststop hook %s failed %s", hook.Path, err.Error()) + } + } + + return nil +} diff --git a/cli/create.go b/cli/create.go new file mode 100644 index 00000000..dbb4707f --- /dev/null +++ b/cli/create.go @@ -0,0 +1,166 @@ +package main + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/hyperhq/runv/hypervisor" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/urfave/cli" +) + +var createCommand = cli.Command{ + Name: "create", + Usage: "create a container", + ArgsUsage: ` + +Where "" is your name for the instance of the container that you +are creating. The name you provide for the container instance must be unique on +your host.`, + Description: `The create command creates an instance of a container for a bundle. The bundle +is a directory with a specification file named "` + specConfig + `" and a root +filesystem. + +The specification file includes an args parameter. The args parameter is used +to specify command(s) that get run when the container is started. To change the +command(s) that get executed on start, edit the args parameter of the spec. See +"runv spec --help" for more explanation.`, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "bundle, b", + Value: getDefaultBundlePath(), + Usage: "path to the root of the bundle directory, defaults to the current directory", + }, + cli.StringFlag{ + Name: "console", + Usage: "specify the pty slave path for use with the container", + }, + cli.StringFlag{ + Name: "console-socket", + Usage: "specify the unix socket for sending the pty master back", + }, + cli.StringFlag{ + Name: "pid-file", + Usage: "specify the file to write the process id to", + }, + cli.BoolFlag{ + Name: "no-pivot", + Usage: "[ignore on runv] do not use pivot root to jail process inside rootfs. This should be used whenever the rootfs is on top of a ramdisk", + }, + }, + Before: func(context *cli.Context) error { + return cmdPrepare(context, true, true) + }, + Action: func(context *cli.Context) error { + if err := cmdCreateContainer(context, true); err != nil { + return cli.NewExitError(fmt.Sprintf("Run Container error: %v", err), -1) + } + return nil + }, +} + +func cmdCreateContainer(context *cli.Context, createOnly bool) error { + root := context.GlobalString("root") + bundle := context.String("bundle") + container := context.Args().First() + ocffile := filepath.Join(bundle, specConfig) + spec, err := loadSpec(ocffile) + if err != nil { + return fmt.Errorf("load config failed: %v", err) + } + if spec.Linux == nil { + return fmt.Errorf("it is not linux container config") + } + if os.Geteuid() != 0 { + return fmt.Errorf("runv should be run as root") + } + if container == "" { + return fmt.Errorf("no container id provided") + } + _, err = os.Stat(filepath.Join(root, container)) + if err == nil { + return fmt.Errorf("container %q exists", container) + } + if err = checkConsole(context, &spec.Process, createOnly); err != nil { + return err + } + + var sharedContainer string + if containerType, ok := spec.Annotations["ocid/container_type"]; ok { + if containerType == "container" { + sharedContainer = spec.Annotations["ocid/sandbox_name"] + } + } else { + for _, ns := range spec.Linux.Namespaces { + if ns.Path != "" { + if strings.Contains(ns.Path, "/") { + return fmt.Errorf("Runv doesn't support path to namespace file, it supports containers name as shared namespaces only") + } + if ns.Type == "mount" { + // TODO support it! + return fmt.Errorf("Runv doesn't support shared mount namespace currently") + } + sharedContainer = ns.Path + _, err = os.Stat(filepath.Join(root, sharedContainer, stateJSON)) + if err != nil { + return fmt.Errorf("The container %q is not existing or not ready", sharedContainer) + } + _, err = os.Stat(filepath.Join(root, sharedContainer, "namespace")) + if err != nil { + return fmt.Errorf("The container %q is not ready", sharedContainer) + } + } + } + } + + var scState *specs.State + var vm *hypervisor.Vm + var lockFile *os.File + if sharedContainer != "" { + scState, err = loadStateFile(root, sharedContainer) + if err != nil { + return err + } + vm, lockFile, err = getSandbox(filepath.Join(context.GlobalString("root"), sharedContainer, "sandbox")) + if err != nil { + return err + } + } else { + f, err := setupFactory(context, spec) + if err != nil { + return nil + } + vm, lockFile, err = createAndLockSandBox(f, spec, context.GlobalInt("default_cpus"), context.GlobalInt("default_memory")) + if err != nil { + return nil + } + } + defer putSandbox(vm, lockFile) + + options := runvOptions{Context: context, withContainer: scState} + _, err = createContainer(options, vm, container, bundle, root, spec) + if err != nil { + return fmt.Errorf("failed to create container: %v", err) + } + + return nil +} + +func checkConsole(context *cli.Context, p *specs.Process, createOnly bool) error { + if context.String("console") != "" && context.String("console-socket") != "" { + return fmt.Errorf("only one of --console & --console-socket can be specified") + } + detach := createOnly + if !createOnly { + detach = context.Bool("detach") + } + if (context.String("console") != "" || context.String("console-socket") != "") && !detach { + return fmt.Errorf("--console[-socket] should be used on detached mode") + } + if (context.String("console") != "" || context.String("console-socket") != "") && !p.Terminal { + return fmt.Errorf("--console[-socket] should be used on tty mode") + } + return nil +} diff --git a/cli/delete.go b/cli/delete.go new file mode 100644 index 00000000..2b9ae32f --- /dev/null +++ b/cli/delete.go @@ -0,0 +1,62 @@ +package main + +import ( + "os" + "path/filepath" + + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/urfave/cli" +) + +var deleteCommand = cli.Command{ + Name: "delete", + Usage: "delete any resources held by the container often used with detached container", + ArgsUsage: ` + +Where "" is the name for the instance of the container. + +EXAMPLE: +For example, if the container id is "ubuntu01" and runv list currently shows the +status of "ubuntu01" as "stopped" the following will delete resources held for +"ubuntu01" removing "ubuntu01" from the runv list of containers: + + # runv delete ubuntu01`, + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "force, f", + Usage: "Forcibly deletes the container if it is still running (uses SIGKILL)", + }, + }, + Before: func(context *cli.Context) error { + return cmdPrepare(context, true, true) + }, + Action: func(context *cli.Context) error { + force := context.Bool("force") + root := context.GlobalString("root") + container := context.Args().First() + + if container == "" { + return cli.NewExitError("Please specify container ID", -1) + } + if os.Geteuid() != 0 { + return cli.NewExitError("runv should be run as root", -1) + } + + state, spec, err := loadStateAndSpec(root, container) + if err != nil { + return cli.NewExitError(err, -1) + } + + return cmdDeleteContainer(context, container, force, spec, state) + }, +} + +func cmdDeleteContainer(context *cli.Context, container string, force bool, spec *specs.Spec, state *specs.State) error { + vm, lockFile, err := getSandbox(filepath.Join(context.GlobalString("root"), container, "sandbox")) + if err != nil { + return err + } + defer putSandbox(vm, lockFile) + + return deleteContainer(vm, context.GlobalString("root"), container, force, spec, state) +} diff --git a/exec.go b/cli/exec.go similarity index 57% rename from exec.go rename to cli/exec.go index a4f3ab48..92ee8d22 100644 --- a/exec.go +++ b/cli/exec.go @@ -1,18 +1,14 @@ package main import ( - "encoding/json" "fmt" "os" "path/filepath" "strconv" "strings" - "github.com/hyperhq/runv/containerd/api/grpc/types" - "github.com/hyperhq/runv/lib/term" "github.com/opencontainers/runtime-spec/specs-go" "github.com/urfave/cli" - netcontext "golang.org/x/net/context" ) var execCommand = cli.Command{ @@ -38,19 +34,19 @@ following will output a list of processes running in the container: }, cli.StringFlag{ Name: "cwd", - Usage: "[TODO] current working directory in the container", + Usage: "current working directory in the container", }, cli.StringSliceFlag{ Name: "env, e", - Usage: "[TODO] set environment variables", + Usage: "set environment variables", }, cli.BoolFlag{ Name: "tty, t", - Usage: "[TODO] allocate a pseudo-TTY", + Usage: "allocate a pseudo-TTY", }, cli.StringFlag{ Name: "user, u", - Usage: "[TODO] UID (format: [:])", + Usage: "UID (format: [:])", }, cli.StringFlag{ Name: "process, p", @@ -58,11 +54,11 @@ following will output a list of processes running in the container: }, cli.BoolFlag{ Name: "detach,d", - Usage: "[TODO] detach from the container's process", + Usage: "detach from the container's process", }, cli.StringFlag{ Name: "pid-file", - Usage: "[TODO] specify the file to write the process id to", + Usage: "specify the file to write the process id to", }, cli.StringFlag{ Name: "process-label", @@ -85,6 +81,9 @@ following will output a list of processes running in the container: Usage: "[ignore on runv] disable the use of the subreaper used to reap reparented processes", }, }, + Before: func(context *cli.Context) error { + return cmdPrepare(context, true, context.Bool("detach")) + }, Action: func(context *cli.Context) error { root := context.GlobalString("root") container := context.Args().First() @@ -96,21 +95,13 @@ following will output a list of processes running in the container: return cli.NewExitError("runv should be run as root", -1) } - // get bundle path from state - path := filepath.Join(root, container, stateJson) - f, err := os.Open(path) + cState, cSpec, err := loadStateAndSpec(root, container) if err != nil { - return cli.NewExitError(fmt.Sprintf("open JSON configuration file failed: %v", err), -1) - } - defer f.Close() - var s *specs.State - if err := json.NewDecoder(f).Decode(&s); err != nil { - return cli.NewExitError(fmt.Sprintf("parse JSON configuration file failed: %v", err), -1) + return cli.NewExitError(err, -1) } - bundle := s.Bundle // get process - config, err := getProcess(context, bundle) + config, err := getProcess(context, cSpec) if err != nil { return cli.NewExitError(fmt.Sprintf("get process config failed %v", err), -1) } @@ -118,7 +109,7 @@ following will output a list of processes running in the container: return cli.NewExitError(err.Error(), -1) } - code, err := runProcess(context, container, config) + code, err := runProcess(context, container, cState, config) if code != 0 { return cli.NewExitError(err, code) } else if err != nil { @@ -128,32 +119,11 @@ following will output a list of processes running in the container: }, } -// loadProcessConfig loads the process configuration from the provided path. -func loadProcessConfig(path string) (*specs.Process, error) { - f, err := os.Open(path) - if err != nil { - if os.IsNotExist(err) { - return nil, fmt.Errorf("JSON configuration file for %s not found", path) - } - return nil, err - } - defer f.Close() - var s *specs.Process - if err := json.NewDecoder(f).Decode(&s); err != nil { - return nil, err - } - return s, nil -} - -func getProcess(context *cli.Context, bundle string) (*specs.Process, error) { +func getProcess(context *cli.Context, spec *specs.Spec) (*specs.Process, error) { if path := context.String("process"); path != "" { return loadProcessConfig(path) } // process via cli flags - spec, err := loadSpec(filepath.Join(bundle, specConfig)) - if err != nil { - return nil, err - } p := spec.Process p.Args = context.Args()[1:] if p.Cwd == "" { @@ -190,56 +160,25 @@ func getProcess(context *cli.Context, bundle string) (*specs.Process, error) { return &p, nil } -func runProcess(context *cli.Context, container string, config *specs.Process) (int, error) { +func runProcess(context *cli.Context, container string, cState *specs.State, config *specs.Process) (int, error) { pid := os.Getpid() process := fmt.Sprintf("p-%x", pid+0xabcdef) // uniq name - c, err := getClient(filepath.Join(context.GlobalString("root"), container, "namespace/namespaced.sock")) - if err != nil { - return -1, fmt.Errorf("failed to get client: %v", err) - } - evChan := containerEvents(c, container) - if !context.Bool("detach") && config.Terminal { - s, err := term.SetRawTerminal(os.Stdin.Fd()) - if err != nil { - return -1, fmt.Errorf("failed to set raw terminal: %v", err) - } - defer term.RestoreTerminal(os.Stdin.Fd(), s) - monitorTtySize(c, container, process) + options := runvOptions{Context: context, withContainer: cState} + + vm, lockFile, err := getSandbox(filepath.Join(context.GlobalString("root"), container, "sandbox")) + if err != nil { + return -1, err } - err = ociCreate(context, container, process, func(stdin, stdout, stderr string) error { - p := &types.AddProcessRequest{ - Id: container, - Pid: process, - Args: config.Args, - Cwd: config.Cwd, - Terminal: config.Terminal, - Env: config.Env, - User: &types.User{ - Uid: config.User.UID, - Gid: config.User.GID, - }, - Stdin: stdin, - Stdout: stdout, - Stderr: stderr, - } - if _, err := c.AddProcess(netcontext.Background(), p); err != nil { - return err - } - return nil - }) + shim, err := addProcess(options, vm, container, process, config) + putSandbox(vm, lockFile) if err != nil { return -1, err } if !context.Bool("detach") { - for e := range evChan { - if e.Type == "exit" && e.Pid == process { - return int(e.Status), nil - } - } - return -1, fmt.Errorf("unknown error") + return osProcessWait(shim) } return 0, nil } diff --git a/cli/filesystem.go b/cli/filesystem.go new file mode 100644 index 00000000..8f4da21f --- /dev/null +++ b/cli/filesystem.go @@ -0,0 +1,190 @@ +package main + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/docker/docker/pkg/mount" + "github.com/docker/docker/pkg/symlink" + "github.com/golang/glog" + "github.com/hyperhq/runv/hypervisor" + "github.com/hyperhq/runv/lib/utils" + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +func mountToRootfs(m *specs.Mount, rootfs, mountLabel string) error { + // TODO: we don't use mountLabel here because it looks like mountLabel is + // only significant when SELinux is enabled. + var ( + dest = m.Destination + ) + if !strings.HasPrefix(dest, rootfs) { + dest = filepath.Join(rootfs, dest) + } + + switch m.Type { + case "proc", "sysfs", "mqueue", "tmpfs", "cgroup", "devpts": + glog.V(3).Infof("Skip mount point %q of type %s", m.Destination, m.Type) + return nil + case "bind": + stat, err := os.Stat(m.Source) + if err != nil { + // error out if the source of a bind mount does not exist as we will be + // unable to bind anything to it. + return err + } + // ensure that the destination of the bind mount is resolved of symlinks at mount time because + // any previous mounts can invalidate the next mount's destination. + // this can happen when a user specifies mounts within other mounts to cause breakouts or other + // evil stuff to try to escape the container's rootfs. + if dest, err = symlink.FollowSymlinkInScope(filepath.Join(rootfs, m.Destination), rootfs); err != nil { + return err + } + if err := checkMountDestination(rootfs, dest); err != nil { + return err + } + // update the mount with the correct dest after symlinks are resolved. + m.Destination = dest + if err := createIfNotExists(dest, stat.IsDir()); err != nil { + return err + } + if err := mount.Mount(m.Source, dest, m.Type, strings.Join(m.Options, ",")); err != nil { + return err + } + default: + if err := os.MkdirAll(dest, 0755); err != nil { + return err + } + return mount.Mount(m.Source, dest, m.Type, strings.Join(m.Options, ",")) + } + return nil +} + +// checkMountDestination checks to ensure that the mount destination is not over the top of /proc. +// dest is required to be an abs path and have any symlinks resolved before calling this function. +func checkMountDestination(rootfs, dest string) error { + invalidDestinations := []string{ + "/proc", + } + // White list, it should be sub directories of invalid destinations + validDestinations := []string{ + // These entries can be bind mounted by files emulated by fuse, + // so commands like top, free displays stats in container. + "/proc/cpuinfo", + "/proc/diskstats", + "/proc/meminfo", + "/proc/stat", + "/proc/net/dev", + } + for _, valid := range validDestinations { + path, err := filepath.Rel(filepath.Join(rootfs, valid), dest) + if err != nil { + return err + } + if path == "." { + return nil + } + } + for _, invalid := range invalidDestinations { + path, err := filepath.Rel(filepath.Join(rootfs, invalid), dest) + if err != nil { + return err + } + if path == "." || !strings.HasPrefix(path, "..") { + return fmt.Errorf("%q cannot be mounted because it is located inside %q", dest, invalid) + } + + } + return nil +} + +// preCreateDirs creates necessary dirs for hyperstart +func preCreateDirs(rootfs string) error { + dirs := []string{ + "proc", + "sys", + "dev", + "lib/modules", + } + for _, dir := range dirs { + err := createIfNotExists(filepath.Join(rootfs, dir), true) + if err != nil { + return err + } + } + return nil +} + +// createIfNotExists creates a file or a directory only if it does not already exist. +func createIfNotExists(path string, isDir bool) error { + if _, err := os.Stat(path); err != nil { + if os.IsNotExist(err) { + if isDir { + return os.MkdirAll(path, 0755) + } + if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { + return err + } + f, err := os.OpenFile(path, os.O_CREATE, 0755) + if err != nil { + return err + } + f.Close() + } + } + return nil +} + +func setupContainerFs(vm *hypervisor.Vm, bundle, container string, spec *specs.Spec) (err error) { + containerSharedFs := filepath.Join(hypervisor.BaseDir, vm.Id, hypervisor.ShareDirTag, container) + rootPath := spec.Root.Path + if !filepath.IsAbs(rootPath) { + rootPath = filepath.Join(bundle, rootPath) + } + + vmRootfs := filepath.Join(containerSharedFs, "rootfs") + os.MkdirAll(vmRootfs, 0755) + + if err = mount.MakePrivate(containerSharedFs); err != nil { + glog.Errorf("Make %q private failed: %v", containerSharedFs, err) + return err + } + + // Mount rootfs + err = utils.Mount(rootPath, vmRootfs) + if err != nil { + glog.Errorf("mount %s to %s failed: %s\n", rootPath, vmRootfs, err.Error()) + return err + } + + // Pre-create dirs necessary for hyperstart before setting rootfs readonly + // TODO: a better way to support readonly rootfs + if err = preCreateDirs(rootPath); err != nil { + return err + } + + // Mount necessary files and directories from spec + for _, m := range spec.Mounts { + if err := mountToRootfs(&m, vmRootfs, ""); err != nil { + return fmt.Errorf("mounting %q to rootfs %q at %q failed: %v", m.Source, m.Destination, vmRootfs, err) + } + } + + // set rootfs readonly + if spec.Root.Readonly { + err = utils.SetReadonly(vmRootfs) + if err != nil { + glog.Errorf("set rootfs %s readonly failed: %s\n", vmRootfs, err.Error()) + return err + } + } + + return nil +} + +func removeContainerFs(vm *hypervisor.Vm, container string) { + containerSharedFs := filepath.Join(hypervisor.BaseDir, vm.Id, hypervisor.ShareDirTag, container) + utils.Umount(containerSharedFs) +} diff --git a/kill.go b/cli/kill.go similarity index 77% rename from kill.go rename to cli/kill.go index b60c61dd..92a99df4 100644 --- a/kill.go +++ b/cli/kill.go @@ -7,10 +7,9 @@ import ( "strings" "syscall" - "github.com/hyperhq/runv/containerd/api/grpc/types" + "github.com/hyperhq/runv/hyperstart/libhyperstart" "github.com/hyperhq/runv/lib/linuxsignal" "github.com/urfave/cli" - netcontext "golang.org/x/net/context" ) var linuxSignalMap = map[string]syscall.Signal{ @@ -75,6 +74,9 @@ signal to the init process of the "ubuntu01" container: Usage: "send the signal to all processes in the container", }, }, + Before: func(context *cli.Context) error { + return cmdPrepare(context, false, true) + }, Action: func(context *cli.Context) error { container := context.Args().First() if container == "" { @@ -90,7 +92,7 @@ signal to the init process of the "ubuntu01" container: return cli.NewExitError(fmt.Sprintf("parse signal failed %v, signal string:%s", err, sigstr), -1) } - c, err := getClient(filepath.Join(context.GlobalString("root"), container, "namespace/namespaced.sock")) + h, err := libhyperstart.NewGrpcBasedHyperstart(filepath.Join(context.GlobalString("root"), container, "sandbox", "hyperstartgrpc.sock")) if err != nil { return cli.NewExitError(fmt.Sprintf("failed to get client: %v", err), -1) } @@ -98,7 +100,7 @@ signal to the init process of the "ubuntu01" container: plist := make([]string, 0) if context.Bool("all") { - if plist, err = getProcessList(c, container); err != nil { + if plist, err = getProcessList(context, container); err != nil { return cli.NewExitError(fmt.Sprintf("can't get process list, %v", err), -1) } } else { @@ -106,11 +108,7 @@ signal to the init process of the "ubuntu01" container: } for _, p := range plist { - if _, err = c.Signal(netcontext.Background(), &types.SignalRequest{ - Id: container, - Pid: p, - Signal: uint32(signal), - }); err != nil { + if err = h.SignalProcess(container, p, signal); err != nil && len(plist) == 1 { return cli.NewExitError(fmt.Sprintf("kill signal failed, %v", err), -1) } } @@ -118,24 +116,8 @@ signal to the init process of the "ubuntu01" container: }, } -func getProcessList(c types.APIClient, container string) ([]string, error) { - s, err := c.State(netcontext.Background(), &types.StateRequest{Id: container}) - if err != nil { - return nil, fmt.Errorf("get container state failed, %v", err) - } - - for _, cc := range s.Containers { - if cc.Id == container { - plist := make([]string, 0) - for _, p := range cc.Processes { - plist = append(plist, p.Pid) - } - - return plist, nil - } - } - - return nil, fmt.Errorf("container %s not found", container) +func getProcessList(context *cli.Context, container string) ([]string, error) { + return nil, fmt.Errorf("getProcessList of container is not supported yet") } func parseSignal(rawSignal string) (syscall.Signal, error) { diff --git a/list.go b/cli/list.go similarity index 86% rename from list.go rename to cli/list.go index 5aabc4a4..2e00c9f2 100644 --- a/list.go +++ b/cli/list.go @@ -9,7 +9,6 @@ import ( "text/tabwriter" "time" - "github.com/opencontainers/runtime-spec/specs-go" "github.com/urfave/cli" ) @@ -49,6 +48,9 @@ in json format: # runv list -f json`, }, }, + Before: func(context *cli.Context) error { + return cmdPrepare(context, false, false) + }, Action: func(context *cli.Context) { s, err := getContainers(context) if err != nil { @@ -105,16 +107,18 @@ func getContainers(context *cli.Context) ([]containerState, error) { var s []containerState for _, item := range list { if item.IsDir() { - stateFile := filepath.Join(absRoot, item.Name(), stateJson) + stateFile := filepath.Join(absRoot, item.Name(), stateJSON) fi, err := os.Stat(stateFile) if err != nil && !os.IsNotExist(err) { return nil, fmt.Errorf("Stat file %s error: %s", stateFile, err.Error()) } - state, err := loadStateFile(stateFile) + state, err := loadStateFile(absRoot, item.Name()) if err != nil { return nil, fmt.Errorf("Load state file %s failed: %s", stateFile, err.Error()) } + // TODO: get the Status by inspecting the shim process + s = append(s, containerState{ ID: state.ID, InitProcessPid: state.Pid, @@ -127,20 +131,6 @@ func getContainers(context *cli.Context) ([]containerState, error) { return s, nil } -func loadStateFile(stateFile string) (*specs.State, error) { - file, err := os.Open(stateFile) - if err != nil { - return nil, err - } - defer file.Close() - - var state specs.State - if err = json.NewDecoder(file).Decode(&state); err != nil { - return nil, fmt.Errorf("Decode state file %s error: %s", stateFile, err.Error()) - } - return &state, nil -} - // fatal prints the error's details if it is a runv specific error type // then exits the program with an exit status of 1. func fatal(err error) { diff --git a/main.go b/cli/main.go similarity index 60% rename from main.go rename to cli/main.go index 64d2aea8..e527ffd0 100644 --- a/main.go +++ b/cli/main.go @@ -3,22 +3,14 @@ package main import ( "flag" "fmt" - "io/ioutil" - "log" - "net" "os" - "time" "github.com/docker/docker/pkg/reexec" "github.com/golang/glog" - "github.com/golang/protobuf/ptypes" - "github.com/hyperhq/runv/containerd" - "github.com/hyperhq/runv/containerd/api/grpc/types" - _ "github.com/hyperhq/runv/supervisor/proxy" // for proxy.init() + "github.com/hyperhq/runv/driverloader" + "github.com/hyperhq/runv/hypervisor" + specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/urfave/cli" - netcontext "golang.org/x/net/context" - "google.golang.org/grpc" - "google.golang.org/grpc/grpclog" ) var ( @@ -28,7 +20,7 @@ var ( const ( specConfig = "config.json" - stateJson = "state.json" + stateJSON = "state.json" usage = `Open Container Initiative hypervisor-based runtime runv is a command line client for running applications packaged according to @@ -127,20 +119,6 @@ func main() { Usage: "runv-compatible boot ISO for the container for vbox driver", }, } - app.Before = func(context *cli.Context) error { - logdir := context.GlobalString("log_dir") - if logdir != "" { - if err := os.MkdirAll(logdir, 0750); err != nil { - return fmt.Errorf("can't create dir %q for log files", logdir) - } - } - if context.GlobalBool("debug") { - flag.CommandLine.Parse([]string{"-v", "3", "--log_dir", logdir, "--alsologtostderr"}) - } else { - flag.CommandLine.Parse([]string{"-v", "1", "--log_dir", logdir}) - } - return nil - } app.After = func(context *cli.Context) error { // make sure glog flush all the messages to file glog.Flush() @@ -158,61 +136,46 @@ func main() { startCommand, stateCommand, manageCommand, - shimCommand, pauseCommand, resumeCommand, - containerd.ContainerdCommand, + deleteCommand, + proxyCommand, + shimCommand, + nsListenCommand, } if err := app.Run(os.Args); err != nil { - fmt.Fprintf(os.Stderr, "%v", err) + glog.Errorf("app.Run(os.Args) failed with err: %#v", err) + fmt.Fprintf(os.Stderr, "%v\n", err) } } -func getClient(address string) (types.APIClient, error) { - // reset the logger for grpc to log to dev/null so that it does not mess with our stdio - grpclog.SetLogger(log.New(ioutil.Discard, "", log.LstdFlags)) - dialOpts := []grpc.DialOption{grpc.WithInsecure(), grpc.WithTimeout(5 * time.Second)} - dialOpts = append(dialOpts, - grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { - return net.DialTimeout("unix", addr, timeout) - }, - )) - conn, err := grpc.Dial(address, dialOpts...) - if err != nil { - return nil, fmt.Errorf("grpc.Dial error: %v", err) - } - return types.NewAPIClient(conn), nil +// runvOptions is used for create aux runv processes (networklistener/shim/proxy) +type runvOptions struct { + *cli.Context + withContainer *specs.State } -func containerEvents(c types.APIClient, container string) <-chan *types.Event { - evChan := make(chan *types.Event) - ts := time.Now() - go func() { - for { - tsp, err := ptypes.TimestampProto(ts) - if err != nil { - close(evChan) - return - } - events, err := c.Events(netcontext.Background(), &types.EventsRequest{Timestamp: tsp}) - if err != nil { - fmt.Printf("c.Events error: %v", err) - // TODO try to find a way to kill the process ? - close(evChan) - return - } - for { - e, err := events.Recv() - if err != nil { - time.Sleep(1 * time.Second) - break - } - ts, err = ptypes.Timestamp(e.Timestamp) - if e.Id == container { - evChan <- e - } - } +func cmdPrepare(context *cli.Context, setupHypervisor, canLogToStderr bool) error { + if setupHypervisor { + var err error + if hypervisor.HDriver, err = driverloader.Probe(context.GlobalString("driver")); err != nil { + return err + } + setupHyperstartFunc(context) + } + + logdir := context.GlobalString("log_dir") + if logdir != "" { + if err := os.MkdirAll(logdir, 0750); err != nil { + return fmt.Errorf("can't create dir %q for log files", logdir) } - }() - return evChan + } + if !context.GlobalBool("debug") { + flag.CommandLine.Parse([]string{"-v", "1", "--log_dir", logdir}) + } else if canLogToStderr { + flag.CommandLine.Parse([]string{"-v", "3", "--log_dir", logdir, "--alsologtostderr"}) + } else { + flag.CommandLine.Parse([]string{"-v", "3", "--log_dir", logdir}) + } + return nil } diff --git a/manage.go b/cli/manage.go similarity index 84% rename from manage.go rename to cli/manage.go index b0179109..331ef7fc 100644 --- a/manage.go +++ b/cli/manage.go @@ -1,14 +1,12 @@ package main import ( - "flag" "fmt" "os" "path/filepath" "syscall" "github.com/golang/glog" - "github.com/hyperhq/runv/driverloader" "github.com/hyperhq/runv/hypervisor" templatecore "github.com/hyperhq/runv/template" "github.com/urfave/cli" @@ -44,6 +42,9 @@ var createTemplateCommand = cli.Command{ }, }, Usage: "create a template VM on the directory specified by the global option --template", + Before: func(context *cli.Context) error { + return cmdPrepare(context, true, true) + }, Action: func(context *cli.Context) error { absOption := func(option string) (string, error) { path := context.GlobalString(option) @@ -73,16 +74,6 @@ var createTemplateCommand = cli.Command{ return cli.NewExitError(fmt.Errorf("Failed to create the template directory: %v", err), -1) } - if context.GlobalBool("debug") { - flag.CommandLine.Parse([]string{"-v", "3", "--log_dir", context.GlobalString("log_dir"), "--alsologtostderr"}) - } else { - flag.CommandLine.Parse([]string{"-v", "1", "--log_dir", context.GlobalString("log_dir")}) - } - - if hypervisor.HDriver, err = driverloader.Probe(context.GlobalString("driver")); err != nil { - return cli.NewExitError(fmt.Errorf("Failed to setup the driver: %v", err), -1) - } - boot := hypervisor.BootConfig{ CPU: context.Int("cpu"), Memory: context.Int("mem"), diff --git a/cli/network.go b/cli/network.go new file mode 100644 index 00000000..f393c3d6 --- /dev/null +++ b/cli/network.go @@ -0,0 +1,559 @@ +package main + +import ( + "encoding/gob" + "fmt" + "io" + "os" + "os/exec" + "strconv" + "strings" + "syscall" + + "github.com/golang/glog" + "github.com/hyperhq/runv/api" + _ "github.com/hyperhq/runv/cli/nsenter" + "github.com/hyperhq/runv/hypervisor" + "github.com/kardianos/osext" + "github.com/urfave/cli" + "github.com/vishvananda/netlink" +) + +type NetlinkUpdateType string + +const ( + UpdateTypeLink NetlinkUpdateType = "link" + UpdateTypeAddr NetlinkUpdateType = "addr" + UpdateTypeRoute NetlinkUpdateType = "route" +) + +// NetlinkUpdate tracks the change of network namespace. +type NetlinkUpdate struct { + // AddrUpdate is used to pass information back from AddrSubscribe() + Addr netlink.AddrUpdate + // RouteUpdate is used to pass information back from RouteSubscribe() + Route netlink.RouteUpdate + // Veth is used to pass information back from LinkSubscribe(). + // We only support veth link at present. + Veth *netlink.Veth + + // UpdateType indicates which part of the netlink information has been changed. + UpdateType NetlinkUpdateType +} + +type InterfaceInfo struct { + Index int + PeerIndex int + Ip string +} + +type nsListener struct { + enc *gob.Encoder + dec *gob.Decoder + cmd *exec.Cmd +} + +func GetBridgeFromIndex(idx int) (string, string, error) { + var attr, bridge *netlink.LinkAttrs + var options string + + links, err := netlink.LinkList() + if err != nil { + glog.Error(err) + return "", "", err + } + + for _, link := range links { + if link.Type() != "veth" { + continue + } + + if link.Attrs().Index == idx { + attr = link.Attrs() + break + } + } + + if attr == nil { + return "", "", fmt.Errorf("cann't find nic whose ifindex is %d", idx) + } + + for _, link := range links { + if link.Type() != "bridge" && link.Type() != "openvswitch" { + continue + } + + if link.Attrs().Index == attr.MasterIndex { + bridge = link.Attrs() + break + } + } + + if bridge == nil { + return "", "", fmt.Errorf("cann't find bridge contains nic whose ifindex is %d", idx) + } + + if bridge.Name == "ovs-system" { + veth, err := netlink.LinkByIndex(idx) + if err != nil { + return "", "", err + } + + out, err := exec.Command("ovs-vsctl", "port-to-br", veth.Attrs().Name).CombinedOutput() + if err != nil { + return "", "", err + } + bridge.Name = strings.TrimSpace(string(out)) + + out, err = exec.Command("ovs-vsctl", "get", "port", veth.Attrs().Name, "tag").CombinedOutput() + if err != nil { + return "", "", err + } + options = "tag=" + strings.TrimSpace(string(out)) + } + + glog.V(3).Infof("find bridge %s", bridge.Name) + + return bridge.Name, options, nil +} + +func initSandboxNetwork(vm *hypervisor.Vm, enc *gob.Encoder, dec *gob.Decoder) error { + /* send collect netns request to nsListener */ + if err := enc.Encode("init"); err != nil { + glog.Errorf("listener.dec.Decode init error: %v", err) + return err + } + + infos := []InterfaceInfo{} + /* read nic information of ns from pipe */ + err := dec.Decode(&infos) + if err != nil { + glog.Error("listener.dec.Decode infos error: %v", err) + return err + } + + routes := []netlink.Route{} + err = dec.Decode(&routes) + if err != nil { + glog.Error("listener.dec.Decode route error: %v", err) + return err + } + + var gw_route *netlink.Route + for idx, route := range routes { + if route.Dst == nil { + gw_route = &routes[idx] + } + } + + glog.V(3).Infof("interface configuration for sandbox ns is %#v", infos) + for _, info := range infos { + bridge, options, err := GetBridgeFromIndex(info.PeerIndex) + if err != nil { + glog.Error(err) + continue + } + + nicId := strconv.Itoa(info.Index) + + conf := &api.InterfaceDescription{ + Id: nicId, //ip as an id + Lo: false, + Bridge: bridge, + Ip: info.Ip, + Options: options, + } + + if gw_route != nil && gw_route.LinkIndex == info.Index { + conf.Gw = gw_route.Gw.String() + } + + // TODO(hukeping): the name here is always eth1, 2, 3, 4, 5, etc., + // which would not be the proper way to name device name, instead it + // should be the same as what we specified in the network namespace. + //err = hp.vm.AddNic(info.Index, fmt.Sprintf("eth%d", idx), conf) + err = vm.AddNic(conf) + if err != nil { + glog.Error(err) + return err + } + } + + err = vm.AddRoute() + if err != nil { + glog.Error(err) + return err + } + + // TODO: does nsListener need to be long living? + //go nsListenerStrap(vm, enc *gob.Encoder, dec *gob.Decoder) + + return nil +} + +func nsListenerStrap(vm *hypervisor.Vm, enc *gob.Encoder, dec *gob.Decoder) { + // Keep watching container network setting + // and then update vm/hyperstart + for { + update := NetlinkUpdate{} + err := dec.Decode(&update) + if err != nil { + if err == io.EOF { + glog.V(3).Infof("listener.dec.Decode NetlinkUpdate: %v", err) + break + } + glog.Error("listener.dec.Decode NetlinkUpdate error: %v", err) + continue + } + + glog.V(3).Infof("network namespace information of %s has been changed", update.UpdateType) + switch update.UpdateType { + case UpdateTypeLink: + link := update.Veth + if link.Attrs().ParentIndex == 0 { + glog.V(3).Infof("The deleted link: %s", link) + err = vm.DeleteNic(strconv.Itoa(link.Attrs().Index)) + if err != nil { + glog.Error(err) + continue + } + + } else { + glog.V(3).Infof("The changed link: %s", link) + } + + case UpdateTypeAddr: + glog.V(3).Infof("The changed address: %s", update.Addr) + + link := update.Veth + + // If there is a delete operation upon an link, it will also trigger + // the address change event which the link will be NIL since it has + // already been deleted before the address change event be triggered. + if link == nil { + glog.V(3).Info("Link for this address has already been deleted.") + continue + } + + // This is just a sanity check. + // + // The link should be the one which the address on it has been changed. + if link.Attrs().Index != update.Addr.LinkIndex { + glog.Errorf("Get the wrong link with ID %d, expect %d", link.Attrs().Index, update.Addr.LinkIndex) + continue + } + + bridge, options, err := GetBridgeFromIndex(link.Attrs().ParentIndex) + if err != nil { + glog.Error(err) + continue + } + + inf := &api.InterfaceDescription{ + Id: strconv.Itoa(link.Attrs().Index), + Lo: false, + Bridge: bridge, + Ip: update.Addr.LinkAddress.String(), + Options: options, + } + + err = vm.AddNic(inf) + if err != nil { + glog.Error(err) + continue + } + + case UpdateTypeRoute: + + } + } +} + +func newPipe() (parent, child *os.File, err error) { + fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0) + if err != nil { + return nil, nil, err + } + return os.NewFile(uintptr(fds[1]), "parent"), os.NewFile(uintptr(fds[0]), "child"), nil +} + +func startNsListener(options runvOptions, vm *hypervisor.Vm) (err error) { + var parentPipe, childPipe *os.File + var path string + + path, err = osext.Executable() + if err != nil { + glog.Errorf("cannot find self executable path for %s: %v", os.Args[0], err) + return err + } + + glog.V(3).Infof("get exec path %s", path) + parentPipe, childPipe, err = newPipe() + if err != nil { + glog.Errorf("create pipe for network-nslisten failed: %v", err) + return err + } + + defer func() { + if err != nil { + parentPipe.Close() + childPipe.Close() + } + }() + + cmd := exec.Cmd{ + Path: path, + Args: []string{"runv", "network-nslisten"}, + Env: append(os.Environ(), fmt.Sprintf("_RUNVNETNSPID=%d", options.withContainer.Pid)), + ExtraFiles: []*os.File{childPipe}, + } + if err = cmd.Start(); err != nil { + glog.Errorf("start network-nslisten failed: %v", err) + return err + } + + childPipe.Close() + + enc := gob.NewEncoder(parentPipe) + dec := gob.NewDecoder(parentPipe) + + defer func() { + if err != nil { + cmd.Process.Kill() + } + cmd.Wait() + }() + + /* Make sure nsListener create new netns */ + var ready string + if err = dec.Decode(&ready); err != nil { + glog.Errorf("Get ready message from network-nslisten failed: %v", err) + return err + } + + if ready != "init" { + err = fmt.Errorf("get incorrect init message from network-nslisten: %s", ready) + return err + } + + initSandboxNetwork(vm, enc, dec) + glog.V(1).Infof("nsListener pid is %d", cmd.Process.Pid) + return nil +} + +var nsListenCommand = cli.Command{ + Name: "network-nslisten", + Usage: "[internal command] collection net namespace's network configuration", + HideHelp: true, + Before: func(context *cli.Context) error { + return cmdPrepare(context, false, false) + }, + Action: func(context *cli.Context) error { + doListen() + return nil + }, +} + +func doListen() { + + childPipe := os.NewFile(uintptr(3), "child") + enc := gob.NewEncoder(childPipe) + dec := gob.NewDecoder(childPipe) + + /* notify `runv create` to execute prestart hooks */ + if err := enc.Encode("init"); err != nil { + glog.Error(err) + return + } + + /* after execute prestart hooks */ + var ready string + if err := dec.Decode(&ready); err != nil { + glog.Error(err) + return + } + + if ready != "init" { + glog.Errorf("get incorrect init message: %s", ready) + return + } + + // Get network namespace info for the first time and send to the `runv create` + /* get route info before link down */ + routes, err := netlink.RouteList(nil, netlink.FAMILY_V4) + if err != nil { + glog.Error(err) + return + } + + /* send interface info to `runv create` */ + infos := collectionInterfaceInfo() + if err := enc.Encode(infos); err != nil { + glog.Error(err) + return + } + + if err := enc.Encode(routes); err != nil { + glog.Error(err) + return + } + + // This is a call back function. + // Use to send netlink update informations to `runv create`. + //netNs2Containerd := func(netlinkUpdate NetlinkUpdate) { + // if err := enc.Encode(netlinkUpdate); err != nil { + // glog.Info("err Encode(netlinkUpdate) is :", err) + // } + //} + // todo: Keep collecting network namespace info and sending to the runv + //setupNetworkNsTrap(netNs2Containerd) +} + +func collectionInterfaceInfo() []InterfaceInfo { + infos := []InterfaceInfo{} + + links, err := netlink.LinkList() + if err != nil { + glog.Error(err) + return infos + } + + for _, link := range links { + if link.Type() != "veth" { + // lo is here too + continue + } + + addrs, err := netlink.AddrList(link, netlink.FAMILY_V4) + if err != nil { + glog.Error(err) + return infos + } + + for _, addr := range addrs { + info := InterfaceInfo{ + Ip: addr.IPNet.String(), + Index: link.Attrs().Index, + PeerIndex: link.Attrs().ParentIndex, + } + glog.Infof("get interface %v", info) + infos = append(infos, info) + } + + // set link down, tap device take over it + netlink.LinkSetDown(link) + } + return infos +} + +// This function should be put into the main process or somewhere that can be +// use to init the network namespace trap. +func setupNetworkNsTrap(netNs2Containerd func(NetlinkUpdate)) { + // Subscribe for links change event + chLink := make(chan netlink.LinkUpdate) + doneLink := make(chan struct{}) + defer close(doneLink) + if err := netlink.LinkSubscribe(chLink, doneLink); err != nil { + glog.Fatal(err) + } + + // Subscribe for addresses change event + chAddr := make(chan netlink.AddrUpdate) + doneAddr := make(chan struct{}) + defer close(doneAddr) + if err := netlink.AddrSubscribe(chAddr, doneAddr); err != nil { + glog.Fatal(err) + } + + // Subscribe for route change event + chRoute := make(chan netlink.RouteUpdate) + doneRoute := make(chan struct{}) + defer close(doneRoute) + if err := netlink.RouteSubscribe(chRoute, doneRoute); err != nil { + glog.Fatal(err) + } + + for { + select { + case updateLink := <-chLink: + handleLink(updateLink, netNs2Containerd) + case updateAddr := <-chAddr: + handleAddr(updateAddr, netNs2Containerd) + case updateRoute := <-chRoute: + handleRoute(updateRoute, netNs2Containerd) + } + } +} + +// Link specific +func handleLink(update netlink.LinkUpdate, callback func(NetlinkUpdate)) { + if update.IfInfomsg.Flags&syscall.IFF_UP == 1 { + fmt.Printf("[Link device up]\tupdateLink is:%+v, flag is:0x%x\n", update.Link.Attrs(), update.IfInfomsg.Flags) + } else { + if update.Link.Attrs().ParentIndex == 0 { + fmt.Printf("[Link device !up][Deleted]\tupdateLink is:%+v, flag is:0x%x\n", update.Link.Attrs(), update.IfInfomsg.Flags) + } else { + fmt.Printf("[Link device !up]\tupdateLink is:%+v, flag is:0x%x\n", update.Link.Attrs(), update.IfInfomsg.Flags) + } + } + + netlinkUpdate := NetlinkUpdate{} + netlinkUpdate.UpdateType = UpdateTypeLink + + // We would like to only handle the veth pair link at present. + if veth, ok := (update.Link).(*netlink.Veth); ok { + netlinkUpdate.Veth = veth + callback(netlinkUpdate) + } +} + +// Address specific +func handleAddr(update netlink.AddrUpdate, callback func(NetlinkUpdate)) { + if update.NewAddr { + fmt.Printf("[Add a address]") + } else { + fmt.Printf("[Delete a address]") + } + + if update.LinkAddress.IP.To4() != nil { + fmt.Printf("[IPv4]\t%+v\n", update) + } else { + // We would not like to handle IPv6 at present. + fmt.Printf("[IPv6]\t%+v\n", update) + return + } + + netlinkUpdate := NetlinkUpdate{} + netlinkUpdate.Addr = update + netlinkUpdate.UpdateType = UpdateTypeAddr + links, err := netlink.LinkList() + if err != nil { + glog.Error(err) + } + for _, link := range links { + if link.Attrs().Index == update.LinkIndex && link.Type() == "veth" { + netlinkUpdate.Veth = link.(*netlink.Veth) + break + } + } + callback(netlinkUpdate) +} + +// Route specific +func handleRoute(update netlink.RouteUpdate, callback func(NetlinkUpdate)) { + // Route type is not a bit mask for a couple of values, but a single + // unsigned int, that's why we use switch here not the "&" operator. + switch update.Type { + case syscall.RTM_NEWROUTE: + fmt.Printf("[Create a route]\t%+v\n", update) + case syscall.RTM_DELROUTE: + fmt.Printf("[Remove a route]\t%+v\n", update) + case syscall.RTM_GETROUTE: + fmt.Printf("[Receive info of a route]\t%+v\n", update) + } + + netlinkUpdate := NetlinkUpdate{} + netlinkUpdate.Route = update + netlinkUpdate.UpdateType = UpdateTypeRoute + callback(netlinkUpdate) +} diff --git a/cli/nsenter/namespace.h b/cli/nsenter/namespace.h new file mode 100644 index 00000000..d62ffd36 --- /dev/null +++ b/cli/nsenter/namespace.h @@ -0,0 +1,14 @@ +#ifndef NSENTER_NAMESPACE_H +#define NSENTER_NAMESPACE_H + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif +#include + +/* All of these are taken from include/uapi/linux/sched.h */ +#ifndef CLONE_NEWNET +# define CLONE_NEWNET 0x40000000 /* New network namespace */ +#endif + +#endif /* NSENTER_NAMESPACE_H */ diff --git a/cli/nsenter/nsenter.go b/cli/nsenter/nsenter.go new file mode 100644 index 00000000..07f4d63e --- /dev/null +++ b/cli/nsenter/nsenter.go @@ -0,0 +1,12 @@ +// +build linux,!gccgo + +package nsenter + +/* +#cgo CFLAGS: -Wall +extern void nsexec(); +void __attribute__((constructor)) init(void) { + nsexec(); +} +*/ +import "C" diff --git a/cli/nsenter/nsenter_gccgo.go b/cli/nsenter/nsenter_gccgo.go new file mode 100644 index 00000000..63c7a3ec --- /dev/null +++ b/cli/nsenter/nsenter_gccgo.go @@ -0,0 +1,25 @@ +// +build linux,gccgo + +package nsenter + +/* +#cgo CFLAGS: -Wall +extern void nsexec(); +void __attribute__((constructor)) init(void) { + nsexec(); +} +*/ +import "C" + +// AlwaysFalse is here to stay false +// (and be exported so the compiler doesn't optimize out its reference) +var AlwaysFalse bool + +func init() { + if AlwaysFalse { + // by referencing this C init() in a noop test, it will ensure the compiler + // links in the C function. + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65134 + C.init() + } +} diff --git a/cli/nsenter/nsenter_unsupported.go b/cli/nsenter/nsenter_unsupported.go new file mode 100644 index 00000000..ac701ca3 --- /dev/null +++ b/cli/nsenter/nsenter_unsupported.go @@ -0,0 +1,5 @@ +// +build !linux !cgo + +package nsenter + +import "C" diff --git a/cli/nsenter/nsexec.c b/cli/nsenter/nsexec.c new file mode 100644 index 00000000..1bc785b2 --- /dev/null +++ b/cli/nsenter/nsexec.c @@ -0,0 +1,54 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +/* Get all of the CLONE_NEW* flags. */ +#include "namespace.h" + +#define bail(fmt, ...) \ + do { \ + int ret = __COUNTER__ + 1; \ + fprintf(stderr, "nsenter: " fmt ": %m\n", ##__VA_ARGS__); \ + exit(ret); \ + } while(0) + +static int get_RUNVNETNSPID(void) +{ + int pid; + char *nspidStr, *endptr; + + nspidStr = getenv("_RUNVNETNSPID"); + if (nspidStr== NULL || *nspidStr == '\0') + return -1; + + pid = strtol(nspidStr, &endptr, 10); + if (*endptr != '\0') + bail("unable to parse _RUNVNETNSPID"); + + return pid; +} + +void nsexec(void) +{ + int nsPid, nsFd; + char *path; + + nsPid = get_RUNVNETNSPID(); + if (nsPid <= 0) { + return; + } + + path = malloc(sizeof(char)*64); + sprintf(path, "/proc/%d/ns/net", nsPid); + nsFd = open(path, O_RDONLY); + setns(nsFd, CLONE_NEWNET); + + free(path); + close(nsFd); + return; +} diff --git a/pause.go b/cli/pause.go similarity index 61% rename from pause.go rename to cli/pause.go index 8e5df69e..63a14d7e 100644 --- a/pause.go +++ b/cli/pause.go @@ -4,38 +4,36 @@ import ( "fmt" "path/filepath" - "github.com/hyperhq/runv/containerd/api/grpc/types" + "github.com/hyperhq/runv/hyperstart/libhyperstart" "github.com/hyperhq/runv/lib/linuxsignal" "github.com/urfave/cli" - netcontext "golang.org/x/net/context" ) var pauseCommand = cli.Command{ Name: "pause", Usage: "suspend all processes in the container", ArgsUsage: ``, + Before: func(context *cli.Context) error { + return cmdPrepare(context, false, false) + }, Action: func(context *cli.Context) error { container := context.Args().First() if container == "" { return cli.NewExitError(fmt.Sprintf("container id cannot be empty"), -1) } - c, err := getClient(filepath.Join(context.GlobalString("root"), container, "namespace/namespaced.sock")) + h, err := libhyperstart.NewGrpcBasedHyperstart(filepath.Join(context.GlobalString("root"), container, "sandbox", "hyperstartgrpc.sock")) if err != nil { - return cli.NewExitError(fmt.Sprintf("failed to get client: %v", err), -1) + return cli.NewExitError(fmt.Sprintf("failed to get hyperstart: %v", err), -1) } - plist, err := getProcessList(c, container) + plist, err := getProcessList(context, container) if err != nil { return cli.NewExitError(fmt.Sprintf("can't get process list, %v", err), -1) } for _, p := range plist { - if _, err := c.Signal(netcontext.Background(), &types.SignalRequest{ - Id: container, - Pid: p, - Signal: uint32(linuxsignal.SIGSTOP), - }); err != nil { + if err := h.SignalProcess(container, p, linuxsignal.SIGSTOP); err != nil { return cli.NewExitError(fmt.Sprintf("suspend signal failed, %v", err), -1) } } @@ -48,28 +46,27 @@ var resumeCommand = cli.Command{ Name: "resume", Usage: "resume all processes in the container", ArgsUsage: ``, + Before: func(context *cli.Context) error { + return cmdPrepare(context, false, false) + }, Action: func(context *cli.Context) error { container := context.Args().First() if container == "" { return cli.NewExitError(fmt.Sprintf("container id cannot be empty"), -1) } - c, err := getClient(filepath.Join(context.GlobalString("root"), container, "namespace/namespaced.sock")) + h, err := libhyperstart.NewGrpcBasedHyperstart(filepath.Join(context.GlobalString("root"), container, "sandbox", "hyperstartgrpc.sock")) if err != nil { return cli.NewExitError(fmt.Sprintf("failed to get client: %v", err), -1) } - plist, err := getProcessList(c, container) + plist, err := getProcessList(context, container) if err != nil { return cli.NewExitError(fmt.Sprintf("can't get process list, %v", err), -1) } for _, p := range plist { - if _, err := c.Signal(netcontext.Background(), &types.SignalRequest{ - Id: container, - Pid: p, - Signal: uint32(linuxsignal.SIGCONT), - }); err != nil { + if err := h.SignalProcess(container, p, linuxsignal.SIGCONT); err != nil { return cli.NewExitError(fmt.Sprintf("resume signal failed, %v", err), -1) } } diff --git a/cli/proxy.go b/cli/proxy.go new file mode 100644 index 00000000..5f3d336a --- /dev/null +++ b/cli/proxy.go @@ -0,0 +1,207 @@ +package main + +import ( + "bufio" + "fmt" + "io" + "net" + "os" + "os/exec" + "path/filepath" + "syscall" + "time" + + "github.com/golang/glog" + "github.com/hyperhq/runv/hyperstart/libhyperstart" + "github.com/hyperhq/runv/hyperstart/proxy" + "github.com/hyperhq/runv/hypervisor" + "github.com/hyperhq/runv/lib/telnet" + "github.com/hyperhq/runv/lib/utils" + "github.com/kardianos/osext" + "github.com/urfave/cli" + "google.golang.org/grpc" +) + +var proxyCommand = cli.Command{ + Name: "proxy", + Usage: "[internal command] proxy hyperstart API into vm and watch vm", + HideHelp: true, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "vmid", + Usage: "the vm name", + }, + cli.StringFlag{ + Name: "hyperstart-ctl-sock", + Usage: "the vm's ctl sock address to be connected", + }, + cli.StringFlag{ + Name: "hyperstart-stream-sock", + Usage: "the vm's stream sock address to be connected", + }, + cli.StringFlag{ + Name: "proxy-hyperstart", + Usage: "gprc sock address to be created for proxying hyperstart API", + }, + cli.StringFlag{ + Name: "watch-vm-console", + Usage: "vm's console sock address to connected(readonly)", + }, + cli.BoolFlag{ + Name: "watch-hyperstart", + Usage: "ping hyperstart for every 60 seconds via Version() API", + }, + cli.BoolFlag{ + Name: "watch-vm", + Usage: "todo: to be implemented", + }, + }, + Before: func(context *cli.Context) error { + return cmdPrepare(context, false, false) + }, + Action: func(context *cli.Context) (err error) { + if context.String("vmid") == "" || context.String("hyperstart-ctl-sock") == "" || + context.String("hyperstart-stream-sock") == "" || context.String("proxy-hyperstart") == "" { + return err + } + glog.Infof("libhyperstart.NewJsonBasedHyperstart") + h, _ := libhyperstart.NewJsonBasedHyperstart(context.String("vmid"), context.String("hyperstart-ctl-sock"), context.String("hyperstart-stream-sock"), 1, false, false) + + var s *grpc.Server + if context.Bool("watch-hyperstart") { + glog.Infof("watchHyperstart") + go func() { + watchHyperstart(h) + s.Stop() + }() + } + if context.String("watch-vm-console") != "" { + glog.Infof("watchConsole() sock: %s", context.String("watch-vm-console")) + err = watchConsole(context.String("watch-vm-console")) + if err != nil { + glog.Errorf("watchConsole() failed, err: %#v", err) + return err + } + } + + grpcSock := context.String("proxy-hyperstart") + glog.Infof("proxy.StartServer") + s, err = proxy.StartServer(grpcSock, h) + if err != nil { + glog.Errorf("proxy.StartServer() failed with err: %#v", err) + return err + } + if _, err := os.Stat(grpcSock); !os.IsNotExist(err) { + return fmt.Errorf("%s existed, someone may be in service", grpcSock) + } + glog.Infof("net.Listen() to grpcsock: %s", grpcSock) + l, err := net.Listen("unix", grpcSock) + if err != nil { + glog.Errorf("net.Listen() failed with err: %#v", err) + return err + } + + glog.Infof("proxy: grpc api on %s", grpcSock) + if err = s.Serve(l); err != nil { + glog.Errorf("proxy serve grpc with error: %v", err) + } + + return err + }, +} + +func watchConsole(console string) error { + conn, err := utils.UnixSocketConnect(console) + if err != nil { + return err + } + tc, err := telnet.NewConn(conn) + if err != nil { + return err + } + br := bufio.NewReader(tc) + + go func() { + for { + log, _, err := br.ReadLine() + if err == io.EOF { + break + } + if err != nil { + glog.Errorf("read console %s failed: %v", console, err) + return + } + if len(log) != 0 { + glog.Info("vmconsole: ", string(log)) + } + } + }() + + return nil +} + +func watchHyperstart(h libhyperstart.Hyperstart) error { + next := time.NewTimer(10 * time.Second) + timeout := time.AfterFunc(60*time.Second, func() { + glog.Errorf("watch hyperstart timeout") + h.Close() + }) + defer next.Stop() + defer timeout.Stop() + + for { + glog.V(2).Infof("issue VERSION request for keep-alive test") + _, err := h.APIVersion() + if err != nil { + h.Close() + glog.Errorf("h.APIVersion() failed with %#v", err) + return err + } + if !timeout.Stop() { + <-timeout.C + } + <-next.C + next.Reset(10 * time.Second) + timeout.Reset(60 * time.Second) + } +} + +func createProxy(context *cli.Context, VMID, ctlSock, streamSock, grpcSock string) error { + path, err := osext.Executable() + if err != nil { + return fmt.Errorf("cannot find self executable path for %s: %v", os.Args[0], err) + } + + var cmd *exec.Cmd + args := []string{ + "runv", "--root", context.GlobalString("root"), + } + if context.GlobalBool("debug") { + args = append(args, "--debug") + } + if context.GlobalString("log_dir") != "" { + args = append(args, "--log_dir", context.GlobalString("log_dir")) + } + args = append(args, "proxy", "--vmid", VMID, "--hyperstart-ctl-sock", ctlSock, + "--hyperstart-stream-sock", streamSock, "--proxy-hyperstart", grpcSock, + "--watch-vm-console", filepath.Join(hypervisor.BaseDir, VMID, hypervisor.ConsoleSockName), + "--watch-hyperstart") + cmd = &exec.Cmd{ + Path: path, + Args: args, + Dir: "/", + SysProcAttr: &syscall.SysProcAttr{ + Setsid: true, + }, + } + + glog.V(2).Infof("start proxy with argument: %v", args) + err = cmd.Start() + if err != nil { + glog.Errorf("createProxy failed with err %#v", err) + return err + } + glog.V(2).Infof("createProxy succeeded with proxy pid: %d", cmd.Process.Pid) + + return nil +} diff --git a/ps.go b/cli/ps.go similarity index 55% rename from ps.go rename to cli/ps.go index 21ff16ed..4fbda12f 100644 --- a/ps.go +++ b/cli/ps.go @@ -4,12 +4,9 @@ import ( "encoding/json" "fmt" "os" - "path/filepath" "text/tabwriter" - "github.com/hyperhq/runv/containerd/api/grpc/types" "github.com/urfave/cli" - netcontext "golang.org/x/net/context" ) var psCommand = cli.Command{ @@ -22,12 +19,15 @@ var psCommand = cli.Command{ Usage: `select one of: ` + formatOptions, }, }, + Before: func(context *cli.Context) error { + return cmdPrepare(context, false, false) + }, Action: func(context *cli.Context) error { container := context.Args().First() if container == "" { return cli.NewExitError("container id cannot be empty", -1) } - c, err := getContainerApi(context, container) + pids, err := getProcessList(context, container) if err != nil { return cli.NewExitError(fmt.Sprintf("can't access container, %v", err), -1) } @@ -36,20 +36,16 @@ var psCommand = cli.Command{ case "table": w := tabwriter.NewWriter(os.Stdout, 12, 1, 3, ' ', 0) fmt.Fprint(w, "PROCESS\tCMD\n") - // we are limited by the containerd interface for now - for _, p := range c.Processes { + for _, p := range pids { fmt.Fprintf(w, "%s\t%s\n", - p.Pid, - p.Args) + p, + "todo process.Args") } if err := w.Flush(); err != nil { fatal(err) } case "json": pids := make([]string, 0) - for _, p := range c.Processes { - pids = append(pids, p.Pid) - } data, err := json.Marshal(pids) if err != nil { @@ -64,23 +60,3 @@ var psCommand = cli.Command{ return nil }, } - -func getContainerApi(context *cli.Context, container string) (*types.Container, error) { - api, err := getClient(filepath.Join(context.GlobalString("root"), container, "namespace/namespaced.sock")) - if err != nil { - return nil, fmt.Errorf("failed to get client: %v", err) - } - - s, err := api.State(netcontext.Background(), &types.StateRequest{Id: container}) - if err != nil { - return nil, fmt.Errorf("get container state failed, %v", err) - } - - for _, c := range s.Containers { - if c.Id == container { - return c, nil - } - } - - return nil, fmt.Errorf("container %s not found", container) -} diff --git a/run.go b/cli/run.go similarity index 61% rename from run.go rename to cli/run.go index e3a9ca22..f63fd105 100644 --- a/run.go +++ b/cli/run.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "os" "github.com/urfave/cli" ) @@ -49,10 +50,46 @@ command(s) that get executed on start, edit the args parameter of the spec. See Usage: "detach from the container's process", }, }, - Action: func(context *cli.Context) error { - if err := runContainer(context, false); err != nil { + Before: func(context *cli.Context) error { + return cmdPrepare(context, true, context.Bool("detach")) + }, + Action: func(context *cli.Context) (retErr error) { + if err := cmdCreateContainer(context, false); err != nil { return cli.NewExitError(fmt.Sprintf("Run Container error: %v", err), -1) } + + container := context.Args().First() + state, spec, err := loadStateAndSpec(context.GlobalString("root"), container) + if err != nil { + // TODO: how to delete the container? + return cli.NewExitError(fmt.Errorf("Failed to load the container after created, err: %#v", err), -1) + } + defer func() { + if !context.Bool("detach") || retErr != nil { + err := cmdDeleteContainer(context, container, true, spec, state) + if retErr == nil { + retErr = err + } + } + }() + + err = cmdStartContainer(context, container, spec, state) + if err != nil { + return cli.NewExitError(err, -1) + } + + if !context.Bool("detach") { + shim, err := os.FindProcess(state.Pid) + if err != nil { + return err + } + ret, err := osProcessWait(shim) + if ret == 0 { + return nil + } + return cli.NewExitError(err, ret) + } + return nil }, } diff --git a/cli/sandbox.go b/cli/sandbox.go new file mode 100644 index 00000000..dbd339d4 --- /dev/null +++ b/cli/sandbox.go @@ -0,0 +1,277 @@ +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "syscall" + "time" + + "github.com/golang/glog" + "github.com/hyperhq/runv/api" + "github.com/hyperhq/runv/factory" + singlefactory "github.com/hyperhq/runv/factory/single" + templatefactory "github.com/hyperhq/runv/factory/template" + "github.com/hyperhq/runv/hyperstart/libhyperstart" + "github.com/hyperhq/runv/hypervisor" + templatecore "github.com/hyperhq/runv/template" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/urfave/cli" +) + +func setupFactory(context *cli.Context, spec *specs.Spec) (factory.Factory, error) { + kernel, initrd, bios, cbfs, err := getKernelFiles(context, spec) + if err != nil { + return nil, fmt.Errorf("can't find kernel/initrd/bios/cbfs files") + } + glog.V(3).Infof("Using kernel: %s; Initrd: %s; bios: %s; cbfs: %s;", kernel, initrd, bios, cbfs) + + driver := context.GlobalString("driver") + vsock := context.GlobalBool("vsock") + template := context.GlobalString("template") + + var tconfig *templatecore.TemplateVmConfig + if template != "" { + path := filepath.Join(template, "config.json") + f, err := os.Open(path) + if err != nil { + err = fmt.Errorf("open template JSON configuration file failed: %v", err) + glog.Error(err) + return nil, err + } + if err := json.NewDecoder(f).Decode(&tconfig); err != nil { + err = fmt.Errorf("parse template JSON configuration file failed: %v", err) + glog.Error(err) + f.Close() + return nil, err + } + f.Close() + + if (driver != "" && driver != tconfig.Driver) || + (kernel != "" && kernel != tconfig.Config.Kernel) || + (initrd != "" && initrd != tconfig.Config.Initrd) || + (bios != "" && bios != tconfig.Config.Bios) || + (cbfs != "" && cbfs != tconfig.Config.Cbfs) { + glog.Warningf("template config is not match the driver, kernel, initrd, bios or cbfs argument, disable template") + template = "" + } else if driver == "" { + driver = tconfig.Driver + } + } else if (bios == "" || cbfs == "") && (kernel == "" || initrd == "") { + err := fmt.Errorf("argument kernel+initrd or bios+cbfs must be set") + glog.Error(err) + return nil, err + } + + if template != "" { + return singlefactory.New(templatefactory.NewFromExisted(tconfig)), nil + } + bootConfig := hypervisor.BootConfig{ + Kernel: kernel, + Initrd: initrd, + Bios: bios, + Cbfs: cbfs, + EnableVsock: vsock, + } + return singlefactory.Dummy(bootConfig), nil +} + +func createAndLockSandBox(f factory.Factory, spec *specs.Spec, cpu int, mem int) (*hypervisor.Vm, *os.File, error) { + if spec.Linux != nil && spec.Linux.Resources != nil && spec.Linux.Resources.Memory != nil && spec.Linux.Resources.Memory.Limit != nil { + mem = int(*spec.Linux.Resources.Memory.Limit >> 20) + } + + vm, err := f.GetVm(cpu, mem) + if err != nil { + glog.Errorf("Create VM failed with err: %v", err) + return nil, nil, err + } + + r := make(chan api.Result, 1) + go func() { + r <- vm.WaitInit() + }() + + sandbox := api.SandboxInfoFromOCF(spec) + vm.InitSandbox(sandbox) + + rsp := <-r + + if !rsp.IsSuccess() { + vm.Kill() + glog.Errorf("StartPod fail, response: %#v", rsp) + return nil, nil, fmt.Errorf("StartPod fail") + } + glog.V(3).Infof("%s init sandbox successfully", rsp.ResultId()) + + lockFile, err := lockSandbox(sandboxPath(vm)) + if err != nil { + vm.Kill() + return nil, nil, err + } + + return vm, lockFile, nil +} + +func lockAndAssociateSandbox(sandboxPath string) (*hypervisor.Vm, *os.File, error) { + sandboxIDPath, err := os.Readlink(sandboxPath) + if err != nil { + return nil, nil, err + } + + lockFile, err := lockSandbox(sandboxPath) + if err != nil { + return nil, nil, err + } + + pinfoPath := filepath.Join(sandboxIDPath, "persist.json") + data, err := ioutil.ReadFile(pinfoPath) + if err != nil { + unlockSandbox(lockFile) + return nil, nil, err + } + sandboxID := filepath.Base(sandboxIDPath) + vm, err := hypervisor.AssociateVm(sandboxID, data) + if err != nil { + unlockSandbox(lockFile) + return nil, nil, err + } + return vm, lockFile, nil +} + +func destroySandbox(vm *hypervisor.Vm, lockFile *os.File) { + result := make(chan api.Result, 1) + go func() { + result <- vm.Shutdown() + }() + select { + case rsp, ok := <-result: + if !ok || !rsp.IsSuccess() { + glog.Errorf("StopPod fail: chan: %v, response: %v", ok, rsp) + break + } + glog.V(1).Infof("StopPod successfully") + case <-time.After(time.Second * 60): + glog.Errorf("StopPod timeout") + } + vm.Kill() + + // cli refactor todo: kill the proxy if vm.Shutdown() failed. + + unlockSandbox(lockFile) + + if err := os.RemoveAll(sandboxPath(vm)); err != nil { + glog.Errorf("can't remove vm dir %q: %v", filepath.Join(hypervisor.BaseDir, vm.Id), err) + } + glog.Flush() +} + +func releaseAndUnlockSandbox(vm *hypervisor.Vm, lockFile *os.File) error { + data, err := vm.Dump() + if err != nil { + unlockSandbox(lockFile) + return err + } + err = vm.ReleaseVm() + if err != nil { + unlockSandbox(lockFile) + return err + } + pinfoPath := filepath.Join(sandboxPath(vm), "persist.json") + err = ioutil.WriteFile(pinfoPath, data, 0644) + if err != nil { + unlockSandbox(lockFile) + return err + } + + unlockSandbox(lockFile) + return nil +} + +var getSandbox = lockAndAssociateSandbox + +func putSandbox(vm *hypervisor.Vm, lockFile *os.File) { + if len(vm.ContainerList()) > 0 { + err := releaseAndUnlockSandbox(vm, lockFile) + if err == nil { + return + } + // fallthrough: can't recover, destory the whole sandbox + } + destroySandbox(vm, lockFile) +} + +func sandboxPath(vm *hypervisor.Vm) string { + return filepath.Join(hypervisor.BaseDir, vm.Id) +} + +func setupHyperstartFunc(context *cli.Context) { + libhyperstart.NewHyperstart = func(vmid, ctlSock, streamSock string, lastStreamSeq uint64, waitReady, paused bool) (libhyperstart.Hyperstart, error) { + return newHyperstart(context, vmid, ctlSock, streamSock) + } +} + +func newHyperstart(context *cli.Context, vmid, ctlSock, streamSock string) (libhyperstart.Hyperstart, error) { + grpcSock := filepath.Join(hypervisor.BaseDir, vmid, "hyperstartgrpc.sock") + + glog.Infof("newHyperstart() on socket: %s", grpcSock) + if st, err := os.Stat(grpcSock); err != nil { + glog.V(2).Infof("grpcSock stat: %#v, err: %#v", st, err) + if !os.IsNotExist(err) { + glog.Errorf("%s existed with wrong stats", grpcSock) + return nil, fmt.Errorf("%s existed with wrong stats", grpcSock) + } + err = createProxy(context, vmid, ctlSock, streamSock, grpcSock) + if err != nil { + return nil, err + } + + for i := 0; i < 500; i++ { + if _, err := os.Stat(grpcSock); !os.IsNotExist(err) { + break + } + time.Sleep(20 * time.Millisecond) + } + } + + h, err := libhyperstart.NewGrpcBasedHyperstart(grpcSock) + if err != nil { + glog.Errorf("libhyperstart.NewGrpcBasedHyperstart() failed with err: %#v", err) + } + return h, err +} + +// lock locks the sandbox to prevent it from being accessed by other processes. +func lockSandbox(sandboxPath string) (*os.File, error) { + lockFilePath := filepath.Join(sandboxPath, "sandbox.lock") + + lockFile, err := os.OpenFile(lockFilePath, os.O_RDWR|os.O_CREATE, 0755) + if err != nil { + return nil, err + } + + err = syscall.Flock(int(lockFile.Fd()), syscall.LOCK_EX) + if err != nil { + return nil, err + } + + return lockFile, nil +} + +// unlock unlocks the sandbox to allow it being accessed by other processes. +func unlockSandbox(lockFile *os.File) error { + if lockFile == nil { + return fmt.Errorf("lockFile cannot be empty") + } + + err := syscall.Flock(int(lockFile.Fd()), syscall.LOCK_UN) + if err != nil { + return err + } + + lockFile.Close() + + return nil +} diff --git a/cli/shim.go b/cli/shim.go new file mode 100644 index 00000000..5713556e --- /dev/null +++ b/cli/shim.go @@ -0,0 +1,251 @@ +package main + +import ( + "fmt" + "io" + "os" + "os/exec" + "os/signal" + "path/filepath" + "sync" + "syscall" + + "github.com/golang/glog" + _ "github.com/hyperhq/runv/cli/nsenter" + "github.com/hyperhq/runv/hyperstart/libhyperstart" + "github.com/hyperhq/runv/lib/term" + "github.com/kardianos/osext" + "github.com/kr/pty" + "github.com/urfave/cli" +) + +var shimCommand = cli.Command{ + Name: "shim", + Usage: "[internal command] proxy operations(io, signal ...) to the container/process", + HideHelp: true, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "container", + }, + cli.StringFlag{ + Name: "process", + }, + cli.BoolFlag{ + Name: "proxy-exit-code", + }, + cli.BoolFlag{ + Name: "proxy-stdio", + }, + cli.BoolFlag{ + Name: "proxy-signal", + }, + cli.BoolFlag{ + Name: "proxy-winsize", + }, + }, + Before: func(context *cli.Context) error { + return cmdPrepare(context, false, false) + }, + Action: func(context *cli.Context) error { + container := context.String("container") + process := context.String("process") + + h, err := libhyperstart.NewGrpcBasedHyperstart(filepath.Join(context.GlobalString("root"), container, "sandbox", "hyperstartgrpc.sock")) + if err != nil { + return cli.NewExitError(fmt.Sprintf("failed to connect to hyperstart proxy: %v", err), -1) + } + + if process == "init" { + waitSigUsr1 := make(chan os.Signal, 1) + signal.Notify(waitSigUsr1, syscall.SIGUSR1) + <-waitSigUsr1 + signal.Stop(waitSigUsr1) + } + + if context.Bool("proxy-stdio") { + wg := &sync.WaitGroup{} + proxyStdio(h, container, process, wg) + defer wg.Wait() + } + + if context.Bool("proxy-winsize") { + glog.V(3).Infof("using shim to proxy winsize") + s, err := term.SetRawTerminal(os.Stdin.Fd()) + if err != nil { + return cli.NewExitError(fmt.Sprintf("failed to set raw terminal: %v", err), -1) + } + defer term.RestoreTerminal(os.Stdin.Fd(), s) + monitorTtySize(h, container, process) + } + + if context.Bool("proxy-signal") { + // TODO + glog.V(3).Infof("using shim to proxy signal") + sigc := forwardAllSignals(h, container, process) + defer signal.Stop(sigc) + } + + // wait until exit + exitcode := h.WaitProcess(container, process) + if context.Bool("proxy-exit-code") { + glog.V(3).Infof("using shim to proxy exit code: %d", exitcode) + if exitcode != 0 { + cli.NewExitError("process returns non zero exit code", exitcode) + } + return nil + } + + return nil + }, +} + +func proxyStdio(h libhyperstart.Hyperstart, container, process string, wg *sync.WaitGroup) { + // don't wait the copying of the stdin, because `io.Copy(inPipe, os.Stdin)` + // can't terminate when no input. todo: find a better way. + wg.Add(2) + inPipe, outPipe, errPipe := libhyperstart.StdioPipe(h, container, process) + go func() { + _, err1 := io.Copy(inPipe, os.Stdin) + err2 := h.CloseStdin(container, process) + glog.V(3).Infof("copy stdin %#v %#v", err1, err2) + }() + + go func() { + _, err := io.Copy(os.Stdout, outPipe) + glog.V(3).Infof("copy stdout %#v", err) + wg.Done() + }() + + go func() { + _, err := io.Copy(os.Stderr, errPipe) + glog.V(3).Infof("copy stderr %#v", err) + wg.Done() + }() +} + +func forwardAllSignals(h libhyperstart.Hyperstart, container, process string) chan os.Signal { + sigc := make(chan os.Signal, 2048) + // handle all signals for the process. + signal.Notify(sigc) + signal.Ignore(syscall.SIGCHLD, syscall.SIGPIPE, syscall.SIGWINCH) + + go func() { + for s := range sigc { + if s == syscall.SIGCHLD || s == syscall.SIGPIPE || s == syscall.SIGWINCH { + //ignore these + continue + } + // forward this signal to container + sysSig, ok := s.(syscall.Signal) + if !ok { + err := fmt.Errorf("can't forward unknown signal %q", s.String()) + fmt.Fprintf(os.Stderr, "%v", err) + glog.Errorf("%v", err) + continue + } + if err := h.SignalProcess(container, process, sysSig); err != nil { + err = fmt.Errorf("forward signal %q failed: %v", s.String(), err) + fmt.Fprintf(os.Stderr, "%v", err) + glog.Errorf("%v", err) + } + } + }() + return sigc +} + +func createShim(options runvOptions, container, process string) (*os.Process, error) { + path, err := osext.Executable() + if err != nil { + return nil, fmt.Errorf("cannot find self executable path for %s: %v", os.Args[0], err) + } + + var ptymaster, tty *os.File + if options.String("console") != "" { + tty, err = os.OpenFile(options.String("console"), os.O_RDWR, 0) + if err != nil { + return nil, err + } + } else if options.String("console-socket") != "" { + ptymaster, tty, err = pty.Open() + if err != nil { + return nil, err + } + if err = sendtty(options.String("console-socket"), ptymaster); err != nil { + return nil, err + } + ptymaster.Close() + } + + args := []string{"runv", "--root", options.GlobalString("root")} + if options.GlobalString("log_dir") != "" { + args = append(args, "--log_dir", filepath.Join(options.GlobalString("log_dir"), "shim-"+container)) + } + if options.GlobalBool("debug") { + args = append(args, "--debug") + } + args = append(args, "shim", "--container", container, "--process", process) + args = append(args, "--proxy-stdio", "--proxy-exit-code", "--proxy-signal") + if tty != nil { + args = append(args, "--proxy-winsize") + } + + cmd := exec.Cmd{ + Path: path, + Args: args, + Dir: "/", + SysProcAttr: &syscall.SysProcAttr{ + Setctty: tty != nil, + Setsid: true, + }, + } + if options.withContainer == nil { + cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWNET + } else { + cmd.Env = append(os.Environ(), fmt.Sprintf("_RUNVNETNSPID=%d", options.withContainer.Pid)) + } + if tty == nil { + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + } else { + defer tty.Close() + cmd.Stdin = tty + cmd.Stdout = tty + cmd.Stderr = tty + } + + err = cmd.Start() + if err != nil { + return nil, err + } + + if options.String("pid-file") != "" { + err = createPidFile(options.String("pid-file"), cmd.Process.Pid) + if err != nil { + cmd.Process.Kill() + return nil, err + } + } + + return cmd.Process, nil +} + +// createPidFile creates a file with the processes pid inside it atomically +// it creates a temp file with the paths filename + '.' infront of it +// then renames the file +func createPidFile(path string, pid int) error { + var ( + tmpDir = filepath.Dir(path) + tmpName = filepath.Join(tmpDir, fmt.Sprintf(".%s", filepath.Base(path))) + ) + f, err := os.OpenFile(tmpName, os.O_RDWR|os.O_CREATE|os.O_EXCL|os.O_SYNC, 0666) + if err != nil { + return err + } + _, err = fmt.Fprintf(f, "%d", pid) + f.Close() + if err != nil { + return err + } + return os.Rename(tmpName, path) +} diff --git a/spec.go b/cli/spec.go similarity index 57% rename from spec.go rename to cli/spec.go index c6713ce3..4e371347 100644 --- a/spec.go +++ b/cli/spec.go @@ -5,8 +5,10 @@ import ( "fmt" "io/ioutil" "os" + "path/filepath" "runtime" + "github.com/golang/glog" "github.com/opencontainers/runtime-spec/specs-go" "github.com/urfave/cli" ) @@ -20,6 +22,9 @@ var specCommand = cli.Command{ Usage: "path to the root of the bundle directory", }, }, + Before: func(context *cli.Context) error { + return cmdPrepare(context, false, false) + }, Action: func(context *cli.Context) { spec := specs.Spec{ Version: specs.Version, @@ -103,3 +108,61 @@ func loadSpec(ocffile string) (*specs.Spec, error) { } return &spec, nil } + +// loadProcessConfig loads the process configuration from the provided path. +func loadProcessConfig(path string) (*specs.Process, error) { + f, err := os.Open(path) + if err != nil { + if os.IsNotExist(err) { + return nil, fmt.Errorf("JSON configuration file for %s not found", path) + } + return nil, err + } + defer f.Close() + var s *specs.Process + if err := json.NewDecoder(f).Decode(&s); err != nil { + return nil, err + } + return s, nil +} + +func saveStateFile(stateFile string, state *specs.State) error { + stateData, err := json.MarshalIndent(state, "", "\t") + if err != nil { + glog.V(1).Infof("%s\n", err.Error()) + return err + } + err = ioutil.WriteFile(stateFile, stateData, 0644) + if err != nil { + glog.V(1).Infof("%s\n", err.Error()) + return err + } + return nil +} + +func loadStateFile(root, container string) (*specs.State, error) { + stateFile := filepath.Join(root, container, stateJSON) + file, err := os.Open(stateFile) + if err != nil { + return nil, err + } + defer file.Close() + + var state specs.State + if err = json.NewDecoder(file).Decode(&state); err != nil { + return nil, fmt.Errorf("Decode state file %s error: %s", stateFile, err.Error()) + } + return &state, nil +} + +func loadStateAndSpec(root, container string) (*specs.State, *specs.Spec, error) { + state, err := loadStateFile(root, container) + if err != nil { + return nil, nil, err + } + spec, err := loadSpec(filepath.Join(state.Bundle, specConfig)) + if err != nil { + return nil, nil, err + } + return state, spec, nil +} diff --git a/cli/start.go b/cli/start.go new file mode 100644 index 00000000..606d5517 --- /dev/null +++ b/cli/start.go @@ -0,0 +1,59 @@ +package main + +import ( + "os" + "path/filepath" + + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/urfave/cli" +) + +var startCommand = cli.Command{ + Name: "start", + Usage: "executes the user defined process in a created container", + ArgsUsage: ` + +Where "" is your name for the instance of the container that you +are starting. The name you provide for the container instance must be unique on +your host.`, + Description: `The start command executes the user defined process in a created container`, + Flags: []cli.Flag{}, + Before: func(context *cli.Context) error { + return cmdPrepare(context, true, true) + }, + Action: func(context *cli.Context) error { + root := context.GlobalString("root") + container := context.Args().First() + + if container == "" { + return cli.NewExitError("Please specify container ID", -1) + } + if os.Geteuid() != 0 { + return cli.NewExitError("runv should be run as root", -1) + } + + state, spec, err := loadStateAndSpec(root, container) + if err != nil { + return cli.NewExitError(err, -1) + } + + err = cmdStartContainer(context, container, spec, state) + if err != nil { + return cli.NewExitError(err, -1) + } + return nil + }, +} + +func cmdStartContainer(context *cli.Context, container string, config *specs.Spec, state *specs.State) error { + vm, fileLock, err := getSandbox(filepath.Join(context.GlobalString("root"), container, "sandbox")) + if err != nil { + return err + } + defer putSandbox(vm, fileLock) // todo handle error when disassociation + err = startContainer(vm, container, config, state) + if err != nil { + // todo remove the container + } + return err +} diff --git a/state.go b/cli/state.go similarity index 92% rename from state.go rename to cli/state.go index a3a7bfb4..2d466c0c 100644 --- a/state.go +++ b/cli/state.go @@ -41,6 +41,9 @@ var stateCommand = cli.Command{ Where "" is your name for the instance of the container.`, Description: `The state command outputs current state information for the instance of a container.`, + Before: func(context *cli.Context) error { + return cmdPrepare(context, false, false) + }, Action: func(context *cli.Context) { id := context.Args().First() if id == "" { @@ -65,7 +68,7 @@ func getContainer(context *cli.Context, name string) (*cState, error) { return nil, err } - stateFile := filepath.Join(absRoot, name, stateJson) + stateFile := filepath.Join(absRoot, name, stateJSON) fi, err := os.Stat(stateFile) if err != nil { if os.IsNotExist(err) { @@ -74,7 +77,7 @@ func getContainer(context *cli.Context, name string) (*cState, error) { return nil, fmt.Errorf("Stat file %s error: %s", stateFile, err.Error()) } - state, err := loadStateFile(stateFile) + state, err := loadStateFile(absRoot, name) if err != nil { return nil, fmt.Errorf("Load state file %s failed: %s", stateFile, err.Error()) } diff --git a/tty.go b/cli/tty.go similarity index 63% rename from tty.go rename to cli/tty.go index 14913734..4150e0f6 100644 --- a/tty.go +++ b/cli/tty.go @@ -7,36 +7,30 @@ import ( "os/signal" "syscall" - "github.com/hyperhq/runv/containerd/api/grpc/types" + "github.com/hyperhq/runv/hyperstart/libhyperstart" "github.com/hyperhq/runv/lib/term" "github.com/opencontainers/runc/libcontainer/utils" - netcontext "golang.org/x/net/context" ) -func resizeTty(c types.APIClient, container, process string) { +func resizeTty(h libhyperstart.Hyperstart, container, process string) { ws, err := term.GetWinsize(os.Stdin.Fd()) if err != nil { fmt.Printf("Error getting size: %s", err.Error()) return } - if _, err = c.UpdateProcess(netcontext.Background(), &types.UpdateProcessRequest{ - Id: container, - Pid: process, - Width: uint32(ws.Width), - Height: uint32(ws.Height), - }); err != nil { + if err = h.TtyWinResize(container, process, uint16(ws.Height), uint16(ws.Width)); err != nil { fmt.Printf("set winsize failed, %v\n", err) } } -func monitorTtySize(c types.APIClient, container, process string) { - resizeTty(c, container, process) +func monitorTtySize(h libhyperstart.Hyperstart, container, process string) { + resizeTty(h, container, process) sigchan := make(chan os.Signal, 1) signal.Notify(sigchan, syscall.SIGWINCH) go func() { for range sigchan { - resizeTty(c, container, process) + resizeTty(h, container, process) } }() } diff --git a/cli/utils.go b/cli/utils.go new file mode 100644 index 00000000..e5601057 --- /dev/null +++ b/cli/utils.go @@ -0,0 +1,152 @@ +package main + +import ( + "fmt" + "os" + "path/filepath" + "strings" + "syscall" + + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/urfave/cli" +) + +const ( + defaultKernelInstallDir string = "/var/lib/hyper" +) + +func firstExistingFile(candidates []string) string { + for _, file := range candidates { + if _, err := os.Stat(file); err == nil { + return file + } + } + return "" +} + +func getDefaultBundlePath() string { + cwd, err := os.Getwd() + if err != nil { + return "" + } + return cwd +} + +// find it from spec.Process.Env, runv's env(todo) and context.GlobalString +func chooseKernelFromConfigs(context *cli.Context, spec *specs.Spec) string { + for k, env := range spec.Process.Env { + slices := strings.Split(env, "=") + if len(slices) == 2 && slices[0] == "hypervisor.kernel" { + // remove kernel env because this is only allow to be used by runv + spec.Process.Env = append(spec.Process.Env[:k], spec.Process.Env[k+1:]...) + return slices[1] + } + } + return context.GlobalString("kernel") +} + +func chooseInitrdFromConfigs(context *cli.Context, spec *specs.Spec) string { + for k, env := range spec.Process.Env { + slices := strings.Split(env, "=") + if len(slices) == 2 && slices[0] == "hypervisor.initrd" { + // remove initrd env because this is only allow to be used by runv + spec.Process.Env = append(spec.Process.Env[:k], spec.Process.Env[k+1:]...) + return slices[1] + } + } + return context.GlobalString("initrd") +} + +func chooseBiosFromConfigs(context *cli.Context, spec *specs.Spec) string { + for k, env := range spec.Process.Env { + slices := strings.Split(env, "=") + if len(slices) == 2 && slices[0] == "hypervisor.bios" { + // remove bios env because this is only allow to be used by runv + spec.Process.Env = append(spec.Process.Env[:k], spec.Process.Env[k+1:]...) + return slices[1] + } + } + return context.GlobalString("bios") +} + +func chooseCbfsFromConfigs(context *cli.Context, spec *specs.Spec) string { + for k, env := range spec.Process.Env { + slices := strings.Split(env, "=") + if len(slices) == 2 && slices[0] == "hypervisor.cbfs" { + // remove cbfs env because this is only allow to be used by runv + spec.Process.Env = append(spec.Process.Env[:k], spec.Process.Env[k+1:]...) + return slices[1] + } + } + return context.GlobalString("cbfs") +} + +// getKernelFiles chooses kernel/initrd/bios/cbfs files based on user specified ones +func getKernelFiles(context *cli.Context, spec *specs.Spec) (string, string, string, string, error) { + kernel := chooseKernelFromConfigs(context, spec) + initrd := chooseInitrdFromConfigs(context, spec) + bios := chooseBiosFromConfigs(context, spec) + cbfs := chooseCbfsFromConfigs(context, spec) + bundle := context.String("bundle") + + if kernel != "" && initrd != "" { + return kernel, initrd, bios, cbfs, nil + } + + // choose from filesystem + for k, v := range map[*string][]string{ + &kernel: { + filepath.Join(bundle, spec.Root.Path, "boot/vmlinuz"), + filepath.Join(bundle, "boot/vmlinuz"), + filepath.Join(bundle, "vmlinuz"), + filepath.Join(defaultKernelInstallDir, "kernel"), + }, + &initrd: { + filepath.Join(bundle, spec.Root.Path, "boot/initrd.img"), + filepath.Join(bundle, "boot/initrd.img"), + filepath.Join(bundle, "initrd.img"), + filepath.Join(defaultKernelInstallDir, "hyper-initrd.img"), + }, + &bios: { + filepath.Join(bundle, spec.Root.Path, "boot/bios.bin"), + filepath.Join(bundle, "boot/bios.bin"), + filepath.Join(bundle, "bios.bin"), + filepath.Join(defaultKernelInstallDir, "bios.bin"), + }, + &cbfs: { + filepath.Join(bundle, spec.Root.Path, "boot/cbfs.rom"), + filepath.Join(bundle, "boot/cbfs.rom"), + filepath.Join(bundle, "cbfs.rom"), + filepath.Join(defaultKernelInstallDir, "cbfs.rom"), + }, + } { + if *k == "" { + *k = firstExistingFile(v) + } + if *k != "" { + var err error + *k, err = filepath.Abs(*k) + if err != nil { + return "", "", "", "", fmt.Errorf("cannot get abs path for kernel files: %v", err) + } + } + } + + return kernel, initrd, bios, cbfs, nil +} + +func osProcessWait(process *os.Process) (int, error) { + state, err := process.Wait() + if err != nil { + return -1, err + } + if state.Success() { + return 0, nil + } + + if status, ok := state.Sys().(syscall.WaitStatus); ok { + return status.ExitStatus(), err + } + + return -1, err +} diff --git a/containerd/README.md b/containerd/README.md deleted file mode 100644 index 181b4092..00000000 --- a/containerd/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# containerd - -runv-containerd is a daemon to control hypercontainer and supports the same gRPC api of the [docker-containerd](https://github.com/docker/containerd/). - -## Getting started - - - You need to [build it from source](https://github.com/hyperhq/runv#build) at first. - - Then you can combind it with [docker](https://github.com/docker/docker) or [containerd-ctr](https://github.com/docker/containerd/tree/master/ctr). - -### Dependencies - - If you want to enable network for container, Kernels newer than `Linux 4.1-rc1` or [this commit](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/drivers/net/veth.c?id=a45253bf32bf49cdb2807bad212b84f5ab51ac26) are required. - -### Try it with docker (test with docker version 17.05.0) - -```bash -# in terminal #1 -runv --debug --driver libvirt --kernel /opt/hyperstart/build/kernel --initrd /opt/hyperstart/build/hyper-initrd.img containerd -# in terminal #2 -dockerd -D -l debug --containerd=/run/runv-containerd/containerd.sock --add-runtime runv=runv --default-runtime runv -# in terminal #3 for trying it -docker run -ti busybox -# ls # (already in the terminal of the busybox container) -# exit # (quit the container) -``` - -## Build -The same as [the build of runv](https://github.com/hyperhq/runv#build) - -## Docs - - * [Containerd Client CLI reference (`ctr`)](https://github.com/docker/containerd/tree/master/docs/cli.md) - * [Creating OCI bundles](https://github.com/docker/containerd/tree/master/docs/bundle.md) - * [Docker CommandLine reference](https://github.com/docker/docker/tree/master/docs/reference/commandline) - diff --git a/containerd/api/grpc/server/server.go b/containerd/api/grpc/server/server.go deleted file mode 100644 index fbf5175e..00000000 --- a/containerd/api/grpc/server/server.go +++ /dev/null @@ -1,241 +0,0 @@ -package server - -import ( - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "path/filepath" - "time" - - "github.com/cloudfoundry/gosigar" - "github.com/golang/glog" - "github.com/golang/protobuf/ptypes" - "github.com/hyperhq/runv/containerd/api/grpc/types" - "github.com/hyperhq/runv/supervisor" - "github.com/opencontainers/runtime-spec/specs-go" - "golang.org/x/net/context" -) - -type apiServer struct { - sv *supervisor.Supervisor -} - -// NewServer returns grpc server instance -func NewServer(sv *supervisor.Supervisor) types.APIServer { - return &apiServer{ - sv: sv, - } -} - -func (s *apiServer) GetServerVersion(ctx context.Context, c *types.GetServerVersionRequest) (*types.GetServerVersionResponse, error) { - return &types.GetServerVersionResponse{ - Major: 0, - Minor: 6, - Patch: 2, - Revision: "runv-containerd", - }, nil -} - -func (s *apiServer) CreateContainer(ctx context.Context, r *types.CreateContainerRequest) (*types.CreateContainerResponse, error) { - glog.V(3).Infof("gRPC handle CreateContainer") - if r.Runtime != "" && r.Runtime != "runv" && r.Runtime != "runv-create" && r.Runtime != "runv-start" { - return nil, fmt.Errorf("unknown runtime: %q", r.Runtime) - } - if r.BundlePath == "" { - return nil, errors.New("empty bundle path") - } - - var spec specs.Spec - ocfData, err := ioutil.ReadFile(filepath.Join(r.BundlePath, "config.json")) - if err != nil { - return nil, err - } - if err := json.Unmarshal(ocfData, &spec); err != nil { - return nil, err - } - - var c *supervisor.Container - var p *supervisor.Process - - if r.Runtime == "" || r.Runtime == "runv" || r.Runtime == "runv-create" { - c, err = s.sv.CreateContainer(r.Id, r.BundlePath, r.Stdin, r.Stdout, r.Stderr, &spec) - if err != nil { - return nil, err - } - } - - if r.Runtime == "" || r.Runtime == "runv" || r.Runtime == "runv-start" { - c, p, err = s.sv.StartContainer(r.Id, &spec) - if err != nil { - return nil, err - } - } - - glog.V(3).Infof("end Supervisor.CreateContainer(), build api Container") - apiC := supervisorContainer2ApiContainer(c) - if p != nil { - apiP := supervisorProcess2ApiProcess(p) - addApiProcess2ApiContainer(apiC, apiP) - } - - glog.V(3).Infof("gRPC respond CreateContainer") - return &types.CreateContainerResponse{ - Container: apiC, - }, nil -} - -func (s *apiServer) Signal(ctx context.Context, r *types.SignalRequest) (*types.SignalResponse, error) { - glog.V(3).Infof("gRPC handle Signal") - err := s.sv.Signal(r.Id, r.Pid, int(r.Signal)) - if err != nil { - return nil, err - } - return &types.SignalResponse{}, nil -} - -func (s *apiServer) AddProcess(ctx context.Context, r *types.AddProcessRequest) (*types.AddProcessResponse, error) { - if r.Id == "" { - return nil, fmt.Errorf("container id cannot be empty") - } - if r.Pid == "" { - return nil, fmt.Errorf("process id cannot be empty") - } - - if len(r.Args) == 0 { - return nil, fmt.Errorf("args cannot be empty") - } - - spec := &specs.Process{ - Terminal: r.Terminal, - Args: r.Args, - Env: r.Env, - Cwd: r.Cwd, - } - _, err := s.sv.AddProcess(r.Id, r.Pid, r.Stdin, r.Stdout, r.Stderr, spec) - if err != nil { - return nil, err - } - return &types.AddProcessResponse{}, nil -} - -func (s *apiServer) State(ctx context.Context, r *types.StateRequest) (*types.StateResponse, error) { - cpu := sigar.CpuList{} - if err := cpu.Get(); err != nil { - return nil, err - } - mem := sigar.Mem{} - if err := mem.Get(); err != nil { - return nil, err - } - state := &types.StateResponse{ - Machine: &types.Machine{ - Cpus: uint32(len(cpu.List)), - Memory: uint64(mem.Total / 1024 / 1024), - }, - } - s.sv.RLock() - for _, c := range s.sv.Containers { - apiC := supervisorContainer2ApiContainer(c) - for _, p := range c.Processes { - addApiProcess2ApiContainer(apiC, supervisorProcess2ApiProcess(p)) - } - state.Containers = append(state.Containers, apiC) - } - s.sv.RUnlock() - return state, nil -} - -func (s *apiServer) UpdateContainer(ctx context.Context, r *types.UpdateContainerRequest) (*types.UpdateContainerResponse, error) { - return nil, errors.New("UpdateContainer() not implemented yet") -} - -func (s *apiServer) UpdateProcess(ctx context.Context, r *types.UpdateProcessRequest) (*types.UpdateProcessResponse, error) { - var err error - if r.CloseStdin { - err = s.sv.CloseStdin(r.Id, r.Pid) - } else { - err = s.sv.TtyResize(r.Id, r.Pid, int(r.Width), int(r.Height)) - } - if err != nil { - return nil, err - } - return &types.UpdateProcessResponse{}, nil -} - -func (s *apiServer) Events(r *types.EventsRequest, stream types.API_EventsServer) error { - t := time.Time{} - if r.Timestamp != nil { - from, err := ptypes.Timestamp(r.Timestamp) - if err != nil { - return err - } - t = from - } - events := s.sv.Events.Events(t) - defer s.sv.Events.Unsubscribe(events) - for e := range events { - tsp, err := ptypes.TimestampProto(e.Timestamp) - if err != nil { - return err - } - if err := stream.Send(&types.Event{ - Id: e.ID, - Type: e.Type, - Timestamp: tsp, - Pid: e.PID, - Status: uint32(e.Status), - }); err != nil { - return err - } - } - return nil -} - -// TODO implement -func (s *apiServer) CreateCheckpoint(ctx context.Context, r *types.CreateCheckpointRequest) (*types.CreateCheckpointResponse, error) { - return nil, errors.New("CreateCheckpoint() not implemented yet") -} - -// TODO implement -func (s *apiServer) DeleteCheckpoint(ctx context.Context, r *types.DeleteCheckpointRequest) (*types.DeleteCheckpointResponse, error) { - return nil, errors.New("DeleteCheckpoint() not implemented yet") -} - -// TODO implement -func (s *apiServer) ListCheckpoint(ctx context.Context, r *types.ListCheckpointRequest) (*types.ListCheckpointResponse, error) { - return nil, errors.New("ListCheckpoint() not implemented yet") -} - -// TODO implement -func (s *apiServer) Stats(ctx context.Context, r *types.StatsRequest) (*types.StatsResponse, error) { - return nil, errors.New("Stats() not implemented yet") -} - -func supervisorProcess2ApiProcess(p *supervisor.Process) *types.Process { - return &types.Process{ - Pid: p.Id, - SystemPid: uint32(p.ProcId), - Terminal: p.Spec.Terminal, - Args: p.Spec.Args, - Env: p.Spec.Env, - Cwd: p.Spec.Cwd, - Stdin: p.Stdin, - Stdout: p.Stdout, - Stderr: p.Stderr, - } -} - -func supervisorContainer2ApiContainer(c *supervisor.Container) *types.Container { - return &types.Container{ - Id: c.Id, - BundlePath: c.BundlePath, - Status: "running", - Runtime: "runv", - } -} - -func addApiProcess2ApiContainer(apiC *types.Container, apiP *types.Process) { - apiC.Processes = append(apiC.Processes, apiP) - apiC.Pids = append(apiC.Pids, apiP.SystemPid) -} diff --git a/containerd/api/grpc/types/api.pb.go b/containerd/api/grpc/types/api.pb.go deleted file mode 100644 index f82777db..00000000 --- a/containerd/api/grpc/types/api.pb.go +++ /dev/null @@ -1,2421 +0,0 @@ -// Code generated by protoc-gen-gogo. -// source: api.proto -// DO NOT EDIT! - -/* -Package types is a generated protocol buffer package. - -It is generated from these files: - api.proto - -It has these top-level messages: - GetServerVersionRequest - GetServerVersionResponse - UpdateProcessRequest - UpdateProcessResponse - CreateContainerRequest - CreateContainerResponse - SignalRequest - SignalResponse - AddProcessRequest - Rlimit - User - AddProcessResponse - CreateCheckpointRequest - CreateCheckpointResponse - DeleteCheckpointRequest - DeleteCheckpointResponse - ListCheckpointRequest - Checkpoint - ListCheckpointResponse - StateRequest - ContainerState - Process - Container - Machine - StateResponse - UpdateContainerRequest - UpdateResource - UpdateContainerResponse - EventsRequest - Event - NetworkStats - CpuUsage - ThrottlingData - CpuStats - PidsStats - MemoryData - MemoryStats - BlkioStatsEntry - BlkioStats - HugetlbStats - CgroupStats - StatsResponse - StatsRequest -*/ -package types - -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import google_protobuf "github.com/golang/protobuf/ptypes/timestamp" - -import ( - context "golang.org/x/net/context" - grpc "google.golang.org/grpc" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package - -type GetServerVersionRequest struct { -} - -func (m *GetServerVersionRequest) Reset() { *m = GetServerVersionRequest{} } -func (m *GetServerVersionRequest) String() string { return proto.CompactTextString(m) } -func (*GetServerVersionRequest) ProtoMessage() {} -func (*GetServerVersionRequest) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{0} } - -type GetServerVersionResponse struct { - Major uint32 `protobuf:"varint,1,opt,name=major,proto3" json:"major,omitempty"` - Minor uint32 `protobuf:"varint,2,opt,name=minor,proto3" json:"minor,omitempty"` - Patch uint32 `protobuf:"varint,3,opt,name=patch,proto3" json:"patch,omitempty"` - Revision string `protobuf:"bytes,4,opt,name=revision,proto3" json:"revision,omitempty"` -} - -func (m *GetServerVersionResponse) Reset() { *m = GetServerVersionResponse{} } -func (m *GetServerVersionResponse) String() string { return proto.CompactTextString(m) } -func (*GetServerVersionResponse) ProtoMessage() {} -func (*GetServerVersionResponse) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{1} } - -func (m *GetServerVersionResponse) GetMajor() uint32 { - if m != nil { - return m.Major - } - return 0 -} - -func (m *GetServerVersionResponse) GetMinor() uint32 { - if m != nil { - return m.Minor - } - return 0 -} - -func (m *GetServerVersionResponse) GetPatch() uint32 { - if m != nil { - return m.Patch - } - return 0 -} - -func (m *GetServerVersionResponse) GetRevision() string { - if m != nil { - return m.Revision - } - return "" -} - -type UpdateProcessRequest struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Pid string `protobuf:"bytes,2,opt,name=pid,proto3" json:"pid,omitempty"` - CloseStdin bool `protobuf:"varint,3,opt,name=closeStdin,proto3" json:"closeStdin,omitempty"` - Width uint32 `protobuf:"varint,4,opt,name=width,proto3" json:"width,omitempty"` - Height uint32 `protobuf:"varint,5,opt,name=height,proto3" json:"height,omitempty"` -} - -func (m *UpdateProcessRequest) Reset() { *m = UpdateProcessRequest{} } -func (m *UpdateProcessRequest) String() string { return proto.CompactTextString(m) } -func (*UpdateProcessRequest) ProtoMessage() {} -func (*UpdateProcessRequest) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{2} } - -func (m *UpdateProcessRequest) GetId() string { - if m != nil { - return m.Id - } - return "" -} - -func (m *UpdateProcessRequest) GetPid() string { - if m != nil { - return m.Pid - } - return "" -} - -func (m *UpdateProcessRequest) GetCloseStdin() bool { - if m != nil { - return m.CloseStdin - } - return false -} - -func (m *UpdateProcessRequest) GetWidth() uint32 { - if m != nil { - return m.Width - } - return 0 -} - -func (m *UpdateProcessRequest) GetHeight() uint32 { - if m != nil { - return m.Height - } - return 0 -} - -type UpdateProcessResponse struct { -} - -func (m *UpdateProcessResponse) Reset() { *m = UpdateProcessResponse{} } -func (m *UpdateProcessResponse) String() string { return proto.CompactTextString(m) } -func (*UpdateProcessResponse) ProtoMessage() {} -func (*UpdateProcessResponse) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{3} } - -type CreateContainerRequest struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - BundlePath string `protobuf:"bytes,2,opt,name=bundlePath,proto3" json:"bundlePath,omitempty"` - Checkpoint string `protobuf:"bytes,3,opt,name=checkpoint,proto3" json:"checkpoint,omitempty"` - Stdin string `protobuf:"bytes,4,opt,name=stdin,proto3" json:"stdin,omitempty"` - Stdout string `protobuf:"bytes,5,opt,name=stdout,proto3" json:"stdout,omitempty"` - Stderr string `protobuf:"bytes,6,opt,name=stderr,proto3" json:"stderr,omitempty"` - Labels []string `protobuf:"bytes,7,rep,name=labels" json:"labels,omitempty"` - NoPivotRoot bool `protobuf:"varint,8,opt,name=noPivotRoot,proto3" json:"noPivotRoot,omitempty"` - Runtime string `protobuf:"bytes,9,opt,name=runtime,proto3" json:"runtime,omitempty"` - RuntimeArgs []string `protobuf:"bytes,10,rep,name=runtimeArgs" json:"runtimeArgs,omitempty"` - CheckpointDir string `protobuf:"bytes,11,opt,name=checkpointDir,proto3" json:"checkpointDir,omitempty"` -} - -func (m *CreateContainerRequest) Reset() { *m = CreateContainerRequest{} } -func (m *CreateContainerRequest) String() string { return proto.CompactTextString(m) } -func (*CreateContainerRequest) ProtoMessage() {} -func (*CreateContainerRequest) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{4} } - -func (m *CreateContainerRequest) GetId() string { - if m != nil { - return m.Id - } - return "" -} - -func (m *CreateContainerRequest) GetBundlePath() string { - if m != nil { - return m.BundlePath - } - return "" -} - -func (m *CreateContainerRequest) GetCheckpoint() string { - if m != nil { - return m.Checkpoint - } - return "" -} - -func (m *CreateContainerRequest) GetStdin() string { - if m != nil { - return m.Stdin - } - return "" -} - -func (m *CreateContainerRequest) GetStdout() string { - if m != nil { - return m.Stdout - } - return "" -} - -func (m *CreateContainerRequest) GetStderr() string { - if m != nil { - return m.Stderr - } - return "" -} - -func (m *CreateContainerRequest) GetLabels() []string { - if m != nil { - return m.Labels - } - return nil -} - -func (m *CreateContainerRequest) GetNoPivotRoot() bool { - if m != nil { - return m.NoPivotRoot - } - return false -} - -func (m *CreateContainerRequest) GetRuntime() string { - if m != nil { - return m.Runtime - } - return "" -} - -func (m *CreateContainerRequest) GetRuntimeArgs() []string { - if m != nil { - return m.RuntimeArgs - } - return nil -} - -func (m *CreateContainerRequest) GetCheckpointDir() string { - if m != nil { - return m.CheckpointDir - } - return "" -} - -type CreateContainerResponse struct { - Container *Container `protobuf:"bytes,1,opt,name=container" json:"container,omitempty"` -} - -func (m *CreateContainerResponse) Reset() { *m = CreateContainerResponse{} } -func (m *CreateContainerResponse) String() string { return proto.CompactTextString(m) } -func (*CreateContainerResponse) ProtoMessage() {} -func (*CreateContainerResponse) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{5} } - -func (m *CreateContainerResponse) GetContainer() *Container { - if m != nil { - return m.Container - } - return nil -} - -type SignalRequest struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Pid string `protobuf:"bytes,2,opt,name=pid,proto3" json:"pid,omitempty"` - Signal uint32 `protobuf:"varint,3,opt,name=signal,proto3" json:"signal,omitempty"` -} - -func (m *SignalRequest) Reset() { *m = SignalRequest{} } -func (m *SignalRequest) String() string { return proto.CompactTextString(m) } -func (*SignalRequest) ProtoMessage() {} -func (*SignalRequest) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{6} } - -func (m *SignalRequest) GetId() string { - if m != nil { - return m.Id - } - return "" -} - -func (m *SignalRequest) GetPid() string { - if m != nil { - return m.Pid - } - return "" -} - -func (m *SignalRequest) GetSignal() uint32 { - if m != nil { - return m.Signal - } - return 0 -} - -type SignalResponse struct { -} - -func (m *SignalResponse) Reset() { *m = SignalResponse{} } -func (m *SignalResponse) String() string { return proto.CompactTextString(m) } -func (*SignalResponse) ProtoMessage() {} -func (*SignalResponse) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{7} } - -type AddProcessRequest struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Terminal bool `protobuf:"varint,2,opt,name=terminal,proto3" json:"terminal,omitempty"` - User *User `protobuf:"bytes,3,opt,name=user" json:"user,omitempty"` - Args []string `protobuf:"bytes,4,rep,name=args" json:"args,omitempty"` - Env []string `protobuf:"bytes,5,rep,name=env" json:"env,omitempty"` - Cwd string `protobuf:"bytes,6,opt,name=cwd,proto3" json:"cwd,omitempty"` - Pid string `protobuf:"bytes,7,opt,name=pid,proto3" json:"pid,omitempty"` - Stdin string `protobuf:"bytes,8,opt,name=stdin,proto3" json:"stdin,omitempty"` - Stdout string `protobuf:"bytes,9,opt,name=stdout,proto3" json:"stdout,omitempty"` - Stderr string `protobuf:"bytes,10,opt,name=stderr,proto3" json:"stderr,omitempty"` - Capabilities []string `protobuf:"bytes,11,rep,name=capabilities" json:"capabilities,omitempty"` - ApparmorProfile string `protobuf:"bytes,12,opt,name=apparmorProfile,proto3" json:"apparmorProfile,omitempty"` - SelinuxLabel string `protobuf:"bytes,13,opt,name=selinuxLabel,proto3" json:"selinuxLabel,omitempty"` - NoNewPrivileges bool `protobuf:"varint,14,opt,name=noNewPrivileges,proto3" json:"noNewPrivileges,omitempty"` - Rlimits []*Rlimit `protobuf:"bytes,15,rep,name=rlimits" json:"rlimits,omitempty"` -} - -func (m *AddProcessRequest) Reset() { *m = AddProcessRequest{} } -func (m *AddProcessRequest) String() string { return proto.CompactTextString(m) } -func (*AddProcessRequest) ProtoMessage() {} -func (*AddProcessRequest) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{8} } - -func (m *AddProcessRequest) GetId() string { - if m != nil { - return m.Id - } - return "" -} - -func (m *AddProcessRequest) GetTerminal() bool { - if m != nil { - return m.Terminal - } - return false -} - -func (m *AddProcessRequest) GetUser() *User { - if m != nil { - return m.User - } - return nil -} - -func (m *AddProcessRequest) GetArgs() []string { - if m != nil { - return m.Args - } - return nil -} - -func (m *AddProcessRequest) GetEnv() []string { - if m != nil { - return m.Env - } - return nil -} - -func (m *AddProcessRequest) GetCwd() string { - if m != nil { - return m.Cwd - } - return "" -} - -func (m *AddProcessRequest) GetPid() string { - if m != nil { - return m.Pid - } - return "" -} - -func (m *AddProcessRequest) GetStdin() string { - if m != nil { - return m.Stdin - } - return "" -} - -func (m *AddProcessRequest) GetStdout() string { - if m != nil { - return m.Stdout - } - return "" -} - -func (m *AddProcessRequest) GetStderr() string { - if m != nil { - return m.Stderr - } - return "" -} - -func (m *AddProcessRequest) GetCapabilities() []string { - if m != nil { - return m.Capabilities - } - return nil -} - -func (m *AddProcessRequest) GetApparmorProfile() string { - if m != nil { - return m.ApparmorProfile - } - return "" -} - -func (m *AddProcessRequest) GetSelinuxLabel() string { - if m != nil { - return m.SelinuxLabel - } - return "" -} - -func (m *AddProcessRequest) GetNoNewPrivileges() bool { - if m != nil { - return m.NoNewPrivileges - } - return false -} - -func (m *AddProcessRequest) GetRlimits() []*Rlimit { - if m != nil { - return m.Rlimits - } - return nil -} - -type Rlimit struct { - Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` - Soft uint64 `protobuf:"varint,2,opt,name=soft,proto3" json:"soft,omitempty"` - Hard uint64 `protobuf:"varint,3,opt,name=hard,proto3" json:"hard,omitempty"` -} - -func (m *Rlimit) Reset() { *m = Rlimit{} } -func (m *Rlimit) String() string { return proto.CompactTextString(m) } -func (*Rlimit) ProtoMessage() {} -func (*Rlimit) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{9} } - -func (m *Rlimit) GetType() string { - if m != nil { - return m.Type - } - return "" -} - -func (m *Rlimit) GetSoft() uint64 { - if m != nil { - return m.Soft - } - return 0 -} - -func (m *Rlimit) GetHard() uint64 { - if m != nil { - return m.Hard - } - return 0 -} - -type User struct { - Uid uint32 `protobuf:"varint,1,opt,name=uid,proto3" json:"uid,omitempty"` - Gid uint32 `protobuf:"varint,2,opt,name=gid,proto3" json:"gid,omitempty"` - AdditionalGids []uint32 `protobuf:"varint,3,rep,packed,name=additionalGids" json:"additionalGids,omitempty"` -} - -func (m *User) Reset() { *m = User{} } -func (m *User) String() string { return proto.CompactTextString(m) } -func (*User) ProtoMessage() {} -func (*User) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{10} } - -func (m *User) GetUid() uint32 { - if m != nil { - return m.Uid - } - return 0 -} - -func (m *User) GetGid() uint32 { - if m != nil { - return m.Gid - } - return 0 -} - -func (m *User) GetAdditionalGids() []uint32 { - if m != nil { - return m.AdditionalGids - } - return nil -} - -type AddProcessResponse struct { -} - -func (m *AddProcessResponse) Reset() { *m = AddProcessResponse{} } -func (m *AddProcessResponse) String() string { return proto.CompactTextString(m) } -func (*AddProcessResponse) ProtoMessage() {} -func (*AddProcessResponse) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{11} } - -type CreateCheckpointRequest struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Checkpoint *Checkpoint `protobuf:"bytes,2,opt,name=checkpoint" json:"checkpoint,omitempty"` - CheckpointDir string `protobuf:"bytes,3,opt,name=checkpointDir,proto3" json:"checkpointDir,omitempty"` -} - -func (m *CreateCheckpointRequest) Reset() { *m = CreateCheckpointRequest{} } -func (m *CreateCheckpointRequest) String() string { return proto.CompactTextString(m) } -func (*CreateCheckpointRequest) ProtoMessage() {} -func (*CreateCheckpointRequest) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{12} } - -func (m *CreateCheckpointRequest) GetId() string { - if m != nil { - return m.Id - } - return "" -} - -func (m *CreateCheckpointRequest) GetCheckpoint() *Checkpoint { - if m != nil { - return m.Checkpoint - } - return nil -} - -func (m *CreateCheckpointRequest) GetCheckpointDir() string { - if m != nil { - return m.CheckpointDir - } - return "" -} - -type CreateCheckpointResponse struct { -} - -func (m *CreateCheckpointResponse) Reset() { *m = CreateCheckpointResponse{} } -func (m *CreateCheckpointResponse) String() string { return proto.CompactTextString(m) } -func (*CreateCheckpointResponse) ProtoMessage() {} -func (*CreateCheckpointResponse) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{13} } - -type DeleteCheckpointRequest struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - CheckpointDir string `protobuf:"bytes,3,opt,name=checkpointDir,proto3" json:"checkpointDir,omitempty"` -} - -func (m *DeleteCheckpointRequest) Reset() { *m = DeleteCheckpointRequest{} } -func (m *DeleteCheckpointRequest) String() string { return proto.CompactTextString(m) } -func (*DeleteCheckpointRequest) ProtoMessage() {} -func (*DeleteCheckpointRequest) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{14} } - -func (m *DeleteCheckpointRequest) GetId() string { - if m != nil { - return m.Id - } - return "" -} - -func (m *DeleteCheckpointRequest) GetName() string { - if m != nil { - return m.Name - } - return "" -} - -func (m *DeleteCheckpointRequest) GetCheckpointDir() string { - if m != nil { - return m.CheckpointDir - } - return "" -} - -type DeleteCheckpointResponse struct { -} - -func (m *DeleteCheckpointResponse) Reset() { *m = DeleteCheckpointResponse{} } -func (m *DeleteCheckpointResponse) String() string { return proto.CompactTextString(m) } -func (*DeleteCheckpointResponse) ProtoMessage() {} -func (*DeleteCheckpointResponse) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{15} } - -type ListCheckpointRequest struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - CheckpointDir string `protobuf:"bytes,2,opt,name=checkpointDir,proto3" json:"checkpointDir,omitempty"` -} - -func (m *ListCheckpointRequest) Reset() { *m = ListCheckpointRequest{} } -func (m *ListCheckpointRequest) String() string { return proto.CompactTextString(m) } -func (*ListCheckpointRequest) ProtoMessage() {} -func (*ListCheckpointRequest) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{16} } - -func (m *ListCheckpointRequest) GetId() string { - if m != nil { - return m.Id - } - return "" -} - -func (m *ListCheckpointRequest) GetCheckpointDir() string { - if m != nil { - return m.CheckpointDir - } - return "" -} - -type Checkpoint struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Exit bool `protobuf:"varint,2,opt,name=exit,proto3" json:"exit,omitempty"` - Tcp bool `protobuf:"varint,3,opt,name=tcp,proto3" json:"tcp,omitempty"` - UnixSockets bool `protobuf:"varint,4,opt,name=unixSockets,proto3" json:"unixSockets,omitempty"` - Shell bool `protobuf:"varint,5,opt,name=shell,proto3" json:"shell,omitempty"` - EmptyNS []string `protobuf:"bytes,6,rep,name=emptyNS" json:"emptyNS,omitempty"` -} - -func (m *Checkpoint) Reset() { *m = Checkpoint{} } -func (m *Checkpoint) String() string { return proto.CompactTextString(m) } -func (*Checkpoint) ProtoMessage() {} -func (*Checkpoint) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{17} } - -func (m *Checkpoint) GetName() string { - if m != nil { - return m.Name - } - return "" -} - -func (m *Checkpoint) GetExit() bool { - if m != nil { - return m.Exit - } - return false -} - -func (m *Checkpoint) GetTcp() bool { - if m != nil { - return m.Tcp - } - return false -} - -func (m *Checkpoint) GetUnixSockets() bool { - if m != nil { - return m.UnixSockets - } - return false -} - -func (m *Checkpoint) GetShell() bool { - if m != nil { - return m.Shell - } - return false -} - -func (m *Checkpoint) GetEmptyNS() []string { - if m != nil { - return m.EmptyNS - } - return nil -} - -type ListCheckpointResponse struct { - Checkpoints []*Checkpoint `protobuf:"bytes,1,rep,name=checkpoints" json:"checkpoints,omitempty"` -} - -func (m *ListCheckpointResponse) Reset() { *m = ListCheckpointResponse{} } -func (m *ListCheckpointResponse) String() string { return proto.CompactTextString(m) } -func (*ListCheckpointResponse) ProtoMessage() {} -func (*ListCheckpointResponse) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{18} } - -func (m *ListCheckpointResponse) GetCheckpoints() []*Checkpoint { - if m != nil { - return m.Checkpoints - } - return nil -} - -type StateRequest struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` -} - -func (m *StateRequest) Reset() { *m = StateRequest{} } -func (m *StateRequest) String() string { return proto.CompactTextString(m) } -func (*StateRequest) ProtoMessage() {} -func (*StateRequest) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{19} } - -func (m *StateRequest) GetId() string { - if m != nil { - return m.Id - } - return "" -} - -type ContainerState struct { - Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` -} - -func (m *ContainerState) Reset() { *m = ContainerState{} } -func (m *ContainerState) String() string { return proto.CompactTextString(m) } -func (*ContainerState) ProtoMessage() {} -func (*ContainerState) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{20} } - -func (m *ContainerState) GetStatus() string { - if m != nil { - return m.Status - } - return "" -} - -type Process struct { - Pid string `protobuf:"bytes,1,opt,name=pid,proto3" json:"pid,omitempty"` - Terminal bool `protobuf:"varint,2,opt,name=terminal,proto3" json:"terminal,omitempty"` - User *User `protobuf:"bytes,3,opt,name=user" json:"user,omitempty"` - Args []string `protobuf:"bytes,4,rep,name=args" json:"args,omitempty"` - Env []string `protobuf:"bytes,5,rep,name=env" json:"env,omitempty"` - Cwd string `protobuf:"bytes,6,opt,name=cwd,proto3" json:"cwd,omitempty"` - SystemPid uint32 `protobuf:"varint,7,opt,name=systemPid,proto3" json:"systemPid,omitempty"` - Stdin string `protobuf:"bytes,8,opt,name=stdin,proto3" json:"stdin,omitempty"` - Stdout string `protobuf:"bytes,9,opt,name=stdout,proto3" json:"stdout,omitempty"` - Stderr string `protobuf:"bytes,10,opt,name=stderr,proto3" json:"stderr,omitempty"` - Capabilities []string `protobuf:"bytes,11,rep,name=capabilities" json:"capabilities,omitempty"` - ApparmorProfile string `protobuf:"bytes,12,opt,name=apparmorProfile,proto3" json:"apparmorProfile,omitempty"` - SelinuxLabel string `protobuf:"bytes,13,opt,name=selinuxLabel,proto3" json:"selinuxLabel,omitempty"` - NoNewPrivileges bool `protobuf:"varint,14,opt,name=noNewPrivileges,proto3" json:"noNewPrivileges,omitempty"` - Rlimits []*Rlimit `protobuf:"bytes,15,rep,name=rlimits" json:"rlimits,omitempty"` -} - -func (m *Process) Reset() { *m = Process{} } -func (m *Process) String() string { return proto.CompactTextString(m) } -func (*Process) ProtoMessage() {} -func (*Process) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{21} } - -func (m *Process) GetPid() string { - if m != nil { - return m.Pid - } - return "" -} - -func (m *Process) GetTerminal() bool { - if m != nil { - return m.Terminal - } - return false -} - -func (m *Process) GetUser() *User { - if m != nil { - return m.User - } - return nil -} - -func (m *Process) GetArgs() []string { - if m != nil { - return m.Args - } - return nil -} - -func (m *Process) GetEnv() []string { - if m != nil { - return m.Env - } - return nil -} - -func (m *Process) GetCwd() string { - if m != nil { - return m.Cwd - } - return "" -} - -func (m *Process) GetSystemPid() uint32 { - if m != nil { - return m.SystemPid - } - return 0 -} - -func (m *Process) GetStdin() string { - if m != nil { - return m.Stdin - } - return "" -} - -func (m *Process) GetStdout() string { - if m != nil { - return m.Stdout - } - return "" -} - -func (m *Process) GetStderr() string { - if m != nil { - return m.Stderr - } - return "" -} - -func (m *Process) GetCapabilities() []string { - if m != nil { - return m.Capabilities - } - return nil -} - -func (m *Process) GetApparmorProfile() string { - if m != nil { - return m.ApparmorProfile - } - return "" -} - -func (m *Process) GetSelinuxLabel() string { - if m != nil { - return m.SelinuxLabel - } - return "" -} - -func (m *Process) GetNoNewPrivileges() bool { - if m != nil { - return m.NoNewPrivileges - } - return false -} - -func (m *Process) GetRlimits() []*Rlimit { - if m != nil { - return m.Rlimits - } - return nil -} - -type Container struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - BundlePath string `protobuf:"bytes,2,opt,name=bundlePath,proto3" json:"bundlePath,omitempty"` - Processes []*Process `protobuf:"bytes,3,rep,name=processes" json:"processes,omitempty"` - Status string `protobuf:"bytes,4,opt,name=status,proto3" json:"status,omitempty"` - Labels []string `protobuf:"bytes,5,rep,name=labels" json:"labels,omitempty"` - Pids []uint32 `protobuf:"varint,6,rep,packed,name=pids" json:"pids,omitempty"` - Runtime string `protobuf:"bytes,7,opt,name=runtime,proto3" json:"runtime,omitempty"` -} - -func (m *Container) Reset() { *m = Container{} } -func (m *Container) String() string { return proto.CompactTextString(m) } -func (*Container) ProtoMessage() {} -func (*Container) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{22} } - -func (m *Container) GetId() string { - if m != nil { - return m.Id - } - return "" -} - -func (m *Container) GetBundlePath() string { - if m != nil { - return m.BundlePath - } - return "" -} - -func (m *Container) GetProcesses() []*Process { - if m != nil { - return m.Processes - } - return nil -} - -func (m *Container) GetStatus() string { - if m != nil { - return m.Status - } - return "" -} - -func (m *Container) GetLabels() []string { - if m != nil { - return m.Labels - } - return nil -} - -func (m *Container) GetPids() []uint32 { - if m != nil { - return m.Pids - } - return nil -} - -func (m *Container) GetRuntime() string { - if m != nil { - return m.Runtime - } - return "" -} - -// Machine is information about machine on which containerd is run -type Machine struct { - Cpus uint32 `protobuf:"varint,1,opt,name=cpus,proto3" json:"cpus,omitempty"` - Memory uint64 `protobuf:"varint,2,opt,name=memory,proto3" json:"memory,omitempty"` -} - -func (m *Machine) Reset() { *m = Machine{} } -func (m *Machine) String() string { return proto.CompactTextString(m) } -func (*Machine) ProtoMessage() {} -func (*Machine) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{23} } - -func (m *Machine) GetCpus() uint32 { - if m != nil { - return m.Cpus - } - return 0 -} - -func (m *Machine) GetMemory() uint64 { - if m != nil { - return m.Memory - } - return 0 -} - -// StateResponse is information about containerd daemon -type StateResponse struct { - Containers []*Container `protobuf:"bytes,1,rep,name=containers" json:"containers,omitempty"` - Machine *Machine `protobuf:"bytes,2,opt,name=machine" json:"machine,omitempty"` -} - -func (m *StateResponse) Reset() { *m = StateResponse{} } -func (m *StateResponse) String() string { return proto.CompactTextString(m) } -func (*StateResponse) ProtoMessage() {} -func (*StateResponse) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{24} } - -func (m *StateResponse) GetContainers() []*Container { - if m != nil { - return m.Containers - } - return nil -} - -func (m *StateResponse) GetMachine() *Machine { - if m != nil { - return m.Machine - } - return nil -} - -type UpdateContainerRequest struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Pid string `protobuf:"bytes,2,opt,name=pid,proto3" json:"pid,omitempty"` - Status string `protobuf:"bytes,3,opt,name=status,proto3" json:"status,omitempty"` - Resources *UpdateResource `protobuf:"bytes,4,opt,name=resources" json:"resources,omitempty"` -} - -func (m *UpdateContainerRequest) Reset() { *m = UpdateContainerRequest{} } -func (m *UpdateContainerRequest) String() string { return proto.CompactTextString(m) } -func (*UpdateContainerRequest) ProtoMessage() {} -func (*UpdateContainerRequest) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{25} } - -func (m *UpdateContainerRequest) GetId() string { - if m != nil { - return m.Id - } - return "" -} - -func (m *UpdateContainerRequest) GetPid() string { - if m != nil { - return m.Pid - } - return "" -} - -func (m *UpdateContainerRequest) GetStatus() string { - if m != nil { - return m.Status - } - return "" -} - -func (m *UpdateContainerRequest) GetResources() *UpdateResource { - if m != nil { - return m.Resources - } - return nil -} - -type UpdateResource struct { - BlkioWeight uint64 `protobuf:"varint,1,opt,name=blkioWeight,proto3" json:"blkioWeight,omitempty"` - CpuShares uint64 `protobuf:"varint,2,opt,name=cpuShares,proto3" json:"cpuShares,omitempty"` - CpuPeriod uint64 `protobuf:"varint,3,opt,name=cpuPeriod,proto3" json:"cpuPeriod,omitempty"` - CpuQuota uint64 `protobuf:"varint,4,opt,name=cpuQuota,proto3" json:"cpuQuota,omitempty"` - CpusetCpus string `protobuf:"bytes,5,opt,name=cpusetCpus,proto3" json:"cpusetCpus,omitempty"` - CpusetMems string `protobuf:"bytes,6,opt,name=cpusetMems,proto3" json:"cpusetMems,omitempty"` - MemoryLimit uint64 `protobuf:"varint,7,opt,name=memoryLimit,proto3" json:"memoryLimit,omitempty"` - MemorySwap uint64 `protobuf:"varint,8,opt,name=memorySwap,proto3" json:"memorySwap,omitempty"` - MemoryReservation uint64 `protobuf:"varint,9,opt,name=memoryReservation,proto3" json:"memoryReservation,omitempty"` - KernelMemoryLimit uint64 `protobuf:"varint,10,opt,name=kernelMemoryLimit,proto3" json:"kernelMemoryLimit,omitempty"` - KernelTCPMemoryLimit uint64 `protobuf:"varint,11,opt,name=kernelTCPMemoryLimit,proto3" json:"kernelTCPMemoryLimit,omitempty"` -} - -func (m *UpdateResource) Reset() { *m = UpdateResource{} } -func (m *UpdateResource) String() string { return proto.CompactTextString(m) } -func (*UpdateResource) ProtoMessage() {} -func (*UpdateResource) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{26} } - -func (m *UpdateResource) GetBlkioWeight() uint64 { - if m != nil { - return m.BlkioWeight - } - return 0 -} - -func (m *UpdateResource) GetCpuShares() uint64 { - if m != nil { - return m.CpuShares - } - return 0 -} - -func (m *UpdateResource) GetCpuPeriod() uint64 { - if m != nil { - return m.CpuPeriod - } - return 0 -} - -func (m *UpdateResource) GetCpuQuota() uint64 { - if m != nil { - return m.CpuQuota - } - return 0 -} - -func (m *UpdateResource) GetCpusetCpus() string { - if m != nil { - return m.CpusetCpus - } - return "" -} - -func (m *UpdateResource) GetCpusetMems() string { - if m != nil { - return m.CpusetMems - } - return "" -} - -func (m *UpdateResource) GetMemoryLimit() uint64 { - if m != nil { - return m.MemoryLimit - } - return 0 -} - -func (m *UpdateResource) GetMemorySwap() uint64 { - if m != nil { - return m.MemorySwap - } - return 0 -} - -func (m *UpdateResource) GetMemoryReservation() uint64 { - if m != nil { - return m.MemoryReservation - } - return 0 -} - -func (m *UpdateResource) GetKernelMemoryLimit() uint64 { - if m != nil { - return m.KernelMemoryLimit - } - return 0 -} - -func (m *UpdateResource) GetKernelTCPMemoryLimit() uint64 { - if m != nil { - return m.KernelTCPMemoryLimit - } - return 0 -} - -type UpdateContainerResponse struct { -} - -func (m *UpdateContainerResponse) Reset() { *m = UpdateContainerResponse{} } -func (m *UpdateContainerResponse) String() string { return proto.CompactTextString(m) } -func (*UpdateContainerResponse) ProtoMessage() {} -func (*UpdateContainerResponse) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{27} } - -type EventsRequest struct { - // Tag 1 is deprecated (old uint64 timestamp) - Timestamp *google_protobuf.Timestamp `protobuf:"bytes,2,opt,name=timestamp" json:"timestamp,omitempty"` - StoredOnly bool `protobuf:"varint,3,opt,name=storedOnly,proto3" json:"storedOnly,omitempty"` - Id string `protobuf:"bytes,4,opt,name=id,proto3" json:"id,omitempty"` -} - -func (m *EventsRequest) Reset() { *m = EventsRequest{} } -func (m *EventsRequest) String() string { return proto.CompactTextString(m) } -func (*EventsRequest) ProtoMessage() {} -func (*EventsRequest) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{28} } - -func (m *EventsRequest) GetTimestamp() *google_protobuf.Timestamp { - if m != nil { - return m.Timestamp - } - return nil -} - -func (m *EventsRequest) GetStoredOnly() bool { - if m != nil { - return m.StoredOnly - } - return false -} - -func (m *EventsRequest) GetId() string { - if m != nil { - return m.Id - } - return "" -} - -type Event struct { - Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` - Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` - Status uint32 `protobuf:"varint,3,opt,name=status,proto3" json:"status,omitempty"` - Pid string `protobuf:"bytes,4,opt,name=pid,proto3" json:"pid,omitempty"` - // Tag 5 is deprecated (old uint64 timestamp) - Timestamp *google_protobuf.Timestamp `protobuf:"bytes,6,opt,name=timestamp" json:"timestamp,omitempty"` -} - -func (m *Event) Reset() { *m = Event{} } -func (m *Event) String() string { return proto.CompactTextString(m) } -func (*Event) ProtoMessage() {} -func (*Event) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{29} } - -func (m *Event) GetType() string { - if m != nil { - return m.Type - } - return "" -} - -func (m *Event) GetId() string { - if m != nil { - return m.Id - } - return "" -} - -func (m *Event) GetStatus() uint32 { - if m != nil { - return m.Status - } - return 0 -} - -func (m *Event) GetPid() string { - if m != nil { - return m.Pid - } - return "" -} - -func (m *Event) GetTimestamp() *google_protobuf.Timestamp { - if m != nil { - return m.Timestamp - } - return nil -} - -type NetworkStats struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - RxBytes uint64 `protobuf:"varint,2,opt,name=rx_bytes,json=rxBytes,proto3" json:"rx_bytes,omitempty"` - Rx_Packets uint64 `protobuf:"varint,3,opt,name=rx_Packets,json=rxPackets,proto3" json:"rx_Packets,omitempty"` - RxErrors uint64 `protobuf:"varint,4,opt,name=Rx_errors,json=rxErrors,proto3" json:"Rx_errors,omitempty"` - RxDropped uint64 `protobuf:"varint,5,opt,name=Rx_dropped,json=rxDropped,proto3" json:"Rx_dropped,omitempty"` - TxBytes uint64 `protobuf:"varint,6,opt,name=Tx_bytes,json=txBytes,proto3" json:"Tx_bytes,omitempty"` - TxPackets uint64 `protobuf:"varint,7,opt,name=Tx_packets,json=txPackets,proto3" json:"Tx_packets,omitempty"` - TxErrors uint64 `protobuf:"varint,8,opt,name=Tx_errors,json=txErrors,proto3" json:"Tx_errors,omitempty"` - TxDropped uint64 `protobuf:"varint,9,opt,name=Tx_dropped,json=txDropped,proto3" json:"Tx_dropped,omitempty"` -} - -func (m *NetworkStats) Reset() { *m = NetworkStats{} } -func (m *NetworkStats) String() string { return proto.CompactTextString(m) } -func (*NetworkStats) ProtoMessage() {} -func (*NetworkStats) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{30} } - -func (m *NetworkStats) GetName() string { - if m != nil { - return m.Name - } - return "" -} - -func (m *NetworkStats) GetRxBytes() uint64 { - if m != nil { - return m.RxBytes - } - return 0 -} - -func (m *NetworkStats) GetRx_Packets() uint64 { - if m != nil { - return m.Rx_Packets - } - return 0 -} - -func (m *NetworkStats) GetRxErrors() uint64 { - if m != nil { - return m.RxErrors - } - return 0 -} - -func (m *NetworkStats) GetRxDropped() uint64 { - if m != nil { - return m.RxDropped - } - return 0 -} - -func (m *NetworkStats) GetTxBytes() uint64 { - if m != nil { - return m.TxBytes - } - return 0 -} - -func (m *NetworkStats) GetTxPackets() uint64 { - if m != nil { - return m.TxPackets - } - return 0 -} - -func (m *NetworkStats) GetTxErrors() uint64 { - if m != nil { - return m.TxErrors - } - return 0 -} - -func (m *NetworkStats) GetTxDropped() uint64 { - if m != nil { - return m.TxDropped - } - return 0 -} - -type CpuUsage struct { - TotalUsage uint64 `protobuf:"varint,1,opt,name=total_usage,json=totalUsage,proto3" json:"total_usage,omitempty"` - PercpuUsage []uint64 `protobuf:"varint,2,rep,packed,name=percpu_usage,json=percpuUsage" json:"percpu_usage,omitempty"` - UsageInKernelmode uint64 `protobuf:"varint,3,opt,name=usage_in_kernelmode,json=usageInKernelmode,proto3" json:"usage_in_kernelmode,omitempty"` - UsageInUsermode uint64 `protobuf:"varint,4,opt,name=usage_in_usermode,json=usageInUsermode,proto3" json:"usage_in_usermode,omitempty"` -} - -func (m *CpuUsage) Reset() { *m = CpuUsage{} } -func (m *CpuUsage) String() string { return proto.CompactTextString(m) } -func (*CpuUsage) ProtoMessage() {} -func (*CpuUsage) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{31} } - -func (m *CpuUsage) GetTotalUsage() uint64 { - if m != nil { - return m.TotalUsage - } - return 0 -} - -func (m *CpuUsage) GetPercpuUsage() []uint64 { - if m != nil { - return m.PercpuUsage - } - return nil -} - -func (m *CpuUsage) GetUsageInKernelmode() uint64 { - if m != nil { - return m.UsageInKernelmode - } - return 0 -} - -func (m *CpuUsage) GetUsageInUsermode() uint64 { - if m != nil { - return m.UsageInUsermode - } - return 0 -} - -type ThrottlingData struct { - Periods uint64 `protobuf:"varint,1,opt,name=periods,proto3" json:"periods,omitempty"` - ThrottledPeriods uint64 `protobuf:"varint,2,opt,name=throttled_periods,json=throttledPeriods,proto3" json:"throttled_periods,omitempty"` - ThrottledTime uint64 `protobuf:"varint,3,opt,name=throttled_time,json=throttledTime,proto3" json:"throttled_time,omitempty"` -} - -func (m *ThrottlingData) Reset() { *m = ThrottlingData{} } -func (m *ThrottlingData) String() string { return proto.CompactTextString(m) } -func (*ThrottlingData) ProtoMessage() {} -func (*ThrottlingData) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{32} } - -func (m *ThrottlingData) GetPeriods() uint64 { - if m != nil { - return m.Periods - } - return 0 -} - -func (m *ThrottlingData) GetThrottledPeriods() uint64 { - if m != nil { - return m.ThrottledPeriods - } - return 0 -} - -func (m *ThrottlingData) GetThrottledTime() uint64 { - if m != nil { - return m.ThrottledTime - } - return 0 -} - -type CpuStats struct { - CpuUsage *CpuUsage `protobuf:"bytes,1,opt,name=cpu_usage,json=cpuUsage" json:"cpu_usage,omitempty"` - ThrottlingData *ThrottlingData `protobuf:"bytes,2,opt,name=throttling_data,json=throttlingData" json:"throttling_data,omitempty"` - SystemUsage uint64 `protobuf:"varint,3,opt,name=system_usage,json=systemUsage,proto3" json:"system_usage,omitempty"` -} - -func (m *CpuStats) Reset() { *m = CpuStats{} } -func (m *CpuStats) String() string { return proto.CompactTextString(m) } -func (*CpuStats) ProtoMessage() {} -func (*CpuStats) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{33} } - -func (m *CpuStats) GetCpuUsage() *CpuUsage { - if m != nil { - return m.CpuUsage - } - return nil -} - -func (m *CpuStats) GetThrottlingData() *ThrottlingData { - if m != nil { - return m.ThrottlingData - } - return nil -} - -func (m *CpuStats) GetSystemUsage() uint64 { - if m != nil { - return m.SystemUsage - } - return 0 -} - -type PidsStats struct { - Current uint64 `protobuf:"varint,1,opt,name=current,proto3" json:"current,omitempty"` - Limit uint64 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"` -} - -func (m *PidsStats) Reset() { *m = PidsStats{} } -func (m *PidsStats) String() string { return proto.CompactTextString(m) } -func (*PidsStats) ProtoMessage() {} -func (*PidsStats) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{34} } - -func (m *PidsStats) GetCurrent() uint64 { - if m != nil { - return m.Current - } - return 0 -} - -func (m *PidsStats) GetLimit() uint64 { - if m != nil { - return m.Limit - } - return 0 -} - -type MemoryData struct { - Usage uint64 `protobuf:"varint,1,opt,name=usage,proto3" json:"usage,omitempty"` - MaxUsage uint64 `protobuf:"varint,2,opt,name=max_usage,json=maxUsage,proto3" json:"max_usage,omitempty"` - Failcnt uint64 `protobuf:"varint,3,opt,name=failcnt,proto3" json:"failcnt,omitempty"` - Limit uint64 `protobuf:"varint,4,opt,name=limit,proto3" json:"limit,omitempty"` -} - -func (m *MemoryData) Reset() { *m = MemoryData{} } -func (m *MemoryData) String() string { return proto.CompactTextString(m) } -func (*MemoryData) ProtoMessage() {} -func (*MemoryData) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{35} } - -func (m *MemoryData) GetUsage() uint64 { - if m != nil { - return m.Usage - } - return 0 -} - -func (m *MemoryData) GetMaxUsage() uint64 { - if m != nil { - return m.MaxUsage - } - return 0 -} - -func (m *MemoryData) GetFailcnt() uint64 { - if m != nil { - return m.Failcnt - } - return 0 -} - -func (m *MemoryData) GetLimit() uint64 { - if m != nil { - return m.Limit - } - return 0 -} - -type MemoryStats struct { - Cache uint64 `protobuf:"varint,1,opt,name=cache,proto3" json:"cache,omitempty"` - Usage *MemoryData `protobuf:"bytes,2,opt,name=usage" json:"usage,omitempty"` - SwapUsage *MemoryData `protobuf:"bytes,3,opt,name=swap_usage,json=swapUsage" json:"swap_usage,omitempty"` - KernelUsage *MemoryData `protobuf:"bytes,4,opt,name=kernel_usage,json=kernelUsage" json:"kernel_usage,omitempty"` - Stats map[string]uint64 `protobuf:"bytes,5,rep,name=stats" json:"stats,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` -} - -func (m *MemoryStats) Reset() { *m = MemoryStats{} } -func (m *MemoryStats) String() string { return proto.CompactTextString(m) } -func (*MemoryStats) ProtoMessage() {} -func (*MemoryStats) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{36} } - -func (m *MemoryStats) GetCache() uint64 { - if m != nil { - return m.Cache - } - return 0 -} - -func (m *MemoryStats) GetUsage() *MemoryData { - if m != nil { - return m.Usage - } - return nil -} - -func (m *MemoryStats) GetSwapUsage() *MemoryData { - if m != nil { - return m.SwapUsage - } - return nil -} - -func (m *MemoryStats) GetKernelUsage() *MemoryData { - if m != nil { - return m.KernelUsage - } - return nil -} - -func (m *MemoryStats) GetStats() map[string]uint64 { - if m != nil { - return m.Stats - } - return nil -} - -type BlkioStatsEntry struct { - Major uint64 `protobuf:"varint,1,opt,name=major,proto3" json:"major,omitempty"` - Minor uint64 `protobuf:"varint,2,opt,name=minor,proto3" json:"minor,omitempty"` - Op string `protobuf:"bytes,3,opt,name=op,proto3" json:"op,omitempty"` - Value uint64 `protobuf:"varint,4,opt,name=value,proto3" json:"value,omitempty"` -} - -func (m *BlkioStatsEntry) Reset() { *m = BlkioStatsEntry{} } -func (m *BlkioStatsEntry) String() string { return proto.CompactTextString(m) } -func (*BlkioStatsEntry) ProtoMessage() {} -func (*BlkioStatsEntry) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{37} } - -func (m *BlkioStatsEntry) GetMajor() uint64 { - if m != nil { - return m.Major - } - return 0 -} - -func (m *BlkioStatsEntry) GetMinor() uint64 { - if m != nil { - return m.Minor - } - return 0 -} - -func (m *BlkioStatsEntry) GetOp() string { - if m != nil { - return m.Op - } - return "" -} - -func (m *BlkioStatsEntry) GetValue() uint64 { - if m != nil { - return m.Value - } - return 0 -} - -type BlkioStats struct { - IoServiceBytesRecursive []*BlkioStatsEntry `protobuf:"bytes,1,rep,name=io_service_bytes_recursive,json=ioServiceBytesRecursive" json:"io_service_bytes_recursive,omitempty"` - IoServicedRecursive []*BlkioStatsEntry `protobuf:"bytes,2,rep,name=io_serviced_recursive,json=ioServicedRecursive" json:"io_serviced_recursive,omitempty"` - IoQueuedRecursive []*BlkioStatsEntry `protobuf:"bytes,3,rep,name=io_queued_recursive,json=ioQueuedRecursive" json:"io_queued_recursive,omitempty"` - IoServiceTimeRecursive []*BlkioStatsEntry `protobuf:"bytes,4,rep,name=io_service_time_recursive,json=ioServiceTimeRecursive" json:"io_service_time_recursive,omitempty"` - IoWaitTimeRecursive []*BlkioStatsEntry `protobuf:"bytes,5,rep,name=io_wait_time_recursive,json=ioWaitTimeRecursive" json:"io_wait_time_recursive,omitempty"` - IoMergedRecursive []*BlkioStatsEntry `protobuf:"bytes,6,rep,name=io_merged_recursive,json=ioMergedRecursive" json:"io_merged_recursive,omitempty"` - IoTimeRecursive []*BlkioStatsEntry `protobuf:"bytes,7,rep,name=io_time_recursive,json=ioTimeRecursive" json:"io_time_recursive,omitempty"` - SectorsRecursive []*BlkioStatsEntry `protobuf:"bytes,8,rep,name=sectors_recursive,json=sectorsRecursive" json:"sectors_recursive,omitempty"` -} - -func (m *BlkioStats) Reset() { *m = BlkioStats{} } -func (m *BlkioStats) String() string { return proto.CompactTextString(m) } -func (*BlkioStats) ProtoMessage() {} -func (*BlkioStats) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{38} } - -func (m *BlkioStats) GetIoServiceBytesRecursive() []*BlkioStatsEntry { - if m != nil { - return m.IoServiceBytesRecursive - } - return nil -} - -func (m *BlkioStats) GetIoServicedRecursive() []*BlkioStatsEntry { - if m != nil { - return m.IoServicedRecursive - } - return nil -} - -func (m *BlkioStats) GetIoQueuedRecursive() []*BlkioStatsEntry { - if m != nil { - return m.IoQueuedRecursive - } - return nil -} - -func (m *BlkioStats) GetIoServiceTimeRecursive() []*BlkioStatsEntry { - if m != nil { - return m.IoServiceTimeRecursive - } - return nil -} - -func (m *BlkioStats) GetIoWaitTimeRecursive() []*BlkioStatsEntry { - if m != nil { - return m.IoWaitTimeRecursive - } - return nil -} - -func (m *BlkioStats) GetIoMergedRecursive() []*BlkioStatsEntry { - if m != nil { - return m.IoMergedRecursive - } - return nil -} - -func (m *BlkioStats) GetIoTimeRecursive() []*BlkioStatsEntry { - if m != nil { - return m.IoTimeRecursive - } - return nil -} - -func (m *BlkioStats) GetSectorsRecursive() []*BlkioStatsEntry { - if m != nil { - return m.SectorsRecursive - } - return nil -} - -type HugetlbStats struct { - Usage uint64 `protobuf:"varint,1,opt,name=usage,proto3" json:"usage,omitempty"` - MaxUsage uint64 `protobuf:"varint,2,opt,name=max_usage,json=maxUsage,proto3" json:"max_usage,omitempty"` - Failcnt uint64 `protobuf:"varint,3,opt,name=failcnt,proto3" json:"failcnt,omitempty"` - Limit uint64 `protobuf:"varint,4,opt,name=limit,proto3" json:"limit,omitempty"` -} - -func (m *HugetlbStats) Reset() { *m = HugetlbStats{} } -func (m *HugetlbStats) String() string { return proto.CompactTextString(m) } -func (*HugetlbStats) ProtoMessage() {} -func (*HugetlbStats) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{39} } - -func (m *HugetlbStats) GetUsage() uint64 { - if m != nil { - return m.Usage - } - return 0 -} - -func (m *HugetlbStats) GetMaxUsage() uint64 { - if m != nil { - return m.MaxUsage - } - return 0 -} - -func (m *HugetlbStats) GetFailcnt() uint64 { - if m != nil { - return m.Failcnt - } - return 0 -} - -func (m *HugetlbStats) GetLimit() uint64 { - if m != nil { - return m.Limit - } - return 0 -} - -type CgroupStats struct { - CpuStats *CpuStats `protobuf:"bytes,1,opt,name=cpu_stats,json=cpuStats" json:"cpu_stats,omitempty"` - MemoryStats *MemoryStats `protobuf:"bytes,2,opt,name=memory_stats,json=memoryStats" json:"memory_stats,omitempty"` - BlkioStats *BlkioStats `protobuf:"bytes,3,opt,name=blkio_stats,json=blkioStats" json:"blkio_stats,omitempty"` - HugetlbStats map[string]*HugetlbStats `protobuf:"bytes,4,rep,name=hugetlb_stats,json=hugetlbStats" json:"hugetlb_stats,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"` - PidsStats *PidsStats `protobuf:"bytes,5,opt,name=pids_stats,json=pidsStats" json:"pids_stats,omitempty"` -} - -func (m *CgroupStats) Reset() { *m = CgroupStats{} } -func (m *CgroupStats) String() string { return proto.CompactTextString(m) } -func (*CgroupStats) ProtoMessage() {} -func (*CgroupStats) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{40} } - -func (m *CgroupStats) GetCpuStats() *CpuStats { - if m != nil { - return m.CpuStats - } - return nil -} - -func (m *CgroupStats) GetMemoryStats() *MemoryStats { - if m != nil { - return m.MemoryStats - } - return nil -} - -func (m *CgroupStats) GetBlkioStats() *BlkioStats { - if m != nil { - return m.BlkioStats - } - return nil -} - -func (m *CgroupStats) GetHugetlbStats() map[string]*HugetlbStats { - if m != nil { - return m.HugetlbStats - } - return nil -} - -func (m *CgroupStats) GetPidsStats() *PidsStats { - if m != nil { - return m.PidsStats - } - return nil -} - -type StatsResponse struct { - NetworkStats []*NetworkStats `protobuf:"bytes,1,rep,name=network_stats,json=networkStats" json:"network_stats,omitempty"` - CgroupStats *CgroupStats `protobuf:"bytes,2,opt,name=cgroup_stats,json=cgroupStats" json:"cgroup_stats,omitempty"` - // Tag 3 is deprecated (old uint64 timestamp) - Timestamp *google_protobuf.Timestamp `protobuf:"bytes,4,opt,name=timestamp" json:"timestamp,omitempty"` -} - -func (m *StatsResponse) Reset() { *m = StatsResponse{} } -func (m *StatsResponse) String() string { return proto.CompactTextString(m) } -func (*StatsResponse) ProtoMessage() {} -func (*StatsResponse) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{41} } - -func (m *StatsResponse) GetNetworkStats() []*NetworkStats { - if m != nil { - return m.NetworkStats - } - return nil -} - -func (m *StatsResponse) GetCgroupStats() *CgroupStats { - if m != nil { - return m.CgroupStats - } - return nil -} - -func (m *StatsResponse) GetTimestamp() *google_protobuf.Timestamp { - if m != nil { - return m.Timestamp - } - return nil -} - -type StatsRequest struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` -} - -func (m *StatsRequest) Reset() { *m = StatsRequest{} } -func (m *StatsRequest) String() string { return proto.CompactTextString(m) } -func (*StatsRequest) ProtoMessage() {} -func (*StatsRequest) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{42} } - -func (m *StatsRequest) GetId() string { - if m != nil { - return m.Id - } - return "" -} - -func init() { - proto.RegisterType((*GetServerVersionRequest)(nil), "types.GetServerVersionRequest") - proto.RegisterType((*GetServerVersionResponse)(nil), "types.GetServerVersionResponse") - proto.RegisterType((*UpdateProcessRequest)(nil), "types.UpdateProcessRequest") - proto.RegisterType((*UpdateProcessResponse)(nil), "types.UpdateProcessResponse") - proto.RegisterType((*CreateContainerRequest)(nil), "types.CreateContainerRequest") - proto.RegisterType((*CreateContainerResponse)(nil), "types.CreateContainerResponse") - proto.RegisterType((*SignalRequest)(nil), "types.SignalRequest") - proto.RegisterType((*SignalResponse)(nil), "types.SignalResponse") - proto.RegisterType((*AddProcessRequest)(nil), "types.AddProcessRequest") - proto.RegisterType((*Rlimit)(nil), "types.Rlimit") - proto.RegisterType((*User)(nil), "types.User") - proto.RegisterType((*AddProcessResponse)(nil), "types.AddProcessResponse") - proto.RegisterType((*CreateCheckpointRequest)(nil), "types.CreateCheckpointRequest") - proto.RegisterType((*CreateCheckpointResponse)(nil), "types.CreateCheckpointResponse") - proto.RegisterType((*DeleteCheckpointRequest)(nil), "types.DeleteCheckpointRequest") - proto.RegisterType((*DeleteCheckpointResponse)(nil), "types.DeleteCheckpointResponse") - proto.RegisterType((*ListCheckpointRequest)(nil), "types.ListCheckpointRequest") - proto.RegisterType((*Checkpoint)(nil), "types.Checkpoint") - proto.RegisterType((*ListCheckpointResponse)(nil), "types.ListCheckpointResponse") - proto.RegisterType((*StateRequest)(nil), "types.StateRequest") - proto.RegisterType((*ContainerState)(nil), "types.ContainerState") - proto.RegisterType((*Process)(nil), "types.Process") - proto.RegisterType((*Container)(nil), "types.Container") - proto.RegisterType((*Machine)(nil), "types.Machine") - proto.RegisterType((*StateResponse)(nil), "types.StateResponse") - proto.RegisterType((*UpdateContainerRequest)(nil), "types.UpdateContainerRequest") - proto.RegisterType((*UpdateResource)(nil), "types.UpdateResource") - proto.RegisterType((*UpdateContainerResponse)(nil), "types.UpdateContainerResponse") - proto.RegisterType((*EventsRequest)(nil), "types.EventsRequest") - proto.RegisterType((*Event)(nil), "types.Event") - proto.RegisterType((*NetworkStats)(nil), "types.NetworkStats") - proto.RegisterType((*CpuUsage)(nil), "types.CpuUsage") - proto.RegisterType((*ThrottlingData)(nil), "types.ThrottlingData") - proto.RegisterType((*CpuStats)(nil), "types.CpuStats") - proto.RegisterType((*PidsStats)(nil), "types.PidsStats") - proto.RegisterType((*MemoryData)(nil), "types.MemoryData") - proto.RegisterType((*MemoryStats)(nil), "types.MemoryStats") - proto.RegisterType((*BlkioStatsEntry)(nil), "types.BlkioStatsEntry") - proto.RegisterType((*BlkioStats)(nil), "types.BlkioStats") - proto.RegisterType((*HugetlbStats)(nil), "types.HugetlbStats") - proto.RegisterType((*CgroupStats)(nil), "types.CgroupStats") - proto.RegisterType((*StatsResponse)(nil), "types.StatsResponse") - proto.RegisterType((*StatsRequest)(nil), "types.StatsRequest") -} - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConn - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// Client API for API service - -type APIClient interface { - GetServerVersion(ctx context.Context, in *GetServerVersionRequest, opts ...grpc.CallOption) (*GetServerVersionResponse, error) - CreateContainer(ctx context.Context, in *CreateContainerRequest, opts ...grpc.CallOption) (*CreateContainerResponse, error) - UpdateContainer(ctx context.Context, in *UpdateContainerRequest, opts ...grpc.CallOption) (*UpdateContainerResponse, error) - Signal(ctx context.Context, in *SignalRequest, opts ...grpc.CallOption) (*SignalResponse, error) - UpdateProcess(ctx context.Context, in *UpdateProcessRequest, opts ...grpc.CallOption) (*UpdateProcessResponse, error) - AddProcess(ctx context.Context, in *AddProcessRequest, opts ...grpc.CallOption) (*AddProcessResponse, error) - CreateCheckpoint(ctx context.Context, in *CreateCheckpointRequest, opts ...grpc.CallOption) (*CreateCheckpointResponse, error) - DeleteCheckpoint(ctx context.Context, in *DeleteCheckpointRequest, opts ...grpc.CallOption) (*DeleteCheckpointResponse, error) - ListCheckpoint(ctx context.Context, in *ListCheckpointRequest, opts ...grpc.CallOption) (*ListCheckpointResponse, error) - State(ctx context.Context, in *StateRequest, opts ...grpc.CallOption) (*StateResponse, error) - Events(ctx context.Context, in *EventsRequest, opts ...grpc.CallOption) (API_EventsClient, error) - Stats(ctx context.Context, in *StatsRequest, opts ...grpc.CallOption) (*StatsResponse, error) -} - -type aPIClient struct { - cc *grpc.ClientConn -} - -func NewAPIClient(cc *grpc.ClientConn) APIClient { - return &aPIClient{cc} -} - -func (c *aPIClient) GetServerVersion(ctx context.Context, in *GetServerVersionRequest, opts ...grpc.CallOption) (*GetServerVersionResponse, error) { - out := new(GetServerVersionResponse) - err := grpc.Invoke(ctx, "/types.API/GetServerVersion", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *aPIClient) CreateContainer(ctx context.Context, in *CreateContainerRequest, opts ...grpc.CallOption) (*CreateContainerResponse, error) { - out := new(CreateContainerResponse) - err := grpc.Invoke(ctx, "/types.API/CreateContainer", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *aPIClient) UpdateContainer(ctx context.Context, in *UpdateContainerRequest, opts ...grpc.CallOption) (*UpdateContainerResponse, error) { - out := new(UpdateContainerResponse) - err := grpc.Invoke(ctx, "/types.API/UpdateContainer", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *aPIClient) Signal(ctx context.Context, in *SignalRequest, opts ...grpc.CallOption) (*SignalResponse, error) { - out := new(SignalResponse) - err := grpc.Invoke(ctx, "/types.API/Signal", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *aPIClient) UpdateProcess(ctx context.Context, in *UpdateProcessRequest, opts ...grpc.CallOption) (*UpdateProcessResponse, error) { - out := new(UpdateProcessResponse) - err := grpc.Invoke(ctx, "/types.API/UpdateProcess", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *aPIClient) AddProcess(ctx context.Context, in *AddProcessRequest, opts ...grpc.CallOption) (*AddProcessResponse, error) { - out := new(AddProcessResponse) - err := grpc.Invoke(ctx, "/types.API/AddProcess", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *aPIClient) CreateCheckpoint(ctx context.Context, in *CreateCheckpointRequest, opts ...grpc.CallOption) (*CreateCheckpointResponse, error) { - out := new(CreateCheckpointResponse) - err := grpc.Invoke(ctx, "/types.API/CreateCheckpoint", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *aPIClient) DeleteCheckpoint(ctx context.Context, in *DeleteCheckpointRequest, opts ...grpc.CallOption) (*DeleteCheckpointResponse, error) { - out := new(DeleteCheckpointResponse) - err := grpc.Invoke(ctx, "/types.API/DeleteCheckpoint", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *aPIClient) ListCheckpoint(ctx context.Context, in *ListCheckpointRequest, opts ...grpc.CallOption) (*ListCheckpointResponse, error) { - out := new(ListCheckpointResponse) - err := grpc.Invoke(ctx, "/types.API/ListCheckpoint", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *aPIClient) State(ctx context.Context, in *StateRequest, opts ...grpc.CallOption) (*StateResponse, error) { - out := new(StateResponse) - err := grpc.Invoke(ctx, "/types.API/State", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *aPIClient) Events(ctx context.Context, in *EventsRequest, opts ...grpc.CallOption) (API_EventsClient, error) { - stream, err := grpc.NewClientStream(ctx, &_API_serviceDesc.Streams[0], c.cc, "/types.API/Events", opts...) - if err != nil { - return nil, err - } - x := &aPIEventsClient{stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err - } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - return x, nil -} - -type API_EventsClient interface { - Recv() (*Event, error) - grpc.ClientStream -} - -type aPIEventsClient struct { - grpc.ClientStream -} - -func (x *aPIEventsClient) Recv() (*Event, error) { - m := new(Event) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -func (c *aPIClient) Stats(ctx context.Context, in *StatsRequest, opts ...grpc.CallOption) (*StatsResponse, error) { - out := new(StatsResponse) - err := grpc.Invoke(ctx, "/types.API/Stats", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// Server API for API service - -type APIServer interface { - GetServerVersion(context.Context, *GetServerVersionRequest) (*GetServerVersionResponse, error) - CreateContainer(context.Context, *CreateContainerRequest) (*CreateContainerResponse, error) - UpdateContainer(context.Context, *UpdateContainerRequest) (*UpdateContainerResponse, error) - Signal(context.Context, *SignalRequest) (*SignalResponse, error) - UpdateProcess(context.Context, *UpdateProcessRequest) (*UpdateProcessResponse, error) - AddProcess(context.Context, *AddProcessRequest) (*AddProcessResponse, error) - CreateCheckpoint(context.Context, *CreateCheckpointRequest) (*CreateCheckpointResponse, error) - DeleteCheckpoint(context.Context, *DeleteCheckpointRequest) (*DeleteCheckpointResponse, error) - ListCheckpoint(context.Context, *ListCheckpointRequest) (*ListCheckpointResponse, error) - State(context.Context, *StateRequest) (*StateResponse, error) - Events(*EventsRequest, API_EventsServer) error - Stats(context.Context, *StatsRequest) (*StatsResponse, error) -} - -func RegisterAPIServer(s *grpc.Server, srv APIServer) { - s.RegisterService(&_API_serviceDesc, srv) -} - -func _API_GetServerVersion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetServerVersionRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(APIServer).GetServerVersion(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/types.API/GetServerVersion", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(APIServer).GetServerVersion(ctx, req.(*GetServerVersionRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _API_CreateContainer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateContainerRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(APIServer).CreateContainer(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/types.API/CreateContainer", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(APIServer).CreateContainer(ctx, req.(*CreateContainerRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _API_UpdateContainer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UpdateContainerRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(APIServer).UpdateContainer(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/types.API/UpdateContainer", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(APIServer).UpdateContainer(ctx, req.(*UpdateContainerRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _API_Signal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SignalRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(APIServer).Signal(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/types.API/Signal", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(APIServer).Signal(ctx, req.(*SignalRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _API_UpdateProcess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UpdateProcessRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(APIServer).UpdateProcess(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/types.API/UpdateProcess", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(APIServer).UpdateProcess(ctx, req.(*UpdateProcessRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _API_AddProcess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(AddProcessRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(APIServer).AddProcess(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/types.API/AddProcess", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(APIServer).AddProcess(ctx, req.(*AddProcessRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _API_CreateCheckpoint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateCheckpointRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(APIServer).CreateCheckpoint(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/types.API/CreateCheckpoint", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(APIServer).CreateCheckpoint(ctx, req.(*CreateCheckpointRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _API_DeleteCheckpoint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteCheckpointRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(APIServer).DeleteCheckpoint(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/types.API/DeleteCheckpoint", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(APIServer).DeleteCheckpoint(ctx, req.(*DeleteCheckpointRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _API_ListCheckpoint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListCheckpointRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(APIServer).ListCheckpoint(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/types.API/ListCheckpoint", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(APIServer).ListCheckpoint(ctx, req.(*ListCheckpointRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _API_State_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(StateRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(APIServer).State(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/types.API/State", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(APIServer).State(ctx, req.(*StateRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _API_Events_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(EventsRequest) - if err := stream.RecvMsg(m); err != nil { - return err - } - return srv.(APIServer).Events(m, &aPIEventsServer{stream}) -} - -type API_EventsServer interface { - Send(*Event) error - grpc.ServerStream -} - -type aPIEventsServer struct { - grpc.ServerStream -} - -func (x *aPIEventsServer) Send(m *Event) error { - return x.ServerStream.SendMsg(m) -} - -func _API_Stats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(StatsRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(APIServer).Stats(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/types.API/Stats", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(APIServer).Stats(ctx, req.(*StatsRequest)) - } - return interceptor(ctx, in, info, handler) -} - -var _API_serviceDesc = grpc.ServiceDesc{ - ServiceName: "types.API", - HandlerType: (*APIServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "GetServerVersion", - Handler: _API_GetServerVersion_Handler, - }, - { - MethodName: "CreateContainer", - Handler: _API_CreateContainer_Handler, - }, - { - MethodName: "UpdateContainer", - Handler: _API_UpdateContainer_Handler, - }, - { - MethodName: "Signal", - Handler: _API_Signal_Handler, - }, - { - MethodName: "UpdateProcess", - Handler: _API_UpdateProcess_Handler, - }, - { - MethodName: "AddProcess", - Handler: _API_AddProcess_Handler, - }, - { - MethodName: "CreateCheckpoint", - Handler: _API_CreateCheckpoint_Handler, - }, - { - MethodName: "DeleteCheckpoint", - Handler: _API_DeleteCheckpoint_Handler, - }, - { - MethodName: "ListCheckpoint", - Handler: _API_ListCheckpoint_Handler, - }, - { - MethodName: "State", - Handler: _API_State_Handler, - }, - { - MethodName: "Stats", - Handler: _API_Stats_Handler, - }, - }, - Streams: []grpc.StreamDesc{ - { - StreamName: "Events", - Handler: _API_Events_Handler, - ServerStreams: true, - }, - }, - Metadata: "api.proto", -} - -func init() { proto.RegisterFile("api.proto", fileDescriptorApi) } - -var fileDescriptorApi = []byte{ - // 2425 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xec, 0x59, 0x4b, 0x73, 0x1c, 0x49, - 0x11, 0xf6, 0xcc, 0xb4, 0x66, 0x34, 0x39, 0x0f, 0x49, 0x6d, 0x59, 0x6e, 0xcf, 0xee, 0xda, 0xda, - 0x8e, 0x85, 0x15, 0xe0, 0x90, 0x8d, 0xbc, 0x0e, 0x1c, 0x10, 0x41, 0x84, 0x2d, 0x9b, 0xc5, 0xac, - 0xe5, 0x1d, 0x97, 0x24, 0xf6, 0x38, 0xd1, 0xea, 0x2e, 0xcf, 0x14, 0xea, 0xe9, 0x6a, 0x57, 0x57, - 0x4b, 0xa3, 0x0b, 0x07, 0x0e, 0x70, 0x83, 0x2b, 0x11, 0x70, 0xe3, 0xc6, 0x9d, 0x03, 0xfc, 0x01, - 0x22, 0xf8, 0x21, 0xdc, 0xb8, 0x73, 0x24, 0xea, 0xd1, 0xd5, 0xd5, 0xf3, 0xf0, 0xe3, 0x40, 0x70, - 0xe1, 0x32, 0x51, 0xf9, 0x55, 0x56, 0x66, 0x56, 0x56, 0x66, 0x56, 0x76, 0x0d, 0xb4, 0x83, 0x94, - 0xec, 0xa7, 0x8c, 0x72, 0xea, 0xae, 0xf1, 0xab, 0x14, 0x67, 0x83, 0x3b, 0x63, 0x4a, 0xc7, 0x31, - 0xbe, 0x27, 0xc1, 0xb3, 0xfc, 0xf5, 0x3d, 0x4e, 0xa6, 0x38, 0xe3, 0xc1, 0x34, 0x55, 0x7c, 0xfe, - 0x2d, 0xb8, 0xf9, 0x25, 0xe6, 0xc7, 0x98, 0x5d, 0x60, 0xf6, 0x73, 0xcc, 0x32, 0x42, 0x13, 0x84, - 0xdf, 0xe4, 0x38, 0xe3, 0xfe, 0x0c, 0xbc, 0xc5, 0xa9, 0x2c, 0xa5, 0x49, 0x86, 0xdd, 0x6d, 0x58, - 0x9b, 0x06, 0xbf, 0xa0, 0xcc, 0xab, 0xed, 0xd6, 0xf6, 0x7a, 0x48, 0x11, 0x12, 0x25, 0x09, 0x65, - 0x5e, 0x5d, 0xa3, 0x82, 0x10, 0x68, 0x1a, 0xf0, 0x70, 0xe2, 0x35, 0x14, 0x2a, 0x09, 0x77, 0x00, - 0xeb, 0x0c, 0x5f, 0x10, 0x21, 0xd5, 0x73, 0x76, 0x6b, 0x7b, 0x6d, 0x64, 0x68, 0xff, 0xd7, 0x35, - 0xd8, 0x3e, 0x4d, 0xa3, 0x80, 0xe3, 0x21, 0xa3, 0x21, 0xce, 0x32, 0x6d, 0x92, 0xdb, 0x87, 0x3a, - 0x89, 0xa4, 0xce, 0x36, 0xaa, 0x93, 0xc8, 0xdd, 0x84, 0x46, 0x4a, 0x22, 0xa9, 0xae, 0x8d, 0xc4, - 0xd0, 0xbd, 0x0d, 0x10, 0xc6, 0x34, 0xc3, 0xc7, 0x3c, 0x22, 0x89, 0xd4, 0xb8, 0x8e, 0x2c, 0x44, - 0x18, 0x73, 0x49, 0x22, 0x3e, 0x91, 0x3a, 0x7b, 0x48, 0x11, 0xee, 0x0e, 0x34, 0x27, 0x98, 0x8c, - 0x27, 0xdc, 0x5b, 0x93, 0xb0, 0xa6, 0xfc, 0x9b, 0x70, 0x63, 0xce, 0x0e, 0xb5, 0x7f, 0xff, 0x1f, - 0x75, 0xd8, 0x39, 0x64, 0x38, 0xe0, 0xf8, 0x90, 0x26, 0x3c, 0x20, 0x09, 0x66, 0xab, 0x6c, 0xbc, - 0x0d, 0x70, 0x96, 0x27, 0x51, 0x8c, 0x87, 0x01, 0x9f, 0x68, 0x53, 0x2d, 0x44, 0x5a, 0x3c, 0xc1, - 0xe1, 0x79, 0x4a, 0x49, 0xc2, 0xa5, 0xc5, 0x6d, 0x64, 0x21, 0xc2, 0xe2, 0x4c, 0x6e, 0x46, 0x79, - 0x49, 0x11, 0xc2, 0xe2, 0x8c, 0x47, 0x34, 0x57, 0x16, 0xb7, 0x91, 0xa6, 0x34, 0x8e, 0x19, 0xf3, - 0x9a, 0x06, 0xc7, 0x8c, 0x09, 0x3c, 0x0e, 0xce, 0x70, 0x9c, 0x79, 0xad, 0xdd, 0x86, 0xc0, 0x15, - 0xe5, 0xee, 0x42, 0x27, 0xa1, 0x43, 0x72, 0x41, 0x39, 0xa2, 0x94, 0x7b, 0xeb, 0xd2, 0x61, 0x36, - 0xe4, 0x7a, 0xd0, 0x62, 0x79, 0x22, 0xe2, 0xc6, 0x6b, 0x4b, 0x91, 0x05, 0x29, 0xd6, 0xea, 0xe1, - 0x63, 0x36, 0xce, 0x3c, 0x90, 0x82, 0x6d, 0xc8, 0xfd, 0x0c, 0x7a, 0xe5, 0x4e, 0x9e, 0x12, 0xe6, - 0x75, 0xa4, 0x84, 0x2a, 0xe8, 0x3f, 0x87, 0x9b, 0x0b, 0xbe, 0xd4, 0x71, 0xb6, 0x0f, 0xed, 0xb0, - 0x00, 0xa5, 0x4f, 0x3b, 0x07, 0x9b, 0xfb, 0x32, 0xb4, 0xf7, 0x4b, 0xe6, 0x92, 0xc5, 0x7f, 0x0e, - 0xbd, 0x63, 0x32, 0x4e, 0x82, 0xf8, 0xfd, 0x23, 0x46, 0x78, 0x4c, 0x2e, 0xd1, 0xf1, 0xa9, 0x29, - 0x7f, 0x13, 0xfa, 0x85, 0x28, 0x7d, 0xe8, 0x7f, 0x69, 0xc0, 0xd6, 0xe3, 0x28, 0x7a, 0x47, 0x4c, - 0x0e, 0x60, 0x9d, 0x63, 0x36, 0x25, 0x42, 0x62, 0x5d, 0xba, 0xd3, 0xd0, 0xee, 0x1d, 0x70, 0xf2, - 0x0c, 0x33, 0xa9, 0xa9, 0x73, 0xd0, 0xd1, 0x3b, 0x39, 0xcd, 0x30, 0x43, 0x72, 0xc2, 0x75, 0xc1, - 0x09, 0x84, 0x2f, 0x1d, 0xe9, 0x4b, 0x39, 0x16, 0x26, 0xe3, 0xe4, 0xc2, 0x5b, 0x93, 0x90, 0x18, - 0x0a, 0x24, 0xbc, 0x8c, 0xf4, 0x09, 0x8b, 0x61, 0xb1, 0xad, 0x56, 0xb9, 0x2d, 0x13, 0x36, 0xeb, - 0xcb, 0xc3, 0xa6, 0xbd, 0x22, 0x6c, 0xa0, 0x12, 0x36, 0x3e, 0x74, 0xc3, 0x20, 0x0d, 0xce, 0x48, - 0x4c, 0x38, 0xc1, 0x99, 0xd7, 0x91, 0x46, 0x54, 0x30, 0x77, 0x0f, 0x36, 0x82, 0x34, 0x0d, 0xd8, - 0x94, 0xb2, 0x21, 0xa3, 0xaf, 0x49, 0x8c, 0xbd, 0xae, 0x14, 0x32, 0x0f, 0x0b, 0x69, 0x19, 0x8e, - 0x49, 0x92, 0xcf, 0x5e, 0x88, 0xe8, 0xf3, 0x7a, 0x92, 0xad, 0x82, 0x09, 0x69, 0x09, 0x7d, 0x89, - 0x2f, 0x87, 0x8c, 0x5c, 0x90, 0x18, 0x8f, 0x71, 0xe6, 0xf5, 0xa5, 0x17, 0xe7, 0x61, 0xf7, 0x73, - 0x68, 0xb1, 0x98, 0x4c, 0x09, 0xcf, 0xbc, 0x8d, 0xdd, 0xc6, 0x5e, 0xe7, 0xa0, 0xa7, 0xfd, 0x89, - 0x24, 0x8a, 0x8a, 0x59, 0xff, 0x29, 0x34, 0x15, 0x24, 0xdc, 0x2b, 0x58, 0xf4, 0x69, 0xc9, 0xb1, - 0xc0, 0x32, 0xfa, 0x9a, 0xcb, 0xb3, 0x72, 0x90, 0x1c, 0x0b, 0x6c, 0x12, 0xb0, 0x48, 0x9e, 0x93, - 0x83, 0xe4, 0xd8, 0x47, 0xe0, 0x88, 0x83, 0x12, 0xae, 0xce, 0xf5, 0x81, 0xf7, 0x90, 0x18, 0x0a, - 0x64, 0xac, 0x63, 0xaa, 0x87, 0xc4, 0xd0, 0xfd, 0x36, 0xf4, 0x83, 0x28, 0x22, 0x9c, 0xd0, 0x24, - 0x88, 0xbf, 0x24, 0x51, 0xe6, 0x35, 0x76, 0x1b, 0x7b, 0x3d, 0x34, 0x87, 0xfa, 0xdb, 0xe0, 0xda, - 0x01, 0xa5, 0xe3, 0xec, 0x57, 0x35, 0x93, 0x10, 0x26, 0x4f, 0x56, 0x45, 0xdb, 0xf7, 0x2b, 0xd5, - 0xa3, 0x2e, 0xe3, 0x6a, 0xab, 0xc8, 0x90, 0x72, 0xb5, 0x5d, 0x50, 0x16, 0x92, 0xb2, 0xb1, 0x2c, - 0x29, 0x07, 0xe0, 0x2d, 0xda, 0xa0, 0x0d, 0x0c, 0xe1, 0xe6, 0x53, 0x1c, 0xe3, 0xf7, 0xb1, 0xcf, - 0x05, 0x27, 0x09, 0xa6, 0x58, 0x27, 0x9c, 0x1c, 0xbf, 0xbf, 0x01, 0x8b, 0x4a, 0xb4, 0x01, 0x47, - 0x70, 0xe3, 0x05, 0xc9, 0xf8, 0xbb, 0xd5, 0x2f, 0xa8, 0xaa, 0x2f, 0x53, 0xf5, 0xfb, 0x1a, 0x40, - 0x29, 0xcb, 0xd8, 0x5c, 0xb3, 0x6c, 0x76, 0xc1, 0xc1, 0x33, 0xc2, 0x75, 0x46, 0xcb, 0xb1, 0x38, - 0x77, 0x1e, 0xa6, 0xfa, 0x92, 0x11, 0x43, 0x51, 0x11, 0xf3, 0x84, 0xcc, 0x8e, 0x69, 0x78, 0x8e, - 0x79, 0x26, 0x2b, 0xf6, 0x3a, 0xb2, 0x21, 0x99, 0x96, 0x13, 0x1c, 0xc7, 0xb2, 0x6c, 0xaf, 0x23, - 0x45, 0x88, 0x1a, 0x8b, 0xa7, 0x29, 0xbf, 0x7a, 0x79, 0xec, 0x35, 0x65, 0x86, 0x15, 0xa4, 0x7f, - 0x04, 0x3b, 0xf3, 0x3b, 0xd5, 0xa5, 0xf1, 0x01, 0x74, 0xca, 0x5d, 0x64, 0x5e, 0x4d, 0xa6, 0xc0, - 0x92, 0xa3, 0xb7, 0xb9, 0xfc, 0xdb, 0xd0, 0x3d, 0xe6, 0x01, 0xc7, 0x2b, 0xfc, 0xe5, 0xef, 0x41, - 0xdf, 0xd4, 0x55, 0xc9, 0xa8, 0x2a, 0x43, 0xc0, 0xf3, 0x4c, 0x73, 0x69, 0xca, 0xff, 0x6b, 0x03, - 0x5a, 0x3a, 0x70, 0x8b, 0xea, 0x53, 0x2b, 0xab, 0xcf, 0xff, 0xa4, 0x08, 0x7e, 0x0c, 0xed, 0xec, - 0x2a, 0xe3, 0x78, 0x3a, 0xd4, 0xa5, 0xb0, 0x87, 0x4a, 0xe0, 0xff, 0x05, 0xb1, 0x2c, 0x88, 0x7f, - 0xaf, 0x41, 0xdb, 0x1c, 0xf3, 0x07, 0x37, 0x2c, 0x77, 0xa1, 0x9d, 0xaa, 0x83, 0xc7, 0xaa, 0xae, - 0x75, 0x0e, 0xfa, 0x5a, 0x51, 0x51, 0xc9, 0x4a, 0x06, 0x2b, 0x7e, 0x1c, 0x3b, 0x7e, 0xac, 0x86, - 0x64, 0xad, 0xd2, 0x90, 0xb8, 0xe0, 0xa4, 0xa2, 0x60, 0x36, 0x65, 0xc1, 0x94, 0x63, 0xbb, 0x05, - 0x69, 0x55, 0x5a, 0x10, 0xff, 0x21, 0xb4, 0x8e, 0x82, 0x70, 0x42, 0x12, 0x99, 0xa1, 0x61, 0xaa, - 0xc3, 0xb4, 0x87, 0xe4, 0x58, 0x28, 0x99, 0xe2, 0x29, 0x65, 0x57, 0xba, 0xba, 0x6b, 0xca, 0x3f, - 0x87, 0x9e, 0x4e, 0x03, 0x9d, 0x4c, 0xf7, 0x01, 0x4c, 0x13, 0x51, 0xe4, 0xd2, 0x62, 0xa3, 0x61, - 0xf1, 0xb8, 0x7b, 0xd0, 0x9a, 0x2a, 0xcd, 0xba, 0xea, 0x16, 0x3e, 0xd0, 0xf6, 0xa0, 0x62, 0xda, - 0xff, 0x4d, 0x0d, 0x76, 0x54, 0x17, 0xf9, 0xce, 0x5e, 0x71, 0x79, 0x77, 0xa2, 0xdc, 0xd7, 0xa8, - 0xb8, 0xef, 0x01, 0xb4, 0x19, 0xce, 0x68, 0xce, 0x42, 0xac, 0x3c, 0xdb, 0x39, 0xb8, 0x51, 0x64, - 0x92, 0xd4, 0x85, 0xf4, 0x2c, 0x2a, 0xf9, 0xfc, 0x3f, 0x36, 0xa0, 0x5f, 0x9d, 0x15, 0x15, 0xeb, - 0x2c, 0x3e, 0x27, 0xf4, 0x1b, 0xd5, 0xfe, 0xd6, 0xa4, 0x9b, 0x6c, 0x48, 0x64, 0x55, 0x98, 0xe6, - 0xc7, 0x93, 0x80, 0xe1, 0x4c, 0xbb, 0xb1, 0x04, 0xf4, 0xec, 0x10, 0x33, 0x42, 0x8b, 0xeb, 0xb2, - 0x04, 0x44, 0x19, 0x08, 0xd3, 0xfc, 0x55, 0x4e, 0x79, 0x20, 0x8d, 0x74, 0x90, 0xa1, 0x65, 0xdf, - 0x9b, 0xe6, 0x19, 0xe6, 0x87, 0xe2, 0xd4, 0xd6, 0x74, 0xdf, 0x6b, 0x90, 0x72, 0xfe, 0x08, 0x4f, - 0x33, 0x9d, 0xe6, 0x16, 0x22, 0x2c, 0x57, 0xa7, 0xf9, 0x42, 0x04, 0xb5, 0x0c, 0x0c, 0x07, 0xd9, - 0x90, 0x90, 0xa0, 0xc8, 0xe3, 0xcb, 0x20, 0x95, 0x69, 0xef, 0x20, 0x0b, 0x71, 0xef, 0xc2, 0x96, - 0xa2, 0x10, 0xce, 0x30, 0xbb, 0x08, 0xc4, 0xc5, 0x2c, 0xcb, 0x80, 0x83, 0x16, 0x27, 0x04, 0xf7, - 0x39, 0x66, 0x09, 0x8e, 0x8f, 0x2c, 0xad, 0xa0, 0xb8, 0x17, 0x26, 0xdc, 0x03, 0xd8, 0x56, 0xe0, - 0xc9, 0xe1, 0xd0, 0x5e, 0xd0, 0x91, 0x0b, 0x96, 0xce, 0x89, 0x6f, 0xb1, 0x85, 0x38, 0xd1, 0x17, - 0xde, 0x15, 0xf4, 0x9e, 0x5d, 0xe0, 0x84, 0x9b, 0xae, 0xf3, 0x11, 0xb4, 0xcd, 0xa7, 0x9c, 0x0e, - 0xc0, 0xc1, 0xbe, 0xfa, 0xd8, 0xdb, 0x2f, 0x3e, 0xf6, 0xf6, 0x4f, 0x0a, 0x0e, 0x54, 0x32, 0x0b, - 0xaf, 0x64, 0x9c, 0x32, 0x1c, 0x7d, 0x9d, 0xc4, 0x57, 0xc5, 0x17, 0x52, 0x89, 0xe8, 0x98, 0x74, - 0xcc, 0x95, 0xf0, 0xbb, 0x1a, 0xac, 0x49, 0xdd, 0x4b, 0xbb, 0x27, 0xc5, 0x5d, 0x37, 0x11, 0x5c, - 0x8d, 0xd7, 0x9e, 0x89, 0x57, 0x1d, 0xd9, 0x4e, 0x19, 0xd9, 0x95, 0x1d, 0x34, 0x3f, 0x60, 0x07, - 0xfe, 0x6f, 0xeb, 0xd0, 0x7d, 0x89, 0xf9, 0x25, 0x65, 0xe7, 0x22, 0x8b, 0xb3, 0xa5, 0x17, 0xf6, - 0x2d, 0x58, 0x67, 0xb3, 0xd1, 0xd9, 0x15, 0x37, 0x51, 0xdb, 0x62, 0xb3, 0x27, 0x82, 0x74, 0x3f, - 0x01, 0x60, 0xb3, 0xd1, 0x30, 0x50, 0x97, 0xb4, 0x0e, 0x5a, 0x36, 0xd3, 0x80, 0xfb, 0x11, 0xb4, - 0xd1, 0x6c, 0x84, 0x19, 0xa3, 0x2c, 0x2b, 0xa2, 0x96, 0xcd, 0x9e, 0x49, 0x5a, 0xac, 0x45, 0xb3, - 0x51, 0xc4, 0x68, 0x9a, 0xe2, 0x48, 0x46, 0xad, 0x5c, 0xfb, 0x54, 0x01, 0x42, 0xeb, 0x49, 0xa1, - 0xb5, 0xa9, 0xb4, 0xf2, 0x52, 0xeb, 0xc9, 0x6c, 0x94, 0x6a, 0xad, 0x2a, 0x5c, 0xdb, 0xdc, 0xd6, - 0x7a, 0x62, 0xb4, 0xaa, 0x58, 0x5d, 0xe7, 0x96, 0xd6, 0x93, 0x52, 0x6b, 0xbb, 0x58, 0xab, 0xb5, - 0xfa, 0x7f, 0xae, 0xc1, 0xfa, 0x61, 0x9a, 0x9f, 0x66, 0xc1, 0x18, 0xbb, 0x77, 0xa0, 0xc3, 0x29, - 0x0f, 0xe2, 0x51, 0x2e, 0x48, 0x9d, 0xd1, 0x20, 0x21, 0xc5, 0xf0, 0x29, 0x74, 0x53, 0xcc, 0xc2, - 0x34, 0xd7, 0x1c, 0xf5, 0xdd, 0x86, 0xc8, 0x1c, 0x85, 0x29, 0x96, 0x7d, 0xb8, 0x2e, 0xe7, 0x46, - 0x24, 0x19, 0xa9, 0x50, 0x9d, 0xd2, 0x08, 0x6b, 0x57, 0x6d, 0xc9, 0xa9, 0xe7, 0xc9, 0x57, 0x66, - 0xc2, 0xfd, 0x2e, 0x6c, 0x19, 0x7e, 0x71, 0x85, 0x4b, 0x6e, 0xe5, 0xba, 0x0d, 0xcd, 0x7d, 0xaa, - 0x61, 0xff, 0x97, 0xd0, 0x3f, 0x99, 0x30, 0xca, 0x79, 0x4c, 0x92, 0xf1, 0xd3, 0x80, 0x07, 0xa2, - 0xbc, 0xa7, 0xb2, 0x5e, 0x64, 0xda, 0xda, 0x82, 0x74, 0xbf, 0x07, 0x5b, 0x5c, 0xf1, 0xe2, 0x68, - 0x54, 0xf0, 0xa8, 0xd3, 0xdc, 0x34, 0x13, 0x43, 0xcd, 0xfc, 0x2d, 0xe8, 0x97, 0xcc, 0xf2, 0xb2, - 0x50, 0xf6, 0xf6, 0x0c, 0x2a, 0xa2, 0xc9, 0xff, 0x83, 0x72, 0x96, 0x8a, 0x9c, 0xbb, 0xb2, 0x7c, - 0x59, 0xae, 0xea, 0x1c, 0x6c, 0x14, 0x65, 0x5f, 0x3b, 0x43, 0x96, 0x2c, 0xe5, 0x96, 0x1f, 0xc3, - 0x06, 0x37, 0xa6, 0x8f, 0xa2, 0x80, 0x07, 0x3a, 0xf5, 0x8a, 0xd2, 0x5b, 0xdd, 0x18, 0xea, 0xf3, - 0xea, 0x46, 0x3f, 0x85, 0xae, 0xea, 0x47, 0xb4, 0x42, 0x65, 0x5f, 0x47, 0x61, 0x52, 0x85, 0xff, - 0x23, 0x68, 0x0f, 0x49, 0x94, 0x29, 0xeb, 0x3c, 0x68, 0x85, 0x39, 0x63, 0x38, 0x29, 0x0a, 0x73, - 0x41, 0x8a, 0x66, 0x46, 0xde, 0xe5, 0xda, 0x19, 0x8a, 0xf0, 0x29, 0x80, 0xaa, 0x27, 0x52, 0xdb, - 0x36, 0xac, 0xd9, 0x21, 0xa0, 0x08, 0x11, 0x67, 0xd3, 0x60, 0x66, 0x8e, 0x5e, 0xc6, 0xd9, 0x34, - 0x98, 0xa9, 0x0d, 0x7a, 0xd0, 0x7a, 0x1d, 0x90, 0x38, 0xd4, 0x0f, 0x11, 0x0e, 0x2a, 0xc8, 0x52, - 0xa1, 0x63, 0x2b, 0xfc, 0x53, 0x1d, 0x3a, 0x4a, 0xa3, 0x32, 0x78, 0x1b, 0xd6, 0xc2, 0x20, 0x9c, - 0x18, 0x95, 0x92, 0x70, 0x3f, 0x2f, 0x0c, 0xa9, 0x7e, 0x9e, 0x94, 0xa6, 0x16, 0xb6, 0xdd, 0x07, - 0xc8, 0x2e, 0x83, 0xd4, 0xf2, 0xce, 0x52, 0xee, 0xb6, 0x60, 0x52, 0x06, 0x7f, 0x01, 0x5d, 0x15, - 0x9f, 0x7a, 0x8d, 0xb3, 0x6a, 0x4d, 0x47, 0xb1, 0xa9, 0x55, 0x0f, 0x44, 0x2b, 0x18, 0x70, 0xd5, - 0x7a, 0x74, 0x0e, 0x3e, 0xa9, 0xb0, 0xcb, 0x9d, 0xec, 0xcb, 0xdf, 0x67, 0x09, 0x67, 0x57, 0x48, - 0xf1, 0x0e, 0x1e, 0x01, 0x94, 0xa0, 0xa8, 0x67, 0xe7, 0xf8, 0xaa, 0x68, 0x79, 0xcf, 0xf1, 0x95, - 0xd8, 0xfb, 0x45, 0x10, 0xe7, 0x85, 0x53, 0x15, 0xf1, 0xc3, 0xfa, 0xa3, 0x9a, 0x1f, 0xc2, 0xc6, - 0x13, 0x71, 0xa1, 0x5a, 0xcb, 0x2b, 0xef, 0x67, 0xce, 0xd2, 0xf7, 0x33, 0xa7, 0x78, 0x3f, 0xeb, - 0x43, 0x9d, 0xa6, 0xfa, 0xfa, 0xaf, 0xd3, 0xb4, 0x54, 0xe4, 0x58, 0x8a, 0xfc, 0x7f, 0x3a, 0x00, - 0xa5, 0x16, 0xf7, 0x18, 0x06, 0x84, 0x8e, 0xc4, 0xed, 0x45, 0x42, 0xac, 0x0a, 0xd2, 0x88, 0xe1, - 0x30, 0x67, 0x19, 0xb9, 0xc0, 0xba, 0xc1, 0xd9, 0xd1, 0xfb, 0x9e, 0x33, 0x0e, 0xdd, 0x24, 0xf4, - 0x58, 0x2d, 0x94, 0x95, 0x0b, 0x15, 0xcb, 0xdc, 0x9f, 0xc1, 0x8d, 0x52, 0x68, 0x64, 0xc9, 0xab, - 0xbf, 0x55, 0xde, 0x75, 0x23, 0x2f, 0x2a, 0x65, 0xfd, 0x04, 0xae, 0x13, 0x3a, 0x7a, 0x93, 0xe3, - 0xbc, 0x22, 0xa9, 0xf1, 0x56, 0x49, 0x5b, 0x84, 0xbe, 0x92, 0x2b, 0x4a, 0x39, 0xaf, 0xe0, 0x96, - 0xb5, 0x51, 0x91, 0xf6, 0x96, 0x34, 0xe7, 0xad, 0xd2, 0x76, 0x8c, 0x5d, 0xa2, 0x30, 0x94, 0x22, - 0xbf, 0x82, 0x1d, 0x42, 0x47, 0x97, 0x01, 0xe1, 0xf3, 0xf2, 0xd6, 0xde, 0xb5, 0xcf, 0x6f, 0x02, - 0xc2, 0xab, 0xc2, 0xd4, 0x3e, 0xa7, 0x98, 0x8d, 0x2b, 0xfb, 0x6c, 0xbe, 0x6b, 0x9f, 0x47, 0x72, - 0x45, 0x29, 0xe7, 0x09, 0x6c, 0x11, 0x3a, 0x6f, 0x4f, 0xeb, 0xad, 0x52, 0x36, 0x08, 0xad, 0xda, - 0x72, 0x08, 0x5b, 0x19, 0x0e, 0x39, 0x65, 0x76, 0x2c, 0xac, 0xbf, 0x55, 0xc6, 0xa6, 0x5e, 0x60, - 0x84, 0xf8, 0x6f, 0xa0, 0xfb, 0xd3, 0x7c, 0x8c, 0x79, 0x7c, 0x66, 0x72, 0xfe, 0xbf, 0x5d, 0x66, - 0xfe, 0x5d, 0x87, 0xce, 0xe1, 0x98, 0xd1, 0x3c, 0xad, 0x54, 0x6d, 0x95, 0xc3, 0x0b, 0x55, 0x5b, - 0xf2, 0xc8, 0xaa, 0xad, 0xb8, 0x1f, 0x42, 0x57, 0x75, 0x73, 0x7a, 0x81, 0xaa, 0x42, 0xee, 0x62, - 0xd2, 0x17, 0xdd, 0xa3, 0x5a, 0x76, 0xa0, 0x3b, 0x63, 0xbd, 0xaa, 0x5a, 0x8d, 0x4a, 0x37, 0x21, - 0x38, 0x2b, 0xb3, 0xee, 0x39, 0xf4, 0x26, 0xca, 0x37, 0x7a, 0x95, 0x0a, 0xc0, 0xcf, 0x0a, 0xe3, - 0xca, 0x3d, 0xec, 0xdb, 0x3e, 0x54, 0xae, 0xee, 0x4e, 0x6c, 0xb7, 0xde, 0x03, 0x10, 0xdf, 0x3e, - 0xa3, 0xa2, 0x50, 0xd9, 0x4f, 0x9f, 0xe6, 0x86, 0x40, 0xed, 0xb4, 0x18, 0x0e, 0x4e, 0x60, 0x6b, - 0x41, 0xe6, 0x92, 0x32, 0xf5, 0x1d, 0xbb, 0x4c, 0x75, 0x0e, 0xae, 0x6b, 0x91, 0xf6, 0x52, 0xbb, - 0x76, 0xfd, 0xad, 0xa6, 0x3e, 0x95, 0xcc, 0xeb, 0x94, 0xfb, 0x08, 0x7a, 0x89, 0x6a, 0xbe, 0xcc, - 0x01, 0x34, 0x2c, 0x41, 0x76, 0x63, 0x86, 0xba, 0x89, 0xdd, 0xa6, 0x3d, 0x84, 0x6e, 0x28, 0x3d, - 0xb0, 0xf4, 0x20, 0x2c, 0xe7, 0xa0, 0x4e, 0x68, 0x9d, 0x76, 0xa5, 0x51, 0x74, 0x3e, 0xa4, 0x51, - 0xd4, 0xaf, 0x1d, 0xab, 0x9e, 0x6a, 0x0f, 0xfe, 0xd5, 0x84, 0xc6, 0xe3, 0xe1, 0x73, 0xf7, 0x14, - 0x36, 0xe7, 0xff, 0xe9, 0x70, 0x6f, 0x6b, 0xb3, 0x56, 0xfc, 0x3b, 0x32, 0xb8, 0xb3, 0x72, 0x5e, - 0xb7, 0xec, 0xd7, 0x5c, 0x04, 0x1b, 0x73, 0xef, 0xda, 0x6e, 0x71, 0xd5, 0x2c, 0xff, 0xef, 0x60, - 0x70, 0x7b, 0xd5, 0xb4, 0x2d, 0x73, 0xee, 0x1b, 0xc1, 0xc8, 0x5c, 0xfe, 0x8d, 0x69, 0x64, 0xae, - 0xfa, 0xb4, 0xb8, 0xe6, 0xfe, 0x00, 0x9a, 0xea, 0xa5, 0xdb, 0xdd, 0xd6, 0xbc, 0x95, 0x37, 0xf4, - 0xc1, 0x8d, 0x39, 0xd4, 0x2c, 0x7c, 0x01, 0xbd, 0xca, 0xdf, 0x23, 0xee, 0x47, 0x15, 0x5d, 0xd5, - 0x87, 0xf2, 0xc1, 0xc7, 0xcb, 0x27, 0x8d, 0xb4, 0x43, 0x80, 0xf2, 0x31, 0xd4, 0xf5, 0x34, 0xf7, - 0xc2, 0x83, 0xfb, 0xe0, 0xd6, 0x92, 0x19, 0x23, 0xe4, 0x14, 0x36, 0xe7, 0x9f, 0x2d, 0xdd, 0x39, - 0xaf, 0xce, 0x3f, 0x1a, 0x9a, 0xa3, 0x5c, 0xf9, 0xde, 0x29, 0xc5, 0xce, 0x3f, 0x46, 0x1a, 0xb1, - 0x2b, 0x9e, 0x42, 0x8d, 0xd8, 0x95, 0xaf, 0x98, 0xd7, 0xdc, 0xaf, 0xa1, 0x5f, 0x7d, 0xdd, 0x73, - 0x0b, 0x27, 0x2d, 0x7d, 0xde, 0x1c, 0x7c, 0xb2, 0x62, 0xd6, 0x08, 0xfc, 0x02, 0xd6, 0xd4, 0xb3, - 0x5d, 0x91, 0x8e, 0xf6, 0x6b, 0xdf, 0x60, 0xbb, 0x0a, 0x9a, 0x55, 0xf7, 0xa1, 0xa9, 0xbe, 0x2e, - 0x4d, 0x00, 0x54, 0x3e, 0x36, 0x07, 0x5d, 0x1b, 0xf5, 0xaf, 0xdd, 0xaf, 0x15, 0x7a, 0xb2, 0x8a, - 0x9e, 0x6c, 0x99, 0x1e, 0xeb, 0x70, 0xce, 0x9a, 0x32, 0x5d, 0x1f, 0xfc, 0x27, 0x00, 0x00, 0xff, - 0xff, 0xc9, 0x06, 0x1e, 0xda, 0xa8, 0x1c, 0x00, 0x00, -} diff --git a/containerd/api/grpc/types/api.proto b/containerd/api/grpc/types/api.proto deleted file mode 100644 index 17051013..00000000 --- a/containerd/api/grpc/types/api.proto +++ /dev/null @@ -1,320 +0,0 @@ -syntax = "proto3"; - -package types; - -import "google/protobuf/timestamp.proto"; - -service API { - rpc GetServerVersion(GetServerVersionRequest) returns (GetServerVersionResponse) {} - rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse) {} - rpc UpdateContainer(UpdateContainerRequest) returns (UpdateContainerResponse) {} - rpc Signal(SignalRequest) returns (SignalResponse) {} - rpc UpdateProcess(UpdateProcessRequest) returns (UpdateProcessResponse) {} - rpc AddProcess(AddProcessRequest) returns (AddProcessResponse) {} - rpc CreateCheckpoint(CreateCheckpointRequest) returns (CreateCheckpointResponse) {} - rpc DeleteCheckpoint(DeleteCheckpointRequest) returns (DeleteCheckpointResponse) {} - rpc ListCheckpoint(ListCheckpointRequest) returns (ListCheckpointResponse) {} - rpc State(StateRequest) returns (StateResponse) {} - rpc Events(EventsRequest) returns (stream Event) {} - rpc Stats(StatsRequest) returns (StatsResponse) {} -} - -message GetServerVersionRequest { -} - -message GetServerVersionResponse { - uint32 major = 1; - uint32 minor = 2; - uint32 patch = 3; - string revision = 4; -} - -message UpdateProcessRequest { - string id = 1; - string pid = 2; - bool closeStdin = 3; // Close stdin of the container - uint32 width = 4; - uint32 height = 5; -} - -message UpdateProcessResponse { -} - -message CreateContainerRequest { - string id = 1; // ID of container - string bundlePath = 2; // path to OCI bundle - string checkpoint = 3; // checkpoint name if you want to create immediate checkpoint (optional) - string stdin = 4; // path to the file where stdin will be read (optional) - string stdout = 5; // path to file where stdout will be written (optional) - string stderr = 6; // path to file where stderr will be written (optional) - repeated string labels = 7; - bool noPivotRoot = 8; - string runtime = 9; - repeated string runtimeArgs = 10; - string checkpointDir = 11; // Directory where checkpoints are stored -} - -message CreateContainerResponse { - Container container = 1; -} - -message SignalRequest { - string id = 1; // ID of container - string pid = 2; // PID of process inside container - uint32 signal = 3; // Signal which will be sent, you can find value in "man 7 signal" -} - -message SignalResponse { -} - -message AddProcessRequest { - string id = 1; // ID of container - bool terminal = 2; // Use tty for container stdio - User user = 3; // User under which process will be run - repeated string args = 4; // Arguments for process, first is binary path itself - repeated string env = 5; // List of environment variables for process - string cwd = 6; // Workind directory of process - string pid = 7; // Process ID - string stdin = 8; // path to the file where stdin will be read (optional) - string stdout = 9; // path to file where stdout will be written (optional) - string stderr = 10; // path to file where stderr will be written (optional) - repeated string capabilities = 11; - string apparmorProfile = 12; - string selinuxLabel = 13; - bool noNewPrivileges = 14; - repeated Rlimit rlimits = 15; -} - -message Rlimit { - string type = 1; - uint64 soft = 2; - uint64 hard = 3; -} - -message User { - uint32 uid = 1; // UID of user - uint32 gid = 2; // GID of user - repeated uint32 additionalGids = 3; // Additional groups to which user will be added -} - -message AddProcessResponse { -} - -message CreateCheckpointRequest { - string id = 1; // ID of container - Checkpoint checkpoint = 2; // Checkpoint configuration - string checkpointDir = 3; // Directory where checkpoints are stored -} - -message CreateCheckpointResponse { -} - -message DeleteCheckpointRequest { - string id = 1; // ID of container - string name = 2; // Name of checkpoint - string checkpointDir = 3; // Directory where checkpoints are stored -} - -message DeleteCheckpointResponse { -} - -message ListCheckpointRequest { - string id = 1; // ID of container - string checkpointDir = 2; // Directory where checkpoints are stored -} - -message Checkpoint { - string name = 1; // Name of checkpoint - bool exit = 2; // checkpoint configuration: should container exit on checkpoint or not - bool tcp = 3; // allow open tcp connections - bool unixSockets = 4; // allow external unix sockets - bool shell = 5; // allow shell-jobs - repeated string emptyNS = 6; -} - -message ListCheckpointResponse { - repeated Checkpoint checkpoints = 1; // List of checkpoints -} - -message StateRequest { - string id = 1; // container id for a single container -} - -message ContainerState { - string status = 1; -} - -message Process { - string pid = 1; - bool terminal = 2; // Use tty for container stdio - User user = 3; // User under which process will be run - repeated string args = 4; // Arguments for process, first is binary path itself - repeated string env = 5; // List of environment variables for process - string cwd = 6; // Workind directory of process - uint32 systemPid = 7; - string stdin = 8; // path to the file where stdin will be read (optional) - string stdout = 9; // path to file where stdout will be written (optional) - string stderr = 10; // path to file where stderr will be written (optional) - repeated string capabilities = 11; - string apparmorProfile = 12; - string selinuxLabel = 13; - bool noNewPrivileges = 14; - repeated Rlimit rlimits = 15; -} - -message Container { - string id = 1; // ID of container - string bundlePath = 2; // Path to OCI bundle - repeated Process processes = 3; // List of processes which run in container - string status = 4; // Container status ("running", "paused", etc.) - repeated string labels = 5; - repeated uint32 pids = 6; - string runtime = 7; // runtime used to execute the container -} - -// Machine is information about machine on which containerd is run -message Machine { - uint32 cpus = 1; // number of cpus - uint64 memory = 2; // amount of memory -} - -// StateResponse is information about containerd daemon -message StateResponse { - repeated Container containers = 1; - Machine machine = 2; -} - -message UpdateContainerRequest { - string id = 1; // ID of container - string pid = 2; - string status = 3; // Status to which containerd will try to change - UpdateResource resources =4; -} - -message UpdateResource { - uint64 blkioWeight =1; - uint64 cpuShares = 2; - uint64 cpuPeriod = 3; - uint64 cpuQuota = 4; - string cpusetCpus = 5; - string cpusetMems = 6; - uint64 memoryLimit = 7; - uint64 memorySwap = 8; - uint64 memoryReservation = 9; - uint64 kernelMemoryLimit = 10; - uint64 kernelTCPMemoryLimit = 11; -} - -message UpdateContainerResponse { -} - -message EventsRequest { - // Tag 1 is deprecated (old uint64 timestamp) - google.protobuf.Timestamp timestamp = 2; - bool storedOnly = 3; - string id = 4; -} - -message Event { - string type = 1; - string id = 2; - uint32 status = 3; - string pid = 4; - // Tag 5 is deprecated (old uint64 timestamp) - google.protobuf.Timestamp timestamp = 6; -} - -message NetworkStats { - string name = 1; // name of network interface - uint64 rx_bytes = 2; - uint64 rx_Packets = 3; - uint64 Rx_errors = 4; - uint64 Rx_dropped = 5; - uint64 Tx_bytes = 6; - uint64 Tx_packets = 7; - uint64 Tx_errors = 8; - uint64 Tx_dropped = 9; -} - -message CpuUsage { - uint64 total_usage = 1; - repeated uint64 percpu_usage = 2; - uint64 usage_in_kernelmode = 3; - uint64 usage_in_usermode = 4; -} - -message ThrottlingData { - uint64 periods = 1; - uint64 throttled_periods = 2; - uint64 throttled_time = 3; -} - -message CpuStats { - CpuUsage cpu_usage = 1; - ThrottlingData throttling_data = 2; - uint64 system_usage = 3; -} - -message PidsStats { - uint64 current = 1; - uint64 limit = 2; -} - -message MemoryData { - uint64 usage = 1; - uint64 max_usage = 2; - uint64 failcnt = 3; - uint64 limit = 4; -} - -message MemoryStats { - uint64 cache = 1; - MemoryData usage = 2; - MemoryData swap_usage = 3; - MemoryData kernel_usage = 4; - map stats = 5; -} - -message BlkioStatsEntry { - uint64 major = 1; - uint64 minor = 2; - string op = 3; - uint64 value = 4; -} - -message BlkioStats { - repeated BlkioStatsEntry io_service_bytes_recursive = 1; // number of bytes transferred to and from the block device - repeated BlkioStatsEntry io_serviced_recursive = 2; - repeated BlkioStatsEntry io_queued_recursive = 3; - repeated BlkioStatsEntry io_service_time_recursive = 4; - repeated BlkioStatsEntry io_wait_time_recursive = 5; - repeated BlkioStatsEntry io_merged_recursive = 6; - repeated BlkioStatsEntry io_time_recursive = 7; - repeated BlkioStatsEntry sectors_recursive = 8; -} - -message HugetlbStats { - uint64 usage = 1; - uint64 max_usage = 2; - uint64 failcnt = 3; - uint64 limit = 4; -} - -message CgroupStats { - CpuStats cpu_stats = 1; - MemoryStats memory_stats = 2; - BlkioStats blkio_stats = 3; - map hugetlb_stats = 4; // the map is in the format "size of hugepage: stats of the hugepage" - PidsStats pids_stats = 5; -} - -message StatsResponse { - repeated NetworkStats network_stats = 1; - CgroupStats cgroup_stats = 2; - // Tag 3 is deprecated (old uint64 timestamp) - google.protobuf.Timestamp timestamp = 4; -}; - -message StatsRequest { - string id = 1; -} diff --git a/containerd/containerd.go b/containerd/containerd.go deleted file mode 100644 index 771b3bb1..00000000 --- a/containerd/containerd.go +++ /dev/null @@ -1,229 +0,0 @@ -package containerd - -import ( - "encoding/json" - "flag" - "fmt" - "net" - "os" - "os/signal" - "path/filepath" - "syscall" - "time" - - "github.com/golang/glog" - "github.com/hyperhq/runv/containerd/api/grpc/server" - "github.com/hyperhq/runv/containerd/api/grpc/types" - "github.com/hyperhq/runv/driverloader" - "github.com/hyperhq/runv/factory" - singlefactory "github.com/hyperhq/runv/factory/single" - templatefactory "github.com/hyperhq/runv/factory/template" - "github.com/hyperhq/runv/hypervisor" - "github.com/hyperhq/runv/supervisor" - templatecore "github.com/hyperhq/runv/template" - "github.com/urfave/cli" - "google.golang.org/grpc" - "google.golang.org/grpc/health" - "google.golang.org/grpc/health/grpc_health_v1" -) - -const ( - usage = `High performance hypervisor based container daemon` - defaultStateDir = "/run/runv-containerd" - defaultListenType = "unix" - defaultGRPCEndpoint = "/run/runv-containerd/containerd.sock" - // runv-containerd is a relatively short term program - // since we can't change the flush interval in glog, flush here manually. - glogFlushInterval = 5 * time.Second -) - -var ContainerdCommand = cli.Command{ - Name: "containerd", - Usage: usage, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "state-dir", - Value: defaultStateDir, - Usage: "runtime state directory", - }, - cli.StringFlag{ - Name: "containerd-dir", - Value: defaultStateDir, - Usage: "containerd daemon state directory", - }, - cli.StringFlag{ - Name: "listen,l", - Value: defaultGRPCEndpoint, - Usage: "Address on which GRPC API will listen", - }, - cli.BoolFlag{ - Name: "solo-namespaced", - Usage: "launch as a solo namespaced for shared containers", - }, - }, - Before: func(context *cli.Context) error { - logdir := context.GlobalString("log_dir") - if logdir != "" { - if err := os.MkdirAll(logdir, 0750); err != nil { - return fmt.Errorf("can't create dir %s for log files", logdir) - } - } - if context.GlobalBool("debug") { - flag.CommandLine.Parse([]string{"-v", "3", "--log_dir", logdir, "--alsologtostderr"}) - } else { - flag.CommandLine.Parse([]string{"-v", "1", "--log_dir", logdir}) - } - return nil - }, - - Action: func(context *cli.Context) error { - driver := context.GlobalString("driver") - kernel := context.GlobalString("kernel") - initrd := context.GlobalString("initrd") - bios := context.GlobalString("bios") - cbfs := context.GlobalString("cbfs") - vsock := context.GlobalBool("vsock") - template := context.GlobalString("template") - stateDir := context.String("state-dir") - containerdDir := context.String("containerd-dir") - if containerdDir == "" { - containerdDir = stateDir - } - - var tconfig *templatecore.TemplateVmConfig - if template != "" { - path := filepath.Join(template, "config.json") - f, err := os.Open(path) - if err != nil { - err = fmt.Errorf("open template JSON configuration file failed: %v", err) - glog.Error(err) - return cli.NewExitError(err.Error(), -1) - } - if err := json.NewDecoder(f).Decode(&tconfig); err != nil { - err = fmt.Errorf("parse template JSON configuration file failed: %v", err) - glog.Error(err) - f.Close() - return cli.NewExitError(err.Error(), -1) - } - f.Close() - - if (driver != "" && driver != tconfig.Driver) || - (kernel != "" && kernel != tconfig.Config.Kernel) || - (initrd != "" && initrd != tconfig.Config.Initrd) || - (bios != "" && bios != tconfig.Config.Bios) || - (cbfs != "" && cbfs != tconfig.Config.Cbfs) { - glog.Warningf("template config is not match the driver, kernel, initrd, bios or cbfs argument, disable template") - template = "" - } else if driver == "" { - driver = tconfig.Driver - } - } else if (bios == "" || cbfs == "") && (kernel == "" || initrd == "") { - err := fmt.Errorf("argument kernel+initrd or bios+cbfs must be set") - glog.Error(err) - return cli.NewExitError(err.Error(), -1) - } - - hypervisor.InterfaceCount = 0 - var err error - if hypervisor.HDriver, err = driverloader.Probe(driver); err != nil { - glog.Errorf("%v", err) - return cli.NewExitError(err.Error(), -1) - } - - var f factory.Factory - if template != "" { - f = singlefactory.New(templatefactory.NewFromExisted(tconfig)) - } else { - bootConfig := hypervisor.BootConfig{ - Kernel: kernel, - Initrd: initrd, - Bios: bios, - Cbfs: cbfs, - EnableVsock: vsock, - } - f = singlefactory.Dummy(bootConfig) - } - sv, err := supervisor.New(stateDir, containerdDir, f, - context.GlobalInt("default_cpus"), context.GlobalInt("default_memory")) - if err != nil { - glog.Errorf("%v", err) - return cli.NewExitError(err.Error(), -1) - } - - if context.Bool("solo-namespaced") { - go namespaceShare(sv, containerdDir, stateDir) - } - - if err = daemon(sv, context.String("listen")); err != nil { - glog.Errorf("%v", err) - return cli.NewExitError(err.Error(), -1) - } - - if context.Bool("solo-namespaced") { - os.RemoveAll(containerdDir) - } - return nil - }, -} - -func daemon(sv *supervisor.Supervisor, address string) error { - s := make(chan os.Signal, 2048) - signal.Notify(s, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT) - - go glogFlushDaemon() - - server, err := startServer(address, sv) - if err != nil { - return err - } - glog.V(1).Infof("containerd daemon started successfully.") - sig := <-s - glog.V(1).Infof("stopping containerd after receiving %s", sig) - time.Sleep(3 * time.Second) // TODO: fix it by proper way - server.Stop() - glog.Flush() - return nil -} - -func namespaceShare(sv *supervisor.Supervisor, namespace, state string) { - events := sv.Events.Events(time.Time{}) - containerCount := 0 - for e := range events { - if e.Type == supervisor.EventContainerStart { - containerCount++ - } else if e.Type == supervisor.EventExit && e.PID == "init" { - containerCount-- - if containerCount == 0 { - syscall.Kill(0, syscall.SIGQUIT) - } - } - } -} - -func startServer(address string, sv *supervisor.Supervisor) (*grpc.Server, error) { - if err := os.RemoveAll(address); err != nil { - return nil, err - } - l, err := net.Listen(defaultListenType, address) - if err != nil { - return nil, err - } - s := grpc.NewServer() - types.RegisterAPIServer(s, server.NewServer(sv)) - healthServer := health.NewServer() - grpc_health_v1.RegisterHealthServer(s, healthServer) - go func() { - glog.V(3).Infof("containerd: grpc api on %s", address) - if err := s.Serve(l); err != nil { - glog.Errorf("containerd serve grpc error: %v", err) - } - glog.V(1).Infof("containerd grpc server started") - }() - return s, nil -} - -func glogFlushDaemon() { - for range time.NewTicker(glogFlushInterval).C { - glog.Flush() - } -} diff --git a/create.go b/create.go deleted file mode 100644 index d7224322..00000000 --- a/create.go +++ /dev/null @@ -1,374 +0,0 @@ -package main - -import ( - "fmt" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "strings" - "syscall" - "time" - - "github.com/hyperhq/runv/containerd/api/grpc/types" - "github.com/kardianos/osext" - "github.com/kr/pty" - "github.com/opencontainers/runtime-spec/specs-go" - "github.com/urfave/cli" - netcontext "golang.org/x/net/context" -) - -var createCommand = cli.Command{ - Name: "create", - Usage: "create a container", - ArgsUsage: ` - -Where "" is your name for the instance of the container that you -are creating. The name you provide for the container instance must be unique on -your host.`, - Description: `The create command creates an instance of a container for a bundle. The bundle -is a directory with a specification file named "` + specConfig + `" and a root -filesystem. - -The specification file includes an args parameter. The args parameter is used -to specify command(s) that get run when the container is started. To change the -command(s) that get executed on start, edit the args parameter of the spec. See -"runv spec --help" for more explanation.`, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "bundle, b", - Value: getDefaultBundlePath(), - Usage: "path to the root of the bundle directory, defaults to the current directory", - }, - cli.StringFlag{ - Name: "console", - Usage: "specify the pty slave path for use with the container", - }, - cli.StringFlag{ - Name: "console-socket", - Usage: "specify the unix socket for sending the pty master back", - }, - cli.StringFlag{ - Name: "pid-file", - Usage: "specify the file to write the process id to", - }, - cli.BoolFlag{ - Name: "no-pivot", - Usage: "[ignore on runv] do not use pivot root to jail process inside rootfs. This should be used whenever the rootfs is on top of a ramdisk", - }, - }, - Action: func(context *cli.Context) error { - if err := runContainer(context, true); err != nil { - return cli.NewExitError(fmt.Sprintf("Run Container error: %v", err), -1) - } - return nil - }, -} - -func runContainer(context *cli.Context, createOnly bool) error { - root := context.GlobalString("root") - bundle := context.String("bundle") - container := context.Args().First() - ocffile := filepath.Join(bundle, specConfig) - spec, err := loadSpec(ocffile) - if err != nil { - return fmt.Errorf("load config failed: %v", err) - } - if spec.Linux == nil { - return fmt.Errorf("it is not linux container config") - } - if os.Geteuid() != 0 { - return fmt.Errorf("runv should be run as root") - } - if container == "" { - return fmt.Errorf("no container id provided") - } - _, err = os.Stat(filepath.Join(root, container)) - if err == nil { - return fmt.Errorf("container %q exists", container) - } - if err = checkConsole(context, &spec.Process, createOnly); err != nil { - return err - } - - var sharedContainer string - if containerType, ok := spec.Annotations["ocid/container_type"]; ok { - if containerType == "container" { - sharedContainer = spec.Annotations["ocid/sandbox_name"] - } - } else { - for _, ns := range spec.Linux.Namespaces { - if ns.Path != "" { - if strings.Contains(ns.Path, "/") { - return fmt.Errorf("Runv doesn't support path to namespace file, it supports containers name as shared namespaces only") - } - if ns.Type == "mount" { - // TODO support it! - return fmt.Errorf("Runv doesn't support shared mount namespace currently") - } - sharedContainer = ns.Path - _, err = os.Stat(filepath.Join(root, sharedContainer, stateJson)) - if err != nil { - return fmt.Errorf("The container %q is not existing or not ready", sharedContainer) - } - _, err = os.Stat(filepath.Join(root, sharedContainer, "namespace")) - if err != nil { - return fmt.Errorf("The container %q is not ready", sharedContainer) - } - } - } - } - - var namespace string - var cmd *exec.Cmd - if sharedContainer != "" { - namespace = filepath.Join(root, sharedContainer, "namespace") - namespace, err = os.Readlink(namespace) - if err != nil { - return fmt.Errorf("cannot get namespace link of the shared container: %v", err) - } - } else { - path, err := osext.Executable() - if err != nil { - return fmt.Errorf("cannot find self executable path for %s: %v", os.Args[0], err) - } - - kernel, initrd, bios, cbfs, err := getKernelFiles(context, spec.Root.Path) - if err != nil { - return fmt.Errorf("can't find kernel/initrd/bios/cbfs files") - } - - namespace, err = ioutil.TempDir("/run", "runv-namespace-") - if err != nil { - return fmt.Errorf("failed to create runv namespace path: %v", err) - } - - args := []string{ - "--default_cpus", fmt.Sprintf("%d", context.GlobalInt("default_cpus")), - "--default_memory", fmt.Sprintf("%d", context.GlobalInt("default_memory")), - } - - // if bios+cbfs exist, use them first. - if bios != "" && cbfs != "" { - args = append(args, "--bios", bios, "--cbfs", cbfs) - } else if kernel != "" && initrd != "" { - args = append(args, "--kernel", kernel, "--initrd", initrd) - } else { - fmt.Fprintf(os.Stderr, "either bios+cbfs or kernel+initrd must be specified") - os.Exit(-1) - } - - if context.GlobalBool("debug") { - args = append(args, "--debug") - } - if context.GlobalIsSet("driver") { - args = append(args, "--driver", context.GlobalString("driver")) - } - for _, goption := range []string{"log_dir", "template"} { - if context.GlobalIsSet(goption) { - abs_path, err := filepath.Abs(context.GlobalString(goption)) - if err != nil { - return fmt.Errorf("Cannot get abs path for %s: %v\n", goption, err) - } - args = append(args, "--"+goption, abs_path) - } - } - args = append(args, - "containerd", "--solo-namespaced", - "--containerd-dir", namespace, - "--state-dir", root, - "--listen", filepath.Join(namespace, "namespaced.sock"), - ) - cmd = &exec.Cmd{ - Path: path, - Args: append([]string{"runv"}, args...), - } - cmd.Dir = "/" - cmd.SysProcAttr = &syscall.SysProcAttr{ - Setsid: true, - } - err = cmd.Start() - if err != nil { - return fmt.Errorf("failed to launch runv containerd: %v", err) - } - if _, err = os.Stat(filepath.Join(namespace, "namespaced.sock")); os.IsNotExist(err) { - time.Sleep(3 * time.Second) - } - } - - err = createContainer(context, container, namespace, spec) - if err != nil { - cmd.Process.Signal(syscall.SIGINT) - return fmt.Errorf("failed to create container: %v", err) - } - if !createOnly { - address := filepath.Join(namespace, "namespaced.sock") - startContainer(context, bundle, container, address, spec, context.Bool("detach")) - } - return nil -} - -func checkConsole(context *cli.Context, p *specs.Process, createOnly bool) error { - if context.String("console") != "" && context.String("console-socket") != "" { - return fmt.Errorf("only one of --console & --console-socket can be specified") - } - detach := createOnly - if !createOnly { - detach = context.Bool("detach") - } - if (context.String("console") != "" || context.String("console-socket") != "") && !detach { - return fmt.Errorf("--console[-socket] should be used on detached mode\n") - } - if (context.String("console") != "" || context.String("console-socket") != "") && !p.Terminal { - return fmt.Errorf("--console[-socket] should be used on tty mode\n") - } - return nil -} - -// Shared namespaces multiple containers suppurt -// The runv supports pod-style shared namespaces currently. -// (More fine grain shared namespaces style (docker/runc style) is under implementation) -// -// Pod-style shared namespaces: -// * if two containers share at least one type of namespace, they share all kinds of namespaces except the mount namespace -// * mount namespace can't be shared, each container has its own mount namespace -// -// Implementation detail: -// * Shared namespaces is configured in Spec.Linux.Namespaces, the namespace Path should be existing container name. -// * In runv, shared namespaces multiple containers are located in the same VM which is managed by a runv-daemon. -// * Any running container can exit in any arbitrary order, the runv-daemon and the VM are existed until the last container of the VM is existed - -func createContainer(context *cli.Context, container, namespace string, config *specs.Spec) error { - address := filepath.Join(namespace, "namespaced.sock") - c, err := getClient(address) - if err != nil { - return err - } - - return ociCreate(context, container, "init", func(stdin, stdout, stderr string) error { - r := &types.CreateContainerRequest{ - Id: container, - Runtime: "runv-create", - BundlePath: context.String("bundle"), - Stdin: stdin, - Stdout: stdout, - Stderr: stderr, - } - - if _, err := c.CreateContainer(netcontext.Background(), r); err != nil { - return err - } - - // create symbol link to namespace file - namespaceDir := filepath.Join(context.GlobalString("root"), container, "namespace") - if err := os.Symlink(namespace, namespaceDir); err != nil { - return fmt.Errorf("failed to create symbol link %q: %v", filepath.Join(namespaceDir, "namespaced.sock"), err) - } - return nil - }) - -} - -func ociCreate(context *cli.Context, container, process string, createFunc func(stdin, stdout, stderr string) error) error { - path, err := osext.Executable() - if err != nil { - return fmt.Errorf("cannot find self executable path for %s: %v\n", os.Args[0], err) - } - - var stdin, stdout, stderr string - var ptymaster, tty *os.File - if context.String("console") != "" { - tty, err = os.OpenFile(context.String("console"), os.O_RDWR, 0) - if err != nil { - return err - } - } else if context.String("console-socket") != "" { - ptymaster, tty, err = pty.Open() - if err != nil { - return err - } - if err = sendtty(context.String("console-socket"), ptymaster); err != nil { - return err - } - ptymaster.Close() - } - if tty == nil { - pid := os.Getpid() - stdin = fmt.Sprintf("/proc/%d/fd/0", pid) - stdout = fmt.Sprintf("/proc/%d/fd/1", pid) - stderr = fmt.Sprintf("/proc/%d/fd/2", pid) - } else { - defer tty.Close() - stdin = tty.Name() - stdout = tty.Name() - stderr = tty.Name() - } - err = createFunc(stdin, stdout, stderr) - if err != nil { - return err - } - - var cmd *exec.Cmd - if context.String("pid-file") != "" || tty != nil { - args := []string{ - "runv", "--root", context.GlobalString("root"), - } - if context.GlobalString("log_dir") != "" { - args = append(args, "--log_dir", filepath.Join(context.GlobalString("log_dir"), "shim-"+container)) - } - if context.GlobalBool("debug") { - args = append(args, "--debug") - } - args = append(args, "shim", "--container", container, "--process", process) - if context.String("pid-file") != "" { - args = append(args, "--proxy-exit-code", "--proxy-signal") - } - if tty != nil { - args = append(args, "--proxy-winsize") - } - cmd = &exec.Cmd{ - Path: path, - Args: args, - Dir: "/", - Stdin: tty, - Stdout: tty, - Stderr: tty, - SysProcAttr: &syscall.SysProcAttr{ - Setctty: tty != nil, - Setsid: true, - }, - } - err = cmd.Start() - if err != nil { - return err - } - } - if context.String("pid-file") != "" { - err = createPidFile(context.String("pid-file"), cmd.Process.Pid) - if err != nil { - return err - } - } - - return nil -} - -// createPidFile creates a file with the processes pid inside it atomically -// it creates a temp file with the paths filename + '.' infront of it -// then renames the file -func createPidFile(path string, pid int) error { - var ( - tmpDir = filepath.Dir(path) - tmpName = filepath.Join(tmpDir, fmt.Sprintf(".%s", filepath.Base(path))) - ) - f, err := os.OpenFile(tmpName, os.O_RDWR|os.O_CREATE|os.O_EXCL|os.O_SYNC, 0666) - if err != nil { - return err - } - _, err = fmt.Fprintf(f, "%d", pid) - f.Close() - if err != nil { - return err - } - return os.Rename(tmpName, path) -} diff --git a/hack/update-generated-hyperstart-proto.sh b/hack/update-generated-hyperstart-proto.sh new file mode 100755 index 00000000..d5e762c6 --- /dev/null +++ b/hack/update-generated-hyperstart-proto.sh @@ -0,0 +1 @@ +protoc --proto_path=hyperstart/api/grpc/ --go_out=plugins=grpc:hyperstart/api/grpc/ hyperstart/api/grpc/hyperstart.proto diff --git a/hyperstart/api/grpc/hyperstart.pb.go b/hyperstart/api/grpc/hyperstart.pb.go new file mode 100644 index 00000000..a509f707 --- /dev/null +++ b/hyperstart/api/grpc/hyperstart.pb.go @@ -0,0 +1,1263 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: hyperstart.proto + +/* +Package grpc is a generated protocol buffer package. + +It is generated from these files: + hyperstart.proto + +It has these top-level messages: + AddContainerRequest + AddProcessRequest + SignalProcessRequest + WaitProcessRequest + WaitProcessResponse + WriteStreamRequest + WriteStreamResponse + ReadStreamRequest + ReadStreamResponse + CloseStdinRequest + TtyWinResizeRequest + StartSandboxRequest + DestroySandboxRequest + UpdateInterfaceRequest + AddRouteRequest + OnlineCPUMemRequest + Container + Mount + Process + User + Rlimit + Route +*/ +package grpc + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" +import google_protobuf "github.com/golang/protobuf/ptypes/empty" + +import ( + context "golang.org/x/net/context" + grpc1 "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type AddContainerRequest struct { + Container *Container `protobuf:"bytes,1,opt,name=container" json:"container,omitempty"` + Init *Process `protobuf:"bytes,2,opt,name=init" json:"init,omitempty"` +} + +func (m *AddContainerRequest) Reset() { *m = AddContainerRequest{} } +func (m *AddContainerRequest) String() string { return proto.CompactTextString(m) } +func (*AddContainerRequest) ProtoMessage() {} +func (*AddContainerRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *AddContainerRequest) GetContainer() *Container { + if m != nil { + return m.Container + } + return nil +} + +func (m *AddContainerRequest) GetInit() *Process { + if m != nil { + return m.Init + } + return nil +} + +type AddProcessRequest struct { + Container string `protobuf:"bytes,1,opt,name=container" json:"container,omitempty"` + Process *Process `protobuf:"bytes,2,opt,name=process" json:"process,omitempty"` +} + +func (m *AddProcessRequest) Reset() { *m = AddProcessRequest{} } +func (m *AddProcessRequest) String() string { return proto.CompactTextString(m) } +func (*AddProcessRequest) ProtoMessage() {} +func (*AddProcessRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *AddProcessRequest) GetContainer() string { + if m != nil { + return m.Container + } + return "" +} + +func (m *AddProcessRequest) GetProcess() *Process { + if m != nil { + return m.Process + } + return nil +} + +type SignalProcessRequest struct { + Container string `protobuf:"bytes,1,opt,name=container" json:"container,omitempty"` + Process string `protobuf:"bytes,2,opt,name=process" json:"process,omitempty"` + Signal uint32 `protobuf:"varint,3,opt,name=signal" json:"signal,omitempty"` +} + +func (m *SignalProcessRequest) Reset() { *m = SignalProcessRequest{} } +func (m *SignalProcessRequest) String() string { return proto.CompactTextString(m) } +func (*SignalProcessRequest) ProtoMessage() {} +func (*SignalProcessRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *SignalProcessRequest) GetContainer() string { + if m != nil { + return m.Container + } + return "" +} + +func (m *SignalProcessRequest) GetProcess() string { + if m != nil { + return m.Process + } + return "" +} + +func (m *SignalProcessRequest) GetSignal() uint32 { + if m != nil { + return m.Signal + } + return 0 +} + +type WaitProcessRequest struct { + Container string `protobuf:"bytes,1,opt,name=container" json:"container,omitempty"` + Process string `protobuf:"bytes,2,opt,name=process" json:"process,omitempty"` +} + +func (m *WaitProcessRequest) Reset() { *m = WaitProcessRequest{} } +func (m *WaitProcessRequest) String() string { return proto.CompactTextString(m) } +func (*WaitProcessRequest) ProtoMessage() {} +func (*WaitProcessRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *WaitProcessRequest) GetContainer() string { + if m != nil { + return m.Container + } + return "" +} + +func (m *WaitProcessRequest) GetProcess() string { + if m != nil { + return m.Process + } + return "" +} + +type WaitProcessResponse struct { + Status int32 `protobuf:"varint,1,opt,name=status" json:"status,omitempty"` +} + +func (m *WaitProcessResponse) Reset() { *m = WaitProcessResponse{} } +func (m *WaitProcessResponse) String() string { return proto.CompactTextString(m) } +func (*WaitProcessResponse) ProtoMessage() {} +func (*WaitProcessResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +func (m *WaitProcessResponse) GetStatus() int32 { + if m != nil { + return m.Status + } + return 0 +} + +type WriteStreamRequest struct { + Container string `protobuf:"bytes,1,opt,name=container" json:"container,omitempty"` + Process string `protobuf:"bytes,2,opt,name=process" json:"process,omitempty"` + Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` +} + +func (m *WriteStreamRequest) Reset() { *m = WriteStreamRequest{} } +func (m *WriteStreamRequest) String() string { return proto.CompactTextString(m) } +func (*WriteStreamRequest) ProtoMessage() {} +func (*WriteStreamRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } + +func (m *WriteStreamRequest) GetContainer() string { + if m != nil { + return m.Container + } + return "" +} + +func (m *WriteStreamRequest) GetProcess() string { + if m != nil { + return m.Process + } + return "" +} + +func (m *WriteStreamRequest) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +type WriteStreamResponse struct { + Len uint32 `protobuf:"varint,1,opt,name=len" json:"len,omitempty"` +} + +func (m *WriteStreamResponse) Reset() { *m = WriteStreamResponse{} } +func (m *WriteStreamResponse) String() string { return proto.CompactTextString(m) } +func (*WriteStreamResponse) ProtoMessage() {} +func (*WriteStreamResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } + +func (m *WriteStreamResponse) GetLen() uint32 { + if m != nil { + return m.Len + } + return 0 +} + +type ReadStreamRequest struct { + Container string `protobuf:"bytes,1,opt,name=container" json:"container,omitempty"` + Process string `protobuf:"bytes,2,opt,name=process" json:"process,omitempty"` + Len uint32 `protobuf:"varint,3,opt,name=len" json:"len,omitempty"` +} + +func (m *ReadStreamRequest) Reset() { *m = ReadStreamRequest{} } +func (m *ReadStreamRequest) String() string { return proto.CompactTextString(m) } +func (*ReadStreamRequest) ProtoMessage() {} +func (*ReadStreamRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } + +func (m *ReadStreamRequest) GetContainer() string { + if m != nil { + return m.Container + } + return "" +} + +func (m *ReadStreamRequest) GetProcess() string { + if m != nil { + return m.Process + } + return "" +} + +func (m *ReadStreamRequest) GetLen() uint32 { + if m != nil { + return m.Len + } + return 0 +} + +type ReadStreamResponse struct { + Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` +} + +func (m *ReadStreamResponse) Reset() { *m = ReadStreamResponse{} } +func (m *ReadStreamResponse) String() string { return proto.CompactTextString(m) } +func (*ReadStreamResponse) ProtoMessage() {} +func (*ReadStreamResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } + +func (m *ReadStreamResponse) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +type CloseStdinRequest struct { + Container string `protobuf:"bytes,1,opt,name=container" json:"container,omitempty"` + Process string `protobuf:"bytes,2,opt,name=process" json:"process,omitempty"` +} + +func (m *CloseStdinRequest) Reset() { *m = CloseStdinRequest{} } +func (m *CloseStdinRequest) String() string { return proto.CompactTextString(m) } +func (*CloseStdinRequest) ProtoMessage() {} +func (*CloseStdinRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } + +func (m *CloseStdinRequest) GetContainer() string { + if m != nil { + return m.Container + } + return "" +} + +func (m *CloseStdinRequest) GetProcess() string { + if m != nil { + return m.Process + } + return "" +} + +type TtyWinResizeRequest struct { + Container string `protobuf:"bytes,1,opt,name=container" json:"container,omitempty"` + Process string `protobuf:"bytes,2,opt,name=process" json:"process,omitempty"` + Row uint32 `protobuf:"varint,3,opt,name=row" json:"row,omitempty"` + Column uint32 `protobuf:"varint,4,opt,name=column" json:"column,omitempty"` +} + +func (m *TtyWinResizeRequest) Reset() { *m = TtyWinResizeRequest{} } +func (m *TtyWinResizeRequest) String() string { return proto.CompactTextString(m) } +func (*TtyWinResizeRequest) ProtoMessage() {} +func (*TtyWinResizeRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } + +func (m *TtyWinResizeRequest) GetContainer() string { + if m != nil { + return m.Container + } + return "" +} + +func (m *TtyWinResizeRequest) GetProcess() string { + if m != nil { + return m.Process + } + return "" +} + +func (m *TtyWinResizeRequest) GetRow() uint32 { + if m != nil { + return m.Row + } + return 0 +} + +func (m *TtyWinResizeRequest) GetColumn() uint32 { + if m != nil { + return m.Column + } + return 0 +} + +type StartSandboxRequest struct { + Hostname string `protobuf:"bytes,1,opt,name=hostname" json:"hostname,omitempty"` + Dns []string `protobuf:"bytes,2,rep,name=dns" json:"dns,omitempty"` +} + +func (m *StartSandboxRequest) Reset() { *m = StartSandboxRequest{} } +func (m *StartSandboxRequest) String() string { return proto.CompactTextString(m) } +func (*StartSandboxRequest) ProtoMessage() {} +func (*StartSandboxRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } + +func (m *StartSandboxRequest) GetHostname() string { + if m != nil { + return m.Hostname + } + return "" +} + +func (m *StartSandboxRequest) GetDns() []string { + if m != nil { + return m.Dns + } + return nil +} + +type DestroySandboxRequest struct { +} + +func (m *DestroySandboxRequest) Reset() { *m = DestroySandboxRequest{} } +func (m *DestroySandboxRequest) String() string { return proto.CompactTextString(m) } +func (*DestroySandboxRequest) ProtoMessage() {} +func (*DestroySandboxRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } + +type UpdateInterfaceRequest struct { + Device string `protobuf:"bytes,1,opt,name=device" json:"device,omitempty"` + Address string `protobuf:"bytes,2,opt,name=address" json:"address,omitempty"` + Mask string `protobuf:"bytes,3,opt,name=mask" json:"mask,omitempty"` +} + +func (m *UpdateInterfaceRequest) Reset() { *m = UpdateInterfaceRequest{} } +func (m *UpdateInterfaceRequest) String() string { return proto.CompactTextString(m) } +func (*UpdateInterfaceRequest) ProtoMessage() {} +func (*UpdateInterfaceRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} } + +func (m *UpdateInterfaceRequest) GetDevice() string { + if m != nil { + return m.Device + } + return "" +} + +func (m *UpdateInterfaceRequest) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *UpdateInterfaceRequest) GetMask() string { + if m != nil { + return m.Mask + } + return "" +} + +type AddRouteRequest struct { + Routes []*Route `protobuf:"bytes,1,rep,name=routes" json:"routes,omitempty"` +} + +func (m *AddRouteRequest) Reset() { *m = AddRouteRequest{} } +func (m *AddRouteRequest) String() string { return proto.CompactTextString(m) } +func (*AddRouteRequest) ProtoMessage() {} +func (*AddRouteRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} } + +func (m *AddRouteRequest) GetRoutes() []*Route { + if m != nil { + return m.Routes + } + return nil +} + +type OnlineCPUMemRequest struct { +} + +func (m *OnlineCPUMemRequest) Reset() { *m = OnlineCPUMemRequest{} } +func (m *OnlineCPUMemRequest) String() string { return proto.CompactTextString(m) } +func (*OnlineCPUMemRequest) ProtoMessage() {} +func (*OnlineCPUMemRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} } + +type Container struct { + Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` + Mounts []*Mount `protobuf:"bytes,2,rep,name=mounts" json:"mounts,omitempty"` + Sysctl map[string]string `protobuf:"bytes,3,rep,name=sysctl" json:"sysctl,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` +} + +func (m *Container) Reset() { *m = Container{} } +func (m *Container) String() string { return proto.CompactTextString(m) } +func (*Container) ProtoMessage() {} +func (*Container) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} } + +func (m *Container) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *Container) GetMounts() []*Mount { + if m != nil { + return m.Mounts + } + return nil +} + +func (m *Container) GetSysctl() map[string]string { + if m != nil { + return m.Sysctl + } + return nil +} + +// @dest the path inside the container expect when it starts with "tmp:/" +// @source the path inside the container expect when it starts with "vm:/dev/" or "tmp:/" +// the path which starts with "vm:/dev/" refers the guest vm's "/dev", +// especially, "vm:/dev/hostfs/" refers to the shared filesystem. +// "tmp:/" is a temporary directory which is used for temporary mounts. +// message Mount (APIs about rootfs/mounts/volumes) would be changed very devastatingly +type Mount struct { + Dest string `protobuf:"bytes,1,opt,name=dest" json:"dest,omitempty"` + Source string `protobuf:"bytes,2,opt,name=source" json:"source,omitempty"` + Type string `protobuf:"bytes,3,opt,name=type" json:"type,omitempty"` + Options []string `protobuf:"bytes,4,rep,name=options" json:"options,omitempty"` +} + +func (m *Mount) Reset() { *m = Mount{} } +func (m *Mount) String() string { return proto.CompactTextString(m) } +func (*Mount) ProtoMessage() {} +func (*Mount) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} } + +func (m *Mount) GetDest() string { + if m != nil { + return m.Dest + } + return "" +} + +func (m *Mount) GetSource() string { + if m != nil { + return m.Source + } + return "" +} + +func (m *Mount) GetType() string { + if m != nil { + return m.Type + } + return "" +} + +func (m *Mount) GetOptions() []string { + if m != nil { + return m.Options + } + return nil +} + +type Process struct { + Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` + Args []string `protobuf:"bytes,2,rep,name=args" json:"args,omitempty"` + Envs map[string]string `protobuf:"bytes,3,rep,name=envs" json:"envs,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + User *User `protobuf:"bytes,4,opt,name=user" json:"user,omitempty"` + Workdir string `protobuf:"bytes,5,opt,name=workdir" json:"workdir,omitempty"` + Terminal bool `protobuf:"varint,6,opt,name=terminal" json:"terminal,omitempty"` + Rlimits []*Rlimit `protobuf:"bytes,7,rep,name=rlimits" json:"rlimits,omitempty"` +} + +func (m *Process) Reset() { *m = Process{} } +func (m *Process) String() string { return proto.CompactTextString(m) } +func (*Process) ProtoMessage() {} +func (*Process) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} } + +func (m *Process) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *Process) GetArgs() []string { + if m != nil { + return m.Args + } + return nil +} + +func (m *Process) GetEnvs() map[string]string { + if m != nil { + return m.Envs + } + return nil +} + +func (m *Process) GetUser() *User { + if m != nil { + return m.User + } + return nil +} + +func (m *Process) GetWorkdir() string { + if m != nil { + return m.Workdir + } + return "" +} + +func (m *Process) GetTerminal() bool { + if m != nil { + return m.Terminal + } + return false +} + +func (m *Process) GetRlimits() []*Rlimit { + if m != nil { + return m.Rlimits + } + return nil +} + +type User struct { + Uid string `protobuf:"bytes,1,opt,name=uid" json:"uid,omitempty"` + Gid string `protobuf:"bytes,2,opt,name=gid" json:"gid,omitempty"` + AdditionalGids []uint32 `protobuf:"varint,3,rep,packed,name=additionalGids" json:"additionalGids,omitempty"` +} + +func (m *User) Reset() { *m = User{} } +func (m *User) String() string { return proto.CompactTextString(m) } +func (*User) ProtoMessage() {} +func (*User) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{19} } + +func (m *User) GetUid() string { + if m != nil { + return m.Uid + } + return "" +} + +func (m *User) GetGid() string { + if m != nil { + return m.Gid + } + return "" +} + +func (m *User) GetAdditionalGids() []uint32 { + if m != nil { + return m.AdditionalGids + } + return nil +} + +type Rlimit struct { + Type string `protobuf:"bytes,1,opt,name=type" json:"type,omitempty"` + Hard uint64 `protobuf:"varint,2,opt,name=hard" json:"hard,omitempty"` + Soft uint64 `protobuf:"varint,3,opt,name=soft" json:"soft,omitempty"` +} + +func (m *Rlimit) Reset() { *m = Rlimit{} } +func (m *Rlimit) String() string { return proto.CompactTextString(m) } +func (*Rlimit) ProtoMessage() {} +func (*Rlimit) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{20} } + +func (m *Rlimit) GetType() string { + if m != nil { + return m.Type + } + return "" +} + +func (m *Rlimit) GetHard() uint64 { + if m != nil { + return m.Hard + } + return 0 +} + +func (m *Rlimit) GetSoft() uint64 { + if m != nil { + return m.Soft + } + return 0 +} + +type Route struct { + Dest string `protobuf:"bytes,1,opt,name=dest" json:"dest,omitempty"` + Gateway string `protobuf:"bytes,2,opt,name=gateway" json:"gateway,omitempty"` + Device string `protobuf:"bytes,3,opt,name=device" json:"device,omitempty"` +} + +func (m *Route) Reset() { *m = Route{} } +func (m *Route) String() string { return proto.CompactTextString(m) } +func (*Route) ProtoMessage() {} +func (*Route) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{21} } + +func (m *Route) GetDest() string { + if m != nil { + return m.Dest + } + return "" +} + +func (m *Route) GetGateway() string { + if m != nil { + return m.Gateway + } + return "" +} + +func (m *Route) GetDevice() string { + if m != nil { + return m.Device + } + return "" +} + +func init() { + proto.RegisterType((*AddContainerRequest)(nil), "grpc.AddContainerRequest") + proto.RegisterType((*AddProcessRequest)(nil), "grpc.AddProcessRequest") + proto.RegisterType((*SignalProcessRequest)(nil), "grpc.SignalProcessRequest") + proto.RegisterType((*WaitProcessRequest)(nil), "grpc.WaitProcessRequest") + proto.RegisterType((*WaitProcessResponse)(nil), "grpc.WaitProcessResponse") + proto.RegisterType((*WriteStreamRequest)(nil), "grpc.WriteStreamRequest") + proto.RegisterType((*WriteStreamResponse)(nil), "grpc.WriteStreamResponse") + proto.RegisterType((*ReadStreamRequest)(nil), "grpc.ReadStreamRequest") + proto.RegisterType((*ReadStreamResponse)(nil), "grpc.ReadStreamResponse") + proto.RegisterType((*CloseStdinRequest)(nil), "grpc.CloseStdinRequest") + proto.RegisterType((*TtyWinResizeRequest)(nil), "grpc.TtyWinResizeRequest") + proto.RegisterType((*StartSandboxRequest)(nil), "grpc.StartSandboxRequest") + proto.RegisterType((*DestroySandboxRequest)(nil), "grpc.DestroySandboxRequest") + proto.RegisterType((*UpdateInterfaceRequest)(nil), "grpc.UpdateInterfaceRequest") + proto.RegisterType((*AddRouteRequest)(nil), "grpc.AddRouteRequest") + proto.RegisterType((*OnlineCPUMemRequest)(nil), "grpc.OnlineCPUMemRequest") + proto.RegisterType((*Container)(nil), "grpc.Container") + proto.RegisterType((*Mount)(nil), "grpc.Mount") + proto.RegisterType((*Process)(nil), "grpc.Process") + proto.RegisterType((*User)(nil), "grpc.User") + proto.RegisterType((*Rlimit)(nil), "grpc.Rlimit") + proto.RegisterType((*Route)(nil), "grpc.Route") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc1.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc1.SupportPackageIsVersion4 + +// Client API for HyperstartService service + +type HyperstartServiceClient interface { + // execution + AddContainer(ctx context.Context, in *AddContainerRequest, opts ...grpc1.CallOption) (*google_protobuf.Empty, error) + AddProcess(ctx context.Context, in *AddProcessRequest, opts ...grpc1.CallOption) (*google_protobuf.Empty, error) + SignalProcess(ctx context.Context, in *SignalProcessRequest, opts ...grpc1.CallOption) (*google_protobuf.Empty, error) + WaitProcess(ctx context.Context, in *WaitProcessRequest, opts ...grpc1.CallOption) (*WaitProcessResponse, error) + // stdio + WriteStdin(ctx context.Context, in *WriteStreamRequest, opts ...grpc1.CallOption) (*WriteStreamResponse, error) + ReadStdout(ctx context.Context, in *ReadStreamRequest, opts ...grpc1.CallOption) (*ReadStreamResponse, error) + ReadStderr(ctx context.Context, in *ReadStreamRequest, opts ...grpc1.CallOption) (*ReadStreamResponse, error) + CloseStdin(ctx context.Context, in *CloseStdinRequest, opts ...grpc1.CallOption) (*google_protobuf.Empty, error) + TtyWinResize(ctx context.Context, in *TtyWinResizeRequest, opts ...grpc1.CallOption) (*google_protobuf.Empty, error) + // misc (TODO: some rpcs can be replaced by hyperstart-exec) + StartSandbox(ctx context.Context, in *StartSandboxRequest, opts ...grpc1.CallOption) (*google_protobuf.Empty, error) + DestroySandbox(ctx context.Context, in *DestroySandboxRequest, opts ...grpc1.CallOption) (*google_protobuf.Empty, error) + UpdateInterface(ctx context.Context, in *UpdateInterfaceRequest, opts ...grpc1.CallOption) (*google_protobuf.Empty, error) + AddRoute(ctx context.Context, in *AddRouteRequest, opts ...grpc1.CallOption) (*google_protobuf.Empty, error) + OnlineCPUMem(ctx context.Context, in *OnlineCPUMemRequest, opts ...grpc1.CallOption) (*google_protobuf.Empty, error) +} + +type hyperstartServiceClient struct { + cc *grpc1.ClientConn +} + +func NewHyperstartServiceClient(cc *grpc1.ClientConn) HyperstartServiceClient { + return &hyperstartServiceClient{cc} +} + +func (c *hyperstartServiceClient) AddContainer(ctx context.Context, in *AddContainerRequest, opts ...grpc1.CallOption) (*google_protobuf.Empty, error) { + out := new(google_protobuf.Empty) + err := grpc1.Invoke(ctx, "/grpc.HyperstartService/AddContainer", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *hyperstartServiceClient) AddProcess(ctx context.Context, in *AddProcessRequest, opts ...grpc1.CallOption) (*google_protobuf.Empty, error) { + out := new(google_protobuf.Empty) + err := grpc1.Invoke(ctx, "/grpc.HyperstartService/AddProcess", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *hyperstartServiceClient) SignalProcess(ctx context.Context, in *SignalProcessRequest, opts ...grpc1.CallOption) (*google_protobuf.Empty, error) { + out := new(google_protobuf.Empty) + err := grpc1.Invoke(ctx, "/grpc.HyperstartService/SignalProcess", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *hyperstartServiceClient) WaitProcess(ctx context.Context, in *WaitProcessRequest, opts ...grpc1.CallOption) (*WaitProcessResponse, error) { + out := new(WaitProcessResponse) + err := grpc1.Invoke(ctx, "/grpc.HyperstartService/WaitProcess", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *hyperstartServiceClient) WriteStdin(ctx context.Context, in *WriteStreamRequest, opts ...grpc1.CallOption) (*WriteStreamResponse, error) { + out := new(WriteStreamResponse) + err := grpc1.Invoke(ctx, "/grpc.HyperstartService/WriteStdin", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *hyperstartServiceClient) ReadStdout(ctx context.Context, in *ReadStreamRequest, opts ...grpc1.CallOption) (*ReadStreamResponse, error) { + out := new(ReadStreamResponse) + err := grpc1.Invoke(ctx, "/grpc.HyperstartService/ReadStdout", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *hyperstartServiceClient) ReadStderr(ctx context.Context, in *ReadStreamRequest, opts ...grpc1.CallOption) (*ReadStreamResponse, error) { + out := new(ReadStreamResponse) + err := grpc1.Invoke(ctx, "/grpc.HyperstartService/ReadStderr", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *hyperstartServiceClient) CloseStdin(ctx context.Context, in *CloseStdinRequest, opts ...grpc1.CallOption) (*google_protobuf.Empty, error) { + out := new(google_protobuf.Empty) + err := grpc1.Invoke(ctx, "/grpc.HyperstartService/CloseStdin", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *hyperstartServiceClient) TtyWinResize(ctx context.Context, in *TtyWinResizeRequest, opts ...grpc1.CallOption) (*google_protobuf.Empty, error) { + out := new(google_protobuf.Empty) + err := grpc1.Invoke(ctx, "/grpc.HyperstartService/TtyWinResize", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *hyperstartServiceClient) StartSandbox(ctx context.Context, in *StartSandboxRequest, opts ...grpc1.CallOption) (*google_protobuf.Empty, error) { + out := new(google_protobuf.Empty) + err := grpc1.Invoke(ctx, "/grpc.HyperstartService/StartSandbox", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *hyperstartServiceClient) DestroySandbox(ctx context.Context, in *DestroySandboxRequest, opts ...grpc1.CallOption) (*google_protobuf.Empty, error) { + out := new(google_protobuf.Empty) + err := grpc1.Invoke(ctx, "/grpc.HyperstartService/DestroySandbox", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *hyperstartServiceClient) UpdateInterface(ctx context.Context, in *UpdateInterfaceRequest, opts ...grpc1.CallOption) (*google_protobuf.Empty, error) { + out := new(google_protobuf.Empty) + err := grpc1.Invoke(ctx, "/grpc.HyperstartService/UpdateInterface", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *hyperstartServiceClient) AddRoute(ctx context.Context, in *AddRouteRequest, opts ...grpc1.CallOption) (*google_protobuf.Empty, error) { + out := new(google_protobuf.Empty) + err := grpc1.Invoke(ctx, "/grpc.HyperstartService/AddRoute", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *hyperstartServiceClient) OnlineCPUMem(ctx context.Context, in *OnlineCPUMemRequest, opts ...grpc1.CallOption) (*google_protobuf.Empty, error) { + out := new(google_protobuf.Empty) + err := grpc1.Invoke(ctx, "/grpc.HyperstartService/OnlineCPUMem", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for HyperstartService service + +type HyperstartServiceServer interface { + // execution + AddContainer(context.Context, *AddContainerRequest) (*google_protobuf.Empty, error) + AddProcess(context.Context, *AddProcessRequest) (*google_protobuf.Empty, error) + SignalProcess(context.Context, *SignalProcessRequest) (*google_protobuf.Empty, error) + WaitProcess(context.Context, *WaitProcessRequest) (*WaitProcessResponse, error) + // stdio + WriteStdin(context.Context, *WriteStreamRequest) (*WriteStreamResponse, error) + ReadStdout(context.Context, *ReadStreamRequest) (*ReadStreamResponse, error) + ReadStderr(context.Context, *ReadStreamRequest) (*ReadStreamResponse, error) + CloseStdin(context.Context, *CloseStdinRequest) (*google_protobuf.Empty, error) + TtyWinResize(context.Context, *TtyWinResizeRequest) (*google_protobuf.Empty, error) + // misc (TODO: some rpcs can be replaced by hyperstart-exec) + StartSandbox(context.Context, *StartSandboxRequest) (*google_protobuf.Empty, error) + DestroySandbox(context.Context, *DestroySandboxRequest) (*google_protobuf.Empty, error) + UpdateInterface(context.Context, *UpdateInterfaceRequest) (*google_protobuf.Empty, error) + AddRoute(context.Context, *AddRouteRequest) (*google_protobuf.Empty, error) + OnlineCPUMem(context.Context, *OnlineCPUMemRequest) (*google_protobuf.Empty, error) +} + +func RegisterHyperstartServiceServer(s *grpc1.Server, srv HyperstartServiceServer) { + s.RegisterService(&_HyperstartService_serviceDesc, srv) +} + +func _HyperstartService_AddContainer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc1.UnaryServerInterceptor) (interface{}, error) { + in := new(AddContainerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HyperstartServiceServer).AddContainer(ctx, in) + } + info := &grpc1.UnaryServerInfo{ + Server: srv, + FullMethod: "/grpc.HyperstartService/AddContainer", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HyperstartServiceServer).AddContainer(ctx, req.(*AddContainerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HyperstartService_AddProcess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc1.UnaryServerInterceptor) (interface{}, error) { + in := new(AddProcessRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HyperstartServiceServer).AddProcess(ctx, in) + } + info := &grpc1.UnaryServerInfo{ + Server: srv, + FullMethod: "/grpc.HyperstartService/AddProcess", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HyperstartServiceServer).AddProcess(ctx, req.(*AddProcessRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HyperstartService_SignalProcess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc1.UnaryServerInterceptor) (interface{}, error) { + in := new(SignalProcessRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HyperstartServiceServer).SignalProcess(ctx, in) + } + info := &grpc1.UnaryServerInfo{ + Server: srv, + FullMethod: "/grpc.HyperstartService/SignalProcess", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HyperstartServiceServer).SignalProcess(ctx, req.(*SignalProcessRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HyperstartService_WaitProcess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc1.UnaryServerInterceptor) (interface{}, error) { + in := new(WaitProcessRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HyperstartServiceServer).WaitProcess(ctx, in) + } + info := &grpc1.UnaryServerInfo{ + Server: srv, + FullMethod: "/grpc.HyperstartService/WaitProcess", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HyperstartServiceServer).WaitProcess(ctx, req.(*WaitProcessRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HyperstartService_WriteStdin_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc1.UnaryServerInterceptor) (interface{}, error) { + in := new(WriteStreamRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HyperstartServiceServer).WriteStdin(ctx, in) + } + info := &grpc1.UnaryServerInfo{ + Server: srv, + FullMethod: "/grpc.HyperstartService/WriteStdin", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HyperstartServiceServer).WriteStdin(ctx, req.(*WriteStreamRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HyperstartService_ReadStdout_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc1.UnaryServerInterceptor) (interface{}, error) { + in := new(ReadStreamRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HyperstartServiceServer).ReadStdout(ctx, in) + } + info := &grpc1.UnaryServerInfo{ + Server: srv, + FullMethod: "/grpc.HyperstartService/ReadStdout", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HyperstartServiceServer).ReadStdout(ctx, req.(*ReadStreamRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HyperstartService_ReadStderr_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc1.UnaryServerInterceptor) (interface{}, error) { + in := new(ReadStreamRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HyperstartServiceServer).ReadStderr(ctx, in) + } + info := &grpc1.UnaryServerInfo{ + Server: srv, + FullMethod: "/grpc.HyperstartService/ReadStderr", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HyperstartServiceServer).ReadStderr(ctx, req.(*ReadStreamRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HyperstartService_CloseStdin_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc1.UnaryServerInterceptor) (interface{}, error) { + in := new(CloseStdinRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HyperstartServiceServer).CloseStdin(ctx, in) + } + info := &grpc1.UnaryServerInfo{ + Server: srv, + FullMethod: "/grpc.HyperstartService/CloseStdin", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HyperstartServiceServer).CloseStdin(ctx, req.(*CloseStdinRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HyperstartService_TtyWinResize_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc1.UnaryServerInterceptor) (interface{}, error) { + in := new(TtyWinResizeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HyperstartServiceServer).TtyWinResize(ctx, in) + } + info := &grpc1.UnaryServerInfo{ + Server: srv, + FullMethod: "/grpc.HyperstartService/TtyWinResize", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HyperstartServiceServer).TtyWinResize(ctx, req.(*TtyWinResizeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HyperstartService_StartSandbox_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc1.UnaryServerInterceptor) (interface{}, error) { + in := new(StartSandboxRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HyperstartServiceServer).StartSandbox(ctx, in) + } + info := &grpc1.UnaryServerInfo{ + Server: srv, + FullMethod: "/grpc.HyperstartService/StartSandbox", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HyperstartServiceServer).StartSandbox(ctx, req.(*StartSandboxRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HyperstartService_DestroySandbox_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc1.UnaryServerInterceptor) (interface{}, error) { + in := new(DestroySandboxRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HyperstartServiceServer).DestroySandbox(ctx, in) + } + info := &grpc1.UnaryServerInfo{ + Server: srv, + FullMethod: "/grpc.HyperstartService/DestroySandbox", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HyperstartServiceServer).DestroySandbox(ctx, req.(*DestroySandboxRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HyperstartService_UpdateInterface_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc1.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateInterfaceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HyperstartServiceServer).UpdateInterface(ctx, in) + } + info := &grpc1.UnaryServerInfo{ + Server: srv, + FullMethod: "/grpc.HyperstartService/UpdateInterface", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HyperstartServiceServer).UpdateInterface(ctx, req.(*UpdateInterfaceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HyperstartService_AddRoute_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc1.UnaryServerInterceptor) (interface{}, error) { + in := new(AddRouteRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HyperstartServiceServer).AddRoute(ctx, in) + } + info := &grpc1.UnaryServerInfo{ + Server: srv, + FullMethod: "/grpc.HyperstartService/AddRoute", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HyperstartServiceServer).AddRoute(ctx, req.(*AddRouteRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HyperstartService_OnlineCPUMem_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc1.UnaryServerInterceptor) (interface{}, error) { + in := new(OnlineCPUMemRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HyperstartServiceServer).OnlineCPUMem(ctx, in) + } + info := &grpc1.UnaryServerInfo{ + Server: srv, + FullMethod: "/grpc.HyperstartService/OnlineCPUMem", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HyperstartServiceServer).OnlineCPUMem(ctx, req.(*OnlineCPUMemRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _HyperstartService_serviceDesc = grpc1.ServiceDesc{ + ServiceName: "grpc.HyperstartService", + HandlerType: (*HyperstartServiceServer)(nil), + Methods: []grpc1.MethodDesc{ + { + MethodName: "AddContainer", + Handler: _HyperstartService_AddContainer_Handler, + }, + { + MethodName: "AddProcess", + Handler: _HyperstartService_AddProcess_Handler, + }, + { + MethodName: "SignalProcess", + Handler: _HyperstartService_SignalProcess_Handler, + }, + { + MethodName: "WaitProcess", + Handler: _HyperstartService_WaitProcess_Handler, + }, + { + MethodName: "WriteStdin", + Handler: _HyperstartService_WriteStdin_Handler, + }, + { + MethodName: "ReadStdout", + Handler: _HyperstartService_ReadStdout_Handler, + }, + { + MethodName: "ReadStderr", + Handler: _HyperstartService_ReadStderr_Handler, + }, + { + MethodName: "CloseStdin", + Handler: _HyperstartService_CloseStdin_Handler, + }, + { + MethodName: "TtyWinResize", + Handler: _HyperstartService_TtyWinResize_Handler, + }, + { + MethodName: "StartSandbox", + Handler: _HyperstartService_StartSandbox_Handler, + }, + { + MethodName: "DestroySandbox", + Handler: _HyperstartService_DestroySandbox_Handler, + }, + { + MethodName: "UpdateInterface", + Handler: _HyperstartService_UpdateInterface_Handler, + }, + { + MethodName: "AddRoute", + Handler: _HyperstartService_AddRoute_Handler, + }, + { + MethodName: "OnlineCPUMem", + Handler: _HyperstartService_OnlineCPUMem_Handler, + }, + }, + Streams: []grpc1.StreamDesc{}, + Metadata: "hyperstart.proto", +} + +func init() { proto.RegisterFile("hyperstart.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 1023 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x56, 0x6d, 0x6b, 0x1b, 0x47, + 0x10, 0x46, 0xd6, 0x59, 0xb2, 0x46, 0x7e, 0x89, 0x57, 0xb1, 0x7d, 0x51, 0x42, 0x71, 0xaf, 0x90, + 0x08, 0x4a, 0x14, 0x70, 0xa0, 0x6d, 0x0a, 0xa1, 0xa8, 0x8a, 0x49, 0x4b, 0x6b, 0x1a, 0x4e, 0x35, + 0x86, 0x42, 0x4b, 0xd7, 0xda, 0xb5, 0xbc, 0x58, 0xda, 0x55, 0x77, 0xf7, 0xac, 0xaa, 0xff, 0xa2, + 0xff, 0xa4, 0x9f, 0xfa, 0xfb, 0xca, 0xbe, 0x9d, 0x4e, 0xd6, 0x29, 0x90, 0xc6, 0xdf, 0x66, 0x66, + 0xe7, 0x9e, 0x79, 0xd9, 0x99, 0x7d, 0x0e, 0x1e, 0x5c, 0xcf, 0xa7, 0x54, 0x2a, 0x8d, 0xa5, 0xee, + 0x4e, 0xa5, 0xd0, 0x02, 0x45, 0x23, 0x39, 0x1d, 0xb6, 0x1f, 0x8f, 0x84, 0x18, 0x8d, 0xe9, 0x0b, + 0x6b, 0xbb, 0xcc, 0xae, 0x5e, 0xd0, 0xc9, 0x54, 0xcf, 0x9d, 0x4b, 0x32, 0x82, 0x56, 0x8f, 0x90, + 0xbe, 0xe0, 0x1a, 0x33, 0x4e, 0x65, 0x4a, 0xff, 0xc8, 0xa8, 0xd2, 0xe8, 0x39, 0x34, 0x86, 0xc1, + 0x16, 0x57, 0x8e, 0x2b, 0x9d, 0xe6, 0xc9, 0x5e, 0xd7, 0xa0, 0x75, 0x17, 0xae, 0x0b, 0x0f, 0xf4, + 0x29, 0x44, 0x8c, 0x33, 0x1d, 0x6f, 0x58, 0xcf, 0x1d, 0xe7, 0xf9, 0x4e, 0x8a, 0x21, 0x55, 0x2a, + 0xb5, 0x47, 0xc9, 0x2f, 0xb0, 0xdf, 0x23, 0x24, 0xd8, 0x7c, 0x98, 0x27, 0x77, 0xc3, 0x34, 0x8a, + 0xa8, 0xcf, 0xa0, 0x3e, 0x75, 0xfe, 0xe5, 0xc0, 0xe1, 0x34, 0xb9, 0x82, 0x87, 0x03, 0x36, 0xe2, + 0x78, 0xfc, 0x41, 0xf0, 0xf1, 0x32, 0x7c, 0x23, 0xc7, 0x43, 0x87, 0x50, 0x53, 0x16, 0x2f, 0xae, + 0x1e, 0x57, 0x3a, 0x3b, 0xa9, 0xd7, 0x92, 0x1f, 0x01, 0x5d, 0x60, 0xa6, 0xef, 0x27, 0x4a, 0xf2, + 0x1c, 0x5a, 0x4b, 0x68, 0x6a, 0x2a, 0xb8, 0xa2, 0x36, 0xb8, 0xc6, 0x3a, 0x53, 0x16, 0x6b, 0x33, + 0xf5, 0x5a, 0xf2, 0x3b, 0xa0, 0x0b, 0xc9, 0x34, 0x1d, 0x68, 0x49, 0xf1, 0xe4, 0x63, 0x4b, 0x44, + 0x10, 0x11, 0xac, 0xb1, 0x2d, 0x70, 0x3b, 0xb5, 0x72, 0xf2, 0x0c, 0x5a, 0x4b, 0x11, 0x7c, 0x42, + 0x0f, 0xa0, 0x3a, 0xa6, 0xdc, 0x82, 0xef, 0xa4, 0x46, 0x4c, 0x7e, 0x85, 0xfd, 0x94, 0x62, 0x72, + 0x3f, 0x99, 0x78, 0xf8, 0xea, 0x02, 0xbe, 0x03, 0xa8, 0x08, 0xef, 0xd3, 0x08, 0x19, 0x57, 0x0a, + 0x19, 0xff, 0x00, 0xfb, 0xfd, 0xb1, 0x50, 0x74, 0xa0, 0x09, 0xe3, 0x1f, 0x7b, 0x1f, 0x33, 0x68, + 0xfd, 0xac, 0xe7, 0x17, 0x06, 0x48, 0xb1, 0xbf, 0xe8, 0x3d, 0xd4, 0x25, 0xc5, 0x2c, 0xd4, 0x25, + 0xc5, 0xcc, 0xdc, 0xec, 0x50, 0x8c, 0xb3, 0x09, 0x8f, 0x23, 0x37, 0x56, 0x4e, 0x4b, 0xfa, 0xd0, + 0x1a, 0x98, 0xad, 0x1d, 0x60, 0x4e, 0x2e, 0xc5, 0x9f, 0x21, 0x70, 0x1b, 0xb6, 0xae, 0x85, 0xd2, + 0x1c, 0x4f, 0xa8, 0x8f, 0x9b, 0xeb, 0x06, 0x9c, 0x70, 0x13, 0xb2, 0xda, 0x69, 0xa4, 0x46, 0x4c, + 0x8e, 0xe0, 0xe0, 0x0d, 0x55, 0x5a, 0x8a, 0xf9, 0x32, 0x4c, 0xf2, 0x1b, 0x1c, 0x9e, 0x4f, 0x09, + 0xd6, 0xf4, 0x7b, 0xae, 0xa9, 0xbc, 0xc2, 0xc3, 0xbc, 0xb2, 0x43, 0xa8, 0x11, 0x7a, 0xcb, 0x86, + 0x01, 0xde, 0x6b, 0xa6, 0x26, 0x4c, 0x88, 0x2c, 0xd4, 0xe4, 0x55, 0x73, 0x07, 0x13, 0xac, 0x6e, + 0x6c, 0x51, 0x8d, 0xd4, 0xca, 0xc9, 0x17, 0xb0, 0xd7, 0x23, 0x24, 0x15, 0x99, 0xce, 0x81, 0x3f, + 0x83, 0x9a, 0x34, 0xba, 0x19, 0xe1, 0x6a, 0xa7, 0x79, 0xd2, 0x74, 0x7b, 0xeb, 0x7c, 0xfc, 0x51, + 0x72, 0x00, 0xad, 0x9f, 0xf8, 0x98, 0x71, 0xda, 0x7f, 0x77, 0x7e, 0x46, 0xc3, 0x18, 0x25, 0xff, + 0x54, 0xa0, 0x91, 0xbf, 0x31, 0x68, 0x17, 0x36, 0x18, 0xf1, 0xe9, 0x6d, 0x30, 0x62, 0x90, 0x27, + 0x22, 0xe3, 0xda, 0x95, 0x9e, 0x23, 0x9f, 0x19, 0x5b, 0xea, 0x8f, 0xd0, 0x4b, 0xa8, 0xa9, 0xb9, + 0x1a, 0x6a, 0xb3, 0xbe, 0xc6, 0xe9, 0xf1, 0x9d, 0x97, 0xab, 0x3b, 0xb0, 0xa7, 0xa7, 0x5c, 0xcb, + 0x79, 0xea, 0x5d, 0xdb, 0xaf, 0xa0, 0x59, 0x30, 0x9b, 0x06, 0xdf, 0xd0, 0xb9, 0x8f, 0x6c, 0x44, + 0xf4, 0x10, 0x36, 0x6f, 0xf1, 0x38, 0xa3, 0xbe, 0x27, 0x4e, 0xf9, 0x7a, 0xe3, 0xab, 0x4a, 0x82, + 0x61, 0xd3, 0x26, 0x60, 0x47, 0x94, 0x2a, 0xed, 0xbf, 0xb2, 0xb2, 0x5d, 0x67, 0x91, 0xc9, 0x61, + 0xf8, 0xce, 0x6b, 0xc6, 0x57, 0xcf, 0xa7, 0x34, 0xb4, 0xd2, 0xc8, 0xa6, 0xf1, 0x62, 0xaa, 0x99, + 0xe0, 0x2a, 0x8e, 0xec, 0xcd, 0x06, 0x35, 0xf9, 0x7b, 0x03, 0xea, 0xfe, 0xa1, 0x58, 0xe9, 0x09, + 0x82, 0x08, 0xcb, 0x51, 0x18, 0x06, 0x2b, 0xa3, 0xcf, 0x21, 0xa2, 0xfc, 0x56, 0xf9, 0x06, 0x1c, + 0x2d, 0xbd, 0x9b, 0xdd, 0x53, 0x7e, 0xab, 0x5c, 0xf1, 0xd6, 0x09, 0x7d, 0x02, 0x51, 0xa6, 0xa8, + 0xb4, 0x53, 0xd9, 0x3c, 0x01, 0xe7, 0x7c, 0xae, 0xa8, 0x4c, 0xad, 0xdd, 0xa4, 0x35, 0x13, 0xf2, + 0x86, 0x30, 0x19, 0x6f, 0xba, 0x79, 0xf0, 0xaa, 0x19, 0x51, 0x4d, 0xe5, 0x84, 0x99, 0xa7, 0xb2, + 0x76, 0x5c, 0xe9, 0x6c, 0xa5, 0xb9, 0x8e, 0x9e, 0x42, 0x5d, 0x8e, 0xd9, 0x84, 0x69, 0x15, 0xd7, + 0x6d, 0x16, 0xdb, 0x7e, 0x0a, 0xac, 0x31, 0x0d, 0x87, 0xed, 0x2f, 0xa1, 0x91, 0x27, 0xf4, 0x41, + 0x6d, 0x4f, 0x21, 0x32, 0x49, 0x9a, 0x6f, 0xb2, 0xbc, 0x21, 0x46, 0x34, 0x96, 0x11, 0x23, 0xfe, + 0x0b, 0x23, 0xa2, 0xa7, 0xb0, 0x8b, 0x09, 0x61, 0xa6, 0x99, 0x78, 0xfc, 0x96, 0x11, 0xd7, 0x99, + 0x9d, 0xf4, 0x8e, 0x35, 0x79, 0x03, 0x35, 0x97, 0x5f, 0x7e, 0x3f, 0x95, 0xc2, 0xfd, 0x20, 0x88, + 0xae, 0xb1, 0x74, 0xc0, 0x51, 0x6a, 0x65, 0x63, 0x53, 0xe2, 0x4a, 0xdb, 0x7b, 0x8c, 0x52, 0x2b, + 0x27, 0x67, 0xb0, 0x69, 0x67, 0xbd, 0x74, 0x20, 0x62, 0xa8, 0x8f, 0xb0, 0xa6, 0x33, 0x3c, 0x0f, + 0xdb, 0xe5, 0xd5, 0xc2, 0x3e, 0x56, 0x8b, 0xfb, 0x78, 0xf2, 0x6f, 0x1d, 0xf6, 0xbf, 0xcb, 0xb9, + 0x7d, 0x40, 0xa5, 0xdd, 0xd2, 0x1e, 0x6c, 0x17, 0x99, 0x1b, 0x3d, 0x72, 0xed, 0x2d, 0x61, 0xf3, + 0xf6, 0x61, 0xd7, 0xfd, 0x02, 0x74, 0xc3, 0x2f, 0x40, 0xf7, 0xd4, 0xfc, 0x02, 0xa0, 0xd7, 0x00, + 0x0b, 0x4e, 0x46, 0x47, 0x39, 0xc0, 0x32, 0xc1, 0xad, 0xfd, 0xbc, 0x0f, 0x3b, 0x4b, 0xb4, 0x8b, + 0xda, 0x0e, 0xa1, 0x8c, 0x8b, 0xd7, 0x82, 0x7c, 0x0b, 0xcd, 0x02, 0x0b, 0xa2, 0xd8, 0x41, 0xac, + 0xd2, 0x6c, 0xfb, 0x51, 0xc9, 0x89, 0xa7, 0x86, 0x1e, 0x80, 0x27, 0x2e, 0xc2, 0x78, 0x0e, 0xb1, + 0x42, 0x96, 0x39, 0x44, 0x09, 0xc9, 0x7d, 0x03, 0xe0, 0x38, 0x87, 0x88, 0x4c, 0x87, 0x56, 0xac, + 0x90, 0x5c, 0x3b, 0x5e, 0x3d, 0x58, 0x01, 0xa0, 0x52, 0xfe, 0x1f, 0x80, 0xd7, 0x00, 0x0b, 0x2e, + 0x0b, 0x00, 0x2b, 0xec, 0xb6, 0xb6, 0x8f, 0x3d, 0xd8, 0x2e, 0xb2, 0x57, 0x18, 0x87, 0x12, 0x46, + 0x7b, 0x1f, 0x44, 0x91, 0x87, 0x02, 0x44, 0x09, 0x37, 0xad, 0x85, 0x38, 0x85, 0xdd, 0x65, 0x16, + 0x42, 0xfe, 0xf1, 0x2d, 0xe5, 0xa6, 0xb5, 0x30, 0x6f, 0x61, 0xef, 0x0e, 0x67, 0xa1, 0x27, 0xfe, + 0x59, 0x2a, 0xa5, 0xb2, 0xb5, 0x40, 0xaf, 0x60, 0x2b, 0x90, 0x13, 0x3a, 0xc8, 0xe7, 0xbb, 0x48, + 0x56, 0xef, 0xeb, 0x46, 0x91, 0x9f, 0x42, 0x37, 0x4a, 0x38, 0x6b, 0x1d, 0xc4, 0x65, 0xcd, 0xea, + 0x2f, 0xff, 0x0b, 0x00, 0x00, 0xff, 0xff, 0xec, 0xb2, 0xa0, 0x1c, 0x9a, 0x0b, 0x00, 0x00, +} diff --git a/hyperstart/api/grpc/hyperstart.proto b/hyperstart/api/grpc/hyperstart.proto new file mode 100644 index 00000000..00dad0ca --- /dev/null +++ b/hyperstart/api/grpc/hyperstart.proto @@ -0,0 +1,153 @@ +syntax = "proto3"; + +package grpc; + +import "google/protobuf/empty.proto"; + +// unstable +service HyperstartService { + // execution + rpc AddContainer(AddContainerRequest) returns (google.protobuf.Empty); + rpc AddProcess(AddProcessRequest) returns (google.protobuf.Empty); + rpc SignalProcess(SignalProcessRequest) returns (google.protobuf.Empty); + rpc WaitProcess(WaitProcessRequest) returns (WaitProcessResponse); // wait & reap like waitpid(2) + + // stdio + rpc WriteStdin(WriteStreamRequest) returns (WriteStreamResponse); + rpc ReadStdout(ReadStreamRequest) returns (ReadStreamResponse); + rpc ReadStderr(ReadStreamRequest) returns (ReadStreamResponse); + rpc CloseStdin(CloseStdinRequest) returns (google.protobuf.Empty); + rpc TtyWinResize(TtyWinResizeRequest) returns (google.protobuf.Empty); + + // misc (TODO: some rpcs can be replaced by hyperstart-exec) + rpc StartSandbox(StartSandboxRequest) returns (google.protobuf.Empty); + rpc DestroySandbox(DestroySandboxRequest) returns (google.protobuf.Empty); + rpc UpdateInterface(UpdateInterfaceRequest) returns (google.protobuf.Empty); + rpc AddRoute(AddRouteRequest) returns (google.protobuf.Empty); + rpc OnlineCPUMem(OnlineCPUMemRequest) returns (google.protobuf.Empty); +} + +message AddContainerRequest { + Container container = 1; + Process init = 2; +} + +message AddProcessRequest { + string container = 1; + Process process = 2; +} + +message SignalProcessRequest { + string container = 1; + string process = 2; + uint32 signal = 3; +} + +message WaitProcessRequest { + string container = 1; + string process = 2; +} + +message WaitProcessResponse { + int32 status = 1; +} + +message WriteStreamRequest { + string container = 1; + string process = 2; + bytes data = 3; +} + +message WriteStreamResponse { + uint32 len = 1; +} + +message ReadStreamRequest { + string container = 1; + string process = 2; + uint32 len = 3; +} + +message ReadStreamResponse { + bytes data = 1; +} + +message CloseStdinRequest { + string container = 1; + string process = 2; +} + +message TtyWinResizeRequest { + string container = 1; + string process = 2; + uint32 row = 3; + uint32 column = 4; +} + +message StartSandboxRequest { + string hostname = 1; + repeated string dns = 2; +} + +message DestroySandboxRequest { +} + +message UpdateInterfaceRequest { + string device = 1; + string address = 2; + string mask = 3; +} + +message AddRouteRequest { + repeated Route routes = 1; +} + +message OnlineCPUMemRequest { +} + +message Container { + string id = 1; + repeated Mount mounts = 2; + map sysctl = 3; +} + +// @dest the path inside the container expect when it starts with "tmp:/" +// @source the path inside the container expect when it starts with "vm:/dev/" or "tmp:/" +// the path which starts with "vm:/dev/" refers the guest vm's "/dev", +// especially, "vm:/dev/hostfs/" refers to the shared filesystem. +// "tmp:/" is a temporary directory which is used for temporary mounts. +// message Mount (APIs about rootfs/mounts/volumes) would be changed very devastatingly +message Mount { + string dest = 1; + string source = 2; + string type = 3; + repeated string options = 4; +} + +message Process { + string id = 1; + repeated string args = 2; + map envs = 3; + User user = 4; + string workdir = 5; + bool terminal = 6; + repeated Rlimit rlimits = 7; +} + +message User { + string uid = 1; + string gid = 2; + repeated uint32 additionalGids = 3; +} + +message Rlimit { + string type = 1; + uint64 hard = 2; + uint64 soft = 3; +} + +message Route { + string dest = 1; + string gateway = 2; + string device = 3; +} diff --git a/hyperstart/libhyperstart/grpc.go b/hyperstart/libhyperstart/grpc.go new file mode 100644 index 00000000..ae81d411 --- /dev/null +++ b/hyperstart/libhyperstart/grpc.go @@ -0,0 +1,242 @@ +package libhyperstart + +import ( + "context" + "fmt" + "io/ioutil" + "log" + "net" + "syscall" + "time" + + hyperstartgrpc "github.com/hyperhq/runv/hyperstart/api/grpc" + hyperstartjson "github.com/hyperhq/runv/hyperstart/api/json" + "google.golang.org/grpc" + "google.golang.org/grpc/grpclog" +) + +type grpcBasedHyperstart struct { + ctx context.Context + conn *grpc.ClientConn + grpc hyperstartgrpc.HyperstartServiceClient +} + +// NewGrpcBasedHyperstart create hyperstart interface with grpc protocol +func NewGrpcBasedHyperstart(hyperstartGRPCSock string) (Hyperstart, error) { + grpclog.SetLogger(log.New(ioutil.Discard, "", log.LstdFlags)) + dialOpts := []grpc.DialOption{grpc.WithInsecure(), grpc.WithTimeout(5 * time.Second)} + dialOpts = append(dialOpts, + grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { + return net.DialTimeout("unix", addr, timeout) + }, + )) + conn, err := grpc.Dial(hyperstartGRPCSock, dialOpts...) + if err != nil { + return nil, err + } + return &grpcBasedHyperstart{ + ctx: context.Background(), + conn: conn, + grpc: hyperstartgrpc.NewHyperstartServiceClient(conn), + }, nil +} + +func (h *grpcBasedHyperstart) Close() { + h.conn.Close() +} + +func (h *grpcBasedHyperstart) LastStreamSeq() uint64 { + return 0 +} + +func (h *grpcBasedHyperstart) APIVersion() (uint32, error) { + return 4244, nil +} + +func (h *grpcBasedHyperstart) PauseSync() error { + // TODO: + return nil +} + +func (h *grpcBasedHyperstart) Unpause() error { + // TODO: + return nil +} + +func (h *grpcBasedHyperstart) WriteFile(container, path string, data []byte) error { + return fmt.Errorf("WriteFile() is unsupported on grpc based hyperstart API") +} + +func (h *grpcBasedHyperstart) ReadFile(container, path string) ([]byte, error) { + return nil, fmt.Errorf("ReadFile() is unsupported on grpc based hyperstart API") +} + +func (h *grpcBasedHyperstart) AddRoute(routes []hyperstartjson.Route) error { + req := &hyperstartgrpc.AddRouteRequest{} + for _, r := range routes { + req.Routes = append(req.Routes, &hyperstartgrpc.Route{ + Dest: r.Dest, + Gateway: r.Gateway, + Device: r.Device, + }) + } + _, err := h.grpc.AddRoute(h.ctx, req) + return err +} + +func (h *grpcBasedHyperstart) UpdateInterface(dev, ip, mask string) error { + _, err := h.grpc.UpdateInterface(h.ctx, &hyperstartgrpc.UpdateInterfaceRequest{ + Device: dev, + Address: ip, + Mask: mask, + }) + return err +} + +func (h *grpcBasedHyperstart) WriteStdin(container, process string, data []byte) (int, error) { + ret, err := h.grpc.WriteStdin(h.ctx, &hyperstartgrpc.WriteStreamRequest{ + Container: container, + Process: process, + Data: data, + }) + if err == nil { + return int(ret.Len), nil + } + // todo check if it is &grpc.rpcError{code:0xb, desc:"EOF"} and return io.EOF instead + return 0, err +} + +func (h *grpcBasedHyperstart) ReadStdout(container, process string, data []byte) (int, error) { + ret, err := h.grpc.ReadStdout(h.ctx, &hyperstartgrpc.ReadStreamRequest{ + Container: container, + Process: process, + Len: uint32(len(data)), + }) + if err == nil { + copy(data, ret.Data) + return len(ret.Data), nil + } + // todo check if it is &grpc.rpcError{code:0xb, desc:"EOF"} and return io.EOF instead + return 0, err +} + +func (h *grpcBasedHyperstart) ReadStderr(container, process string, data []byte) (int, error) { + ret, err := h.grpc.ReadStderr(h.ctx, &hyperstartgrpc.ReadStreamRequest{ + Container: container, + Process: process, + Len: uint32(len(data)), + }) + if err == nil { + copy(data, ret.Data) + return len(ret.Data), nil + } + // todo check if it is &grpc.rpcError{code:0xb, desc:"EOF"} and return io.EOF instead + return 0, err +} + +func (h *grpcBasedHyperstart) CloseStdin(container, process string) error { + _, err := h.grpc.CloseStdin(h.ctx, &hyperstartgrpc.CloseStdinRequest{ + Container: container, + Process: process, + }) + return err +} + +func (h *grpcBasedHyperstart) TtyWinResize(container, process string, row, col uint16) error { + _, err := h.grpc.TtyWinResize(h.ctx, &hyperstartgrpc.TtyWinResizeRequest{ + Container: container, + Process: process, + Row: uint32(row), + Column: uint32(col), + }) + return err +} + +func (h *grpcBasedHyperstart) OnlineCpuMem() error { + _, err := h.grpc.OnlineCPUMem(h.ctx, &hyperstartgrpc.OnlineCPUMemRequest{}) + return err +} + +func process4json2grpc(p *hyperstartjson.Process) *hyperstartgrpc.Process { + envs := map[string]string{} + for _, env := range p.Envs { + envs[env.Env] = env.Value + } + return &hyperstartgrpc.Process{ + Id: p.Id, + User: &hyperstartgrpc.User{Uid: p.User, Gid: p.Group}, + Terminal: p.Terminal, + Envs: envs, + Args: p.Args, + Workdir: p.Workdir, + } +} + +func container4json2grpc(c *hyperstartjson.Container) *hyperstartgrpc.Container { + if c.Fstype != "" || c.Addr != "" || len(c.Volumes) != 0 || len(c.Fsmap) != 0 { + panic("unsupported rootfs(temporary)") + } + mounts := []*hyperstartgrpc.Mount{{Dest: "/", Source: "vm:/dev/hostfs/" + c.Image + "/" + c.Rootfs}} + return &hyperstartgrpc.Container{ + Id: c.Id, + Mounts: mounts, + Sysctl: c.Sysctl, + } +} + +func (h *grpcBasedHyperstart) NewContainer(c *hyperstartjson.Container) error { + _, err := h.grpc.AddContainer(h.ctx, &hyperstartgrpc.AddContainerRequest{ + Container: container4json2grpc(c), + Init: process4json2grpc(c.Process), + }) + return err +} + +func (h *grpcBasedHyperstart) RestoreContainer(c *hyperstartjson.Container) error { + // nothing to do + return nil +} + +func (h *grpcBasedHyperstart) AddProcess(container string, p *hyperstartjson.Process) error { + _, err := h.grpc.AddProcess(h.ctx, &hyperstartgrpc.AddProcessRequest{ + Container: container, + Process: process4json2grpc(p), + }) + return err +} + +func (h *grpcBasedHyperstart) SignalProcess(container, process string, signal syscall.Signal) error { + _, err := h.grpc.SignalProcess(h.ctx, &hyperstartgrpc.SignalProcessRequest{ + Container: container, + Process: process, + Signal: uint32(signal), + }) + return err +} + +// wait the process until exit. like waitpid() +// the state is saved until someone calls WaitProcess() if the process exited earlier +// the non-first call of WaitProcess() after process started MAY fail to find the process if the process exited earlier +func (h *grpcBasedHyperstart) WaitProcess(container, process string) int { + ret, err := h.grpc.WaitProcess(h.ctx, &hyperstartgrpc.WaitProcessRequest{ + Container: container, + Process: process, + }) + if err != nil { + return -1 + } + return int(ret.Status) +} + +func (h *grpcBasedHyperstart) StartSandbox(pod *hyperstartjson.Pod) error { + _, err := h.grpc.StartSandbox(h.ctx, &hyperstartgrpc.StartSandboxRequest{ + Hostname: pod.Hostname, + Dns: pod.Dns, + }) + return err +} + +func (h *grpcBasedHyperstart) DestroySandbox() error { + _, err := h.grpc.DestroySandbox(h.ctx, &hyperstartgrpc.DestroySandboxRequest{}) + return err +} diff --git a/hyperstart/libhyperstart/hyperstart.go b/hyperstart/libhyperstart/hyperstart.go index 7b834afa..24d9006b 100644 --- a/hyperstart/libhyperstart/hyperstart.go +++ b/hyperstart/libhyperstart/hyperstart.go @@ -1,12 +1,12 @@ package libhyperstart import ( - "io" "syscall" hyperstartapi "github.com/hyperhq/runv/hyperstart/api/json" ) +// Hyperstart interface to hyperstart API type Hyperstart interface { Close() LastStreamSeq() uint64 @@ -15,11 +15,16 @@ type Hyperstart interface { Unpause() error APIVersion() (uint32, error) - NewContainer(c *hyperstartapi.Container) (io.WriteCloser, io.ReadCloser, io.ReadCloser, error) - RestoreContainer(c *hyperstartapi.Container) (io.WriteCloser, io.ReadCloser, io.ReadCloser, error) - AddProcess(container string, p *hyperstartapi.Process) (io.WriteCloser, io.ReadCloser, io.ReadCloser, error) + NewContainer(c *hyperstartapi.Container) error + RestoreContainer(c *hyperstartapi.Container) error + AddProcess(container string, p *hyperstartapi.Process) error SignalProcess(container, process string, signal syscall.Signal) error WaitProcess(container, process string) int + + WriteStdin(container, process string, data []byte) (int, error) + ReadStdout(container, process string, data []byte) (int, error) + ReadStderr(container, process string, data []byte) (int, error) + CloseStdin(container, process string) error TtyWinResize(container, process string, row, col uint16) error StartSandbox(pod *hyperstartapi.Pod) error @@ -30,3 +35,5 @@ type Hyperstart interface { UpdateInterface(dev, ip, mask string) error OnlineCpuMem() error } + +var NewHyperstart = NewJsonBasedHyperstart diff --git a/hyperstart/libhyperstart/json.go b/hyperstart/libhyperstart/json.go index 604904b3..b74f7ecb 100644 --- a/hyperstart/libhyperstart/json.go +++ b/hyperstart/libhyperstart/json.go @@ -60,7 +60,7 @@ type hyperstartCmd struct { result chan<- error } -func NewJsonBasedHyperstart(id, ctlSock, streamSock string, lastStreamSeq uint64, waitReady, paused bool) Hyperstart { +func NewJsonBasedHyperstart(id, ctlSock, streamSock string, lastStreamSeq uint64, waitReady, paused bool) (Hyperstart, error) { h := &jsonBasedHyperstart{ logPrefix: fmt.Sprintf("SB[%s] ", id), procs: make(map[pKey]*pState), @@ -71,7 +71,7 @@ func NewJsonBasedHyperstart(id, ctlSock, streamSock string, lastStreamSeq uint64 } go handleStreamSock(h, streamSock) go handleCtlSock(h, ctlSock, waitReady, paused) - return h + return h, nil } func (h *jsonBasedHyperstart) LogLevel(level hlog.LogLevel) bool { @@ -654,6 +654,53 @@ func (h *jsonBasedHyperstart) UpdateInterface(dev, ip, mask string) error { }) } +func (h *jsonBasedHyperstart) WriteStdin(container, process string, data []byte) (int, error) { + h.RLock() + p, ok := h.procs[pKey{c: container, p: process}] + h.RUnlock() + if !ok { + h.Log(ERROR, "cannot find process: %s, %s", container, process) + return 0, fmt.Errorf("cannot find process: %s, %s", container, process) + } + return p.stdinPipe.Write(data) +} + +func (h *jsonBasedHyperstart) ReadStdout(container, process string, data []byte) (int, error) { + h.RLock() + p, ok := h.procs[pKey{c: container, p: process}] + h.RUnlock() + if !ok { + h.Log(ERROR, "cannot find process: %s, %s", container, process) + return 0, fmt.Errorf("cannot find process: %s, %s", container, process) + } + return p.stdoutPipe.Read(data) +} + +func (h *jsonBasedHyperstart) ReadStderr(container, process string, data []byte) (int, error) { + h.RLock() + p, ok := h.procs[pKey{c: container, p: process}] + h.RUnlock() + if !ok { + h.Log(ERROR, "cannot find process: %s, %s", container, process) + return 0, fmt.Errorf("cannot find process: %s, %s", container, process) + } + if p.stderrSeq == 0 { + return 0, io.EOF + } + return p.stderrPipe.Read(data) +} + +func (h *jsonBasedHyperstart) CloseStdin(container, process string) error { + h.RLock() + p, ok := h.procs[pKey{c: container, p: process}] + h.RUnlock() + if !ok { + h.Log(ERROR, "cannot find process: %s, %s", container, process) + return fmt.Errorf("cannot find process: %s, %s", container, process) + } + return p.stdinPipe.Close() +} + func (h *jsonBasedHyperstart) TtyWinResize4242(container, process string, row, col uint16) error { h.RLock() p, ok := h.procs[pKey{c: container, p: process}] @@ -728,11 +775,11 @@ func (h *jsonBasedHyperstart) removeProcess(container, process string) { } } -func (h *jsonBasedHyperstart) NewContainer(c *hyperstartapi.Container) (io.WriteCloser, io.ReadCloser, io.ReadCloser, error) { +func (h *jsonBasedHyperstart) NewContainer(c *hyperstartapi.Container) error { h.Lock() if _, existed := h.procs[pKey{c: c.Id, p: c.Process.Id}]; existed { h.Unlock() - return nil, nil, nil, fmt.Errorf("process id conflicts, the process of the id %s already exists", c.Process.Id) + return fmt.Errorf("process id conflicts, the process of the id %s already exists", c.Process.Id) } ps := &pState{} h.setupProcessIo(ps, c.Process.Terminal) @@ -745,22 +792,22 @@ func (h *jsonBasedHyperstart) NewContainer(c *hyperstartapi.Container) (io.Write err := h.hyperstartCommand(hyperstartapi.INIT_NEWCONTAINER, c) if err != nil { h.removeProcess(c.Id, c.Process.Id) - return nil, nil, nil, err + return err } - return &ps.stdinPipe, ps.stdoutPipe, ps.stderrPipe, err + return err } -func (h *jsonBasedHyperstart) RestoreContainer(c *hyperstartapi.Container) (io.WriteCloser, io.ReadCloser, io.ReadCloser, error) { +func (h *jsonBasedHyperstart) RestoreContainer(c *hyperstartapi.Container) error { h.Lock() if _, existed := h.procs[pKey{c: c.Id, p: c.Process.Id}]; existed { h.Unlock() - return nil, nil, nil, fmt.Errorf("process id conflicts, the process of the id %s already exists", c.Process.Id) + return fmt.Errorf("process id conflicts, the process of the id %s already exists", c.Process.Id) } h.Unlock() // Send SIGCONT signal to init to test whether it's alive. err := h.SignalProcess(c.Id, "init", syscall.SIGCONT) if err != nil { - return nil, nil, nil, fmt.Errorf("container not exist or already stopped: %v", err) + return fmt.Errorf("container not exist or already stopped: %v", err) } // restore procs/streamOuts map ps := &pState{ @@ -772,14 +819,14 @@ func (h *jsonBasedHyperstart) RestoreContainer(c *hyperstartapi.Container) (io.W h.procs[pKey{c: c.Id, p: c.Process.Id}] = ps h.Unlock() - return &ps.stdinPipe, ps.stdoutPipe, ps.stderrPipe, nil + return nil } -func (h *jsonBasedHyperstart) AddProcess(container string, p *hyperstartapi.Process) (io.WriteCloser, io.ReadCloser, io.ReadCloser, error) { +func (h *jsonBasedHyperstart) AddProcess(container string, p *hyperstartapi.Process) error { h.Lock() if _, existed := h.procs[pKey{c: container, p: p.Id}]; existed { h.Unlock() - return nil, nil, nil, fmt.Errorf("process id conflicts, the process of the id %s already exists", p.Id) + return fmt.Errorf("process id conflicts, the process of the id %s already exists", p.Id) } ps := &pState{} h.setupProcessIo(ps, p.Terminal) @@ -794,9 +841,9 @@ func (h *jsonBasedHyperstart) AddProcess(container string, p *hyperstartapi.Proc }) if err != nil { h.removeProcess(container, p.Id) - return nil, nil, nil, err + return err } - return &ps.stdinPipe, ps.stdoutPipe, ps.stderrPipe, err + return err } func (h *jsonBasedHyperstart) SignalProcess(container, process string, signal syscall.Signal) error { diff --git a/hyperstart/libhyperstart/pipe.go b/hyperstart/libhyperstart/pipe.go new file mode 100644 index 00000000..e2a2b192 --- /dev/null +++ b/hyperstart/libhyperstart/pipe.go @@ -0,0 +1,38 @@ +package libhyperstart + +import "io" + +type inPipe struct { + h Hyperstart + c, p string +} + +func (p *inPipe) Write(data []byte) (n int, err error) { + return p.h.WriteStdin(p.c, p.p, data) +} + +func (p *inPipe) Close() error { + return p.h.CloseStdin(p.c, p.p) +} + +type outPipe struct { + h Hyperstart + c, p string +} + +func (p *outPipe) Read(data []byte) (n int, err error) { + return p.h.ReadStdout(p.c, p.p, data) +} + +type errPipe struct { + h Hyperstart + c, p string +} + +func (p *errPipe) Read(data []byte) (n int, err error) { + return p.h.ReadStderr(p.c, p.p, data) +} + +func StdioPipe(h Hyperstart, c, p string) (io.WriteCloser, io.Reader, io.Reader) { + return &inPipe{h: h, c: c, p: p}, &outPipe{h: h, c: c, p: p}, &errPipe{h: h, c: c, p: p} +} diff --git a/hyperstart/proxy/proxy.go b/hyperstart/proxy/proxy.go new file mode 100644 index 00000000..d37e4d37 --- /dev/null +++ b/hyperstart/proxy/proxy.go @@ -0,0 +1,157 @@ +package proxy + +import ( + "path/filepath" + "strings" + "syscall" + "time" + + google_protobuf "github.com/golang/protobuf/ptypes/empty" + hyperstartgrpc "github.com/hyperhq/runv/hyperstart/api/grpc" + hyperstartjson "github.com/hyperhq/runv/hyperstart/api/json" + "github.com/hyperhq/runv/hyperstart/libhyperstart" + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/health" + "google.golang.org/grpc/health/grpc_health_v1" +) + +type jsonProxy struct { + // json hyperstart api + json libhyperstart.Hyperstart + + // grpc server + address string + self *grpc.Server +} + +func process4grpc2json(p *hyperstartgrpc.Process) *hyperstartjson.Process { + envs := []hyperstartjson.EnvironmentVar{} + for e, v := range p.Envs { + envs = append(envs, hyperstartjson.EnvironmentVar{Env: e, Value: v}) + } + return &hyperstartjson.Process{ + Id: p.Id, + User: p.User.Uid, + Group: p.User.Gid, + Terminal: p.Terminal, + Envs: envs, + Args: p.Args, + Workdir: p.Workdir, + } +} + +func container4grpc2json(c *hyperstartgrpc.Container, init *hyperstartgrpc.Process) *hyperstartjson.Container { + hostfs := "vm:/dev/hostfs/" + if len(c.Mounts) != 1 || !strings.HasPrefix(c.Mounts[0].Source, hostfs) || c.Mounts[0].Dest != "/" { + panic("unsupported rootfs(temporary)") + } + return &hyperstartjson.Container{ + Id: c.Id, + Rootfs: filepath.Base(strings.TrimPrefix(c.Mounts[0].Source, hostfs)), + Image: filepath.Dir(strings.TrimPrefix(c.Mounts[0].Source, hostfs)), + Sysctl: c.Sysctl, + Process: process4grpc2json(init), + } +} + +func pbEmpty(err error) *google_protobuf.Empty { + if err != nil { + return nil + } + return &google_protobuf.Empty{} +} + +// execution +func (proxy *jsonProxy) AddContainer(ctx context.Context, req *hyperstartgrpc.AddContainerRequest) (*google_protobuf.Empty, error) { + c := container4grpc2json(req.Container, req.Init) + err := proxy.json.NewContainer(c) + return pbEmpty(err), err +} +func (proxy *jsonProxy) AddProcess(ctx context.Context, req *hyperstartgrpc.AddProcessRequest) (*google_protobuf.Empty, error) { + p := process4grpc2json(req.Process) + err := proxy.json.AddProcess(req.Container, p) + return pbEmpty(err), err +} +func (proxy *jsonProxy) SignalProcess(ctx context.Context, req *hyperstartgrpc.SignalProcessRequest) (*google_protobuf.Empty, error) { + err := proxy.json.SignalProcess(req.Container, req.Process, syscall.Signal(req.Signal)) + return pbEmpty(err), err +} +func (proxy *jsonProxy) WaitProcess(ctx context.Context, req *hyperstartgrpc.WaitProcessRequest) (*hyperstartgrpc.WaitProcessResponse, error) { + ret := proxy.json.WaitProcess(req.Container, req.Process) + return &hyperstartgrpc.WaitProcessResponse{Status: int32(ret)}, nil +} + +// stdio +func (proxy *jsonProxy) WriteStdin(ctx context.Context, req *hyperstartgrpc.WriteStreamRequest) (*hyperstartgrpc.WriteStreamResponse, error) { + length, err := proxy.json.WriteStdin(req.Container, req.Process, req.Data) + return &hyperstartgrpc.WriteStreamResponse{Len: uint32(length)}, err +} +func (proxy *jsonProxy) ReadStdout(ctx context.Context, req *hyperstartgrpc.ReadStreamRequest) (*hyperstartgrpc.ReadStreamResponse, error) { + data := make([]byte, req.Len) + length, err := proxy.json.ReadStdout(req.Container, req.Process, data) + return &hyperstartgrpc.ReadStreamResponse{Data: data[0:length]}, err +} +func (proxy *jsonProxy) ReadStderr(ctx context.Context, req *hyperstartgrpc.ReadStreamRequest) (*hyperstartgrpc.ReadStreamResponse, error) { + data := make([]byte, req.Len) + length, err := proxy.json.ReadStderr(req.Container, req.Process, data) + return &hyperstartgrpc.ReadStreamResponse{Data: data[0:length]}, err +} +func (proxy *jsonProxy) CloseStdin(ctx context.Context, req *hyperstartgrpc.CloseStdinRequest) (*google_protobuf.Empty, error) { + err := proxy.json.CloseStdin(req.Container, req.Process) + return pbEmpty(err), err +} +func (proxy *jsonProxy) TtyWinResize(ctx context.Context, req *hyperstartgrpc.TtyWinResizeRequest) (*google_protobuf.Empty, error) { + err := proxy.json.TtyWinResize(req.Container, req.Process, uint16(req.Row), uint16(req.Column)) + return pbEmpty(err), err +} + +// misc (TODO: some rpcs can be replaced by hyperstart-exec) +func (proxy *jsonProxy) StartSandbox(ctx context.Context, req *hyperstartgrpc.StartSandboxRequest) (*google_protobuf.Empty, error) { + pod := &hyperstartjson.Pod{ + Hostname: req.Hostname, + Dns: req.Dns, + ShareDir: "share_dir", + } + err := proxy.json.StartSandbox(pod) + return pbEmpty(err), err +} +func (proxy *jsonProxy) DestroySandbox(ctx context.Context, req *hyperstartgrpc.DestroySandboxRequest) (*google_protobuf.Empty, error) { + err := proxy.json.DestroySandbox() + // we can not call proxy.self.GracefulStop() directly, otherwise the err would be "transport is closing" + time.AfterFunc(10*time.Millisecond, proxy.self.GracefulStop) + return pbEmpty(err), err +} +func (proxy *jsonProxy) UpdateInterface(ctx context.Context, req *hyperstartgrpc.UpdateInterfaceRequest) (*google_protobuf.Empty, error) { + err := proxy.json.UpdateInterface(req.Device, req.Address, req.Mask) + return pbEmpty(err), err +} +func (proxy *jsonProxy) AddRoute(ctx context.Context, req *hyperstartgrpc.AddRouteRequest) (*google_protobuf.Empty, error) { + routes := []hyperstartjson.Route{} + for _, r := range req.Routes { + routes = append(routes, hyperstartjson.Route{ + Dest: r.Dest, + Gateway: r.Gateway, + Device: r.Device, + }) + } + err := proxy.json.AddRoute(routes) + return pbEmpty(err), err +} +func (proxy *jsonProxy) OnlineCPUMem(ctx context.Context, req *hyperstartgrpc.OnlineCPUMemRequest) (*google_protobuf.Empty, error) { + err := proxy.json.OnlineCpuMem() + return pbEmpty(err), err +} + +func StartServer(address string, json libhyperstart.Hyperstart) (*grpc.Server, error) { + + s := grpc.NewServer() + jp := &jsonProxy{ + json: json, + self: s, + } + hyperstartgrpc.RegisterHyperstartServiceServer(s, jp) + healthServer := health.NewServer() + grpc_health_v1.RegisterHealthServer(s, healthServer) + return s, nil +} diff --git a/hypervisor/container.go b/hypervisor/container.go index 69d652a7..d54259e3 100644 --- a/hypervisor/container.go +++ b/hypervisor/container.go @@ -23,8 +23,8 @@ type ContainerContext struct { // TODO move streamCopy() to hyperd and remove all these tty and pipes tty *TtyIO stdinPipe io.WriteCloser - stdoutPipe io.ReadCloser - stderrPipe io.ReadCloser + stdoutPipe io.Reader + stderrPipe io.Reader logPrefix string } diff --git a/hypervisor/context.go b/hypervisor/context.go index 69bd6d6d..d8887438 100644 --- a/hypervisor/context.go +++ b/hypervisor/context.go @@ -385,6 +385,17 @@ func (ctx *VmContext) RemoveContainer(id string, result chan<- api.Result) { cc.root.remove(result) } +func (ctx *VmContext) containerList() []string { + ctx.lock.Lock() + defer ctx.lock.Unlock() + + list := []string{} + for c := range ctx.containers { + list = append(list, c) + } + return list +} + func (ctx *VmContext) AddVolume(vol *api.VolumeDescription, result chan api.Result) { ctx.lock.Lock() defer ctx.lock.Unlock() diff --git a/hypervisor/hypervisor.go b/hypervisor/hypervisor.go index 13531367..d6b458d8 100644 --- a/hypervisor/hypervisor.go +++ b/hypervisor/hypervisor.go @@ -65,17 +65,22 @@ loop: } func (ctx *VmContext) Launch() { + var err error + ctx.DCtx.Launch(ctx) //launch routines if ctx.Boot.BootFromTemplate { ctx.Log(TRACE, "boot from template") ctx.PauseState = PauseStatePaused - ctx.hyperstart = libhyperstart.NewJsonBasedHyperstart(ctx.Id, ctx.ctlSockAddr(), ctx.ttySockAddr(), 1, false, true) + ctx.hyperstart, err = libhyperstart.NewHyperstart(ctx.Id, ctx.ctlSockAddr(), ctx.ttySockAddr(), 1, false, true) } else { - ctx.hyperstart = libhyperstart.NewJsonBasedHyperstart(ctx.Id, ctx.ctlSockAddr(), ctx.ttySockAddr(), 1, true, false) + ctx.hyperstart, err = libhyperstart.NewHyperstart(ctx.Id, ctx.ctlSockAddr(), ctx.ttySockAddr(), 1, true, false) go ctx.watchHyperstart() } + if err != nil { + ctx.Log(ERROR, "failed to create hypervisor") + } if ctx.LogLevel(DEBUG) { go watchVmConsole(ctx) } @@ -94,6 +99,10 @@ func VmAssociate(vmId string, hub chan VmEvent, client chan *types.VmResponse, p return nil, err } + if hlog.IsLogLevel(hlog.DEBUG) { + hlog.Log(DEBUG, "VM %s trying to reload with deserialized pinfo: %#v", vmId, pinfo) + } + if pinfo.Id != vmId { return nil, fmt.Errorf("VM ID mismatch, %v vs %v", vmId, pinfo.Id) } @@ -104,7 +113,12 @@ func VmAssociate(vmId string, hub chan VmEvent, client chan *types.VmResponse, p } paused := context.PauseState == PauseStatePaused - context.hyperstart = libhyperstart.NewJsonBasedHyperstart(context.Id, context.ctlSockAddr(), context.ttySockAddr(), pinfo.HwStat.AttachId, false, paused) + context.hyperstart, err = libhyperstart.NewHyperstart(context.Id, context.ctlSockAddr(), context.ttySockAddr(), pinfo.HwStat.AttachId, false, paused) + if err != nil { + context.Log(ERROR, "failed to create hypervisor") + return nil, err + } + context.DCtx.Associate(context) if context.LogLevel(DEBUG) { diff --git a/hypervisor/qemu/qemu_process.go b/hypervisor/qemu/qemu_process.go index 3ea931eb..8f938482 100644 --- a/hypervisor/qemu/qemu_process.go +++ b/hypervisor/qemu/qemu_process.go @@ -67,7 +67,7 @@ func (f *QemuLogFile) Watch() { break } if err != nil { - glog.Errorf("read log file %s failed: %v", f.Name, err) + glog.Infof("read log file %s failed: %v", f.Name, err) return } if len(log) != 0 { diff --git a/hypervisor/vm.go b/hypervisor/vm.go index a87de686..0f138e2d 100644 --- a/hypervisor/vm.go +++ b/hypervisor/vm.go @@ -15,6 +15,7 @@ import ( "github.com/hyperhq/hypercontainer-utils/hlog" "github.com/hyperhq/runv/api" hyperstartapi "github.com/hyperhq/runv/hyperstart/api/json" + "github.com/hyperhq/runv/hyperstart/libhyperstart" "github.com/hyperhq/runv/hypervisor/types" "github.com/hyperhq/runv/lib/utils" ) @@ -498,7 +499,7 @@ func (vm *Vm) AddProcess(process *api.Process, tty *TtyIO) error { } } - stdinPipe, stdoutPipe, stderrPipe, err := vm.ctx.hyperstart.AddProcess(process.Container, &hyperstartapi.Process{ + err := vm.ctx.hyperstart.AddProcess(process.Container, &hyperstartapi.Process{ Id: process.Id, Terminal: process.Terminal, Args: process.Args, @@ -511,8 +512,12 @@ func (vm *Vm) AddProcess(process *api.Process, tty *TtyIO) error { if err != nil { return fmt.Errorf("exec command %v failed: %v", process.Args, err) } + if tty == nil { + return nil + } - go streamCopy(tty, stdinPipe, stdoutPipe, stderrPipe) + inPipe, outPipe, errPipe := libhyperstart.StdioPipe(vm.ctx.hyperstart, process.Container, process.Id) + go streamCopy(tty, inPipe, outPipe, errPipe) go func() { status := vm.ctx.hyperstart.WaitProcess(process.Container, process.Id) vm.ctx.reportProcessFinished(types.E_EXEC_FINISHED, &types.ProcessFinished{ @@ -632,6 +637,14 @@ func (vm *Vm) Stats() *types.PodStats { return stats } +func (vm *Vm) ContainerList() []string { + if !vm.ctx.IsRunning() { + vm.ctx.Log(WARNING, "could not get container list from non-running pod") + return nil + } + return vm.ctx.containerList() +} + func (vm *Vm) Pause(pause bool) error { ctx := vm.ctx if !vm.ctx.IsRunning() { diff --git a/hypervisor/vm_states.go b/hypervisor/vm_states.go index 02e3386b..538532a6 100644 --- a/hypervisor/vm_states.go +++ b/hypervisor/vm_states.go @@ -9,6 +9,7 @@ import ( "time" "github.com/hyperhq/hypercontainer-utils/hlog" + "github.com/hyperhq/runv/hyperstart/libhyperstart" "github.com/hyperhq/runv/hypervisor/types" ) @@ -40,9 +41,12 @@ func (ctx *VmContext) newContainer(id string) error { if ok { ctx.Log(TRACE, "start sending INIT_NEWCONTAINER") var err error - c.stdinPipe, c.stdoutPipe, c.stderrPipe, err = ctx.hyperstart.NewContainer(c.VmSpec()) - if err == nil && c.tty != nil { - go streamCopy(c.tty, c.stdinPipe, c.stdoutPipe, c.stderrPipe) + err = ctx.hyperstart.NewContainer(c.VmSpec()) + if err == nil { + c.stdinPipe, c.stdoutPipe, c.stderrPipe = libhyperstart.StdioPipe(ctx.hyperstart, id, "init") + if c.tty != nil { + go streamCopy(c.tty, c.stdinPipe, c.stdoutPipe, c.stderrPipe) + } } ctx.Log(TRACE, "sent INIT_NEWCONTAINER") go func() { @@ -80,7 +84,7 @@ func (ctx *VmContext) restoreContainer(id string) (alive bool, err error) { return false, fmt.Errorf("try to associate a container not exist in sandbox") } // FIXME do we need filter some error type? error=stopped isn't always true. - c.stdinPipe, c.stdoutPipe, c.stderrPipe, err = ctx.hyperstart.RestoreContainer(c.VmSpec()) + err = ctx.hyperstart.RestoreContainer(c.VmSpec()) if err != nil { ctx.Log(ERROR, "restore conatiner failed in hyperstart, mark as stopped: %v", err) if strings.Contains(err.Error(), "hyperstart closed") { @@ -168,18 +172,13 @@ func (tty *TtyIO) Close() { } // TODO move this logic to hyperd -func streamCopy(tty *TtyIO, stdinPipe io.WriteCloser, stdoutPipe, stderrPipe io.ReadCloser) { +func streamCopy(tty *TtyIO, stdinPipe io.WriteCloser, stdoutPipe, stderrPipe io.Reader) { var wg sync.WaitGroup // old way cleanup all(expect stdinPipe) no matter what kinds of fails, TODO: change it var once sync.Once // cleanup closes tty.Stdin and thus terminates the first go routine cleanup := func() { tty.Close() - // stdinPipe is directly closed in the first go routine - stdoutPipe.Close() - if stderrPipe != nil { - stderrPipe.Close() - } } if tty.Stdin != nil { go func() { @@ -216,6 +215,7 @@ func streamCopy(tty *TtyIO, stdinPipe io.WriteCloser, stdoutPipe, stderrPipe io. } func (ctx *VmContext) startPod() error { + ctx.Log(INFO, "startPod: %#v", ctx.networks.sandboxInfo()) err := ctx.hyperstart.StartSandbox(ctx.networks.sandboxInfo()) if err == nil { ctx.Log(INFO, "pod start successfully") @@ -235,8 +235,8 @@ func (ctx *VmContext) shutdownVM() { ctx.Log(DEBUG, "POD destroyed") ctx.poweroffVM(false, "") } else { - ctx.Log(WARNING, "Destroy pod failed") - ctx.poweroffVM(true, "Destroy pod failed") + ctx.Log(WARNING, "Destroy pod failed: %#v", err) + ctx.poweroffVM(true, fmt.Sprintf("Destroy pod failed: %#v", err)) ctx.Close() } } diff --git a/shim.go b/shim.go deleted file mode 100644 index 6f7ce13d..00000000 --- a/shim.go +++ /dev/null @@ -1,112 +0,0 @@ -package main - -import ( - "fmt" - "os" - "os/signal" - "path/filepath" - "syscall" - - "github.com/golang/glog" - "github.com/hyperhq/runv/containerd/api/grpc/types" - "github.com/hyperhq/runv/lib/term" - "github.com/urfave/cli" - "golang.org/x/net/context" -) - -var shimCommand = cli.Command{ - Name: "shim", - Usage: "internal command for proxy changes to the container/process", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "container", - }, - cli.StringFlag{ - Name: "process", - }, - cli.BoolFlag{ - Name: "proxy-exit-code", - }, - cli.BoolFlag{ - Name: "proxy-signal", - }, - cli.BoolFlag{ - Name: "proxy-winsize", - }, - }, - Action: func(context *cli.Context) error { - root := context.GlobalString("root") - container := context.String("container") - process := context.String("process") - c, err := getClient(filepath.Join(root, container, "namespace", "namespaced.sock")) - if err != nil { - return cli.NewExitError(fmt.Sprintf("failed to get client: %v", err), -1) - } - exitcode := -1 - if context.Bool("proxy-exit-code") { - glog.V(3).Infof("using shim to proxy exit code") - defer func() { os.Exit(exitcode) }() - } - - if context.Bool("proxy-winsize") { - glog.V(3).Infof("using shim to proxy winsize") - s, err := term.SetRawTerminal(os.Stdin.Fd()) - if err != nil { - return cli.NewExitError(fmt.Sprintf("failed to set raw terminal: %v", err), -1) - } - defer term.RestoreTerminal(os.Stdin.Fd(), s) - monitorTtySize(c, container, process) - } - - if context.Bool("proxy-signal") { - // TODO - glog.V(3).Infof("using shim to proxy signal") - sigc := forwardAllSignals(c, container, process) - defer signal.Stop(sigc) - } - - // wait until exit - evChan := containerEvents(c, container) - for e := range evChan { - if e.Type == "exit" && e.Pid == process { - exitcode = int(e.Status) - break - } - } - return nil - }, -} - -func forwardAllSignals(c types.APIClient, cid, process string) chan os.Signal { - sigc := make(chan os.Signal, 2048) - // handle all signals for the process. - signal.Notify(sigc) - signal.Ignore(syscall.SIGCHLD, syscall.SIGPIPE, syscall.SIGWINCH) - - go func() { - for s := range sigc { - if s == syscall.SIGCHLD || s == syscall.SIGPIPE || s == syscall.SIGWINCH { - //ignore these - continue - } - // forward this signal to containerd - sysSig, ok := s.(syscall.Signal) - if !ok { - err := fmt.Errorf("can't forward unknown signal %q", s.String()) - fmt.Fprintf(os.Stderr, "%v", err) - glog.Errorf("%v", err) - continue - } - if _, err := c.Signal(context.Background(), &types.SignalRequest{ - Id: cid, - Pid: process, - Signal: uint32(sysSig), - }); err != nil { - err = fmt.Errorf("forward signal %q failed: %v", s.String(), err) - fmt.Fprintf(os.Stderr, "%v", err) - glog.Errorf("%v", err) - } - } - }() - return sigc -} diff --git a/start.go b/start.go deleted file mode 100644 index c30e26e0..00000000 --- a/start.go +++ /dev/null @@ -1,114 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - - "github.com/hyperhq/runv/containerd/api/grpc/types" - "github.com/hyperhq/runv/lib/term" - "github.com/opencontainers/runtime-spec/specs-go" - "github.com/urfave/cli" - netcontext "golang.org/x/net/context" -) - -var startCommand = cli.Command{ - Name: "start", - Usage: "executes the user defined process in a created container", - ArgsUsage: ` - -Where "" is your name for the instance of the container that you -are starting. The name you provide for the container instance must be unique on -your host.`, - Description: `The start command executes the user defined process in a created container`, - Flags: []cli.Flag{}, - Action: func(context *cli.Context) error { - root := context.GlobalString("root") - container := context.Args().First() - - if container == "" { - return cli.NewExitError("Please specify container ID", -1) - } - if os.Geteuid() != 0 { - return cli.NewExitError("runv should be run as root", -1) - } - - // get bundle path from state - path := filepath.Join(root, container, stateJson) - f, err := os.Open(path) - if err != nil { - return cli.NewExitError(fmt.Sprintf("open JSON configuration file failed: %v", err), -1) - } - defer f.Close() - var s *specs.State - if err := json.NewDecoder(f).Decode(&s); err != nil { - return cli.NewExitError(fmt.Sprintf("parse JSON configuration file failed: %v", err), -1) - } - bundle := s.Bundle - - // get spec from bundle - ocffile := filepath.Join(bundle, specConfig) - spec, err := loadSpec(ocffile) - if err != nil { - return cli.NewExitError(fmt.Sprintf("load config failed: %v", err), -1) - } - - address := filepath.Join(root, container, "namespace/namespaced.sock") - status, err := startContainer(context, bundle, container, address, spec, true) - // TODO, kill the containerd if it is the first container - if status != 0 { - return cli.NewExitError(err, status) - } else if err != nil { - return cli.NewExitError(err, -1) - } - return nil - }, -} - -func startContainer(context *cli.Context, bundle, container, address string, config *specs.Spec, detach bool) (int, error) { - r := &types.CreateContainerRequest{ - Id: container, - Runtime: "runv-start", - BundlePath: bundle, - } - - c, err := getClient(address) - if err != nil { - return -1, fmt.Errorf("failed to get client: %v", err) - } - evChan := containerEvents(c, container) - if _, err := c.CreateContainer(netcontext.Background(), r); err != nil { - return -1, fmt.Errorf("failed to create container %v", err) - } - if !detach && config.Process.Terminal { - s, err := term.SetRawTerminal(os.Stdin.Fd()) - if err != nil { - return -1, fmt.Errorf("failed to set raw terminal %v", err) - } - defer term.RestoreTerminal(os.Stdin.Fd(), s) - monitorTtySize(c, container, "init") - } - var started bool - for e := range evChan { - if e.Type == "exit" && e.Pid == "init" { - return int(e.Status), fmt.Errorf("get exit event before start event") - } - if e.Type == "start-container" { - started = true - break - } - } - if !started { - return -1, fmt.Errorf("failed to get the start event") - } - if !detach { - for e := range evChan { - if e.Type == "exit" && e.Pid == "init" { - return int(e.Status), nil - } - } - return -1, fmt.Errorf("unknown error") - } - return 0, nil -} diff --git a/supervisor/container.go b/supervisor/container.go deleted file mode 100644 index e31ccb81..00000000 --- a/supervisor/container.go +++ /dev/null @@ -1,480 +0,0 @@ -package supervisor - -import ( - "bytes" - "encoding/json" - "fmt" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "strings" - "time" - - "github.com/docker/docker/pkg/mount" - "github.com/docker/docker/pkg/symlink" - "github.com/golang/glog" - "github.com/hyperhq/runv/api" - "github.com/hyperhq/runv/hypervisor" - "github.com/hyperhq/runv/lib/utils" - "github.com/opencontainers/runtime-spec/specs-go" -) - -type Container struct { - Id string - BundlePath string - Spec *specs.Spec - Processes map[string]*Process - - ownerPod *HyperPod -} - -func (c *Container) start(p *Process) error { - res := c.ownerPod.vm.WaitProcess(true, []string{c.Id}, -1) - if res == nil { - c.ownerPod.sv.reap(c.Id, p.Id) - return fmt.Errorf("Failed to prepare waiting on the container") - } - - err := c.ownerPod.vm.Attach(p.stdio, c.Id) - if err != nil { - glog.Errorf("Start Containter fail: fail to set up tty connection.\n") - return err - } - - //Todo: vm.AddContainer here - err = c.ownerPod.vm.StartContainer(c.Id) - if err != nil { - glog.V(1).Infof("Start Container fail: fail to start container.\n") - return err - } - - go func() { - e := Event{ - ID: c.Id, - Type: EventContainerStart, - Timestamp: time.Now(), - } - c.ownerPod.sv.Events.notifySubscribers(e) - - exit, err := c.wait(p, res) - e = Event{ - ID: c.Id, - Type: EventExit, - Timestamp: time.Now(), - PID: p.Id, - Status: -1, - } - if err == nil && exit != nil { - e.Timestamp = exit.FinishedAt - e.Status = exit.Code - } - c.ownerPod.sv.Events.notifySubscribers(e) - }() - return nil -} - -func (c *Container) create() error { - glog.V(3).Infof("prepare hypervisor info") - config := api.ContainerDescriptionFromOCF(c.Id, c.Spec) - - rootPath := c.Spec.Root.Path - if !filepath.IsAbs(rootPath) { - rootPath = filepath.Join(c.BundlePath, rootPath) - } - containerRoot := filepath.Join(hypervisor.BaseDir, c.ownerPod.vm.Id, hypervisor.ShareDirTag, c.Id) - vmRootfs := filepath.Join(containerRoot, "rootfs") - os.MkdirAll(vmRootfs, 0755) - - if err := mount.MakePrivate(containerRoot); err != nil { - glog.Errorf("Make %q private failed: %v", containerRoot, err) - return err - } - - // Mount rootfs - err := utils.Mount(rootPath, vmRootfs) - if err != nil { - glog.Errorf("mount %s to %s failed: %s\n", rootPath, vmRootfs, err.Error()) - return err - } - - // Pre-create dirs necessary for hyperstart before setting rootfs readonly - // TODO: a better way to support readonly rootfs - if err = preCreateDirs(rootPath); err != nil { - return err - } - - // Mount necessary files and directories from spec - for _, m := range c.Spec.Mounts { - if err := mountToRootfs(&m, vmRootfs, ""); err != nil { - return fmt.Errorf("mounting %q to rootfs %q at %q failed: %v", m.Source, m.Destination, vmRootfs, err) - } - } - - // set rootfs readonly - if c.Spec.Root.Readonly { - err = utils.SetReadonly(vmRootfs) - if err != nil { - glog.Errorf("set rootfs %s readonly failed: %s\n", vmRootfs, err.Error()) - return err - } - } - - r := c.ownerPod.vm.AddContainer(config) - if !r.IsSuccess() { - return fmt.Errorf("add container %s failed: %s", c.Id, r.Message()) - } - - // save the state - glog.V(3).Infof("save state id %s, boundle %s", c.Id, c.BundlePath) - stateDir := filepath.Join(c.ownerPod.sv.StateDir, c.Id) - _, err = os.Stat(stateDir) - if err == nil { - glog.Errorf("Container %s exists\n", c.Id) - return fmt.Errorf("Container %s exists\n", c.Id) - } - err = os.MkdirAll(stateDir, 0644) - if err != nil { - glog.V(1).Infof("%s\n", err.Error()) - return err - } - - state := &specs.State{ - Version: c.Spec.Version, - ID: c.Id, - Pid: c.ownerPod.getNsPid(), - Bundle: c.BundlePath, - } - stateData, err := json.MarshalIndent(state, "", "\t") - if err != nil { - glog.V(1).Infof("%s\n", err.Error()) - return err - } - stateFile := filepath.Join(stateDir, "state.json") - err = ioutil.WriteFile(stateFile, stateData, 0644) - if err != nil { - glog.V(1).Infof("%s\n", err.Error()) - return err - } - - // Create vm root dir symbol link in container root dir - vmRootLinkPath := filepath.Join(stateDir, "vm-root") - vmRootPath := filepath.Join(hypervisor.BaseDir, c.ownerPod.vm.Id) - if err := os.Symlink(vmRootPath, vmRootLinkPath); err != nil { - return fmt.Errorf("failed to create symbol link %q: %v", vmRootLinkPath, err) - } - - err = execPrestartHooks(c.Spec, state) - if err != nil { - glog.V(1).Infof("execute Prestart hooks failed, %s\n", err.Error()) - return err - } - - err = c.ownerPod.initPodNetwork(c) - if err != nil { - glog.Errorf("fail to initialize pod network %v", err) - return err - } - - // TODO: should be in start(), but `runv create` uses /proc/self/, so the files should be open here - err = c.Processes["init"].setupIO() - if err != nil { - return err - } - - return nil -} - -func (c *Container) wait(p *Process, result <-chan *api.ProcessExit) (*api.ProcessExit, error) { - state := &specs.State{ - Version: c.Spec.Version, - ID: c.Id, - Pid: -1, - Bundle: c.BundlePath, - } - - err := execPoststartHooks(c.Spec, state) - if err != nil { - glog.V(1).Infof("execute Poststart hooks failed %s\n", err.Error()) - } - - exit, ok := <-result - if !ok { - exit = nil - glog.V(1).Info("get exit code failed (channel already closed)") - } - - err = execPoststopHooks(c.Spec, state) - if err != nil { - glog.V(1).Infof("execute Poststop hooks failed %s\n", err.Error()) - return exit, err - } - return exit, nil -} - -func (c *Container) addProcess(processId, stdin, stdout, stderr string, spec *specs.Process) (*Process, error) { - if _, ok := c.ownerPod.Processes[processId]; ok { - return nil, fmt.Errorf("conflict process ID") - } - if _, ok := c.Processes[processId]; ok { - return nil, fmt.Errorf("conflict process ID") - } - if _, ok := c.Processes["init"]; !ok { - return nil, fmt.Errorf("init process of the container %s had already exited", c.Id) - } - if processId == "init" { // test in case the init process is being reaped - return nil, fmt.Errorf("conflict process ID") - } - - p := &Process{ - Id: processId, - Stdin: stdin, - Stdout: stdout, - Stderr: stderr, - Spec: spec, - ProcId: -1, - - inerId: processId, - ownerCont: c, - } - err := p.setupIO() - if err != nil { - return nil, err - } - - c.ownerPod.Processes[processId] = p - c.Processes[processId] = p - - e := Event{ - ID: c.Id, - Type: EventProcessStart, - Timestamp: time.Now(), - PID: processId, - } - c.ownerPod.sv.Events.notifySubscribers(e) - - go func() { - var ( - exit *api.ProcessExit - ) - reschan := c.ownerPod.vm.WaitProcess(false, []string{processId}, -1) - - err := c.ownerPod.vm.AddProcess(&api.Process{ - Container: c.Id, - Id: processId, - Terminal: spec.Terminal, - Args: spec.Args, - Envs: spec.Env, - Workdir: spec.Cwd}, p.stdio) - - if err != nil { - glog.V(1).Infof("add process to container failed: %v\n", err) - } else { - exit, _ = <-reschan - } - - e := Event{ - ID: c.Id, - Type: EventExit, - Timestamp: time.Now(), - PID: processId, - Status: -1, - } - if err != nil { - glog.V(1).Infof("get exit code failed %s\n", err.Error()) - } else { - if exit != nil { - e.Status = int(exit.Code) - e.Timestamp = exit.FinishedAt - } - } - c.ownerPod.sv.Events.notifySubscribers(e) - }() - return p, nil -} - -func execHook(hook specs.Hook, state *specs.State) error { - b, err := json.Marshal(state) - if err != nil { - return err - } - cmd := exec.Cmd{ - Path: hook.Path, - Args: hook.Args, - Env: hook.Env, - Stdin: bytes.NewReader(b), - } - return cmd.Run() -} - -func execPrestartHooks(rt *specs.Spec, state *specs.State) error { - if rt.Hooks == nil { - return nil - } - for _, hook := range rt.Hooks.Prestart { - err := execHook(hook, state) - if err != nil { - return err - } - } - - return nil -} - -func execPoststartHooks(rt *specs.Spec, state *specs.State) error { - if rt.Hooks == nil { - return nil - } - for _, hook := range rt.Hooks.Poststart { - err := execHook(hook, state) - if err != nil { - glog.V(1).Infof("exec Poststart hook %s failed %s", hook.Path, err.Error()) - } - } - - return nil -} - -func execPoststopHooks(rt *specs.Spec, state *specs.State) error { - if rt.Hooks == nil { - return nil - } - for _, hook := range rt.Hooks.Poststop { - err := execHook(hook, state) - if err != nil { - glog.V(1).Infof("exec Poststop hook %s failed %s", hook.Path, err.Error()) - } - } - - return nil -} - -func (c *Container) reap() { - containerRoot := filepath.Join(hypervisor.BaseDir, c.ownerPod.vm.Id, hypervisor.ShareDirTag, c.Id) - utils.Umount(containerRoot) - os.RemoveAll(containerRoot) - os.RemoveAll(filepath.Join(c.ownerPod.sv.StateDir, c.Id)) -} - -func mountToRootfs(m *specs.Mount, rootfs, mountLabel string) error { - // TODO: we don't use mountLabel here because it looks like mountLabel is - // only significant when SELinux is enabled. - var ( - dest = m.Destination - ) - if !strings.HasPrefix(dest, rootfs) { - dest = filepath.Join(rootfs, dest) - } - - switch m.Type { - case "proc", "sysfs", "mqueue", "tmpfs", "cgroup", "devpts": - glog.V(3).Infof("Skip mount point %q of type %s", m.Destination, m.Type) - return nil - case "bind": - stat, err := os.Stat(m.Source) - if err != nil { - // error out if the source of a bind mount does not exist as we will be - // unable to bind anything to it. - return err - } - // ensure that the destination of the bind mount is resolved of symlinks at mount time because - // any previous mounts can invalidate the next mount's destination. - // this can happen when a user specifies mounts within other mounts to cause breakouts or other - // evil stuff to try to escape the container's rootfs. - if dest, err = symlink.FollowSymlinkInScope(filepath.Join(rootfs, m.Destination), rootfs); err != nil { - return err - } - if err := checkMountDestination(rootfs, dest); err != nil { - return err - } - // update the mount with the correct dest after symlinks are resolved. - m.Destination = dest - if err := createIfNotExists(dest, stat.IsDir()); err != nil { - return err - } - if err := mount.Mount(m.Source, dest, m.Type, strings.Join(m.Options, ",")); err != nil { - return err - } - default: - if err := os.MkdirAll(dest, 0755); err != nil { - return err - } - return mount.Mount(m.Source, dest, m.Type, strings.Join(m.Options, ",")) - } - return nil -} - -// checkMountDestination checks to ensure that the mount destination is not over the top of /proc. -// dest is required to be an abs path and have any symlinks resolved before calling this function. -func checkMountDestination(rootfs, dest string) error { - invalidDestinations := []string{ - "/proc", - } - // White list, it should be sub directories of invalid destinations - validDestinations := []string{ - // These entries can be bind mounted by files emulated by fuse, - // so commands like top, free displays stats in container. - "/proc/cpuinfo", - "/proc/diskstats", - "/proc/meminfo", - "/proc/stat", - "/proc/net/dev", - } - for _, valid := range validDestinations { - path, err := filepath.Rel(filepath.Join(rootfs, valid), dest) - if err != nil { - return err - } - if path == "." { - return nil - } - } - for _, invalid := range invalidDestinations { - path, err := filepath.Rel(filepath.Join(rootfs, invalid), dest) - if err != nil { - return err - } - if path == "." || !strings.HasPrefix(path, "..") { - return fmt.Errorf("%q cannot be mounted because it is located inside %q", dest, invalid) - } - - } - return nil -} - -// preCreateDirs creates necessary dirs for hyperstart -func preCreateDirs(rootfs string) error { - dirs := []string{ - "proc", - "sys", - "dev", - "lib/modules", - } - for _, dir := range dirs { - err := createIfNotExists(filepath.Join(rootfs, dir), true) - if err != nil { - return err - } - } - return nil -} - -// createIfNotExists creates a file or a directory only if it does not already exist. -func createIfNotExists(path string, isDir bool) error { - if _, err := os.Stat(path); err != nil { - if os.IsNotExist(err) { - if isDir { - return os.MkdirAll(path, 0755) - } - if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { - return err - } - f, err := os.OpenFile(path, os.O_CREATE, 0755) - if err != nil { - return err - } - f.Close() - } - } - return nil -} diff --git a/supervisor/events.go b/supervisor/events.go deleted file mode 100644 index 3ac2ea49..00000000 --- a/supervisor/events.go +++ /dev/null @@ -1,132 +0,0 @@ -package supervisor - -import ( - "encoding/json" - "io" - "os" - "path/filepath" - "sync" - "time" - - "github.com/golang/glog" -) - -const ( - EventExit = "exit" - EventContainerStart = "start-container" - EventProcessStart = "start-process" -) - -var ( - defaultEventsBufferSize = 128 -) - -type Event struct { - ID string `json:"id"` - Type string `json:"type"` - Timestamp time.Time `json:"timestamp"` - PID string `json:"pid,omitempty"` - Status int `json:"status,omitempty"` -} - -// TODO: copied code, including two bugs -// eventLog is not protected -// Events() might be deadlocked - -type SvEvents struct { - sync.RWMutex - subscribers map[chan Event]struct{} - eventLog []Event -} - -func (se *SvEvents) setupEventLog(logDir string) error { - if err := se.readEventLog(logDir); err != nil { - return err - } - events := se.Events(time.Time{}) - f, err := os.OpenFile(filepath.Join(logDir, "events.log"), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0755) - if err != nil { - return err - } - enc := json.NewEncoder(f) - go func() { - for e := range events { - glog.V(3).Infof("write event log: %v", e) - se.eventLog = append(se.eventLog, e) - if err := enc.Encode(e); err != nil { - glog.Errorf("containerd fail to write event to journal: %v", err) - } - } - }() - return nil -} - -func (se *SvEvents) readEventLog(logDir string) error { - f, err := os.Open(filepath.Join(logDir, "events.log")) - if err != nil { - if os.IsNotExist(err) { - return nil - } - return err - } - defer f.Close() - dec := json.NewDecoder(f) - for { - var e Event - if err := dec.Decode(&e); err != nil { - if err == io.EOF { - break - } - return err - } - se.eventLog = append(se.eventLog, e) - } - return nil -} - -// Events returns an event channel that external consumers can use to receive updates -// on container events -func (se *SvEvents) Events(from time.Time) chan Event { - se.Lock() - defer se.Unlock() - c := make(chan Event, defaultEventsBufferSize) - se.subscribers[c] = struct{}{} - if !from.IsZero() { - // replay old event - for _, e := range se.eventLog { - if e.Timestamp.After(from) { - c <- e - } - } - // Notify the client that from now on it's live events - c <- Event{ - Type: "live", - Timestamp: time.Now(), - } - } - return c -} - -// Unsubscribe removes the provided channel from receiving any more events -func (se *SvEvents) Unsubscribe(sub chan Event) { - se.Lock() - defer se.Unlock() - delete(se.subscribers, sub) - close(sub) -} - -// notifySubscribers will send the provided event to the external subscribers -// of the events channel -func (se *SvEvents) notifySubscribers(e Event) { - glog.V(3).Infof("notifySubscribers: %v", e) - se.RLock() - defer se.RUnlock() - for sub := range se.subscribers { - // do a non-blocking send for the channel - select { - case sub <- e: - default: - glog.V(3).Infof("containerd: event not sent to subscriber") - } - } -} diff --git a/supervisor/hyperpod.go b/supervisor/hyperpod.go deleted file mode 100644 index b8e33fb6..00000000 --- a/supervisor/hyperpod.go +++ /dev/null @@ -1,542 +0,0 @@ -package supervisor - -import ( - "encoding/gob" - "fmt" - "io" - "os" - "os/exec" - "path/filepath" - "strconv" - "strings" - "syscall" - "time" - - "github.com/golang/glog" - "github.com/hyperhq/runv/api" - "github.com/hyperhq/runv/factory" - "github.com/hyperhq/runv/hypervisor" - "github.com/kardianos/osext" - "github.com/opencontainers/runtime-spec/specs-go" - "github.com/vishvananda/netlink" -) - -type NetlinkUpdateType string - -const ( - UpdateTypeLink NetlinkUpdateType = "link" - UpdateTypeAddr NetlinkUpdateType = "addr" - UpdateTypeRoute NetlinkUpdateType = "route" -) - -// NetlinkUpdate tracks the change of network namespace. -type NetlinkUpdate struct { - // AddrUpdate is used to pass information back from AddrSubscribe() - Addr netlink.AddrUpdate - // RouteUpdate is used to pass information back from RouteSubscribe() - Route netlink.RouteUpdate - // Veth is used to pass information back from LinkSubscribe(). - // We only support veth link at present. - Veth *netlink.Veth - - // UpdateType indicates which part of the netlink information has been changed. - UpdateType NetlinkUpdateType -} - -type HyperPod struct { - Containers map[string]*Container - Processes map[string]*Process - - //userPod *pod.UserPod - //podStatus *hypervisor.PodStatus - vm *hypervisor.Vm - sv *Supervisor - - nslistener *nsListener -} - -type InterfaceInfo struct { - Index int - PeerIndex int - Ip string -} - -type nsListener struct { - enc *gob.Encoder - dec *gob.Decoder - cmd *exec.Cmd -} - -func GetBridgeFromIndex(idx int) (string, string, error) { - var attr, bridge *netlink.LinkAttrs - var options string - - links, err := netlink.LinkList() - if err != nil { - glog.Error(err) - return "", "", err - } - - for _, link := range links { - if link.Type() != "veth" { - continue - } - - if link.Attrs().Index == idx { - attr = link.Attrs() - break - } - } - - if attr == nil { - return "", "", fmt.Errorf("cann't find nic whose ifindex is %d", idx) - } - - for _, link := range links { - if link.Type() != "bridge" && link.Type() != "openvswitch" { - continue - } - - if link.Attrs().Index == attr.MasterIndex { - bridge = link.Attrs() - break - } - } - - if bridge == nil { - return "", "", fmt.Errorf("cann't find bridge contains nic whose ifindex is %d", idx) - } - - if bridge.Name == "ovs-system" { - veth, err := netlink.LinkByIndex(idx) - if err != nil { - return "", "", err - } - - out, err := exec.Command("ovs-vsctl", "port-to-br", veth.Attrs().Name).CombinedOutput() - if err != nil { - return "", "", err - } - bridge.Name = strings.TrimSpace(string(out)) - - out, err = exec.Command("ovs-vsctl", "get", "port", veth.Attrs().Name, "tag").CombinedOutput() - if err != nil { - return "", "", err - } - options = "tag=" + strings.TrimSpace(string(out)) - } - - glog.V(3).Infof("find bridge %s", bridge.Name) - - return bridge.Name, options, nil -} - -func (hp *HyperPod) initPodNetwork(c *Container) error { - // Only start first container will setup netns - if len(hp.Containers) > 1 { - return nil - } - - // container has no prestart hooks, means no net for this container - if c.Spec.Hooks == nil || len(c.Spec.Hooks.Prestart) == 0 { - // FIXME: need receive interface settting? - return nil - } - - listener := hp.nslistener - - /* send collect netns request to nsListener */ - if err := listener.enc.Encode("init"); err != nil { - glog.Errorf("listener.dec.Decode init error: %v", err) - return err - } - - infos := []InterfaceInfo{} - /* read nic information of ns from pipe */ - err := listener.dec.Decode(&infos) - if err != nil { - glog.Error("listener.dec.Decode infos error: %v", err) - return err - } - - routes := []netlink.Route{} - err = listener.dec.Decode(&routes) - if err != nil { - glog.Error("listener.dec.Decode route error: %v", err) - return err - } - - var gw_route *netlink.Route - for idx, route := range routes { - if route.Dst == nil { - gw_route = &routes[idx] - } - } - - glog.V(3).Infof("interface configuration of pod ns is %#v", infos) - for _, info := range infos { - bridge, options, err := GetBridgeFromIndex(info.PeerIndex) - if err != nil { - glog.Error(err) - continue - } - - nicId := strconv.Itoa(info.Index) - - conf := &api.InterfaceDescription{ - Id: nicId, //ip as an id - Lo: false, - Bridge: bridge, - Ip: info.Ip, - Options: options, - } - - if gw_route != nil && gw_route.LinkIndex == info.Index { - conf.Gw = gw_route.Gw.String() - } - - // TODO(hukeping): the name here is always eth1, 2, 3, 4, 5, etc., - // which would not be the proper way to name device name, instead it - // should be the same as what we specified in the network namespace. - //err = hp.vm.AddNic(info.Index, fmt.Sprintf("eth%d", idx), conf) - err = hp.vm.AddNic(conf) - if err != nil { - glog.Error(err) - return err - } - } - - err = hp.vm.AddRoute() - if err != nil { - glog.Error(err) - return err - } - - go hp.nsListenerStrap() - - return nil -} - -func (hp *HyperPod) nsListenerStrap() { - listener := hp.nslistener - - // Keep watching container network setting - // and then update vm/hyperstart - for { - update := NetlinkUpdate{} - err := listener.dec.Decode(&update) - if err != nil { - if err == io.EOF { - glog.V(3).Infof("listener.dec.Decode NetlinkUpdate: %v", err) - break - } - glog.Error("listener.dec.Decode NetlinkUpdate error: %v", err) - continue - } - - glog.V(3).Infof("network namespace information of %s has been changed", update.UpdateType) - switch update.UpdateType { - case UpdateTypeLink: - link := update.Veth - if link.Attrs().ParentIndex == 0 { - glog.V(3).Infof("The deleted link: %s", link) - err = hp.vm.DeleteNic(strconv.Itoa(link.Attrs().Index)) - if err != nil { - glog.Error(err) - continue - } - - } else { - glog.V(3).Infof("The changed link: %s", link) - } - - case UpdateTypeAddr: - glog.V(3).Infof("The changed address: %s", update.Addr) - - link := update.Veth - - // If there is a delete operation upon an link, it will also trigger - // the address change event which the link will be NIL since it has - // already been deleted before the address change event be triggered. - if link == nil { - glog.V(3).Info("Link for this address has already been deleted.") - continue - } - - // This is just a sanity check. - // - // The link should be the one which the address on it has been changed. - if link.Attrs().Index != update.Addr.LinkIndex { - glog.Errorf("Get the wrong link with ID %d, expect %d", link.Attrs().Index, update.Addr.LinkIndex) - continue - } - - bridge, options, err := GetBridgeFromIndex(link.Attrs().ParentIndex) - if err != nil { - glog.Error(err) - continue - } - - inf := &api.InterfaceDescription{ - Id: strconv.Itoa(link.Attrs().Index), - Lo: false, - Bridge: bridge, - Ip: update.Addr.LinkAddress.String(), - Options: options, - } - - err = hp.vm.AddNic(inf) - if err != nil { - glog.Error(err) - continue - } - - case UpdateTypeRoute: - - } - } -} - -func newPipe() (parent, child *os.File, err error) { - fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0) - if err != nil { - return nil, nil, err - } - return os.NewFile(uintptr(fds[1]), "parent"), os.NewFile(uintptr(fds[0]), "child"), nil -} - -func (hp *HyperPod) startNsListener() (err error) { - var parentPipe, childPipe *os.File - var path string - if hp.nslistener != nil { - return nil - } - - path, err = osext.Executable() - if err != nil { - glog.Errorf("cannot find self executable path for %s: %v", os.Args[0], err) - return err - } - - glog.V(3).Infof("get exec path %s", path) - parentPipe, childPipe, err = newPipe() - if err != nil { - glog.Errorf("create pipe for containerd-nslistener failed: %v", err) - return err - } - - defer func() { - if err != nil { - parentPipe.Close() - childPipe.Close() - } - }() - - cmd := exec.Command(path) - cmd.Args[0] = "containerd-nslistener" - cmd.ExtraFiles = append(cmd.ExtraFiles, childPipe) - if err = cmd.Start(); err != nil { - glog.Errorf("start containerd-nslistener failed: %v", err) - return err - } - - childPipe.Close() - - enc := gob.NewEncoder(parentPipe) - dec := gob.NewDecoder(parentPipe) - - hp.nslistener = &nsListener{ - enc: enc, - dec: dec, - cmd: cmd, - } - - defer func() { - if err != nil { - hp.stopNsListener() - } - }() - - /* Make sure nsListener create new netns */ - var ready string - if err = dec.Decode(&ready); err != nil { - glog.Errorf("Get ready message from containerd-nslistener failed: %v", err) - return err - } - - if ready != "init" { - err = fmt.Errorf("containerd get incorrect init message: %s", ready) - return err - } - - glog.V(1).Infof("nsListener pid is %d", hp.getNsPid()) - return nil -} - -func (hp *HyperPod) stopNsListener() { - if hp.nslistener != nil { - hp.nslistener.cmd.Process.Kill() - } -} - -func (hp *HyperPod) getNsPid() int { - if hp.nslistener == nil { - return -1 - } - - return hp.nslistener.cmd.Process.Pid -} - -func (hp *HyperPod) createContainer(container, bundlePath, stdin, stdout, stderr string, spec *specs.Spec) (*Container, error) { - inerProcessId := container + "-init" - if _, ok := hp.Processes[inerProcessId]; ok { - return nil, fmt.Errorf("The process id: %s is in used", inerProcessId) - } - - c := &Container{ - Id: container, - BundlePath: bundlePath, - Spec: spec, - Processes: make(map[string]*Process), - ownerPod: hp, - } - hp.Containers[container] = c - p := &Process{ - Id: "init", - Stdin: stdin, - Stdout: stdout, - Stderr: stderr, - Spec: &spec.Process, - ProcId: c.ownerPod.getNsPid(), - - inerId: inerProcessId, - ownerCont: c, - init: true, - } - c.Processes["init"] = p - hp.Processes[inerProcessId] = p - return c, nil -} - -func chooseKernel(spec *specs.Spec) (kernel string) { - for k, env := range spec.Process.Env { - slices := strings.Split(env, "=") - if len(slices) == 2 && slices[0] == "hypervisor.kernel" { - kernel = slices[1] - // remove kernel env because this is only allow to be used by runv - spec.Process.Env = append(spec.Process.Env[:k], spec.Process.Env[k+1:]...) - break - } - } - return -} - -func chooseInitrd(spec *specs.Spec) (initrd string) { - for k, env := range spec.Process.Env { - slices := strings.Split(env, "=") - if len(slices) == 2 && slices[0] == "hypervisor.initrd" { - initrd = slices[1] - // remove kernel env because this is only allow to be used by runv - spec.Process.Env = append(spec.Process.Env[:k], spec.Process.Env[k+1:]...) - break - } - } - return -} - -func createHyperPod(f factory.Factory, spec *specs.Spec, defaultCpus int, defaultMemory int) (*HyperPod, error) { - cpu := defaultCpus - mem := defaultMemory - if spec.Linux != nil && spec.Linux.Resources != nil && spec.Linux.Resources.Memory != nil && spec.Linux.Resources.Memory.Limit != nil { - mem = int(*spec.Linux.Resources.Memory.Limit >> 20) - } - - kernel := chooseKernel(spec) - initrd := chooseInitrd(spec) - glog.V(3).Infof("Using kernel: %s; Initrd: %s; vCPU: %d; Memory %d", kernel, initrd, cpu, mem) - - var ( - vm *hypervisor.Vm - err error - ) - if len(kernel) == 0 && len(initrd) == 0 { - vm, err = f.GetVm(cpu, mem) - if err != nil { - glog.Errorf("Create VM failed with default kernel config: %v", err) - return nil, err - } - glog.V(3).Infof("Creating VM with default kernel config") - } else if len(kernel) == 0 || len(initrd) == 0 { - // if user specify a kernel, they must specify an initrd at the same time - return nil, fmt.Errorf("You must specify an initrd if you specify a kernel, or vice-versa") - } else { - boot := &hypervisor.BootConfig{ - CPU: cpu, - Memory: mem, - Kernel: kernel, - Initrd: initrd, - } - - vm, err = hypervisor.GetVm("", boot, true) - if err != nil { - glog.Errorf("Create VM failed: %v", err) - return nil, err - } - glog.V(3).Infof("Creating VM with specific kernel config") - } - - r := make(chan api.Result, 1) - go func() { - r <- vm.WaitInit() - }() - - sandbox := api.SandboxInfoFromOCF(spec) - vm.InitSandbox(sandbox) - - rsp := <-r - - if !rsp.IsSuccess() { - vm.Kill() - glog.Errorf("StartPod fail, response: %#v", rsp) - return nil, fmt.Errorf("StartPod fail") - } - glog.V(3).Infof("%s init sandbox successfully", rsp.ResultId()) - - hp := &HyperPod{ - vm: vm, - Containers: make(map[string]*Container), - Processes: make(map[string]*Process), - } - - // create Listener process running in its own netns - if err = hp.startNsListener(); err != nil { - hp.reap() - glog.Errorf("start ns listener fail: %v", err) - return nil, err - } - - return hp, nil -} - -func (hp *HyperPod) reap() { - result := make(chan api.Result, 1) - go func() { - result <- hp.vm.Shutdown() - }() - select { - case rsp, ok := <-result: - if !ok || !rsp.IsSuccess() { - glog.Errorf("StopPod fail: chan: %v, response: %v", ok, rsp) - break - } - glog.V(1).Infof("StopPod successfully") - case <-time.After(time.Second * 60): - glog.Errorf("StopPod timeout") - } - - hp.stopNsListener() - if err := os.RemoveAll(filepath.Join(hypervisor.BaseDir, hp.vm.Id)); err != nil { - glog.Errorf("can't remove vm dir %q: %v", filepath.Join(hypervisor.BaseDir, hp.vm.Id), err) - } - glog.Flush() -} diff --git a/supervisor/process.go b/supervisor/process.go deleted file mode 100644 index 34e5dd84..00000000 --- a/supervisor/process.go +++ /dev/null @@ -1,102 +0,0 @@ -package supervisor - -import ( - "fmt" - "io" - "os" - "syscall" - - "github.com/golang/glog" - "github.com/hyperhq/runv/hypervisor" - "github.com/opencontainers/runtime-spec/specs-go" -) - -type Process struct { - Id string - Stdin string - Stdout string - Stderr string - Spec *specs.Process - ProcId int - - // inerId is Id or container id + "-init" - // pass to hypervisor package and HyperPod.Processes - inerId string - ownerCont *Container - init bool - stdio *hypervisor.TtyIO - stdinCloser io.Closer -} - -func (p *Process) setupIO() error { - glog.V(3).Infof("process setupIO: stdin %s, stdout %s, stderr %s", p.Stdin, p.Stdout, p.Stderr) - - // use a new go routine to avoid deadlock when stdin is fifo - go func() { - if stdinCloser, err := os.OpenFile(p.Stdin, syscall.O_WRONLY, 0); err == nil { - p.stdinCloser = stdinCloser - } - }() - - var stdin, stdout, stderr *os.File - var err error - - stdin, err = os.OpenFile(p.Stdin, syscall.O_RDONLY, 0) - if err != nil { - return err - } - - stdout, err = os.OpenFile(p.Stdout, syscall.O_RDWR, 0) - if err != nil { - return err - } - - // Docker does not create stderr if it's a terminal process since at least 1.13+ - // github.com/docker/containerd/containerd-shim/process.go:239 - // This stanza keeps the API somewhat consistent - if st, err := os.Stat(p.Stderr); st != nil || !p.Spec.Terminal { - stderr, err = os.OpenFile(p.Stderr, syscall.O_RDWR, 0) - if err != nil { - return err - } - } - - p.stdio = &hypervisor.TtyIO{ - Stdin: stdin, - Stdout: stdout, - Stderr: stderr, - } - glog.V(3).Infof("process setupIO() successfully") - - return nil -} - -func (p *Process) ttyResize(container string, width, height int) error { - // If working on the primary process, do not pass execId (it won't be recognized) - if p.inerId == fmt.Sprintf("%s-init", container) { - return p.ownerCont.ownerPod.vm.Tty(container, "", height, width) - } - return p.ownerCont.ownerPod.vm.Tty(container, p.inerId, height, width) -} - -func (p *Process) closeStdin() error { - var err error - if p.stdinCloser != nil { - err = p.stdinCloser.Close() - p.stdinCloser = nil - } - return err -} - -func (p *Process) signal(sig int) error { - if p.init { - // TODO: change vm.KillContainer() - return p.ownerCont.ownerPod.vm.KillContainer(p.ownerCont.Id, syscall.Signal(sig)) - } else { - return p.ownerCont.ownerPod.vm.SignalProcess(p.ownerCont.Id, p.Id, syscall.Signal(sig)) - } -} - -func (p *Process) reap() { - p.closeStdin() -} diff --git a/supervisor/proxy/nslistener.go b/supervisor/proxy/nslistener.go deleted file mode 100644 index b79cac13..00000000 --- a/supervisor/proxy/nslistener.go +++ /dev/null @@ -1,231 +0,0 @@ -package proxy - -import ( - "encoding/gob" - "fmt" - "os" - "runtime" - "syscall" - - "github.com/docker/docker/pkg/reexec" - "github.com/golang/glog" - "github.com/hyperhq/runv/supervisor" - "github.com/vishvananda/netlink" -) - -func init() { - reexec.Register("containerd-nslistener", setupNsListener) -} - -func setupNsListener() { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - /* create own netns */ - if err := syscall.Unshare(syscall.CLONE_NEWNET); err != nil { - glog.Error(err) - return - } - - childPipe := os.NewFile(uintptr(3), "child") - enc := gob.NewEncoder(childPipe) - dec := gob.NewDecoder(childPipe) - - /* notify containerd to execute prestart hooks */ - if err := enc.Encode("init"); err != nil { - glog.Error(err) - return - } - - /* after execute prestart hooks */ - var ready string - if err := dec.Decode(&ready); err != nil { - glog.Error(err) - return - } - - if ready != "init" { - glog.Errorf("get incorrect init message: %s", ready) - return - } - - // Get network namespace info for the first time and send to the containerd - /* get route info before link down */ - routes, err := netlink.RouteList(nil, netlink.FAMILY_V4) - if err != nil { - glog.Error(err) - return - } - - /* send interface info to containerd */ - infos := collectionInterfaceInfo() - if err := enc.Encode(infos); err != nil { - glog.Error(err) - return - } - - if err := enc.Encode(routes); err != nil { - glog.Error(err) - return - } - - // This is a call back function. - // Use to send netlink update informations to containerd. - netNs2Containerd := func(netlinkUpdate supervisor.NetlinkUpdate) { - if err := enc.Encode(netlinkUpdate); err != nil { - glog.Info("err Encode(netlinkUpdate) is :", err) - } - } - // Keep collecting network namespace info and sending to the containerd - setupNetworkNsTrap(netNs2Containerd) -} - -func collectionInterfaceInfo() []supervisor.InterfaceInfo { - infos := []supervisor.InterfaceInfo{} - - links, err := netlink.LinkList() - if err != nil { - glog.Error(err) - return infos - } - - for _, link := range links { - if link.Type() != "veth" { - // lo is here too - continue - } - - addrs, err := netlink.AddrList(link, netlink.FAMILY_V4) - if err != nil { - glog.Error(err) - return infos - } - - for _, addr := range addrs { - info := supervisor.InterfaceInfo{ - Ip: addr.IPNet.String(), - Index: link.Attrs().Index, - PeerIndex: link.Attrs().ParentIndex, - } - glog.Infof("get interface %v", info) - infos = append(infos, info) - } - - // set link down, tap device take over it - netlink.LinkSetDown(link) - } - return infos -} - -// This function should be put into the main process or somewhere that can be -// use to init the network namespace trap. -func setupNetworkNsTrap(netNs2Containerd func(supervisor.NetlinkUpdate)) { - - // Subscribe for links change event - chLink := make(chan netlink.LinkUpdate) - doneLink := make(chan struct{}) - defer close(doneLink) - if err := netlink.LinkSubscribe(chLink, doneLink); err != nil { - glog.Fatal(err) - } - - // Subscribe for addresses change event - chAddr := make(chan netlink.AddrUpdate) - doneAddr := make(chan struct{}) - defer close(doneAddr) - if err := netlink.AddrSubscribe(chAddr, doneAddr); err != nil { - glog.Fatal(err) - } - - // Subscribe for route change event - chRoute := make(chan netlink.RouteUpdate) - doneRoute := make(chan struct{}) - defer close(doneRoute) - if err := netlink.RouteSubscribe(chRoute, doneRoute); err != nil { - glog.Fatal(err) - } - - for { - select { - case updateLink := <-chLink: - handleLink(updateLink, netNs2Containerd) - case updateAddr := <-chAddr: - handleAddr(updateAddr, netNs2Containerd) - case updateRoute := <-chRoute: - handleRoute(updateRoute, netNs2Containerd) - } - } -} - -// Link specific -func handleLink(update netlink.LinkUpdate, callback func(supervisor.NetlinkUpdate)) { - if update.IfInfomsg.Flags&syscall.IFF_UP == 1 { - fmt.Printf("[Link device up]\tupdateLink is:%+v, flag is:0x%x\n", update.Link.Attrs(), update.IfInfomsg.Flags) - } else { - if update.Link.Attrs().ParentIndex == 0 { - fmt.Printf("[Link device !up][Deleted]\tupdateLink is:%+v, flag is:0x%x\n", update.Link.Attrs(), update.IfInfomsg.Flags) - } else { - fmt.Printf("[Link device !up]\tupdateLink is:%+v, flag is:0x%x\n", update.Link.Attrs(), update.IfInfomsg.Flags) - } - } - - netlinkUpdate := supervisor.NetlinkUpdate{} - netlinkUpdate.UpdateType = supervisor.UpdateTypeLink - - // We would like to only handle the veth pair link at present. - if veth, ok := (update.Link).(*netlink.Veth); ok { - netlinkUpdate.Veth = veth - callback(netlinkUpdate) - } -} - -// Address specific -func handleAddr(update netlink.AddrUpdate, callback func(supervisor.NetlinkUpdate)) { - if update.NewAddr { - fmt.Printf("[Add a address]") - } else { - fmt.Printf("[Delete a address]") - } - - if update.LinkAddress.IP.To4() != nil { - fmt.Printf("[IPv4]\t%+v\n", update) - } else { - // We would not like to handle IPv6 at present. - fmt.Printf("[IPv6]\t%+v\n", update) - return - } - - netlinkUpdate := supervisor.NetlinkUpdate{} - netlinkUpdate.Addr = update - netlinkUpdate.UpdateType = supervisor.UpdateTypeAddr - links, err := netlink.LinkList() - if err != nil { - glog.Error(err) - } - for _, link := range links { - if link.Attrs().Index == update.LinkIndex && link.Type() == "veth" { - netlinkUpdate.Veth = link.(*netlink.Veth) - break - } - } - callback(netlinkUpdate) -} - -// Route specific -func handleRoute(update netlink.RouteUpdate, callback func(supervisor.NetlinkUpdate)) { - // Route type is not a bit mask for a couple of values, but a single - // unsigned int, that's why we use switch here not the "&" operator. - switch update.Type { - case syscall.RTM_NEWROUTE: - fmt.Printf("[Create a route]\t%+v\n", update) - case syscall.RTM_DELROUTE: - fmt.Printf("[Remove a route]\t%+v\n", update) - case syscall.RTM_GETROUTE: - fmt.Printf("[Receive info of a route]\t%+v\n", update) - } - - netlinkUpdate := supervisor.NetlinkUpdate{} - netlinkUpdate.Route = update - netlinkUpdate.UpdateType = supervisor.UpdateTypeRoute - callback(netlinkUpdate) -} diff --git a/supervisor/supervisor.go b/supervisor/supervisor.go deleted file mode 100644 index ca92672e..00000000 --- a/supervisor/supervisor.go +++ /dev/null @@ -1,243 +0,0 @@ -package supervisor - -import ( - "fmt" - "os" - "regexp" - "strconv" - "sync" - "time" - - "github.com/golang/glog" - "github.com/hyperhq/runv/factory" - "github.com/opencontainers/runtime-spec/specs-go" -) - -type Supervisor struct { - StateDir string - Factory factory.Factory - // Default CPU and memory amounts to use when not specified by container - defaultCpus int - defaultMemory int - - Events SvEvents - - sync.RWMutex // Protects Supervisor.Containers, HyperPod.Containers, HyperPod.Processes, Container.Processes - Containers map[string]*Container -} - -func New(stateDir, eventLogDir string, f factory.Factory, defaultCpus int, defaultMemory int) (*Supervisor, error) { - if err := os.MkdirAll(stateDir, 0755); err != nil { - return nil, err - } - if err := os.MkdirAll(eventLogDir, 0755); err != nil { - return nil, err - } - if defaultCpus <= 0 { - return nil, fmt.Errorf("defaultCpu must be greater than 0.") - } - if defaultMemory <= 0 { - return nil, fmt.Errorf("defaultMemory must be greater than 0.") - } - sv := &Supervisor{ - StateDir: stateDir, - Factory: f, - defaultCpus: defaultCpus, - defaultMemory: defaultMemory, - Containers: make(map[string]*Container), - } - sv.Events.subscribers = make(map[chan Event]struct{}) - go sv.reaper() - return sv, sv.Events.setupEventLog(eventLogDir) -} - -func (sv *Supervisor) CreateContainer(container, bundlePath, stdin, stdout, stderr string, spec *specs.Spec) (c *Container, err error) { - defer func() { - if err == nil { - err = c.create() - } - if err != nil { - sv.reap(container, "init") - } - }() - sv.Lock() - defer sv.Unlock() - hp, err := sv.getHyperPod(container, spec) - if err != nil { - return nil, err - } - c, err = hp.createContainer(container, bundlePath, stdin, stdout, stderr, spec) - if err != nil { - return nil, err - } - sv.Containers[container] = c - glog.V(1).Infof("supervisor creates container %q successfully", container) - return c, nil -} - -func (sv *Supervisor) StartContainer(container string, spec *specs.Spec) (c *Container, p *Process, err error) { - defer func() { - glog.V(3).Infof("Supervisor.StartContainer() return: c: %#v p: %#v", c, p) - if err == nil { - err = c.start(p) - } - if err != nil { - glog.Errorf("Supervisor.StartContainer() failed: %#v", err) - sv.reap(container, "init") - } - }() - sv.Lock() - defer sv.Unlock() - if c, ok := sv.Containers[container]; ok { - return c, c.Processes["init"], nil - } - return nil, nil, fmt.Errorf("container %s is not found for StartContainer()", container) -} - -func (sv *Supervisor) AddProcess(container, processId, stdin, stdout, stderr string, spec *specs.Process) (*Process, error) { - sv.Lock() - defer sv.Unlock() - if c, ok := sv.Containers[container]; ok { - return c.addProcess(processId, stdin, stdout, stderr, spec) - } - return nil, fmt.Errorf("container %s is not found for AddProcess()", container) -} - -func (sv *Supervisor) TtyResize(container, processId string, width, height int) error { - sv.RLock() - defer sv.RUnlock() - p := sv.getProcess(container, processId) - if p != nil { - return p.ttyResize(container, width, height) - } - return fmt.Errorf("The container %s or the process %s is not found", container, processId) -} - -func (sv *Supervisor) CloseStdin(container, processId string) error { - sv.RLock() - defer sv.RUnlock() - p := sv.getProcess(container, processId) - if p != nil { - return p.closeStdin() - } - return fmt.Errorf("The container %s or the process %s is not found", container, processId) -} - -func (sv *Supervisor) Signal(container, processId string, sig int) error { - sv.RLock() - defer sv.RUnlock() - p := sv.getProcess(container, processId) - if p != nil { - return p.signal(sig) - } - return fmt.Errorf("The container %s or the process %s is not found", container, processId) -} - -func (sv *Supervisor) getProcess(container, processId string) *Process { - if c, ok := sv.Containers[container]; ok { - if p, ok := c.Processes[processId]; ok { - return p - } - } - return nil -} - -func (sv *Supervisor) reaper() { - events := sv.Events.Events(time.Time{}) - for e := range events { - if e.Type == EventExit { - go sv.reap(e.ID, e.PID) - } - } -} - -func (sv *Supervisor) reap(container, processId string) { - glog.Infof("reap container %s processId %s", container, processId) - sv.Lock() - defer sv.Unlock() - if c, ok := sv.Containers[container]; ok { - if p, ok := c.Processes[processId]; ok { - p.reap() - delete(c.ownerPod.Processes, p.inerId) - delete(c.Processes, processId) - if p.init { - // TODO: kill all the other existing processes in the same container - } - } - if len(c.Processes) == 0 { - c.reap() - delete(c.ownerPod.Containers, container) - delete(sv.Containers, container) - } - if len(c.ownerPod.Containers) == 0 { - c.ownerPod.reap() - } - } -} - -// find shared pod or create a new one -func (sv *Supervisor) getHyperPod(container string, spec *specs.Spec) (hp *HyperPod, err error) { - if _, ok := sv.Containers[container]; ok { - return nil, fmt.Errorf("The container %s is already existing", container) - } - if spec.Linux == nil { - return nil, fmt.Errorf("it is not linux container config") - } - if containerType, ok := spec.Annotations["ocid/container_type"]; ok { - if containerType == "container" { - c := sv.Containers[spec.Annotations["ocid/sandbox_name"]] - if c == nil { - return nil, fmt.Errorf("Can't find the sandbox container") - } - hp = c.ownerPod - } - } else { - for _, ns := range spec.Linux.Namespaces { - if len(ns.Path) > 0 { - if ns.Type == "mount" { - // TODO support it! - return nil, fmt.Errorf("Runv doesn't support shared mount namespace currently") - } - - pidexp := regexp.MustCompile(`/proc/(\d+)/ns/*`) - matches := pidexp.FindStringSubmatch(ns.Path) - if len(matches) != 2 { - return nil, fmt.Errorf("Can't find shared container with network ns path %s", ns.Path) - } - pid, _ := strconv.Atoi(matches[1]) - - for _, c := range sv.Containers { - if c.ownerPod != nil && pid == c.ownerPod.getNsPid() { - if hp != nil && hp != c.ownerPod { - return nil, fmt.Errorf("Conflict share") - } - hp = c.ownerPod - break - } - } - if hp == nil { - return nil, fmt.Errorf("Can't find shared container with network ns path %s", ns.Path) - } - } - } - } - if hp == nil { - // use 'func() + defer' to ensure we regain the lock when createHyperPod() panic. - func() { - sv.Unlock() - defer sv.Lock() - hp, err = createHyperPod(sv.Factory, spec, sv.defaultCpus, sv.defaultMemory) - }() - glog.V(3).Infof("createHyperPod() returns") - if err != nil { - return nil, err - } - hp.sv = sv - // recheck existed - if _, ok := sv.Containers[container]; ok { - go hp.reap() - return nil, fmt.Errorf("The container %s is already existing", container) - } - } - return hp, nil -} diff --git a/utils.go b/utils.go deleted file mode 100644 index 24e65e4c..00000000 --- a/utils.go +++ /dev/null @@ -1,79 +0,0 @@ -package main - -import ( - "fmt" - "os" - "path/filepath" - - "github.com/urfave/cli" -) - -const ( - defaultKernelInstallDir string = "/var/lib/hyper" -) - -func firstExistingFile(candidates []string) string { - for _, file := range candidates { - if _, err := os.Stat(file); err == nil { - return file - } - } - return "" -} - -func getDefaultBundlePath() string { - cwd, err := os.Getwd() - if err != nil { - return "" - } - return cwd -} - -// getKernelFiles chooses kernel/initrd/bios/cbfs files based on user specified ones -func getKernelFiles(context *cli.Context, rootPath string) (string, string, string, string, error) { - kernel := context.GlobalString("kernel") - initrd := context.GlobalString("initrd") - bios := context.GlobalString("bios") - cbfs := context.GlobalString("cbfs") - bundle := context.String("bundle") - - for k, v := range map[*string][]string{ - &kernel: { - filepath.Join(bundle, rootPath, "boot/vmlinuz"), - filepath.Join(bundle, "boot/vmlinuz"), - filepath.Join(bundle, "vmlinuz"), - filepath.Join(defaultKernelInstallDir, "kernel"), - }, - &initrd: { - filepath.Join(bundle, rootPath, "boot/initrd.img"), - filepath.Join(bundle, "boot/initrd.img"), - filepath.Join(bundle, "initrd.img"), - filepath.Join(defaultKernelInstallDir, "hyper-initrd.img"), - }, - &bios: { - filepath.Join(bundle, rootPath, "boot/bios.bin"), - filepath.Join(bundle, "boot/bios.bin"), - filepath.Join(bundle, "bios.bin"), - filepath.Join(defaultKernelInstallDir, "bios.bin"), - }, - &cbfs: { - filepath.Join(bundle, rootPath, "boot/cbfs.rom"), - filepath.Join(bundle, "boot/cbfs.rom"), - filepath.Join(bundle, "cbfs.rom"), - filepath.Join(defaultKernelInstallDir, "cbfs.rom"), - }, - } { - if *k == "" { - *k = firstExistingFile(v) - } - if *k != "" { - var err error - *k, err = filepath.Abs(*k) - if err != nil { - return "", "", "", "", fmt.Errorf("cannot get abs path for kernel files: %v", err) - } - } - } - - return kernel, initrd, bios, cbfs, nil -} diff --git a/vendor/github.com/golang/protobuf/proto/encode.go b/vendor/github.com/golang/protobuf/proto/encode.go index 2b30f846..8b84d1b2 100644 --- a/vendor/github.com/golang/protobuf/proto/encode.go +++ b/vendor/github.com/golang/protobuf/proto/encode.go @@ -174,11 +174,11 @@ func sizeFixed32(x uint64) int { // This is the format used for the sint64 protocol buffer type. func (p *Buffer) EncodeZigzag64(x uint64) error { // use signed number to get arithmetic right shift. - return p.EncodeVarint(uint64((x << 1) ^ uint64((int64(x) >> 63)))) + return p.EncodeVarint((x << 1) ^ uint64((int64(x) >> 63))) } func sizeZigzag64(x uint64) int { - return sizeVarint(uint64((x << 1) ^ uint64((int64(x) >> 63)))) + return sizeVarint((x << 1) ^ uint64((int64(x) >> 63))) } // EncodeZigzag32 writes a zigzag-encoded 32-bit integer diff --git a/vendor/github.com/golang/protobuf/proto/lib.go b/vendor/github.com/golang/protobuf/proto/lib.go index ac4ddbc0..1c225504 100644 --- a/vendor/github.com/golang/protobuf/proto/lib.go +++ b/vendor/github.com/golang/protobuf/proto/lib.go @@ -73,7 +73,6 @@ for a protocol buffer variable v: When the .proto file specifies `syntax="proto3"`, there are some differences: - Non-repeated fields of non-message type are values instead of pointers. - - Getters are only generated for message and oneof fields. - Enum types do not get an Enum method. The simplest way to describe this is to see an example. diff --git a/vendor/github.com/golang/protobuf/proto/text_parser.go b/vendor/github.com/golang/protobuf/proto/text_parser.go index 61f83c1e..5e14513f 100644 --- a/vendor/github.com/golang/protobuf/proto/text_parser.go +++ b/vendor/github.com/golang/protobuf/proto/text_parser.go @@ -865,7 +865,7 @@ func (p *textParser) readAny(v reflect.Value, props *Properties) error { return p.readStruct(fv, terminator) case reflect.Uint32: if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil { - fv.SetUint(uint64(x)) + fv.SetUint(x) return nil } case reflect.Uint64: diff --git a/vendor/github.com/golang/protobuf/ptypes/any.go b/vendor/github.com/golang/protobuf/ptypes/any.go index 89e07ae1..b2af97f4 100644 --- a/vendor/github.com/golang/protobuf/ptypes/any.go +++ b/vendor/github.com/golang/protobuf/ptypes/any.go @@ -51,6 +51,9 @@ const googleApis = "type.googleapis.com/" // function. AnyMessageName is provided for less common use cases like filtering a // sequence of Any messages based on a set of allowed message type names. func AnyMessageName(any *any.Any) (string, error) { + if any == nil { + return "", fmt.Errorf("message is nil") + } slash := strings.LastIndex(any.TypeUrl, "/") if slash < 0 { return "", fmt.Errorf("message type url %q is invalid", any.TypeUrl) diff --git a/vendor/github.com/golang/protobuf/ptypes/any/any.pb.go b/vendor/github.com/golang/protobuf/ptypes/any/any.pb.go index f2c6906b..1fbaa44c 100644 --- a/vendor/github.com/golang/protobuf/ptypes/any/any.pb.go +++ b/vendor/github.com/golang/protobuf/ptypes/any/any.pb.go @@ -1,6 +1,5 @@ -// Code generated by protoc-gen-go. +// Code generated by protoc-gen-go. DO NOT EDIT. // source: github.com/golang/protobuf/ptypes/any/any.proto -// DO NOT EDIT! /* Package any is a generated protocol buffer package. @@ -132,6 +131,20 @@ func (*Any) ProtoMessage() {} func (*Any) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } func (*Any) XXX_WellKnownType() string { return "Any" } +func (m *Any) GetTypeUrl() string { + if m != nil { + return m.TypeUrl + } + return "" +} + +func (m *Any) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + func init() { proto.RegisterType((*Any)(nil), "google.protobuf.Any") } @@ -139,17 +152,17 @@ func init() { func init() { proto.RegisterFile("github.com/golang/protobuf/ptypes/any/any.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 187 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xd2, 0x4f, 0xcf, 0x2c, 0xc9, + // 184 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x4f, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf, 0xcf, 0x49, 0xcc, 0x4b, 0xd7, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0x28, 0xa9, 0x2c, 0x48, 0x2d, 0xd6, 0x4f, 0xcc, 0xab, 0x04, 0x61, 0x3d, 0xb0, 0xb8, 0x10, 0x7f, 0x7a, 0x7e, 0x7e, 0x7a, 0x4e, 0xaa, 0x1e, 0x4c, 0x95, 0x92, 0x19, 0x17, 0xb3, 0x63, 0x5e, 0xa5, 0x90, 0x24, 0x17, 0x07, 0x48, 0x79, 0x7c, 0x69, 0x51, 0x8e, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x67, 0x10, 0x3b, 0x88, 0x1f, 0x5a, 0x94, 0x23, 0x24, 0xc2, 0xc5, 0x5a, 0x96, 0x98, 0x53, 0x9a, 0x2a, 0xc1, 0xa4, 0xc0, 0xa8, 0xc1, 0x13, 0x04, 0xe1, - 0x38, 0x15, 0x71, 0x09, 0x27, 0xe7, 0xe7, 0xea, 0xa1, 0x19, 0xe7, 0xc4, 0xe1, 0x98, 0x57, 0x19, - 0x00, 0xe2, 0x04, 0x30, 0x46, 0xa9, 0x12, 0xe5, 0xb8, 0x05, 0x8c, 0x8c, 0x8b, 0x98, 0x98, 0xdd, - 0x03, 0x9c, 0x56, 0x31, 0xc9, 0xb9, 0x43, 0x4c, 0x0b, 0x80, 0xaa, 0xd2, 0x0b, 0x4f, 0xcd, 0xc9, - 0xf1, 0xce, 0xcb, 0x2f, 0xcf, 0x0b, 0x01, 0xa9, 0x4e, 0x62, 0x03, 0x6b, 0x37, 0x06, 0x04, 0x00, - 0x00, 0xff, 0xff, 0xc6, 0x4d, 0x03, 0x23, 0xf6, 0x00, 0x00, 0x00, + 0x38, 0xe5, 0x73, 0x09, 0x27, 0xe7, 0xe7, 0xea, 0xa1, 0x19, 0xe7, 0xc4, 0xe1, 0x98, 0x57, 0x19, + 0x00, 0xe2, 0x04, 0x30, 0x46, 0xa9, 0x12, 0xe5, 0xb8, 0x45, 0x4c, 0xcc, 0xee, 0x01, 0x4e, 0xab, + 0x98, 0xe4, 0xdc, 0x21, 0x46, 0x05, 0x40, 0x95, 0xe8, 0x85, 0xa7, 0xe6, 0xe4, 0x78, 0xe7, 0xe5, + 0x97, 0xe7, 0x85, 0x80, 0x94, 0x26, 0xb1, 0x81, 0xf5, 0x1a, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, + 0x45, 0x1f, 0x1a, 0xf2, 0xf3, 0x00, 0x00, 0x00, } diff --git a/vendor/github.com/golang/protobuf/ptypes/any/any.proto b/vendor/github.com/golang/protobuf/ptypes/any/any.proto index 81dcf46c..9bd3f50a 100644 --- a/vendor/github.com/golang/protobuf/ptypes/any/any.proto +++ b/vendor/github.com/golang/protobuf/ptypes/any/any.proto @@ -37,7 +37,6 @@ option go_package = "github.com/golang/protobuf/ptypes/any"; option java_package = "com.google.protobuf"; option java_outer_classname = "AnyProto"; option java_multiple_files = true; -option java_generate_equals_and_hash = true; option objc_class_prefix = "GPB"; // `Any` contains an arbitrary serialized protocol buffer message along with a diff --git a/vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go b/vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go index 56974834..fe3350be 100644 --- a/vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go +++ b/vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go @@ -1,6 +1,5 @@ -// Code generated by protoc-gen-go. +// Code generated by protoc-gen-go. DO NOT EDIT. // source: github.com/golang/protobuf/ptypes/duration/duration.proto -// DO NOT EDIT! /* Package duration is a generated protocol buffer package. @@ -35,6 +34,8 @@ const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package // two Timestamp values is a Duration and it can be added or subtracted // from a Timestamp. Range is approximately +-10,000 years. // +// # Examples +// // Example 1: Compute Duration from two Timestamps in pseudo code. // // Timestamp start = ...; @@ -69,10 +70,27 @@ const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package // end.nanos -= 1000000000; // } // +// Example 3: Compute Duration from datetime.timedelta in Python. +// +// td = datetime.timedelta(days=3, minutes=10) +// duration = Duration() +// duration.FromTimedelta(td) +// +// # JSON Mapping +// +// In JSON format, the Duration type is encoded as a string rather than an +// object, where the string ends in the suffix "s" (indicating seconds) and +// is preceded by the number of seconds, with nanoseconds expressed as +// fractional seconds. For example, 3 seconds with 0 nanoseconds should be +// encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should +// be expressed in JSON format as "3.000000001s", and 3 seconds and 1 +// microsecond should be expressed in JSON format as "3.000001s". +// // type Duration struct { // Signed seconds of the span of time. Must be from -315,576,000,000 - // to +315,576,000,000 inclusive. + // to +315,576,000,000 inclusive. Note: these bounds are computed from: + // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years Seconds int64 `protobuf:"varint,1,opt,name=seconds" json:"seconds,omitempty"` // Signed fractions of a second at nanosecond resolution of the span // of time. Durations less than one second are represented with a 0 @@ -89,6 +107,20 @@ func (*Duration) ProtoMessage() {} func (*Duration) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } func (*Duration) XXX_WellKnownType() string { return "Duration" } +func (m *Duration) GetSeconds() int64 { + if m != nil { + return m.Seconds + } + return 0 +} + +func (m *Duration) GetNanos() int32 { + if m != nil { + return m.Nanos + } + return 0 +} + func init() { proto.RegisterType((*Duration)(nil), "google.protobuf.Duration") } @@ -99,7 +131,7 @@ func init() { var fileDescriptor0 = []byte{ // 189 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xb2, 0x4c, 0xcf, 0x2c, 0xc9, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x4c, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf, 0xcf, 0x49, 0xcc, 0x4b, 0xd7, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0x28, 0xa9, 0x2c, 0x48, 0x2d, 0xd6, 0x4f, 0x29, 0x2d, 0x4a, 0x2c, 0xc9, 0xcc, 0xcf, 0x83, 0x33, 0xf4, 0xc0, 0x2a, 0x84, 0xf8, 0xd3, 0xf3, 0xf3, @@ -107,8 +139,8 @@ var fileDescriptor0 = []byte{ 0xd8, 0x8b, 0x53, 0x93, 0xf3, 0xf3, 0x52, 0x8a, 0x25, 0x18, 0x15, 0x18, 0x35, 0x98, 0x83, 0x60, 0x5c, 0x21, 0x11, 0x2e, 0xd6, 0xbc, 0xc4, 0xbc, 0xfc, 0x62, 0x09, 0x26, 0x05, 0x46, 0x0d, 0xd6, 0x20, 0x08, 0xc7, 0xa9, 0x86, 0x4b, 0x38, 0x39, 0x3f, 0x57, 0x0f, 0xcd, 0x48, 0x27, 0x5e, 0x98, - 0x81, 0x01, 0x20, 0x91, 0x00, 0xc6, 0x28, 0x2d, 0xe2, 0xdd, 0xbb, 0x80, 0x91, 0x71, 0x11, 0x13, + 0x81, 0x01, 0x20, 0x91, 0x00, 0xc6, 0x28, 0x2d, 0xe2, 0xdd, 0xfb, 0x83, 0x91, 0x71, 0x11, 0x13, 0xb3, 0x7b, 0x80, 0xd3, 0x2a, 0x26, 0x39, 0x77, 0x88, 0xb9, 0x01, 0x50, 0xa5, 0x7a, 0xe1, 0xa9, 0x39, 0x39, 0xde, 0x79, 0xf9, 0xe5, 0x79, 0x21, 0x20, 0x2d, 0x49, 0x6c, 0x60, 0x33, 0x8c, 0x01, - 0x01, 0x00, 0x00, 0xff, 0xff, 0x62, 0xfb, 0xb1, 0x51, 0x0e, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0xff, 0xff, 0x45, 0x5a, 0x81, 0x3d, 0x0e, 0x01, 0x00, 0x00, } diff --git a/vendor/github.com/golang/protobuf/ptypes/duration/duration.proto b/vendor/github.com/golang/protobuf/ptypes/duration/duration.proto index 96c1796d..975fce41 100644 --- a/vendor/github.com/golang/protobuf/ptypes/duration/duration.proto +++ b/vendor/github.com/golang/protobuf/ptypes/duration/duration.proto @@ -33,11 +33,11 @@ syntax = "proto3"; package google.protobuf; option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option cc_enable_arenas = true; option go_package = "github.com/golang/protobuf/ptypes/duration"; option java_package = "com.google.protobuf"; option java_outer_classname = "DurationProto"; option java_multiple_files = true; -option java_generate_equals_and_hash = true; option objc_class_prefix = "GPB"; // A Duration represents a signed, fixed-length span of time represented @@ -47,6 +47,8 @@ option objc_class_prefix = "GPB"; // two Timestamp values is a Duration and it can be added or subtracted // from a Timestamp. Range is approximately +-10,000 years. // +// # Examples +// // Example 1: Compute Duration from two Timestamps in pseudo code. // // Timestamp start = ...; @@ -81,11 +83,28 @@ option objc_class_prefix = "GPB"; // end.nanos -= 1000000000; // } // +// Example 3: Compute Duration from datetime.timedelta in Python. +// +// td = datetime.timedelta(days=3, minutes=10) +// duration = Duration() +// duration.FromTimedelta(td) +// +// # JSON Mapping +// +// In JSON format, the Duration type is encoded as a string rather than an +// object, where the string ends in the suffix "s" (indicating seconds) and +// is preceded by the number of seconds, with nanoseconds expressed as +// fractional seconds. For example, 3 seconds with 0 nanoseconds should be +// encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should +// be expressed in JSON format as "3.000000001s", and 3 seconds and 1 +// microsecond should be expressed in JSON format as "3.000001s". +// // message Duration { // Signed seconds of the span of time. Must be from -315,576,000,000 - // to +315,576,000,000 inclusive. + // to +315,576,000,000 inclusive. Note: these bounds are computed from: + // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years int64 seconds = 1; // Signed fractions of a second at nanosecond resolution of the span diff --git a/vendor/github.com/golang/protobuf/ptypes/empty/empty.pb.go b/vendor/github.com/golang/protobuf/ptypes/empty/empty.pb.go new file mode 100644 index 00000000..ae159414 --- /dev/null +++ b/vendor/github.com/golang/protobuf/ptypes/empty/empty.pb.go @@ -0,0 +1,68 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: github.com/golang/protobuf/ptypes/empty/empty.proto + +/* +Package empty is a generated protocol buffer package. + +It is generated from these files: + github.com/golang/protobuf/ptypes/empty/empty.proto + +It has these top-level messages: + Empty +*/ +package empty + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +// A generic empty message that you can re-use to avoid defining duplicated +// empty messages in your APIs. A typical example is to use it as the request +// or the response type of an API method. For instance: +// +// service Foo { +// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); +// } +// +// The JSON representation for `Empty` is empty JSON object `{}`. +type Empty struct { +} + +func (m *Empty) Reset() { *m = Empty{} } +func (m *Empty) String() string { return proto.CompactTextString(m) } +func (*Empty) ProtoMessage() {} +func (*Empty) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } +func (*Empty) XXX_WellKnownType() string { return "Empty" } + +func init() { + proto.RegisterType((*Empty)(nil), "google.protobuf.Empty") +} + +func init() { + proto.RegisterFile("github.com/golang/protobuf/ptypes/empty/empty.proto", fileDescriptor0) +} + +var fileDescriptor0 = []byte{ + // 147 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x4e, 0xcf, 0x2c, 0xc9, + 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf, 0xcf, 0x49, 0xcc, 0x4b, 0xd7, 0x2f, 0x28, + 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0x28, 0xa9, 0x2c, 0x48, 0x2d, 0xd6, 0x4f, 0xcd, + 0x2d, 0x28, 0xa9, 0x84, 0x90, 0x7a, 0x60, 0x39, 0x21, 0xfe, 0xf4, 0xfc, 0xfc, 0xf4, 0x9c, 0x54, + 0x3d, 0x98, 0x4a, 0x25, 0x76, 0x2e, 0x56, 0x57, 0x90, 0xbc, 0x53, 0x19, 0x97, 0x70, 0x72, 0x7e, + 0xae, 0x1e, 0x9a, 0xbc, 0x13, 0x17, 0x58, 0x36, 0x00, 0xc4, 0x0d, 0x60, 0x8c, 0x52, 0x27, 0xd2, + 0xce, 0x1f, 0x8c, 0x8c, 0x8b, 0x98, 0x98, 0xdd, 0x03, 0x9c, 0x56, 0x31, 0xc9, 0xb9, 0x43, 0x4c, + 0x0c, 0x80, 0xaa, 0xd3, 0x0b, 0x4f, 0xcd, 0xc9, 0xf1, 0xce, 0xcb, 0x2f, 0xcf, 0x0b, 0x01, 0xa9, + 0x4f, 0x62, 0x03, 0x1b, 0x60, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x6e, 0x8e, 0x0a, 0x06, 0xcf, + 0x00, 0x00, 0x00, +} diff --git a/vendor/github.com/golang/protobuf/ptypes/empty/empty.proto b/vendor/github.com/golang/protobuf/ptypes/empty/empty.proto new file mode 100644 index 00000000..03cacd23 --- /dev/null +++ b/vendor/github.com/golang/protobuf/ptypes/empty/empty.proto @@ -0,0 +1,52 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option go_package = "github.com/golang/protobuf/ptypes/empty"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "EmptyProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option cc_enable_arenas = true; + +// A generic empty message that you can re-use to avoid defining duplicated +// empty messages in your APIs. A typical example is to use it as the request +// or the response type of an API method. For instance: +// +// service Foo { +// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); +// } +// +// The JSON representation for `Empty` is empty JSON object `{}`. +message Empty {} diff --git a/vendor/github.com/golang/protobuf/ptypes/timestamp.go b/vendor/github.com/golang/protobuf/ptypes/timestamp.go index 1b365762..47f10dbc 100644 --- a/vendor/github.com/golang/protobuf/ptypes/timestamp.go +++ b/vendor/github.com/golang/protobuf/ptypes/timestamp.go @@ -99,6 +99,15 @@ func Timestamp(ts *tspb.Timestamp) (time.Time, error) { return t, validateTimestamp(ts) } +// TimestampNow returns a google.protobuf.Timestamp for the current time. +func TimestampNow() *tspb.Timestamp { + ts, err := TimestampProto(time.Now()) + if err != nil { + panic("ptypes: time.Now() out of Timestamp range") + } + return ts +} + // TimestampProto converts the time.Time to a google.protobuf.Timestamp proto. // It returns an error if the resulting Timestamp is invalid. func TimestampProto(t time.Time) (*tspb.Timestamp, error) { diff --git a/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go b/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go index ffcc5159..3b76261e 100644 --- a/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go +++ b/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go @@ -1,6 +1,5 @@ -// Code generated by protoc-gen-go. +// Code generated by protoc-gen-go. DO NOT EDIT. // source: github.com/golang/protobuf/ptypes/timestamp/timestamp.proto -// DO NOT EDIT! /* Package timestamp is a generated protocol buffer package. @@ -40,6 +39,8 @@ const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package // and from RFC 3339 date strings. // See [https://www.ietf.org/rfc/rfc3339.txt](https://www.ietf.org/rfc/rfc3339.txt). // +// # Examples +// // Example 1: Compute Timestamp from POSIX `time()`. // // Timestamp timestamp; @@ -77,15 +78,36 @@ const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package // // Example 5: Compute Timestamp from current time in Python. // -// now = time.time() -// seconds = int(now) -// nanos = int((now - seconds) * 10**9) -// timestamp = Timestamp(seconds=seconds, nanos=nanos) +// timestamp = Timestamp() +// timestamp.GetCurrentTime() +// +// # JSON Mapping +// +// In JSON format, the Timestamp type is encoded as a string in the +// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the +// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" +// where {year} is always expressed using four digits while {month}, {day}, +// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional +// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), +// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone +// is required, though only UTC (as indicated by "Z") is presently supported. +// +// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past +// 01:30 UTC on January 15, 2017. +// +// In JavaScript, one can convert a Date object to this format using the +// standard [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString] +// method. In Python, a standard `datetime.datetime` object can be converted +// to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) +// with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one +// can use the Joda Time's [`ISODateTimeFormat.dateTime()`]( +// http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()) +// to obtain a formatter capable of generating timestamps in this format. // // type Timestamp struct { // Represents seconds of UTC time since Unix epoch - // 1970-01-01T00:00:00Z. Must be from from 0001-01-01T00:00:00Z to + // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to // 9999-12-31T23:59:59Z inclusive. Seconds int64 `protobuf:"varint,1,opt,name=seconds" json:"seconds,omitempty"` // Non-negative fractions of a second at nanosecond resolution. Negative @@ -101,6 +123,20 @@ func (*Timestamp) ProtoMessage() {} func (*Timestamp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } func (*Timestamp) XXX_WellKnownType() string { return "Timestamp" } +func (m *Timestamp) GetSeconds() int64 { + if m != nil { + return m.Seconds + } + return 0 +} + +func (m *Timestamp) GetNanos() int32 { + if m != nil { + return m.Nanos + } + return 0 +} + func init() { proto.RegisterType((*Timestamp)(nil), "google.protobuf.Timestamp") } @@ -110,18 +146,17 @@ func init() { } var fileDescriptor0 = []byte{ - // 194 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xb2, 0x4e, 0xcf, 0x2c, 0xc9, + // 190 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x4e, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf, 0xcf, 0x49, 0xcc, 0x4b, 0xd7, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0x28, 0xa9, 0x2c, 0x48, 0x2d, 0xd6, 0x2f, 0xc9, 0xcc, 0x4d, 0x2d, 0x2e, 0x49, 0xcc, 0x2d, 0x40, 0xb0, 0xf4, 0xc0, 0x6a, 0x84, 0xf8, 0xd3, 0xf3, 0xf3, 0xd3, 0x73, 0x52, 0xf5, 0x60, 0x3a, 0x94, 0xac, 0xb9, 0x38, 0x43, 0x60, 0x6a, 0x84, 0x24, 0xb8, 0xd8, 0x8b, 0x53, 0x93, 0xf3, 0xf3, 0x52, 0x8a, 0x25, 0x18, 0x15, 0x18, 0x35, 0x98, 0x83, 0x60, 0x5c, 0x21, 0x11, 0x2e, 0xd6, 0xbc, 0xc4, 0xbc, 0xfc, 0x62, 0x09, 0x26, 0x05, 0x46, 0x0d, - 0xd6, 0x20, 0x08, 0xc7, 0xa9, 0x91, 0x91, 0x4b, 0x38, 0x39, 0x3f, 0x57, 0x0f, 0xcd, 0x50, 0x27, - 0x3e, 0xb8, 0x91, 0x01, 0x20, 0xa1, 0x00, 0xc6, 0x28, 0x6d, 0x12, 0x1c, 0xbd, 0x80, 0x91, 0xf1, - 0x07, 0x23, 0xe3, 0x22, 0x26, 0x66, 0xf7, 0x00, 0xa7, 0x55, 0x4c, 0x72, 0xee, 0x10, 0xc3, 0x03, - 0xa0, 0xca, 0xf5, 0xc2, 0x53, 0x73, 0x72, 0xbc, 0xf3, 0xf2, 0xcb, 0xf3, 0x42, 0x40, 0xda, 0x92, - 0xd8, 0xc0, 0xe6, 0x18, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x17, 0x5f, 0xb7, 0xdc, 0x17, 0x01, - 0x00, 0x00, + 0xd6, 0x20, 0x08, 0xc7, 0xa9, 0x8e, 0x4b, 0x38, 0x39, 0x3f, 0x57, 0x0f, 0xcd, 0x4c, 0x27, 0x3e, + 0xb8, 0x89, 0x01, 0x20, 0xa1, 0x00, 0xc6, 0x28, 0x6d, 0x12, 0xdc, 0xfc, 0x83, 0x91, 0x71, 0x11, + 0x13, 0xb3, 0x7b, 0x80, 0xd3, 0x2a, 0x26, 0x39, 0x77, 0x88, 0xc9, 0x01, 0x50, 0xb5, 0x7a, 0xe1, + 0xa9, 0x39, 0x39, 0xde, 0x79, 0xf9, 0xe5, 0x79, 0x21, 0x20, 0x3d, 0x49, 0x6c, 0x60, 0x43, 0x8c, + 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x6b, 0x59, 0x0a, 0x4d, 0x13, 0x01, 0x00, 0x00, } diff --git a/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.proto b/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.proto index 7992a858..b7cbd175 100644 --- a/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.proto +++ b/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.proto @@ -38,7 +38,6 @@ option go_package = "github.com/golang/protobuf/ptypes/timestamp"; option java_package = "com.google.protobuf"; option java_outer_classname = "TimestampProto"; option java_multiple_files = true; -option java_generate_equals_and_hash = true; option objc_class_prefix = "GPB"; // A Timestamp represents a point in time independent of any time zone @@ -53,6 +52,8 @@ option objc_class_prefix = "GPB"; // and from RFC 3339 date strings. // See [https://www.ietf.org/rfc/rfc3339.txt](https://www.ietf.org/rfc/rfc3339.txt). // +// # Examples +// // Example 1: Compute Timestamp from POSIX `time()`. // // Timestamp timestamp; @@ -90,16 +91,37 @@ option objc_class_prefix = "GPB"; // // Example 5: Compute Timestamp from current time in Python. // -// now = time.time() -// seconds = int(now) -// nanos = int((now - seconds) * 10**9) -// timestamp = Timestamp(seconds=seconds, nanos=nanos) +// timestamp = Timestamp() +// timestamp.GetCurrentTime() +// +// # JSON Mapping +// +// In JSON format, the Timestamp type is encoded as a string in the +// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the +// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" +// where {year} is always expressed using four digits while {month}, {day}, +// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional +// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), +// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone +// is required, though only UTC (as indicated by "Z") is presently supported. +// +// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past +// 01:30 UTC on January 15, 2017. +// +// In JavaScript, one can convert a Date object to this format using the +// standard [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString] +// method. In Python, a standard `datetime.datetime` object can be converted +// to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) +// with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one +// can use the Joda Time's [`ISODateTimeFormat.dateTime()`]( +// http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()) +// to obtain a formatter capable of generating timestamps in this format. // // message Timestamp { // Represents seconds of UTC time since Unix epoch - // 1970-01-01T00:00:00Z. Must be from from 0001-01-01T00:00:00Z to + // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to // 9999-12-31T23:59:59Z inclusive. int64 seconds = 1;