diff options
Diffstat (limited to 'pkg/adapter/containers_remote.go')
-rw-r--r-- | pkg/adapter/containers_remote.go | 328 |
1 files changed, 227 insertions, 101 deletions
diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go index c34495b3d..5a26f537f 100644 --- a/pkg/adapter/containers_remote.go +++ b/pkg/adapter/containers_remote.go @@ -3,6 +3,7 @@ package adapter import ( + "bufio" "context" "encoding/json" "fmt" @@ -14,9 +15,11 @@ import ( "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/shared" + "github.com/containers/libpod/cmd/podman/shared/parse" iopodman "github.com/containers/libpod/cmd/podman/varlink" "github.com/containers/libpod/libpod" - "github.com/containers/libpod/pkg/inspect" + "github.com/containers/libpod/libpod/define" + "github.com/containers/libpod/libpod/logs" "github.com/containers/libpod/pkg/varlinkapi/virtwriter" "github.com/cri-o/ocicni/pkg/ocicni" "github.com/docker/docker/pkg/term" @@ -29,12 +32,12 @@ import ( ) // Inspect returns an inspect struct from varlink -func (c *Container) Inspect(size bool) (*inspect.ContainerInspectData, error) { +func (c *Container) Inspect(size bool) (*libpod.InspectContainerData, error) { reply, err := iopodman.ContainerInspectData().Call(c.Runtime.Conn, c.ID(), size) if err != nil { return nil, err } - data := inspect.ContainerInspectData{} + data := libpod.InspectContainerData{} if err := json.Unmarshal([]byte(reply), &data); err != nil { return nil, err } @@ -240,11 +243,11 @@ func (r *LocalRuntime) StopContainers(ctx context.Context, cli *cliconfig.StopVa for _, id := range ids { if _, err := iopodman.StopContainer().Call(r.Conn, id, int64(cli.Timeout)); err != nil { transError := TranslateError(err) - if errors.Cause(transError) == libpod.ErrCtrStopped { + if errors.Cause(transError) == define.ErrCtrStopped { ok = append(ok, id) continue } - if errors.Cause(transError) == libpod.ErrCtrStateInvalid && cli.All { + if errors.Cause(transError) == define.ErrCtrStateInvalid && cli.All { ok = append(ok, id) continue } @@ -411,8 +414,8 @@ func BatchContainerOp(ctr *Container, opts shared.PsOptions) (shared.BatchContai return bcs, nil } -// Logs one or more containers over a varlink connection -func (r *LocalRuntime) Log(c *cliconfig.LogsValues, options *libpod.LogOptions) error { +// Log one or more containers over a varlink connection +func (r *LocalRuntime) Log(c *cliconfig.LogsValues, options *logs.LogOptions) error { // GetContainersLogs reply, err := iopodman.GetContainersLogs().Send(r.Conn, uint64(varlink.More), c.InputArgs, c.Follow, c.Latest, options.Since.Format(time.RFC3339Nano), int64(c.Tail), c.Timestamps) if err != nil { @@ -434,7 +437,7 @@ func (r *LocalRuntime) Log(c *cliconfig.LogsValues, options *libpod.LogOptions) if err != nil { return errors.Wrapf(err, "unable to parse time of log %s", log.Time) } - logLine := libpod.LogLine{ + logLine := logs.LogLine{ Device: log.Device, ParseLogType: log.ParseLogType, Time: lTime, @@ -477,7 +480,7 @@ func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode } func ReadExitFile(runtimeTmp, ctrID string) (int, error) { - return 0, libpod.ErrNotImplemented + return 0, define.ErrNotImplemented } // Ps lists containers based on criteria from user @@ -492,6 +495,7 @@ func (r *LocalRuntime) Ps(c *cliconfig.PsValues, opts shared.PsOptions) ([]share NoTrunc: &c.NoTrunct, Pod: &c.Pod, Quiet: &c.Quiet, + Size: &c.Size, Sort: &c.Sort, Sync: &c.Sync, } @@ -516,7 +520,7 @@ func (r *LocalRuntime) Ps(c *cliconfig.PsValues, opts shared.PsOptions) ([]share RootFsSize: ctr.RootFsSize, RwSize: ctr.RwSize, } - state, err := libpod.StringToContainerStatus(ctr.State) + state, err := define.StringToContainerStatus(ctr.State) if err != nil { return nil, err } @@ -552,92 +556,13 @@ func (r *LocalRuntime) Ps(c *cliconfig.PsValues, opts shared.PsOptions) ([]share return psContainers, nil } -func (r *LocalRuntime) attach(ctx context.Context, stdin, stdout *os.File, cid string, start bool, detachKeys string) (chan error, error) { - var ( - oldTermState *term.State - ) - errChan := make(chan error) - spec, err := r.Spec(cid) - if err != nil { - return nil, err - } - resize := make(chan remotecommand.TerminalSize, 5) - haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd())) - - // Check if we are attached to a terminal. If we are, generate resize - // events, and set the terminal to raw mode - if haveTerminal && spec.Process.Terminal { - logrus.Debugf("Handling terminal attach") - - subCtx, cancel := context.WithCancel(ctx) - defer cancel() - - resizeTty(subCtx, resize) - oldTermState, err = term.SaveState(os.Stdin.Fd()) - if err != nil { - return nil, errors.Wrapf(err, "unable to save terminal state") - } - - logrus.SetFormatter(&RawTtyFormatter{}) - term.SetRawTerminal(os.Stdin.Fd()) - - } - // TODO add detach keys support - _, err = iopodman.Attach().Send(r.Conn, varlink.Upgrade, cid, detachKeys, start) - if err != nil { - restoreTerminal(oldTermState) - return nil, err - } - - // These are the varlink sockets - reader := r.Conn.Reader - writer := r.Conn.Writer - - // These are the special writers that encode input from the client. - varlinkStdinWriter := virtwriter.NewVirtWriteCloser(writer, virtwriter.ToStdin) - varlinkResizeWriter := virtwriter.NewVirtWriteCloser(writer, virtwriter.TerminalResize) - - go func() { - // Read from the wire and direct to stdout or stderr - err := virtwriter.Reader(reader, stdout, os.Stderr, nil, nil) - defer restoreTerminal(oldTermState) - errChan <- err - }() - - go func() { - for termResize := range resize { - b, err := json.Marshal(termResize) - if err != nil { - defer restoreTerminal(oldTermState) - errChan <- err - } - _, err = varlinkResizeWriter.Write(b) - if err != nil { - defer restoreTerminal(oldTermState) - errChan <- err - } - } - }() - - // Takes stdinput and sends it over the wire after being encoded - go func() { - if _, err := io.Copy(varlinkStdinWriter, stdin); err != nil { - defer restoreTerminal(oldTermState) - errChan <- err - } - - }() - return errChan, nil - -} - // Attach to a remote terminal func (r *LocalRuntime) Attach(ctx context.Context, c *cliconfig.AttachValues) error { ctr, err := r.LookupContainer(c.InputArgs[0]) if err != nil { return nil } - if ctr.state.State != libpod.ContainerStateRunning { + if ctr.state.State != define.ContainerStateRunning { return errors.New("you can only attach to running containers") } inputStream := os.Stdin @@ -655,7 +580,14 @@ func (r *LocalRuntime) Attach(ctx context.Context, c *cliconfig.AttachValues) er } // Checkpoint one or more containers -func (r *LocalRuntime) Checkpoint(c *cliconfig.CheckpointValues, options libpod.ContainerCheckpointOptions) error { +func (r *LocalRuntime) Checkpoint(c *cliconfig.CheckpointValues) error { + if c.Export != "" { + return errors.New("the remote client does not support exporting checkpoints") + } + if c.IgnoreRootfs { + return errors.New("the remote client does not support --ignore-rootfs") + } + var lastError error ids, err := iopodman.GetContainersByContext().Call(r.Conn, c.All, c.Latest, c.InputArgs) if err != nil { @@ -670,7 +602,7 @@ func (r *LocalRuntime) Checkpoint(c *cliconfig.CheckpointValues, options libpod. if err != nil { return err } - if ctr.state.State == libpod.ContainerStateRunning { + if ctr.state.State == define.ContainerStateRunning { runningIds = append(runningIds, id) } } @@ -678,7 +610,7 @@ func (r *LocalRuntime) Checkpoint(c *cliconfig.CheckpointValues, options libpod. } for _, id := range ids { - if _, err := iopodman.ContainerCheckpoint().Call(r.Conn, id, options.Keep, options.KeepRunning, options.TCPEstablished); err != nil { + if _, err := iopodman.ContainerCheckpoint().Call(r.Conn, id, c.Keep, c.Keep, c.TcpEstablished); err != nil { if lastError != nil { fmt.Fprintln(os.Stderr, lastError) } @@ -691,7 +623,14 @@ func (r *LocalRuntime) Checkpoint(c *cliconfig.CheckpointValues, options libpod. } // Restore one or more containers -func (r *LocalRuntime) Restore(c *cliconfig.RestoreValues, options libpod.ContainerCheckpointOptions) error { +func (r *LocalRuntime) Restore(ctx context.Context, c *cliconfig.RestoreValues) error { + if c.Import != "" { + return errors.New("the remote client does not support importing checkpoints") + } + if c.IgnoreRootfs { + return errors.New("the remote client does not support --ignore-rootfs") + } + var lastError error ids, err := iopodman.GetContainersByContext().Call(r.Conn, c.All, c.Latest, c.InputArgs) if err != nil { @@ -706,7 +645,7 @@ func (r *LocalRuntime) Restore(c *cliconfig.RestoreValues, options libpod.Contai if err != nil { return err } - if ctr.state.State != libpod.ContainerStateRunning { + if ctr.state.State != define.ContainerStateRunning { exitedIDs = append(exitedIDs, id) } } @@ -714,7 +653,7 @@ func (r *LocalRuntime) Restore(c *cliconfig.RestoreValues, options libpod.Contai } for _, id := range ids { - if _, err := iopodman.ContainerRestore().Call(r.Conn, id, options.Keep, options.TCPEstablished); err != nil { + if _, err := iopodman.ContainerRestore().Call(r.Conn, id, c.Keep, c.TcpEstablished); err != nil { if lastError != nil { fmt.Fprintln(os.Stderr, lastError) } @@ -771,6 +710,49 @@ func (r *LocalRuntime) Start(ctx context.Context, c *cliconfig.StartValues, sigP return exitCode, finalErr } +func (r *LocalRuntime) attach(ctx context.Context, stdin, stdout *os.File, cid string, start bool, detachKeys string) (chan error, error) { + var ( + oldTermState *term.State + ) + spec, err := r.Spec(cid) + if err != nil { + return nil, err + } + resize := make(chan remotecommand.TerminalSize, 5) + haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd())) + + // Check if we are attached to a terminal. If we are, generate resize + // events, and set the terminal to raw mode + if haveTerminal && spec.Process.Terminal { + cancel, oldTermState, err := handleTerminalAttach(ctx, resize) + if err != nil { + return nil, err + } + defer cancel() + defer restoreTerminal(oldTermState) + + logrus.SetFormatter(&RawTtyFormatter{}) + term.SetRawTerminal(os.Stdin.Fd()) + } + + reply, err := iopodman.Attach().Send(r.Conn, varlink.Upgrade, cid, detachKeys, start) + if err != nil { + restoreTerminal(oldTermState) + return nil, err + } + + // See if the server accepts the upgraded connection or returns an error + _, err = reply() + + if err != nil { + restoreTerminal(oldTermState) + return nil, err + } + + errChan := configureVarlinkAttachStdio(r.Conn.Reader, r.Conn.Writer, stdin, stdout, oldTermState, resize, nil) + return errChan, nil +} + // PauseContainers pauses container(s) based on CLI inputs. func (r *LocalRuntime) PauseContainers(ctx context.Context, cli *cliconfig.PauseValues) ([]string, map[string]error, error) { var ( @@ -781,7 +763,7 @@ func (r *LocalRuntime) PauseContainers(ctx context.Context, cli *cliconfig.Pause ) if cli.All { - filters := []string{libpod.ContainerStateRunning.String()} + filters := []string{define.ContainerStateRunning.String()} ctrs, err = r.LookupContainersWithStatus(filters) } else { ctrs, err = r.LookupContainers(cli.InputArgs) @@ -818,7 +800,7 @@ func (r *LocalRuntime) UnpauseContainers(ctx context.Context, cli *cliconfig.Unp logrus.Debugf("Setting maximum rm workers to %d", maxWorkers) if cli.All { - filters := []string{libpod.ContainerStatePaused.String()} + filters := []string{define.ContainerStatePaused.String()} ctrs, err = r.LookupContainersWithStatus(filters) } else { ctrs, err = r.LookupContainers(cli.InputArgs) @@ -857,7 +839,7 @@ func (r *LocalRuntime) Restart(ctx context.Context, c *cliconfig.RestartValues) } restartContainers = append(restartContainers, lastCtr) } else if c.Running { - containers, err = r.LookupContainersWithStatus([]string{libpod.ContainerStateRunning.String()}) + containers, err = r.LookupContainersWithStatus([]string{define.ContainerStateRunning.String()}) if err != nil { return nil, nil, err } @@ -925,7 +907,7 @@ func (r *LocalRuntime) Prune(ctx context.Context, maxWorkers int, force bool) ([ ) logrus.Debugf("Setting maximum rm workers to %d", maxWorkers) - filters := []string{libpod.ContainerStateExited.String()} + filters := []string{define.ContainerStateExited.String()} ctrs, err = r.LookupContainersWithStatus(filters) if err != nil { return ok, failures, err @@ -958,7 +940,7 @@ func (r *LocalRuntime) Port(c *cliconfig.PortValues) ([]*Container, error) { containers, err = r.GetContainersByContext(false, c.Latest, c.InputArgs) } else { // we need to only use running containers if all - filters := []string{libpod.ContainerStateRunning.String()} + filters := []string{define.ContainerStateRunning.String()} containers, err = r.LookupContainersWithStatus(filters) } if err != nil { @@ -986,3 +968,147 @@ func (r *LocalRuntime) GetNamespaces(container shared.PsContainerOutput) *shared } return &ns } + +// Commit creates a local image from a container +func (r *LocalRuntime) Commit(ctx context.Context, c *cliconfig.CommitValues, container, imageName string) (string, error) { + var iid string + reply, err := iopodman.Commit().Send(r.Conn, varlink.More, container, imageName, c.Change, c.Author, c.Message, c.Pause, c.Format) + if err != nil { + return "", err + } + for { + responses, flags, err := reply() + if err != nil { + return "", err + } + for _, line := range responses.Logs { + fmt.Fprintln(os.Stderr, line) + } + iid = responses.Id + if flags&varlink.Continues == 0 { + break + } + } + return iid, nil +} + +// ExecContainer executes a command in the container +func (r *LocalRuntime) ExecContainer(ctx context.Context, cli *cliconfig.ExecValues) (int, error) { + var ( + oldTermState *term.State + ec int = define.ExecErrorCodeGeneric + ) + // default invalid command exit code + // Validate given environment variables + env := map[string]string{} + if err := parse.ReadKVStrings(env, []string{}, cli.Env); err != nil { + return -1, errors.Wrapf(err, "Exec unable to process environment variables") + } + + // Build env slice of key=value strings for Exec + envs := []string{} + for k, v := range env { + envs = append(envs, fmt.Sprintf("%s=%s", k, v)) + } + + resize := make(chan remotecommand.TerminalSize, 5) + haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd())) + + // Check if we are attached to a terminal. If we are, generate resize + // events, and set the terminal to raw mode + if haveTerminal && cli.Tty { + cancel, oldTermState, err := handleTerminalAttach(ctx, resize) + if err != nil { + return ec, err + } + defer cancel() + defer restoreTerminal(oldTermState) + + logrus.SetFormatter(&RawTtyFormatter{}) + term.SetRawTerminal(os.Stdin.Fd()) + } + + opts := iopodman.ExecOpts{ + Name: cli.InputArgs[0], + Tty: cli.Tty, + Privileged: cli.Privileged, + Cmd: cli.InputArgs[1:], + User: &cli.User, + Workdir: &cli.Workdir, + Env: &envs, + DetachKeys: &cli.DetachKeys, + } + + inputStream := os.Stdin + if !cli.Interactive { + inputStream = nil + } + + reply, err := iopodman.ExecContainer().Send(r.Conn, varlink.Upgrade, opts) + if err != nil { + return ec, errors.Wrapf(err, "Exec failed to contact service for %s", cli.InputArgs) + } + + _, err = reply() + if err != nil { + return ec, errors.Wrapf(err, "Exec operation failed for %s", cli.InputArgs) + } + ecChan := make(chan int, 1) + errChan := configureVarlinkAttachStdio(r.Conn.Reader, r.Conn.Writer, inputStream, os.Stdout, oldTermState, resize, ecChan) + + ec = <-ecChan + err = <-errChan + + return ec, err +} + +func configureVarlinkAttachStdio(reader *bufio.Reader, writer *bufio.Writer, stdin *os.File, stdout *os.File, oldTermState *term.State, resize chan remotecommand.TerminalSize, ecChan chan int) chan error { + errChan := make(chan error, 1) + // These are the special writers that encode input from the client. + varlinkStdinWriter := virtwriter.NewVirtWriteCloser(writer, virtwriter.ToStdin) + varlinkResizeWriter := virtwriter.NewVirtWriteCloser(writer, virtwriter.TerminalResize) + + go func() { + // Read from the wire and direct to stdout or stderr + err := virtwriter.Reader(reader, stdout, os.Stderr, nil, nil, ecChan) + defer restoreTerminal(oldTermState) + sendGenericError(ecChan) + errChan <- err + }() + + go func() { + for termResize := range resize { + b, err := json.Marshal(termResize) + if err != nil { + defer restoreTerminal(oldTermState) + sendGenericError(ecChan) + errChan <- err + } + _, err = varlinkResizeWriter.Write(b) + if err != nil { + defer restoreTerminal(oldTermState) + sendGenericError(ecChan) + errChan <- err + } + } + }() + + if stdin != nil { + // Takes stdinput and sends it over the wire after being encoded + go func() { + if _, err := io.Copy(varlinkStdinWriter, stdin); err != nil { + defer restoreTerminal(oldTermState) + sendGenericError(ecChan) + errChan <- err + } + + }() + } + return errChan +} + +func sendGenericError(ecChan chan int) { + if ecChan != nil { + ecChan <- define.ExecErrorCodeGeneric + } +} |