diff options
Diffstat (limited to 'pkg/adapter')
-rw-r--r-- | pkg/adapter/containers.go | 156 | ||||
-rw-r--r-- | pkg/adapter/containers_remote.go | 205 | ||||
-rw-r--r-- | pkg/adapter/pods.go | 60 | ||||
-rw-r--r-- | pkg/adapter/pods_remote.go | 108 | ||||
-rw-r--r-- | pkg/adapter/runtime.go | 109 | ||||
-rw-r--r-- | pkg/adapter/runtime_remote.go | 160 | ||||
-rw-r--r-- | pkg/adapter/shortcuts/shortcuts.go | 27 |
7 files changed, 735 insertions, 90 deletions
diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go new file mode 100644 index 000000000..932d209cd --- /dev/null +++ b/pkg/adapter/containers.go @@ -0,0 +1,156 @@ +// +build !remoteclient + +package adapter + +import ( + "context" + "fmt" + "strconv" + "sync" + "syscall" + "time" + + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/adapter/shortcuts" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// GetLatestContainer gets the latest Container and wraps it in an adapter Container +func (r *LocalRuntime) GetLatestContainer() (*Container, error) { + Container := Container{} + c, err := r.Runtime.GetLatestContainer() + Container.Container = c + return &Container, err +} + +// GetAllContainers gets all Containers and wraps each one in an adapter Container +func (r *LocalRuntime) GetAllContainers() ([]*Container, error) { + var containers []*Container + allContainers, err := r.Runtime.GetAllContainers() + if err != nil { + return nil, err + } + + for _, c := range allContainers { + containers = append(containers, &Container{c}) + } + return containers, nil +} + +// LookupContainer gets a Container by name or id and wraps it in an adapter Container +func (r *LocalRuntime) LookupContainer(idOrName string) (*Container, error) { + ctr, err := r.Runtime.LookupContainer(idOrName) + if err != nil { + return nil, err + } + return &Container{ctr}, nil +} + +// StopContainers stops container(s) based on CLI inputs. +// Returns list of successful id(s), map of failed id(s) + error, or error not from container +func (r *LocalRuntime) StopContainers(ctx context.Context, cli *cliconfig.StopValues) ([]string, map[string]error, error) { + var timeout *uint + if cli.Flags().Changed("timeout") || cli.Flags().Changed("time") { + t := uint(cli.Timeout) + timeout = &t + } + + var ( + ok = []string{} + failures = map[string]error{} + ) + + ctrs, err := shortcuts.GetContainersByContext(cli.All, cli.Latest, cli.InputArgs, r.Runtime) + if err != nil { + return ok, failures, err + } + + for _, c := range ctrs { + if timeout == nil { + t := c.StopTimeout() + timeout = &t + logrus.Debugf("Set timeout to container %s default (%d)", c.ID(), *timeout) + } + if err := c.StopWithTimeout(*timeout); err == nil { + ok = append(ok, c.ID()) + } else if errors.Cause(err) == libpod.ErrCtrStopped { + ok = append(ok, c.ID()) + logrus.Debugf("Container %s is already stopped", c.ID()) + } else { + failures[c.ID()] = err + } + } + return ok, failures, nil +} + +// KillContainers sends signal to container(s) based on CLI inputs. +// Returns list of successful id(s), map of failed id(s) + error, or error not from container +func (r *LocalRuntime) KillContainers(ctx context.Context, cli *cliconfig.KillValues, signal syscall.Signal) ([]string, map[string]error, error) { + var ( + ok = []string{} + failures = map[string]error{} + ) + + ctrs, err := shortcuts.GetContainersByContext(cli.All, cli.Latest, cli.InputArgs, r.Runtime) + if err != nil { + return ok, failures, err + } + + for _, c := range ctrs { + if err := c.Kill(uint(signal)); err == nil { + ok = append(ok, c.ID()) + } else { + failures[c.ID()] = err + } + } + return ok, failures, nil +} + +// WaitOnContainers waits for all given container(s) to stop +func (r *LocalRuntime) WaitOnContainers(ctx context.Context, cli *cliconfig.WaitValues, interval time.Duration) ([]string, map[string]error, error) { + var ( + ok = []string{} + failures = map[string]error{} + ) + + ctrs, err := shortcuts.GetContainersByContext(false, cli.Latest, cli.InputArgs, r.Runtime) + if err != nil { + return ok, failures, err + } + + for _, c := range ctrs { + if returnCode, err := c.WaitWithInterval(interval); err == nil { + ok = append(ok, strconv.Itoa(int(returnCode))) + } else { + failures[c.ID()] = err + } + } + return ok, failures, err +} + +// Log logs one or more containers +func (r *LocalRuntime) Log(c *cliconfig.LogsValues, options *libpod.LogOptions) error { + var wg sync.WaitGroup + options.WaitGroup = &wg + if len(c.InputArgs) > 1 { + options.Multi = true + } + logChannel := make(chan *libpod.LogLine, int(c.Tail)*len(c.InputArgs)+1) + containers, err := shortcuts.GetContainersByContext(false, c.Latest, c.InputArgs, r.Runtime) + if err != nil { + return err + } + if err := r.Runtime.Log(containers, options, logChannel); err != nil { + return err + } + go func() { + wg.Wait() + close(logChannel) + }() + for line := range logChannel { + fmt.Println(line.String(options)) + } + return nil +} diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go index 3f43a6905..2982d6cbb 100644 --- a/pkg/adapter/containers_remote.go +++ b/pkg/adapter/containers_remote.go @@ -3,17 +3,26 @@ package adapter import ( + "context" "encoding/json" - "github.com/containers/libpod/cmd/podman/shared" + "fmt" + "strconv" + "syscall" + "time" - iopodman "github.com/containers/libpod/cmd/podman/varlink" + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/cmd/podman/shared" + "github.com/containers/libpod/cmd/podman/varlink" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/inspect" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/varlink/go/varlink" ) // Inspect returns an inspect struct from varlink func (c *Container) Inspect(size bool) (*inspect.ContainerInspectData, error) { - reply, err := iopodman.ContainerInspectData().Call(c.Runtime.Conn, c.ID()) + reply, err := iopodman.ContainerInspectData().Call(c.Runtime.Conn, c.ID(), size) if err != nil { return nil, err } @@ -29,6 +38,70 @@ func (c *Container) ID() string { return c.config.ID } +// Config returns a container config +func (r *LocalRuntime) Config(name string) *libpod.ContainerConfig { + // TODO the Spec being returned is not populated. Matt and I could not figure out why. Will defer + // further looking into it for after devconf. + // The libpod function for this has no errors so we are kind of in a tough + // spot here. Logging the errors for now. + reply, err := iopodman.ContainerConfig().Call(r.Conn, name) + if err != nil { + logrus.Error("call to container.config failed") + } + data := libpod.ContainerConfig{} + if err := json.Unmarshal([]byte(reply), &data); err != nil { + logrus.Error("failed to unmarshal container inspect data") + } + return &data + +} + +// ContainerState returns the "state" of the container. +func (r *LocalRuntime) ContainerState(name string) (*libpod.ContainerState, error) { // no-lint + reply, err := iopodman.ContainerStateData().Call(r.Conn, name) + if err != nil { + return nil, err + } + data := libpod.ContainerState{} + if err := json.Unmarshal([]byte(reply), &data); err != nil { + return nil, err + } + return &data, err + +} + +// LookupContainer gets basic information about container over a varlink +// connection and then translates it to a *Container +func (r *LocalRuntime) LookupContainer(idOrName string) (*Container, error) { + state, err := r.ContainerState(idOrName) + if err != nil { + return nil, err + } + config := r.Config(idOrName) + if err != nil { + return nil, err + } + + return &Container{ + remoteContainer{ + r, + config, + state, + }, + }, nil +} + +func (r *LocalRuntime) GetLatestContainer() (*Container, error) { + reply, err := iopodman.GetContainersByContext().Call(r.Conn, false, true, nil) + if err != nil { + return nil, err + } + if len(reply) > 0 { + return r.LookupContainer(reply[0]) + } + return nil, errors.New("no containers exist") +} + // GetArtifact returns a container's artifacts func (c *Container) GetArtifact(name string) ([]byte, error) { var data []byte @@ -55,18 +128,90 @@ func (c *Container) Name() string { return c.config.Name } +// StopContainers stops requested containers using CLI inputs. +// Returns the list of stopped container ids, map of failed to stop container ids + errors, or any non-container error +func (r *LocalRuntime) StopContainers(ctx context.Context, cli *cliconfig.StopValues) ([]string, map[string]error, error) { + var ( + ok = []string{} + failures = map[string]error{} + ) + + ids, err := iopodman.GetContainersByContext().Call(r.Conn, cli.All, cli.Latest, cli.InputArgs) + if err != nil { + return ok, failures, err + } + + for _, id := range ids { + stopped, err := iopodman.StopContainer().Call(r.Conn, id, int64(cli.Timeout)) + if err != nil { + failures[id] = err + } else { + ok = append(ok, stopped) + } + } + return ok, failures, nil +} + +// KillContainers sends signal to container(s) based on CLI inputs. +// Returns list of successful id(s), map of failed id(s) + error, or error not from container +func (r *LocalRuntime) KillContainers(ctx context.Context, cli *cliconfig.KillValues, signal syscall.Signal) ([]string, map[string]error, error) { + var ( + ok = []string{} + failures = map[string]error{} + ) + + ids, err := iopodman.GetContainersByContext().Call(r.Conn, cli.All, cli.Latest, cli.InputArgs) + if err != nil { + return ok, failures, err + } + + for _, id := range ids { + killed, err := iopodman.KillContainer().Call(r.Conn, id, int64(signal)) + if err != nil { + failures[id] = err + } else { + ok = append(ok, killed) + } + } + return ok, failures, nil +} + +// WaitOnContainers waits for all given container(s) to stop. +// interval is currently ignored. +func (r *LocalRuntime) WaitOnContainers(ctx context.Context, cli *cliconfig.WaitValues, interval time.Duration) ([]string, map[string]error, error) { + var ( + ok = []string{} + failures = map[string]error{} + ) + + ids, err := iopodman.GetContainersByContext().Call(r.Conn, false, cli.Latest, cli.InputArgs) + if err != nil { + return ok, failures, err + } + + for _, id := range ids { + stopped, err := iopodman.WaitContainer().Call(r.Conn, id, int64(interval)) + if err != nil { + failures[id] = err + } else { + ok = append(ok, strconv.FormatInt(stopped, 10)) + } + } + return ok, failures, nil +} + // BatchContainerOp is wrapper func to mimic shared's function with a similar name meant for libpod func BatchContainerOp(ctr *Container, opts shared.PsOptions) (shared.BatchContainerStruct, error) { // TODO If pod ps ever shows container's sizes, re-enable this code; otherwise it isn't needed // and would be a perf hit - //data, err := ctr.Inspect(true) - //if err != nil { - // return shared.BatchContainerStruct{}, err - //} + // data, err := ctr.Inspect(true) + // if err != nil { + // return shared.BatchContainerStruct{}, err + // } // - //size := new(shared.ContainerSize) - //size.RootFsSize = data.SizeRootFs - //size.RwSize = data.SizeRw + // size := new(shared.ContainerSize) + // size.RootFsSize = data.SizeRootFs + // size.RwSize = data.SizeRw bcs := shared.BatchContainerStruct{ ConConfig: ctr.config, @@ -75,7 +220,45 @@ func BatchContainerOp(ctr *Container, opts shared.PsOptions) (shared.BatchContai Pid: ctr.state.PID, StartedTime: ctr.state.StartedTime, ExitedTime: ctr.state.FinishedTime, - //Size: size, + // Size: size, } return bcs, nil } + +// Logs one or more containers over a varlink connection +func (r *LocalRuntime) Log(c *cliconfig.LogsValues, options *libpod.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 { + return errors.Wrapf(err, "failed to get container logs") + } + if len(c.InputArgs) > 1 { + options.Multi = true + } + for { + log, flags, err := reply() + if err != nil { + return err + } + if log.Time == "" && log.Msg == "" { + // We got a blank log line which can signal end of stream + break + } + lTime, err := time.Parse(time.RFC3339Nano, log.Time) + if err != nil { + return errors.Wrapf(err, "unable to parse time of log %s", log.Time) + } + logLine := libpod.LogLine{ + Device: log.Device, + ParseLogType: log.ParseLogType, + Time: lTime, + Msg: log.Msg, + CID: log.Cid, + } + fmt.Println(logLine.String(options)) + if flags&varlink.Continues == 0 { + break + } + } + return nil +} diff --git a/pkg/adapter/pods.go b/pkg/adapter/pods.go index 706a8fe96..669971789 100644 --- a/pkg/adapter/pods.go +++ b/pkg/adapter/pods.go @@ -4,6 +4,7 @@ package adapter import ( "context" + "github.com/pkg/errors" "strings" "github.com/containers/libpod/cmd/podman/cliconfig" @@ -17,6 +18,13 @@ type Pod struct { *libpod.Pod } +// PodContainerStats is struct containing an adapter Pod and a libpod +// ContainerStats and is used primarily for outputing pod stats. +type PodContainerStats struct { + Pod *Pod + ContainerStats map[string]*libpod.ContainerStats +} + // RemovePods ... func (r *LocalRuntime) RemovePods(ctx context.Context, cli *cliconfig.PodRmValues) ([]string, []error) { var ( @@ -321,3 +329,55 @@ func (r *LocalRuntime) RestartPods(ctx context.Context, c *cliconfig.PodRestartV return restartIDs, containerErrors, restartErrors } + +// PodTop is a wrapper function to call GetPodPidInformation in libpod and return its results +// for output +func (r *LocalRuntime) PodTop(c *cliconfig.PodTopValues, descriptors []string) ([]string, error) { + var ( + pod *Pod + err error + ) + + if c.Latest { + pod, err = r.GetLatestPod() + } else { + pod, err = r.LookupPod(c.InputArgs[0]) + } + if err != nil { + return nil, errors.Wrapf(err, "unable to lookup requested container") + } + podStatus, err := pod.GetPodStatus() + if err != nil { + return nil, errors.Wrapf(err, "unable to get status for pod %s", pod.ID()) + } + if podStatus != "Running" { + return nil, errors.Errorf("pod top can only be used on pods with at least one running container") + } + return pod.GetPodPidInformation(descriptors) +} + +// GetStatPods returns pods for use in pod stats +func (r *LocalRuntime) GetStatPods(c *cliconfig.PodStatsValues) ([]*Pod, error) { + var ( + adapterPods []*Pod + pods []*libpod.Pod + err error + ) + + if len(c.InputArgs) > 0 || c.Latest || c.All { + pods, err = shortcuts.GetPodsByContext(c.All, c.Latest, c.InputArgs, r.Runtime) + } else { + pods, err = r.Runtime.GetRunningPods() + } + if err != nil { + return nil, err + } + // convert libpod pods to adapter pods + for _, p := range pods { + adapterPod := Pod{ + p, + } + adapterPods = append(adapterPods, &adapterPod) + } + return adapterPods, nil +} diff --git a/pkg/adapter/pods_remote.go b/pkg/adapter/pods_remote.go index 220f7163f..ef8de90a6 100644 --- a/pkg/adapter/pods_remote.go +++ b/pkg/adapter/pods_remote.go @@ -12,6 +12,7 @@ import ( "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/cmd/podman/varlink" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/varlinkapi" "github.com/pkg/errors" "github.com/ulule/deepcopier" ) @@ -21,6 +22,13 @@ type Pod struct { remotepod } +// PodContainerStats is struct containing an adapter Pod and a libpod +// ContainerStats and is used primarily for outputing pod stats. +type PodContainerStats struct { + Pod *Pod + ContainerStats map[string]*libpod.ContainerStats +} + type remotepod struct { config *libpod.PodConfig state *libpod.PodInspectState @@ -399,3 +407,103 @@ func (r *LocalRuntime) RestartPods(ctx context.Context, c *cliconfig.PodRestartV } return restartIDs, nil, restartErrors } + +// PodTop gets top statistics for a pod +func (r *LocalRuntime) PodTop(c *cliconfig.PodTopValues, descriptors []string) ([]string, error) { + var ( + latest bool + podName string + ) + if c.Latest { + latest = true + } else { + podName = c.InputArgs[0] + } + return iopodman.TopPod().Call(r.Conn, podName, latest, descriptors) +} + +// GetStatPods returns pods for use in pod stats +func (r *LocalRuntime) GetStatPods(c *cliconfig.PodStatsValues) ([]*Pod, error) { + var ( + pods []*Pod + err error + podIDs []string + running bool + ) + + if len(c.InputArgs) > 0 || c.Latest || c.All { + podIDs, err = iopodman.GetPodsByContext().Call(r.Conn, c.All, c.Latest, c.InputArgs) + } else { + podIDs, err = iopodman.GetPodsByContext().Call(r.Conn, true, false, []string{}) + running = true + } + if err != nil { + return nil, err + } + for _, p := range podIDs { + pod, err := r.Inspect(p) + if err != nil { + return nil, err + } + if running { + status, err := pod.GetPodStatus() + if err != nil { + // if we cannot get the status of the pod, skip and move on + continue + } + if strings.ToUpper(status) != "RUNNING" { + // if the pod is not running, skip and move on as well + continue + } + } + pods = append(pods, pod) + } + return pods, nil +} + +// GetPodStats returns the stats for each of its containers +func (p *Pod) GetPodStats(previousContainerStats map[string]*libpod.ContainerStats) (map[string]*libpod.ContainerStats, error) { + var ( + ok bool + prevStat *libpod.ContainerStats + ) + newContainerStats := make(map[string]*libpod.ContainerStats) + containers, err := p.AllContainers() + if err != nil { + return nil, err + } + for _, c := range containers { + if prevStat, ok = previousContainerStats[c.ID()]; !ok { + prevStat = &libpod.ContainerStats{ContainerID: c.ID()} + } + cStats := iopodman.ContainerStats{ + Id: prevStat.ContainerID, + Name: prevStat.Name, + Cpu: prevStat.CPU, + Cpu_nano: int64(prevStat.CPUNano), + System_nano: int64(prevStat.SystemNano), + Mem_usage: int64(prevStat.MemUsage), + Mem_limit: int64(prevStat.MemLimit), + Mem_perc: prevStat.MemPerc, + Net_input: int64(prevStat.NetInput), + Net_output: int64(prevStat.NetOutput), + Block_input: int64(prevStat.BlockInput), + Block_output: int64(prevStat.BlockOutput), + Pids: int64(prevStat.PIDs), + } + stats, err := iopodman.GetContainerStatsWithHistory().Call(p.Runtime.Conn, cStats) + if err != nil { + return nil, err + } + newStats := varlinkapi.ContainerStatsToLibpodContainerStats(stats) + // If the container wasn't running, don't include it + // but also suppress the error + if err != nil && errors.Cause(err) != libpod.ErrCtrStateInvalid { + return nil, err + } + if err == nil { + newContainerStats[c.ID()] = &newStats + } + } + return newContainerStats, nil +} diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go index 8624981b1..6a68a3aea 100644 --- a/pkg/adapter/runtime.go +++ b/pkg/adapter/runtime.go @@ -3,11 +3,13 @@ package adapter import ( + "bufio" "context" "io" "io/ioutil" "os" "strconv" + "text/template" "github.com/containers/buildah" "github.com/containers/buildah/imagebuildah" @@ -16,7 +18,9 @@ import ( "github.com/containers/image/types" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/events" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" @@ -108,15 +112,6 @@ func (r *LocalRuntime) RemoveImage(ctx context.Context, img *ContainerImage, for return r.Runtime.RemoveImage(ctx, img.Image, force) } -// LookupContainer ... -func (r *LocalRuntime) LookupContainer(idOrName string) (*Container, error) { - ctr, err := r.Runtime.LookupContainer(idOrName) - if err != nil { - return nil, err - } - return &Container{ctr}, nil -} - // PruneImages is wrapper into PruneImages within the image pkg func (r *LocalRuntime) PruneImages(all bool) ([]string, error) { return r.ImageRuntime().PruneImages(all) @@ -264,7 +259,7 @@ func (r *LocalRuntime) Build(ctx context.Context, c *cliconfig.BuildValues, opti if err != nil { return errors.Wrapf(err, "error parsing namespace-related options") } - usernsOption, idmappingOptions, err := parse.IDMappingOptions(c.PodmanCommand.Command) + usernsOption, idmappingOptions, err := parse.IDMappingOptions(c.PodmanCommand.Command, options.Isolation) if err != nil { return errors.Wrapf(err, "error parsing ID mapping options") } @@ -341,3 +336,97 @@ func IsImageNotFound(err error) bool { } return false } + +// HealthCheck is a wrapper to same named function in libpod +func (r *LocalRuntime) HealthCheck(c *cliconfig.HealthCheckValues) (libpod.HealthCheckStatus, error) { + return r.Runtime.HealthCheck(c.InputArgs[0]) +} + +// JoinOrCreateRootlessPod joins the specified pod if it is running or it creates a new user namespace +// if the pod is stopped +func (r *LocalRuntime) JoinOrCreateRootlessPod(pod *Pod) (bool, int, error) { + if os.Geteuid() == 0 { + return false, 0, nil + } + opts := rootless.Opts{ + Argument: pod.ID(), + } + + inspect, err := pod.Inspect() + if err != nil { + return false, 0, err + } + for _, ctr := range inspect.Containers { + prevCtr, err := r.LookupContainer(ctr.ID) + if err != nil { + return false, -1, err + } + s, err := prevCtr.State() + if err != nil { + return false, -1, err + } + if s != libpod.ContainerStateRunning && s != libpod.ContainerStatePaused { + continue + } + data, err := ioutil.ReadFile(prevCtr.Config().ConmonPidFile) + if err != nil { + return false, -1, errors.Wrapf(err, "cannot read conmon PID file %q", prevCtr.Config().ConmonPidFile) + } + conmonPid, err := strconv.Atoi(string(data)) + if err != nil { + return false, -1, errors.Wrapf(err, "cannot parse PID %q", data) + } + return rootless.JoinDirectUserAndMountNSWithOpts(uint(conmonPid), &opts) + } + + return rootless.BecomeRootInUserNSWithOpts(&opts) +} + +// Events is a wrapper to libpod to obtain libpod/podman events +func (r *LocalRuntime) Events(c *cliconfig.EventValues) error { + var ( + fromStart bool + eventsError error + ) + options, err := shared.GenerateEventOptions(c.Filter, c.Since, c.Until) + if err != nil { + return errors.Wrapf(err, "unable to generate event options") + } + tmpl, err := template.New("events").Parse(c.Format) + if err != nil { + return err + } + if len(c.Since) > 0 || len(c.Until) > 0 { + fromStart = true + } + eventChannel := make(chan *events.Event) + go func() { + eventsError = r.Runtime.Events(fromStart, c.Stream, options, eventChannel) + }() + + if eventsError != nil { + return eventsError + } + if err != nil { + return errors.Wrapf(err, "unable to tail the events log") + } + w := bufio.NewWriter(os.Stdout) + for event := range eventChannel { + if len(c.Format) > 0 { + if err := tmpl.Execute(w, event); err != nil { + return err + } + } else { + if _, err := w.Write([]byte(event.ToHumanReadable())); err != nil { + return err + } + } + if _, err := w.Write([]byte("\n")); err != nil { + return err + } + if err := w.Flush(); err != nil { + return err + } + } + return nil +} diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go index 29b43e9b0..6c53d0c62 100644 --- a/pkg/adapter/runtime_remote.go +++ b/pkg/adapter/runtime_remote.go @@ -5,12 +5,12 @@ package adapter import ( "bufio" "context" - "encoding/json" "fmt" "io" "io/ioutil" "os" "strings" + "text/template" "time" "github.com/containers/buildah/imagebuildah" @@ -19,6 +19,7 @@ import ( "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/varlink" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/events" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/utils" "github.com/containers/storage/pkg/archive" @@ -49,14 +50,13 @@ func GetRuntime(c *cliconfig.PodmanCommand) (*LocalRuntime, error) { if err != nil { return nil, err } - rr := RemoteRuntime{ - Conn: conn, - Remote: true, - } - foo := LocalRuntime{ - &rr, - } - return &foo, nil + + return &LocalRuntime{ + &RemoteRuntime{ + Conn: conn, + Remote: true, + }, + }, nil } // Shutdown is a bogus wrapper for compat with the libpod runtime @@ -315,66 +315,6 @@ func (ci *ContainerImage) History(ctx context.Context) ([]*image.History, error) return imageHistories, nil } -// LookupContainer gets basic information about container over a varlink -// connection and then translates it to a *Container -func (r *LocalRuntime) LookupContainer(idOrName string) (*Container, error) { - state, err := r.ContainerState(idOrName) - if err != nil { - return nil, err - } - config := r.Config(idOrName) - if err != nil { - return nil, err - } - - rc := remoteContainer{ - r, - config, - state, - } - - c := Container{ - rc, - } - return &c, nil -} - -func (r *LocalRuntime) GetLatestContainer() (*Container, error) { - return nil, libpod.ErrNotImplemented -} - -// ContainerState returns the "state" of the container. -func (r *LocalRuntime) ContainerState(name string) (*libpod.ContainerState, error) { //no-lint - reply, err := iopodman.ContainerStateData().Call(r.Conn, name) - if err != nil { - return nil, err - } - data := libpod.ContainerState{} - if err := json.Unmarshal([]byte(reply), &data); err != nil { - return nil, err - } - return &data, err - -} - -// Config returns a container config -func (r *LocalRuntime) Config(name string) *libpod.ContainerConfig { - // TODO the Spec being returned is not populated. Matt and I could not figure out why. Will defer - // further looking into it for after devconf. - // The libpod function for this has no errors so we are kind of in a tough - // spot here. Logging the errors for now. - reply, err := iopodman.ContainerConfig().Call(r.Conn, name) - if err != nil { - logrus.Error("call to container.config failed") - } - data := libpod.ContainerConfig{} - if err := json.Unmarshal([]byte(reply), &data); err != nil { - logrus.Error("failed to unmarshal container inspect data") - } - return &data - -} - // PruneImages is the wrapper call for a remote-client to prune images func (r *LocalRuntime) PruneImages(all bool) ([]string, error) { return iopodman.ImagesPrune().Call(r.Conn, all) @@ -808,3 +748,85 @@ func IsImageNotFound(err error) bool { } return false } + +// HealthCheck executes a container's healthcheck over a varlink connection +func (r *LocalRuntime) HealthCheck(c *cliconfig.HealthCheckValues) (libpod.HealthCheckStatus, error) { + return -1, libpod.ErrNotImplemented +} + +// JoinOrCreateRootlessPod joins the specified pod if it is running or it creates a new user namespace +// if the pod is stopped +func (r *LocalRuntime) JoinOrCreateRootlessPod(pod *Pod) (bool, int, error) { + // Nothing to do in the remote case + return true, 0, nil +} + +// Events monitors libpod/podman events over a varlink connection +func (r *LocalRuntime) Events(c *cliconfig.EventValues) error { + var more uint64 + if c.Stream { + more = uint64(varlink.More) + } + reply, err := iopodman.GetEvents().Send(r.Conn, more, c.Filter, c.Since, c.Until) + if err != nil { + return errors.Wrapf(err, "unable to obtain events") + } + + w := bufio.NewWriter(os.Stdout) + tmpl, err := template.New("events").Parse(c.Format) + if err != nil { + return err + } + + for { + returnedEvent, flags, err := reply() + if err != nil { + // When the error handling is back into podman, we can flip this to a better way to check + // for problems. For now, this works. + return err + } + if returnedEvent.Time == "" && returnedEvent.Status == "" && returnedEvent.Type == "" { + // We got a blank event return, signals end of stream in certain cases + break + } + eTime, err := time.Parse(time.RFC3339Nano, returnedEvent.Time) + if err != nil { + return errors.Wrapf(err, "unable to parse time of event %s", returnedEvent.Time) + } + eType, err := events.StringToType(returnedEvent.Type) + if err != nil { + return err + } + eStatus, err := events.StringToStatus(returnedEvent.Status) + if err != nil { + return err + } + event := events.Event{ + ID: returnedEvent.Id, + Image: returnedEvent.Image, + Name: returnedEvent.Name, + Status: eStatus, + Time: eTime, + Type: eType, + } + if len(c.Format) > 0 { + if err := tmpl.Execute(w, event); err != nil { + return err + } + } else { + if _, err := w.Write([]byte(event.ToHumanReadable())); err != nil { + return err + } + } + if _, err := w.Write([]byte("\n")); err != nil { + return err + } + if err := w.Flush(); err != nil { + return err + } + if flags&varlink.Continues == 0 { + break + } + } + return nil +} diff --git a/pkg/adapter/shortcuts/shortcuts.go b/pkg/adapter/shortcuts/shortcuts.go index 0633399ae..677d88457 100644 --- a/pkg/adapter/shortcuts/shortcuts.go +++ b/pkg/adapter/shortcuts/shortcuts.go @@ -25,3 +25,30 @@ func GetPodsByContext(all, latest bool, pods []string, runtime *libpod.Runtime) } return outpods, nil } + +// GetContainersByContext gets pods whether all, latest, or a slice of names/ids +func GetContainersByContext(all, latest bool, names []string, runtime *libpod.Runtime) ([]*libpod.Container, error) { + var ctrs = []*libpod.Container{} + + if all { + return runtime.GetAllContainers() + } + + if latest { + c, err := runtime.GetLatestContainer() + if err != nil { + return nil, err + } + ctrs = append(ctrs, c) + return ctrs, nil + } + + for _, c := range names { + ctr, err := runtime.LookupContainer(c) + if err != nil { + return nil, err + } + ctrs = append(ctrs, ctr) + } + return ctrs, nil +} |