diff options
Diffstat (limited to 'vendor/github.com/containerd/go-runc/runc.go')
-rw-r--r-- | vendor/github.com/containerd/go-runc/runc.go | 707 |
1 files changed, 0 insertions, 707 deletions
diff --git a/vendor/github.com/containerd/go-runc/runc.go b/vendor/github.com/containerd/go-runc/runc.go deleted file mode 100644 index 96262afab..000000000 --- a/vendor/github.com/containerd/go-runc/runc.go +++ /dev/null @@ -1,707 +0,0 @@ -/* - Copyright The containerd Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package runc - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "strconv" - "strings" - "syscall" - "time" - - specs "github.com/opencontainers/runtime-spec/specs-go" -) - -// Format is the type of log formatting options avaliable -type Format string - -// TopBody represents the structured data of the full ps output -type TopResults struct { - // Processes running in the container, where each is process is an array of values corresponding to the headers - Processes [][]string `json:"Processes"` - - // Headers are the names of the columns - Headers []string `json:"Headers"` -} - -const ( - none Format = "" - JSON Format = "json" - Text Format = "text" - // DefaultCommand is the default command for Runc - DefaultCommand = "runc" -) - -// Runc is the client to the runc cli -type Runc struct { - //If command is empty, DefaultCommand is used - Command string - Root string - Debug bool - Log string - LogFormat Format - PdeathSignal syscall.Signal - Setpgid bool - Criu string - SystemdCgroup bool - Rootless *bool // nil stands for "auto" -} - -// List returns all containers created inside the provided runc root directory -func (r *Runc) List(context context.Context) ([]*Container, error) { - data, err := cmdOutput(r.command(context, "list", "--format=json"), false) - if err != nil { - return nil, err - } - var out []*Container - if err := json.Unmarshal(data, &out); err != nil { - return nil, err - } - return out, nil -} - -// State returns the state for the container provided by id -func (r *Runc) State(context context.Context, id string) (*Container, error) { - data, err := cmdOutput(r.command(context, "state", id), true) - if err != nil { - return nil, fmt.Errorf("%s: %s", err, data) - } - var c Container - if err := json.Unmarshal(data, &c); err != nil { - return nil, err - } - return &c, nil -} - -type ConsoleSocket interface { - Path() string -} - -type CreateOpts struct { - IO - // PidFile is a path to where a pid file should be created - PidFile string - ConsoleSocket ConsoleSocket - Detach bool - NoPivot bool - NoNewKeyring bool - ExtraFiles []*os.File -} - -func (o *CreateOpts) args() (out []string, err error) { - if o.PidFile != "" { - abs, err := filepath.Abs(o.PidFile) - if err != nil { - return nil, err - } - out = append(out, "--pid-file", abs) - } - if o.ConsoleSocket != nil { - out = append(out, "--console-socket", o.ConsoleSocket.Path()) - } - if o.NoPivot { - out = append(out, "--no-pivot") - } - if o.NoNewKeyring { - out = append(out, "--no-new-keyring") - } - if o.Detach { - out = append(out, "--detach") - } - if o.ExtraFiles != nil { - out = append(out, "--preserve-fds", strconv.Itoa(len(o.ExtraFiles))) - } - return out, nil -} - -// Create creates a new container and returns its pid if it was created successfully -func (r *Runc) Create(context context.Context, id, bundle string, opts *CreateOpts) error { - args := []string{"create", "--bundle", bundle} - if opts != nil { - oargs, err := opts.args() - if err != nil { - return err - } - args = append(args, oargs...) - } - cmd := r.command(context, append(args, id)...) - if opts != nil && opts.IO != nil { - opts.Set(cmd) - } - cmd.ExtraFiles = opts.ExtraFiles - - if cmd.Stdout == nil && cmd.Stderr == nil { - data, err := cmdOutput(cmd, true) - if err != nil { - return fmt.Errorf("%s: %s", err, data) - } - return nil - } - ec, err := Monitor.Start(cmd) - if err != nil { - return err - } - if opts != nil && opts.IO != nil { - if c, ok := opts.IO.(StartCloser); ok { - if err := c.CloseAfterStart(); err != nil { - return err - } - } - } - status, err := Monitor.Wait(cmd, ec) - if err == nil && status != 0 { - err = fmt.Errorf("%s did not terminate sucessfully", cmd.Args[0]) - } - return err -} - -// Start will start an already created container -func (r *Runc) Start(context context.Context, id string) error { - return r.runOrError(r.command(context, "start", id)) -} - -type ExecOpts struct { - IO - PidFile string - ConsoleSocket ConsoleSocket - Detach bool -} - -func (o *ExecOpts) args() (out []string, err error) { - if o.ConsoleSocket != nil { - out = append(out, "--console-socket", o.ConsoleSocket.Path()) - } - if o.Detach { - out = append(out, "--detach") - } - if o.PidFile != "" { - abs, err := filepath.Abs(o.PidFile) - if err != nil { - return nil, err - } - out = append(out, "--pid-file", abs) - } - return out, nil -} - -// Exec executres and additional process inside the container based on a full -// OCI Process specification -func (r *Runc) Exec(context context.Context, id string, spec specs.Process, opts *ExecOpts) error { - f, err := ioutil.TempFile(os.Getenv("XDG_RUNTIME_DIR"), "runc-process") - if err != nil { - return err - } - defer os.Remove(f.Name()) - err = json.NewEncoder(f).Encode(spec) - f.Close() - if err != nil { - return err - } - args := []string{"exec", "--process", f.Name()} - if opts != nil { - oargs, err := opts.args() - if err != nil { - return err - } - args = append(args, oargs...) - } - cmd := r.command(context, append(args, id)...) - if opts != nil && opts.IO != nil { - opts.Set(cmd) - } - if cmd.Stdout == nil && cmd.Stderr == nil { - data, err := cmdOutput(cmd, true) - if err != nil { - return fmt.Errorf("%s: %s", err, data) - } - return nil - } - ec, err := Monitor.Start(cmd) - if err != nil { - return err - } - if opts != nil && opts.IO != nil { - if c, ok := opts.IO.(StartCloser); ok { - if err := c.CloseAfterStart(); err != nil { - return err - } - } - } - status, err := Monitor.Wait(cmd, ec) - if err == nil && status != 0 { - err = fmt.Errorf("%s did not terminate sucessfully", cmd.Args[0]) - } - return err -} - -// Run runs the create, start, delete lifecycle of the container -// and returns its exit status after it has exited -func (r *Runc) Run(context context.Context, id, bundle string, opts *CreateOpts) (int, error) { - args := []string{"run", "--bundle", bundle} - if opts != nil { - oargs, err := opts.args() - if err != nil { - return -1, err - } - args = append(args, oargs...) - } - cmd := r.command(context, append(args, id)...) - if opts != nil && opts.IO != nil { - opts.Set(cmd) - } - ec, err := Monitor.Start(cmd) - if err != nil { - return -1, err - } - return Monitor.Wait(cmd, ec) -} - -type DeleteOpts struct { - Force bool -} - -func (o *DeleteOpts) args() (out []string) { - if o.Force { - out = append(out, "--force") - } - return out -} - -// Delete deletes the container -func (r *Runc) Delete(context context.Context, id string, opts *DeleteOpts) error { - args := []string{"delete"} - if opts != nil { - args = append(args, opts.args()...) - } - return r.runOrError(r.command(context, append(args, id)...)) -} - -// KillOpts specifies options for killing a container and its processes -type KillOpts struct { - All bool -} - -func (o *KillOpts) args() (out []string) { - if o.All { - out = append(out, "--all") - } - return out -} - -// Kill sends the specified signal to the container -func (r *Runc) Kill(context context.Context, id string, sig int, opts *KillOpts) error { - args := []string{ - "kill", - } - if opts != nil { - args = append(args, opts.args()...) - } - return r.runOrError(r.command(context, append(args, id, strconv.Itoa(sig))...)) -} - -// Stats return the stats for a container like cpu, memory, and io -func (r *Runc) Stats(context context.Context, id string) (*Stats, error) { - cmd := r.command(context, "events", "--stats", id) - rd, err := cmd.StdoutPipe() - if err != nil { - return nil, err - } - ec, err := Monitor.Start(cmd) - if err != nil { - return nil, err - } - defer func() { - rd.Close() - Monitor.Wait(cmd, ec) - }() - var e Event - if err := json.NewDecoder(rd).Decode(&e); err != nil { - return nil, err - } - return e.Stats, nil -} - -// Events returns an event stream from runc for a container with stats and OOM notifications -func (r *Runc) Events(context context.Context, id string, interval time.Duration) (chan *Event, error) { - cmd := r.command(context, "events", fmt.Sprintf("--interval=%ds", int(interval.Seconds())), id) - rd, err := cmd.StdoutPipe() - if err != nil { - return nil, err - } - ec, err := Monitor.Start(cmd) - if err != nil { - rd.Close() - return nil, err - } - var ( - dec = json.NewDecoder(rd) - c = make(chan *Event, 128) - ) - go func() { - defer func() { - close(c) - rd.Close() - Monitor.Wait(cmd, ec) - }() - for { - var e Event - if err := dec.Decode(&e); err != nil { - if err == io.EOF { - return - } - e = Event{ - Type: "error", - Err: err, - } - } - c <- &e - } - }() - return c, nil -} - -// Pause the container with the provided id -func (r *Runc) Pause(context context.Context, id string) error { - return r.runOrError(r.command(context, "pause", id)) -} - -// Resume the container with the provided id -func (r *Runc) Resume(context context.Context, id string) error { - return r.runOrError(r.command(context, "resume", id)) -} - -// Ps lists all the processes inside the container returning their pids -func (r *Runc) Ps(context context.Context, id string) ([]int, error) { - data, err := cmdOutput(r.command(context, "ps", "--format", "json", id), true) - if err != nil { - return nil, fmt.Errorf("%s: %s", err, data) - } - var pids []int - if err := json.Unmarshal(data, &pids); err != nil { - return nil, err - } - return pids, nil -} - -// Top lists all the processes inside the container returning the full ps data -func (r *Runc) Top(context context.Context, id string, psOptions string) (*TopResults, error) { - data, err := cmdOutput(r.command(context, "ps", "--format", "table", id, psOptions), true) - if err != nil { - return nil, fmt.Errorf("%s: %s", err, data) - } - - topResults, err := ParsePSOutput(data) - if err != nil { - return nil, fmt.Errorf("%s: ", err) - } - return topResults, nil -} - -type CheckpointOpts struct { - // ImagePath is the path for saving the criu image file - ImagePath string - // WorkDir is the working directory for criu - WorkDir string - // ParentPath is the path for previous image files from a pre-dump - ParentPath string - // AllowOpenTCP allows open tcp connections to be checkpointed - AllowOpenTCP bool - // AllowExternalUnixSockets allows external unix sockets to be checkpointed - AllowExternalUnixSockets bool - // AllowTerminal allows the terminal(pty) to be checkpointed with a container - AllowTerminal bool - // CriuPageServer is the address:port for the criu page server - CriuPageServer string - // FileLocks handle file locks held by the container - FileLocks bool - // Cgroups is the cgroup mode for how to handle the checkpoint of a container's cgroups - Cgroups CgroupMode - // EmptyNamespaces creates a namespace for the container but does not save its properties - // Provide the namespaces you wish to be checkpointed without their settings on restore - EmptyNamespaces []string -} - -type CgroupMode string - -const ( - Soft CgroupMode = "soft" - Full CgroupMode = "full" - Strict CgroupMode = "strict" -) - -func (o *CheckpointOpts) args() (out []string) { - if o.ImagePath != "" { - out = append(out, "--image-path", o.ImagePath) - } - if o.WorkDir != "" { - out = append(out, "--work-path", o.WorkDir) - } - if o.ParentPath != "" { - out = append(out, "--parent-path", o.ParentPath) - } - if o.AllowOpenTCP { - out = append(out, "--tcp-established") - } - if o.AllowExternalUnixSockets { - out = append(out, "--ext-unix-sk") - } - if o.AllowTerminal { - out = append(out, "--shell-job") - } - if o.CriuPageServer != "" { - out = append(out, "--page-server", o.CriuPageServer) - } - if o.FileLocks { - out = append(out, "--file-locks") - } - if string(o.Cgroups) != "" { - out = append(out, "--manage-cgroups-mode", string(o.Cgroups)) - } - for _, ns := range o.EmptyNamespaces { - out = append(out, "--empty-ns", ns) - } - return out -} - -type CheckpointAction func([]string) []string - -// LeaveRunning keeps the container running after the checkpoint has been completed -func LeaveRunning(args []string) []string { - return append(args, "--leave-running") -} - -// PreDump allows a pre-dump of the checkpoint to be made and completed later -func PreDump(args []string) []string { - return append(args, "--pre-dump") -} - -// Checkpoint allows you to checkpoint a container using criu -func (r *Runc) Checkpoint(context context.Context, id string, opts *CheckpointOpts, actions ...CheckpointAction) error { - args := []string{"checkpoint"} - if opts != nil { - args = append(args, opts.args()...) - } - for _, a := range actions { - args = a(args) - } - return r.runOrError(r.command(context, append(args, id)...)) -} - -type RestoreOpts struct { - CheckpointOpts - IO - - Detach bool - PidFile string - NoSubreaper bool - NoPivot bool - ConsoleSocket ConsoleSocket -} - -func (o *RestoreOpts) args() ([]string, error) { - out := o.CheckpointOpts.args() - if o.Detach { - out = append(out, "--detach") - } - if o.PidFile != "" { - abs, err := filepath.Abs(o.PidFile) - if err != nil { - return nil, err - } - out = append(out, "--pid-file", abs) - } - if o.ConsoleSocket != nil { - out = append(out, "--console-socket", o.ConsoleSocket.Path()) - } - if o.NoPivot { - out = append(out, "--no-pivot") - } - if o.NoSubreaper { - out = append(out, "-no-subreaper") - } - return out, nil -} - -// Restore restores a container with the provide id from an existing checkpoint -func (r *Runc) Restore(context context.Context, id, bundle string, opts *RestoreOpts) (int, error) { - args := []string{"restore"} - if opts != nil { - oargs, err := opts.args() - if err != nil { - return -1, err - } - args = append(args, oargs...) - } - args = append(args, "--bundle", bundle) - cmd := r.command(context, append(args, id)...) - if opts != nil && opts.IO != nil { - opts.Set(cmd) - } - ec, err := Monitor.Start(cmd) - if err != nil { - return -1, err - } - if opts != nil && opts.IO != nil { - if c, ok := opts.IO.(StartCloser); ok { - if err := c.CloseAfterStart(); err != nil { - return -1, err - } - } - } - return Monitor.Wait(cmd, ec) -} - -// Update updates the current container with the provided resource spec -func (r *Runc) Update(context context.Context, id string, resources *specs.LinuxResources) error { - buf := getBuf() - defer putBuf(buf) - - if err := json.NewEncoder(buf).Encode(resources); err != nil { - return err - } - args := []string{"update", "--resources", "-", id} - cmd := r.command(context, args...) - cmd.Stdin = buf - return r.runOrError(cmd) -} - -var ErrParseRuncVersion = errors.New("unable to parse runc version") - -type Version struct { - Runc string - Commit string - Spec string -} - -// Version returns the runc and runtime-spec versions -func (r *Runc) Version(context context.Context) (Version, error) { - data, err := cmdOutput(r.command(context, "--version"), false) - if err != nil { - return Version{}, err - } - return parseVersion(data) -} - -func parseVersion(data []byte) (Version, error) { - var v Version - parts := strings.Split(strings.TrimSpace(string(data)), "\n") - if len(parts) != 3 { - return v, nil - } - for i, p := range []struct { - dest *string - split string - }{ - { - dest: &v.Runc, - split: "version ", - }, - { - dest: &v.Commit, - split: ": ", - }, - { - dest: &v.Spec, - split: ": ", - }, - } { - p2 := strings.Split(parts[i], p.split) - if len(p2) != 2 { - return v, fmt.Errorf("unable to parse version line %q", parts[i]) - } - *p.dest = p2[1] - } - return v, nil -} - -func (r *Runc) args() (out []string) { - if r.Root != "" { - out = append(out, "--root", r.Root) - } - if r.Debug { - out = append(out, "--debug") - } - if r.Log != "" { - out = append(out, "--log", r.Log) - } - if r.LogFormat != none { - out = append(out, "--log-format", string(r.LogFormat)) - } - if r.Criu != "" { - out = append(out, "--criu", r.Criu) - } - if r.SystemdCgroup { - out = append(out, "--systemd-cgroup") - } - if r.Rootless != nil { - // nil stands for "auto" (differs from explicit "false") - out = append(out, "--rootless="+strconv.FormatBool(*r.Rootless)) - } - return out -} - -// runOrError will run the provided command. If an error is -// encountered and neither Stdout or Stderr was set the error and the -// stderr of the command will be returned in the format of <error>: -// <stderr> -func (r *Runc) runOrError(cmd *exec.Cmd) error { - if cmd.Stdout != nil || cmd.Stderr != nil { - ec, err := Monitor.Start(cmd) - if err != nil { - return err - } - status, err := Monitor.Wait(cmd, ec) - if err == nil && status != 0 { - err = fmt.Errorf("%s did not terminate sucessfully", cmd.Args[0]) - } - return err - } - data, err := cmdOutput(cmd, true) - if err != nil { - return fmt.Errorf("%s: %s", err, data) - } - return nil -} - -func cmdOutput(cmd *exec.Cmd, combined bool) ([]byte, error) { - b := getBuf() - defer putBuf(b) - - cmd.Stdout = b - if combined { - cmd.Stderr = b - } - ec, err := Monitor.Start(cmd) - if err != nil { - return nil, err - } - - status, err := Monitor.Wait(cmd, ec) - if err == nil && status != 0 { - err = fmt.Errorf("%s did not terminate sucessfully", cmd.Args[0]) - } - - return b.Bytes(), err -} |