summaryrefslogtreecommitdiff
path: root/pkg/domain
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/domain')
-rw-r--r--pkg/domain/entities/container_ps.go30
-rw-r--r--pkg/domain/entities/containers.go25
-rw-r--r--pkg/domain/entities/engine_container.go9
-rw-r--r--pkg/domain/entities/engine_image.go7
-rw-r--r--pkg/domain/entities/events.go61
-rw-r--r--pkg/domain/entities/images.go37
-rw-r--r--pkg/domain/entities/manifest.go16
-rw-r--r--pkg/domain/entities/pods.go50
-rw-r--r--pkg/domain/entities/types.go23
-rw-r--r--pkg/domain/infra/abi/containers.go66
-rw-r--r--pkg/domain/infra/abi/cp.go433
-rw-r--r--pkg/domain/infra/abi/events.go2
-rw-r--r--pkg/domain/infra/abi/healthcheck.go2
-rw-r--r--pkg/domain/infra/abi/images.go256
-rw-r--r--pkg/domain/infra/abi/images_list.go2
-rw-r--r--pkg/domain/infra/abi/manifest.go102
-rw-r--r--pkg/domain/infra/abi/pods.go27
-rw-r--r--pkg/domain/infra/abi/pods_stats.go85
-rw-r--r--pkg/domain/infra/abi/runtime.go6
-rw-r--r--pkg/domain/infra/abi/system.go38
-rw-r--r--pkg/domain/infra/abi/terminal/sigproxy_linux.go2
-rw-r--r--pkg/domain/infra/abi/terminal/terminal.go2
-rw-r--r--pkg/domain/infra/abi/terminal/terminal_linux.go2
-rw-r--r--pkg/domain/infra/runtime_libpod.go36
-rw-r--r--pkg/domain/infra/tunnel/containers.go16
-rw-r--r--pkg/domain/infra/tunnel/events.go5
-rw-r--r--pkg/domain/infra/tunnel/images.go39
-rw-r--r--pkg/domain/infra/tunnel/manifest.go64
-rw-r--r--pkg/domain/infra/tunnel/pods.go4
-rw-r--r--pkg/domain/infra/tunnel/system.go4
30 files changed, 1242 insertions, 209 deletions
diff --git a/pkg/domain/entities/container_ps.go b/pkg/domain/entities/container_ps.go
index 709bb58d6..fd94d93be 100644
--- a/pkg/domain/entities/container_ps.go
+++ b/pkg/domain/entities/container_ps.go
@@ -25,6 +25,8 @@ type ListContainer struct {
ID string `json:"Id"`
// Container image
Image string
+ // Container image ID
+ ImageID string
// If this container is a Pod infra container
IsInfra bool
// Labels for container
@@ -159,3 +161,31 @@ func SortPsOutput(sortBy string, psOutput SortListContainers) (SortListContainer
}
return psOutput, nil
}
+
+func (l ListContainer) CGROUPNS() string {
+ return l.Namespaces.Cgroup
+}
+
+func (l ListContainer) IPC() string {
+ return l.Namespaces.IPC
+}
+
+func (l ListContainer) MNT() string {
+ return l.Namespaces.MNT
+}
+
+func (l ListContainer) NET() string {
+ return l.Namespaces.NET
+}
+
+func (l ListContainer) PIDNS() string {
+ return l.Namespaces.PIDNS
+}
+
+func (l ListContainer) USERNS() string {
+ return l.Namespaces.User
+}
+
+func (l ListContainer) UTS() string {
+ return l.Namespaces.UTS
+}
diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go
index 52327a905..e58258b75 100644
--- a/pkg/domain/entities/containers.go
+++ b/pkg/domain/entities/containers.go
@@ -8,6 +8,7 @@ import (
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/specgen"
+ "github.com/cri-o/ocicni/pkg/ocicni"
)
type WaitOptions struct {
@@ -341,3 +342,27 @@ type ContainerPruneReport struct {
ID map[string]int64
Err map[string]error
}
+
+// ContainerPortOptions describes the options to obtain
+// port information on containers
+type ContainerPortOptions struct {
+ All bool
+ Latest bool
+}
+
+// ContainerPortReport describes the output needed for
+// the CLI to output ports
+type ContainerPortReport struct {
+ Id string
+ Ports []ocicni.PortMapping
+}
+
+// ContainerCpOptions describes input options for cp
+type ContainerCpOptions struct {
+ Pause bool
+ Extract bool
+}
+
+// ContainerCpReport describes the output from a cp operation
+type ContainerCpReport struct {
+}
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
index b730f8743..502279bcf 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -14,8 +14,8 @@ type ContainerEngine interface {
ContainerAttach(ctx context.Context, nameOrId string, options AttachOptions) error
ContainerCheckpoint(ctx context.Context, namesOrIds []string, options CheckpointOptions) ([]*CheckpointReport, error)
ContainerCleanup(ctx context.Context, namesOrIds []string, options ContainerCleanupOptions) ([]*ContainerCleanupReport, error)
- ContainerPrune(ctx context.Context, options ContainerPruneOptions) (*ContainerPruneReport, error)
ContainerCommit(ctx context.Context, nameOrId string, options CommitOptions) (*CommitReport, error)
+ ContainerCp(ctx context.Context, source, dest string, options ContainerCpOptions) (*ContainerCpReport, error)
ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*ContainerCreateReport, error)
ContainerDiff(ctx context.Context, nameOrId string, options DiffOptions) (*DiffReport, error)
ContainerExec(ctx context.Context, nameOrId string, options ExecOptions) (int, error)
@@ -28,6 +28,8 @@ type ContainerEngine interface {
ContainerLogs(ctx context.Context, containers []string, options ContainerLogsOptions) error
ContainerMount(ctx context.Context, nameOrIds []string, options ContainerMountOptions) ([]*ContainerMountReport, error)
ContainerPause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error)
+ ContainerPort(ctx context.Context, nameOrId string, options ContainerPortOptions) ([]*ContainerPortReport, error)
+ ContainerPrune(ctx context.Context, options ContainerPruneOptions) (*ContainerPruneReport, error)
ContainerRestart(ctx context.Context, namesOrIds []string, options RestartOptions) ([]*RestartReport, error)
ContainerRestore(ctx context.Context, namesOrIds []string, options RestoreOptions) ([]*RestoreReport, error)
ContainerRm(ctx context.Context, namesOrIds []string, options RmOptions) ([]*RmReport, error)
@@ -46,16 +48,17 @@ type ContainerEngine interface {
PodInspect(ctx context.Context, options PodInspectOptions) (*PodInspectReport, error)
PodKill(ctx context.Context, namesOrIds []string, options PodKillOptions) ([]*PodKillReport, error)
PodPause(ctx context.Context, namesOrIds []string, options PodPauseOptions) ([]*PodPauseReport, error)
+ PodPrune(ctx context.Context, options PodPruneOptions) ([]*PodPruneReport, error)
PodPs(ctx context.Context, options PodPSOptions) ([]*ListPodsReport, error)
PodRestart(ctx context.Context, namesOrIds []string, options PodRestartOptions) ([]*PodRestartReport, error)
PodRm(ctx context.Context, namesOrIds []string, options PodRmOptions) ([]*PodRmReport, error)
- PodPrune(ctx context.Context, options PodPruneOptions) ([]*PodPruneReport, error)
PodStart(ctx context.Context, namesOrIds []string, options PodStartOptions) ([]*PodStartReport, error)
+ PodStats(ctx context.Context, namesOrIds []string, options PodStatsOptions) ([]*PodStatsReport, error)
PodStop(ctx context.Context, namesOrIds []string, options PodStopOptions) ([]*PodStopReport, error)
PodTop(ctx context.Context, options PodTopOptions) (*StringSliceReport, error)
PodUnpause(ctx context.Context, namesOrIds []string, options PodunpauseOptions) ([]*PodUnpauseReport, error)
- RestService(ctx context.Context, opts ServiceOptions) error
SetupRootless(ctx context.Context, cmd *cobra.Command) error
+ Shutdown(ctx context.Context)
VarlinkService(ctx context.Context, opts ServiceOptions) error
VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IdOrNameResponse, error)
VolumeInspect(ctx context.Context, namesOrIds []string, opts VolumeInspectOptions) ([]*VolumeInspectReport, error)
diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go
index 052e7bee5..b118a4104 100644
--- a/pkg/domain/entities/engine_image.go
+++ b/pkg/domain/entities/engine_image.go
@@ -9,7 +9,6 @@ import (
type ImageEngine interface {
Build(ctx context.Context, containerFiles []string, opts BuildOptions) (*BuildReport, error)
Config(ctx context.Context) (*config.Config, error)
- Delete(ctx context.Context, nameOrId []string, opts ImageDeleteOptions) (*ImageDeleteReport, error)
Diff(ctx context.Context, nameOrId string, options DiffOptions) (*DiffReport, error)
Exists(ctx context.Context, nameOrId string) (*BoolReport, error)
History(ctx context.Context, nameOrId string, opts ImageHistoryOptions) (*ImageHistoryReport, error)
@@ -20,8 +19,14 @@ type ImageEngine interface {
Prune(ctx context.Context, opts ImagePruneOptions) (*ImagePruneReport, error)
Pull(ctx context.Context, rawImage string, opts ImagePullOptions) (*ImagePullReport, error)
Push(ctx context.Context, source string, destination string, opts ImagePushOptions) error
+ Remove(ctx context.Context, images []string, opts ImageRemoveOptions) (*ImageRemoveReport, error)
Save(ctx context.Context, nameOrId string, tags []string, options ImageSaveOptions) error
Search(ctx context.Context, term string, opts ImageSearchOptions) ([]ImageSearchReport, error)
+ Shutdown(ctx context.Context)
Tag(ctx context.Context, nameOrId string, tags []string, options ImageTagOptions) error
+ Tree(ctx context.Context, nameOrId string, options ImageTreeOptions) (*ImageTreeReport, error)
Untag(ctx context.Context, nameOrId string, tags []string, options ImageUntagOptions) error
+ ManifestCreate(ctx context.Context, names, images []string, opts ManifestCreateOptions) (string, error)
+ ManifestInspect(ctx context.Context, name string) ([]byte, error)
+ ManifestAdd(ctx context.Context, opts ManifestAddOptions) (string, error)
}
diff --git a/pkg/domain/entities/events.go b/pkg/domain/entities/events.go
new file mode 100644
index 000000000..8861be158
--- /dev/null
+++ b/pkg/domain/entities/events.go
@@ -0,0 +1,61 @@
+package entities
+
+import (
+ "strconv"
+ "time"
+
+ libpodEvents "github.com/containers/libpod/libpod/events"
+ dockerEvents "github.com/docker/docker/api/types/events"
+)
+
+// Event combines various event-related data such as time, event type, status
+// and more.
+type Event struct {
+ // TODO: it would be nice to have full control over the types at some
+ // point and fork such Docker types.
+ dockerEvents.Message
+}
+
+// ConvertToLibpodEvent converts an entities event to a libpod one.
+func ConvertToLibpodEvent(e Event) *libpodEvents.Event {
+ exitCode, err := strconv.Atoi(e.Actor.Attributes["containerExitCode"])
+ if err != nil {
+ return nil
+ }
+ status, err := libpodEvents.StringToStatus(e.Action)
+ if err != nil {
+ return nil
+ }
+ t, err := libpodEvents.StringToType(e.Type)
+ if err != nil {
+ return nil
+ }
+ return &libpodEvents.Event{
+ ContainerExitCode: exitCode,
+ ID: e.Actor.ID,
+ Image: e.Actor.Attributes["image"],
+ Name: e.Actor.Attributes["name"],
+ Status: status,
+ Time: time.Unix(e.Time, e.TimeNano),
+ Type: t,
+ }
+}
+
+// ConvertToEntitiesEvent converts a libpod event to an entities one.
+func ConvertToEntitiesEvent(e libpodEvents.Event) *Event {
+ return &Event{dockerEvents.Message{
+ Type: e.Type.String(),
+ Action: e.Status.String(),
+ Actor: dockerEvents.Actor{
+ ID: e.ID,
+ Attributes: map[string]string{
+ "image": e.Image,
+ "name": e.Name,
+ "containerExitCode": strconv.Itoa(e.ContainerExitCode),
+ },
+ },
+ Scope: "local",
+ Time: e.Time.Unix(),
+ TimeNano: e.Time.UnixNano(),
+ }}
+}
diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go
index 3a6d159e4..460965b34 100644
--- a/pkg/domain/entities/images.go
+++ b/pkg/domain/entities/images.go
@@ -82,19 +82,24 @@ func (i *ImageSummary) IsDangling() bool {
return i.Dangling
}
-type ImageDeleteOptions struct {
- All bool
+// ImageRemoveOptions can be used to alter image removal.
+type ImageRemoveOptions struct {
+ // All will remove all images.
+ All bool
+ // Foce will force image removal including containers using the images.
Force bool
}
-// ImageDeleteResponse is the response for removing one or more image(s) from storage
-// and containers what was untagged vs actually removed
-type ImageDeleteReport struct {
- Untagged []string `json:",omitempty"`
- Deleted []string `json:",omitempty"`
- Errors []error
- ImageNotFound error
- ImageInUse error
+// ImageRemoveResponse is the response for removing one or more image(s) from storage
+// and containers what was untagged vs actually removed.
+type ImageRemoveReport struct {
+ // Deleted images.
+ Deleted []string `json:",omitempty"`
+ // Untagged images. Can be longer than Deleted.
+ Untagged []string `json:",omitempty"`
+ // ExitCode describes the exit codes as described in the `podman rmi`
+ // man page.
+ ExitCode int
}
type ImageHistoryOptions struct{}
@@ -251,7 +256,7 @@ type ImageLoadOptions struct {
}
type ImageLoadReport struct {
- Name string
+ Names []string
}
type ImageImportOptions struct {
@@ -273,3 +278,13 @@ type ImageSaveOptions struct {
Output string
Quiet bool
}
+
+// ImageTreeOptions provides options for ImageEngine.Tree()
+type ImageTreeOptions struct {
+ WhatRequires bool // Show all child images and layers of the specified image
+}
+
+// ImageTreeReport provides results from ImageEngine.Tree()
+type ImageTreeReport struct {
+ Tree string // TODO: Refactor move presentation work out of server
+}
diff --git a/pkg/domain/entities/manifest.go b/pkg/domain/entities/manifest.go
new file mode 100644
index 000000000..7316735b0
--- /dev/null
+++ b/pkg/domain/entities/manifest.go
@@ -0,0 +1,16 @@
+package entities
+
+type ManifestCreateOptions struct {
+ All bool `schema:"all"`
+}
+
+type ManifestAddOptions struct {
+ All bool `json:"all" schema:"all"`
+ Annotation []string `json:"annotation" schema:"annotation"`
+ Arch string `json:"arch" schema:"arch"`
+ Features []string `json:"features" schema:"features"`
+ Images []string `json:"images" schema:"images"`
+ OS string `json:"os" schema:"os"`
+ OSVersion string `json:"os_version" schema:"os_version"`
+ Variant string `json:"variant" schema:"variant"`
+}
diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go
index 04673ef18..a4896ce4d 100644
--- a/pkg/domain/entities/pods.go
+++ b/pkg/domain/entities/pods.go
@@ -1,6 +1,7 @@
package entities
import (
+ "errors"
"strings"
"time"
@@ -134,7 +135,7 @@ func (p PodCreateOptions) ToPodSpecGen(s *specgen.PodSpecGenerator) {
s.StaticMAC = p.Net.StaticMAC
s.PortMappings = p.Net.PublishPorts
s.CNINetworks = p.Net.CNINetworks
- if p.Net.DNSHost {
+ if p.Net.UseImageResolvConf {
s.NoManageResolvConf = true
}
s.DNSServer = p.Net.DNSServers
@@ -188,3 +189,50 @@ type PodInspectOptions struct {
type PodInspectReport struct {
*define.InspectPodData
}
+
+// PodStatsOptions are options for the pod stats command.
+type PodStatsOptions struct {
+ // All - provide stats for all running pods.
+ All bool
+ // Latest - provide stats for the latest pod.
+ Latest bool
+}
+
+// PodStatsReport includes pod-resource statistics data.
+type PodStatsReport struct {
+ CPU string
+ MemUsage string
+ Mem string
+ NetIO string
+ BlockIO string
+ PIDS string
+ Pod string
+ CID string
+ Name string
+}
+
+// ValidatePodStatsOptions validates the specified slice and options. Allows
+// for sharing code in the front- and the back-end.
+func ValidatePodStatsOptions(args []string, options *PodStatsOptions) error {
+ num := 0
+ if len(args) > 0 {
+ num++
+ }
+ if options.All {
+ num++
+ }
+ if options.Latest {
+ num++
+ }
+ switch num {
+ case 0:
+ // Podman v1 compat: if nothing's specified get all running
+ // pods.
+ options.All = true
+ return nil
+ case 1:
+ return nil
+ default:
+ return errors.New("--all, --latest and arguments cannot be used together")
+ }
+}
diff --git a/pkg/domain/entities/types.go b/pkg/domain/entities/types.go
index b89aa869a..d742cc53d 100644
--- a/pkg/domain/entities/types.go
+++ b/pkg/domain/entities/types.go
@@ -32,17 +32,17 @@ type VolumeDeleteReport struct{ Report }
// NetOptions reflect the shared network options between
// pods and containers
type NetOptions struct {
- AddHosts []string
- CNINetworks []string
- DNSHost bool
- DNSOptions []string
- DNSSearch []string
- DNSServers []net.IP
- Network specgen.Namespace
- NoHosts bool
- PublishPorts []ocicni.PortMapping
- StaticIP *net.IP
- StaticMAC *net.HardwareAddr
+ AddHosts []string
+ CNINetworks []string
+ UseImageResolvConf bool
+ DNSOptions []string
+ DNSSearch []string
+ DNSServers []net.IP
+ Network specgen.Namespace
+ NoHosts bool
+ PublishPorts []ocicni.PortMapping
+ StaticIP *net.IP
+ StaticMAC *net.HardwareAddr
}
// All CLI inspect commands and inspect sub-commands use the same options
@@ -50,6 +50,7 @@ type InspectOptions struct {
Format string `json:",omitempty"`
Latest bool `json:",omitempty"`
Size bool `json:",omitempty"`
+ Type string `json:",omitempty"`
}
// All API and CLI diff commands and diff sub-commands use the same options
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index 50003dbe2..286d37c34 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -1,5 +1,3 @@
-// +build ABISupport
-
package abi
import (
@@ -219,12 +217,23 @@ func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []strin
}
func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []string, options entities.RestartOptions) ([]*entities.RestartReport, error) {
var (
+ ctrs []*libpod.Container
+ err error
reports []*entities.RestartReport
)
- ctrs, err := getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
- if err != nil {
- return nil, err
+
+ if options.Running {
+ ctrs, err = ic.Libpod.GetRunningContainers()
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ ctrs, err = getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
+ if err != nil {
+ return nil, err
+ }
}
+
for _, con := range ctrs {
timeout := con.StopTimeout()
if options.Timeout != nil {
@@ -483,7 +492,7 @@ func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecG
if err := generate.CompleteSpec(ctx, ic.Libpod, s); err != nil {
return nil, err
}
- ctr, err := generate.MakeContainer(ic.Libpod, s)
+ ctr, err := generate.MakeContainer(ctx, ic.Libpod, s)
if err != nil {
return nil, err
}
@@ -671,7 +680,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
if err := generate.CompleteSpec(ctx, ic.Libpod, opts.Spec); err != nil {
return nil, err
}
- ctr, err := generate.MakeContainer(ic.Libpod, opts.Spec)
+ ctr, err := generate.MakeContainer(ctx, ic.Libpod, opts.Spec)
if err != nil {
return nil, err
}
@@ -839,7 +848,13 @@ func (ic *ContainerEngine) ContainerInit(ctx context.Context, namesOrIds []strin
}
for _, ctr := range ctrs {
report := entities.ContainerInitReport{Id: ctr.ID()}
- report.Err = ctr.Init(ctx)
+ err := ctr.Init(ctx)
+
+ // If we're initializing all containers, ignore invalid state errors
+ if options.All && errors.Cause(err) == define.ErrCtrStateInvalid {
+ err = nil
+ }
+ report.Err = err
reports = append(reports, &report)
}
return reports, nil
@@ -931,3 +946,38 @@ func (ic *ContainerEngine) ContainerUnmount(ctx context.Context, nameOrIds []str
func (ic *ContainerEngine) Config(_ context.Context) (*config.Config, error) {
return ic.Libpod.GetConfig()
}
+
+func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrId string, options entities.ContainerPortOptions) ([]*entities.ContainerPortReport, error) {
+ var reports []*entities.ContainerPortReport
+ ctrs, err := getContainersByContext(options.All, false, []string{nameOrId}, ic.Libpod)
+ if err != nil {
+ return nil, err
+ }
+ for _, con := range ctrs {
+ state, err := con.State()
+ if err != nil {
+ return nil, err
+ }
+ if state != define.ContainerStateRunning {
+ continue
+ }
+ portmappings, err := con.PortMappings()
+ if err != nil {
+ return nil, err
+ }
+ if len(portmappings) > 0 {
+ reports = append(reports, &entities.ContainerPortReport{
+ Id: con.ID(),
+ Ports: portmappings,
+ })
+ }
+ }
+ return reports, nil
+}
+
+// Shutdown Libpod engine
+func (ic *ContainerEngine) Shutdown(_ context.Context) {
+ shutdownSync.Do(func() {
+ _ = ic.Libpod.Shutdown(false)
+ })
+}
diff --git a/pkg/domain/infra/abi/cp.go b/pkg/domain/infra/abi/cp.go
new file mode 100644
index 000000000..9fc1e3bee
--- /dev/null
+++ b/pkg/domain/infra/abi/cp.go
@@ -0,0 +1,433 @@
+package abi
+
+import (
+ "archive/tar"
+ "context"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/containers/buildah/pkg/chrootuser"
+ "github.com/containers/buildah/util"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/storage"
+ "github.com/containers/storage/pkg/chrootarchive"
+ "github.com/containers/storage/pkg/idtools"
+ securejoin "github.com/cyphar/filepath-securejoin"
+ "github.com/docker/docker/pkg/archive"
+ "github.com/opencontainers/go-digest"
+ "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, options entities.ContainerCpOptions) (*entities.ContainerCpReport, error) {
+ var extract bool
+
+ srcCtr, srcPath := parsePath(ic.Libpod, source)
+ destCtr, destPath := parsePath(ic.Libpod, dest)
+
+ if (srcCtr == nil && destCtr == nil) || (srcCtr != nil && destCtr != nil) {
+ return nil, errors.Errorf("invalid arguments %s, %s you must use just one container", source, dest)
+ }
+
+ if len(srcPath) == 0 || len(destPath) == 0 {
+ return nil, errors.Errorf("invalid arguments %s, %s you must specify paths", source, dest)
+ }
+ ctr := srcCtr
+ isFromHostToCtr := ctr == nil
+ if isFromHostToCtr {
+ ctr = destCtr
+ }
+
+ mountPoint, err := ctr.Mount()
+ if err != nil {
+ return nil, err
+ }
+ defer func() {
+ if err := ctr.Unmount(false); err != nil {
+ logrus.Errorf("unable to umount container '%s': %q", ctr.ID(), err)
+ }
+ }()
+
+ if options.Pause {
+ if err := ctr.Pause(); err != nil {
+ // An invalid state error is fine.
+ // The container isn't running or is already paused.
+ // TODO: We can potentially start the container while
+ // the copy is running, which still allows a race where
+ // malicious code could mess with the symlink.
+ if errors.Cause(err) != define.ErrCtrStateInvalid {
+ return nil, err
+ }
+ } else {
+ // Only add the defer if we actually paused
+ defer func() {
+ if err := ctr.Unpause(); err != nil {
+ logrus.Errorf("Error unpausing container after copying: %v", err)
+ }
+ }()
+ }
+ }
+
+ user, err := getUser(mountPoint, ctr.User())
+ if err != nil {
+ return nil, err
+ }
+ idMappingOpts, err := ctr.IDMappings()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting IDMappingOptions")
+ }
+ destOwner := idtools.IDPair{UID: int(user.UID), GID: int(user.GID)}
+ hostUID, hostGID, err := util.GetHostIDs(convertIDMap(idMappingOpts.UIDMap), convertIDMap(idMappingOpts.GIDMap), user.UID, user.GID)
+ if err != nil {
+ return nil, err
+ }
+
+ hostOwner := idtools.IDPair{UID: int(hostUID), GID: int(hostGID)}
+
+ if isFromHostToCtr {
+ if isVol, volDestName, volName := isVolumeDestName(destPath, ctr); isVol { //nolint(gocritic)
+ path, err := pathWithVolumeMount(ctr, ic.Libpod, volDestName, volName, destPath)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting destination path from volume %s", volDestName)
+ }
+ destPath = path
+ } else if isBindMount, mount := isBindMountDestName(destPath, ctr); isBindMount { //nolint(gocritic)
+ path, err := pathWithBindMountSource(mount, destPath)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting destination path from bind mount %s", mount.Destination)
+ }
+ destPath = path
+ } else if filepath.IsAbs(destPath) { //nolint(gocritic)
+ cleanedPath, err := securejoin.SecureJoin(mountPoint, destPath)
+ if err != nil {
+ return nil, err
+ }
+ destPath = cleanedPath
+ } else { //nolint(gocritic)
+ ctrWorkDir, err := securejoin.SecureJoin(mountPoint, ctr.WorkingDir())
+ if err != nil {
+ return nil, err
+ }
+ if err = idtools.MkdirAllAndChownNew(ctrWorkDir, 0755, hostOwner); err != nil {
+ return nil, errors.Wrapf(err, "error creating directory %q", destPath)
+ }
+ cleanedPath, err := securejoin.SecureJoin(mountPoint, filepath.Join(ctr.WorkingDir(), destPath))
+ if err != nil {
+ return nil, err
+ }
+ destPath = cleanedPath
+ }
+ } else {
+ destOwner = idtools.IDPair{UID: os.Getuid(), GID: os.Getgid()}
+ if isVol, volDestName, volName := isVolumeDestName(srcPath, ctr); isVol { //nolint(gocritic)
+ path, err := pathWithVolumeMount(ctr, ic.Libpod, volDestName, volName, srcPath)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting source path from volume %s", volDestName)
+ }
+ srcPath = path
+ } else if isBindMount, mount := isBindMountDestName(srcPath, ctr); isBindMount { //nolint(gocritic)
+ path, err := pathWithBindMountSource(mount, srcPath)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting source path from bind mount %s", mount.Destination)
+ }
+ srcPath = path
+ } else if filepath.IsAbs(srcPath) { //nolint(gocritic)
+ cleanedPath, err := securejoin.SecureJoin(mountPoint, srcPath)
+ if err != nil {
+ return nil, err
+ }
+ srcPath = cleanedPath
+ } else { //nolint(gocritic)
+ cleanedPath, err := securejoin.SecureJoin(mountPoint, filepath.Join(ctr.WorkingDir(), srcPath))
+ if err != nil {
+ return nil, err
+ }
+ srcPath = cleanedPath
+ }
+ }
+
+ if !filepath.IsAbs(destPath) {
+ dir, err := os.Getwd()
+ if err != nil {
+ return nil, errors.Wrapf(err, "err getting current working directory")
+ }
+ destPath = filepath.Join(dir, destPath)
+ }
+
+ if source == "-" {
+ srcPath = os.Stdin.Name()
+ extract = true
+ }
+ err = containerCopy(srcPath, destPath, source, dest, idMappingOpts, &destOwner, extract, isFromHostToCtr)
+ return &entities.ContainerCpReport{}, err
+}
+
+func getUser(mountPoint string, userspec string) (specs.User, error) {
+ uid, gid, _, err := chrootuser.GetUser(mountPoint, userspec)
+ u := specs.User{
+ UID: uid,
+ GID: gid,
+ Username: userspec,
+ }
+ if !strings.Contains(userspec, ":") {
+ groups, err2 := chrootuser.GetAdditionalGroupsForUser(mountPoint, uint64(u.UID))
+ if err2 != nil {
+ if errors.Cause(err2) != chrootuser.ErrNoSuchUser && err == nil {
+ err = err2
+ }
+ } else {
+ u.AdditionalGids = groups
+ }
+
+ }
+ return u, err
+}
+
+func parsePath(runtime *libpod.Runtime, path string) (*libpod.Container, string) {
+ pathArr := strings.SplitN(path, ":", 2)
+ if len(pathArr) == 2 {
+ ctr, err := runtime.LookupContainer(pathArr[0])
+ if err == nil {
+ return ctr, pathArr[1]
+ }
+ }
+ return nil, path
+}
+
+func evalSymlinks(path string) (string, error) {
+ if path == os.Stdin.Name() {
+ return path, nil
+ }
+ return filepath.EvalSymlinks(path)
+}
+
+func getPathInfo(path string) (string, os.FileInfo, error) {
+ path, err := evalSymlinks(path)
+ if err != nil {
+ return "", nil, errors.Wrapf(err, "error evaluating symlinks %q", path)
+ }
+ srcfi, err := os.Stat(path)
+ if err != nil {
+ return "", nil, errors.Wrapf(err, "error reading path %q", path)
+ }
+ return path, srcfi, nil
+}
+
+func containerCopy(srcPath, destPath, src, dest string, idMappingOpts storage.IDMappingOptions, chownOpts *idtools.IDPair, extract, isFromHostToCtr bool) error {
+ srcPath, err := evalSymlinks(srcPath)
+ if err != nil {
+ return errors.Wrapf(err, "error evaluating symlinks %q", srcPath)
+ }
+
+ srcPath, srcfi, err := getPathInfo(srcPath)
+ if err != nil {
+ return err
+ }
+
+ filename := filepath.Base(destPath)
+ if filename == "-" && !isFromHostToCtr {
+ err := streamFileToStdout(srcPath, srcfi)
+ if err != nil {
+ return errors.Wrapf(err, "error streaming source file %s to Stdout", srcPath)
+ }
+ return nil
+ }
+
+ destdir := destPath
+ if !srcfi.IsDir() {
+ destdir = filepath.Dir(destPath)
+ }
+ _, err = os.Stat(destdir)
+ if err != nil && !os.IsNotExist(err) {
+ return errors.Wrapf(err, "error checking directory %q", destdir)
+ }
+ destDirIsExist := err == nil
+ if err = os.MkdirAll(destdir, 0755); err != nil {
+ return errors.Wrapf(err, "error creating directory %q", destdir)
+ }
+
+ // return functions for copying items
+ copyFileWithTar := chrootarchive.CopyFileWithTarAndChown(chownOpts, digest.Canonical.Digester().Hash(), idMappingOpts.UIDMap, idMappingOpts.GIDMap)
+ copyWithTar := chrootarchive.CopyWithTarAndChown(chownOpts, digest.Canonical.Digester().Hash(), idMappingOpts.UIDMap, idMappingOpts.GIDMap)
+ untarPath := chrootarchive.UntarPathAndChown(chownOpts, digest.Canonical.Digester().Hash(), idMappingOpts.UIDMap, idMappingOpts.GIDMap)
+
+ if srcfi.IsDir() {
+ logrus.Debugf("copying %q to %q", srcPath+string(os.PathSeparator)+"*", dest+string(os.PathSeparator)+"*")
+ if destDirIsExist && !strings.HasSuffix(src, fmt.Sprintf("%s.", string(os.PathSeparator))) {
+ destPath = filepath.Join(destPath, filepath.Base(srcPath))
+ }
+ if err = copyWithTar(srcPath, destPath); err != nil {
+ return errors.Wrapf(err, "error copying %q to %q", srcPath, dest)
+ }
+ return nil
+ }
+
+ if extract {
+ // We're extracting an archive into the destination directory.
+ logrus.Debugf("extracting contents of %q into %q", srcPath, destPath)
+ if err = untarPath(srcPath, destPath); err != nil {
+ return errors.Wrapf(err, "error extracting %q into %q", srcPath, destPath)
+ }
+ return nil
+ }
+
+ destfi, err := os.Stat(destPath)
+ if err != nil {
+ if !os.IsNotExist(err) || strings.HasSuffix(dest, string(os.PathSeparator)) {
+ return errors.Wrapf(err, "failed to get stat of dest path %s", destPath)
+ }
+ }
+ if destfi != nil && destfi.IsDir() {
+ destPath = filepath.Join(destPath, filepath.Base(srcPath))
+ }
+
+ // Copy the file, preserving attributes.
+ logrus.Debugf("copying %q to %q", srcPath, destPath)
+ if err = copyFileWithTar(srcPath, destPath); err != nil {
+ return errors.Wrapf(err, "error copying %q to %q", srcPath, destPath)
+ }
+ return nil
+}
+
+func convertIDMap(idMaps []idtools.IDMap) (convertedIDMap []specs.LinuxIDMapping) {
+ for _, idmap := range idMaps {
+ tempIDMap := specs.LinuxIDMapping{
+ ContainerID: uint32(idmap.ContainerID),
+ HostID: uint32(idmap.HostID),
+ Size: uint32(idmap.Size),
+ }
+ convertedIDMap = append(convertedIDMap, tempIDMap)
+ }
+ return convertedIDMap
+}
+
+func streamFileToStdout(srcPath string, srcfi os.FileInfo) error {
+ if srcfi.IsDir() {
+ tw := tar.NewWriter(os.Stdout)
+ err := filepath.Walk(srcPath, func(path string, info os.FileInfo, err error) error {
+ if err != nil || !info.Mode().IsRegular() || path == srcPath {
+ return err
+ }
+ hdr, err := tar.FileInfoHeader(info, "")
+ if err != nil {
+ return err
+ }
+
+ if err = tw.WriteHeader(hdr); err != nil {
+ return err
+ }
+ fh, err := os.Open(path)
+ if err != nil {
+ return err
+ }
+ defer fh.Close()
+
+ _, err = io.Copy(tw, fh)
+ return err
+ })
+ if err != nil {
+ return errors.Wrapf(err, "error streaming directory %s to Stdout", srcPath)
+ }
+ return nil
+ }
+
+ file, err := os.Open(srcPath)
+ if err != nil {
+ return errors.Wrapf(err, "error opening file %s", srcPath)
+ }
+ defer file.Close()
+ if !archive.IsArchivePath(srcPath) {
+ tw := tar.NewWriter(os.Stdout)
+ hdr, err := tar.FileInfoHeader(srcfi, "")
+ if err != nil {
+ return err
+ }
+ err = tw.WriteHeader(hdr)
+ if err != nil {
+ return err
+ }
+ _, err = io.Copy(tw, file)
+ if err != nil {
+ return errors.Wrapf(err, "error streaming archive %s to Stdout", srcPath)
+ }
+ return nil
+ }
+
+ _, err = io.Copy(os.Stdout, file)
+ if err != nil {
+ return errors.Wrapf(err, "error streaming file to Stdout")
+ }
+ return nil
+}
+
+func isVolumeDestName(path string, ctr *libpod.Container) (bool, string, string) {
+ separator := string(os.PathSeparator)
+ if filepath.IsAbs(path) {
+ path = strings.TrimPrefix(path, separator)
+ }
+ if path == "" {
+ return false, "", ""
+ }
+ for _, vol := range ctr.Config().NamedVolumes {
+ volNamePath := strings.TrimPrefix(vol.Dest, separator)
+ if matchVolumePath(path, volNamePath) {
+ return true, vol.Dest, vol.Name
+ }
+ }
+ return false, "", ""
+}
+
+// if SRCPATH or DESTPATH is from volume mount's destination -v or --mount type=volume, generates the path with volume mount point
+func pathWithVolumeMount(ctr *libpod.Container, runtime *libpod.Runtime, volDestName, volName, path string) (string, error) {
+ destVolume, err := runtime.GetVolume(volName)
+ if err != nil {
+ return "", errors.Wrapf(err, "error getting volume destination %s", volName)
+ }
+ if !filepath.IsAbs(path) {
+ path = filepath.Join(string(os.PathSeparator), path)
+ }
+ path, err = securejoin.SecureJoin(destVolume.MountPoint(), strings.TrimPrefix(path, volDestName))
+ return path, err
+}
+
+func isBindMountDestName(path string, ctr *libpod.Container) (bool, specs.Mount) {
+ separator := string(os.PathSeparator)
+ if filepath.IsAbs(path) {
+ path = strings.TrimPrefix(path, string(os.PathSeparator))
+ }
+ if path == "" {
+ return false, specs.Mount{}
+ }
+ for _, m := range ctr.Config().Spec.Mounts {
+ if m.Type != "bind" {
+ continue
+ }
+ mDest := strings.TrimPrefix(m.Destination, separator)
+ if matchVolumePath(path, mDest) {
+ return true, m
+ }
+ }
+ return false, specs.Mount{}
+}
+
+func matchVolumePath(path, target string) bool {
+ pathStr := filepath.Clean(path)
+ target = filepath.Clean(target)
+ for len(pathStr) > len(target) && strings.Contains(pathStr, string(os.PathSeparator)) {
+ pathStr = pathStr[:strings.LastIndex(pathStr, string(os.PathSeparator))]
+ }
+ return pathStr == target
+}
+
+func pathWithBindMountSource(m specs.Mount, path string) (string, error) {
+ if !filepath.IsAbs(path) {
+ path = filepath.Join(string(os.PathSeparator), path)
+ }
+ return securejoin.SecureJoin(m.Source, strings.TrimPrefix(path, m.Destination))
+}
diff --git a/pkg/domain/infra/abi/events.go b/pkg/domain/infra/abi/events.go
index 9540a5b96..20773cdce 100644
--- a/pkg/domain/infra/abi/events.go
+++ b/pkg/domain/infra/abi/events.go
@@ -1,5 +1,3 @@
-//+build ABISupport
-
package abi
import (
diff --git a/pkg/domain/infra/abi/healthcheck.go b/pkg/domain/infra/abi/healthcheck.go
index 699483243..351bf4f7e 100644
--- a/pkg/domain/infra/abi/healthcheck.go
+++ b/pkg/domain/infra/abi/healthcheck.go
@@ -1,5 +1,3 @@
-// +build ABISupport
-
package abi
import (
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index 68758a71d..7ac111745 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -1,5 +1,3 @@
-// +build ABISupport
-
package abi
import (
@@ -23,6 +21,7 @@ import (
domainUtils "github.com/containers/libpod/pkg/domain/utils"
"github.com/containers/libpod/pkg/util"
"github.com/containers/storage"
+ "github.com/hashicorp/go-multierror"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -36,76 +35,6 @@ func (ir *ImageEngine) Exists(_ context.Context, nameOrId string) (*entities.Boo
return &entities.BoolReport{Value: err == nil}, nil
}
-func (ir *ImageEngine) Delete(ctx context.Context, nameOrId []string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) {
- report := entities.ImageDeleteReport{}
-
- if opts.All {
- var previousTargets []*libpodImage.Image
- repeatRun:
- targets, err := ir.Libpod.ImageRuntime().GetRWImages()
- if err != nil {
- return &report, errors.Wrapf(err, "unable to query local images")
- }
- if len(targets) == 0 {
- return &report, nil
- }
- if len(targets) > 0 && len(targets) == len(previousTargets) {
- return &report, errors.New("unable to delete all images; re-run the rmi command again.")
- }
- previousTargets = targets
-
- for _, img := range targets {
- isParent, err := img.IsParent(ctx)
- if err != nil {
- return &report, err
- }
- if isParent {
- continue
- }
- err = ir.deleteImage(ctx, img, opts, report)
- report.Errors = append(report.Errors, err)
- }
- if len(previousTargets) != 1 {
- goto repeatRun
- }
- return &report, nil
- }
-
- for _, id := range nameOrId {
- image, err := ir.Libpod.ImageRuntime().NewFromLocal(id)
- if err != nil {
- return nil, err
- }
-
- err = ir.deleteImage(ctx, image, opts, report)
- if err != nil {
- return &report, err
- }
- }
- return &report, nil
-}
-
-func (ir *ImageEngine) deleteImage(ctx context.Context, img *libpodImage.Image, opts entities.ImageDeleteOptions, report entities.ImageDeleteReport) error {
- results, err := ir.Libpod.RemoveImage(ctx, img, opts.Force)
- switch errors.Cause(err) {
- case nil:
- break
- case storage.ErrImageUsedByContainer:
- report.ImageInUse = errors.New(
- fmt.Sprintf("A container associated with containers/storage, i.e. via Buildah, CRI-O, etc., may be associated with this image: %-12.12s\n", img.ID()))
- return nil
- case libpodImage.ErrNoSuchImage:
- report.ImageNotFound = err
- return nil
- default:
- return err
- }
-
- report.Deleted = append(report.Deleted, results.Deleted)
- report.Untagged = append(report.Untagged, results.Untagged...)
- return nil
-}
-
func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) {
results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, opts.Filter)
if err != nil {
@@ -171,7 +100,7 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entiti
if imageRef.Transport().Name() == dockerarchive.Transport.Name() {
newImage, err := ir.Libpod.ImageRuntime().LoadFromArchiveReference(ctx, imageRef, options.SignaturePolicy, writer)
if err != nil {
- return nil, errors.Wrapf(err, "error pulling image %q", rawImage)
+ return nil, err
}
return &entities.ImagePullReport{Images: []string{newImage[0].ID()}}, nil
}
@@ -195,7 +124,7 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entiti
if !options.AllTags {
newImage, err := ir.Libpod.ImageRuntime().New(ctx, rawImage, options.SignaturePolicy, options.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, nil, util.PullImageAlways)
if err != nil {
- return nil, errors.Wrapf(err, "error pulling image %q", rawImage)
+ return nil, err
}
return &entities.ImagePullReport{Images: []string{newImage.ID()}}, nil
}
@@ -236,7 +165,7 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entiti
}
if len(tags) != len(foundIDs) {
- return nil, errors.Errorf("error pulling image %q", rawImage)
+ return nil, err
}
return &entities.ImagePullReport{Images: foundIDs}, nil
}
@@ -373,6 +302,10 @@ func (ir *ImageEngine) Untag(ctx context.Context, nameOrId string, tags []string
if err != nil {
return err
}
+ // If only one arg is provided, all names are to be untagged
+ if len(tags) == 0 {
+ tags = newImage.Names()
+ }
for _, tag := range tags {
if err := newImage.UntagImage(tag); err != nil {
return err
@@ -392,16 +325,19 @@ func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions)
if err != nil {
return nil, err
}
- newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(name)
- if err != nil {
- return nil, errors.Wrap(err, "image loaded but no additional tags were created")
- }
- if len(opts.Name) > 0 {
- if err := newImage.TagImage(fmt.Sprintf("%s:%s", opts.Name, opts.Tag)); err != nil {
- return nil, errors.Wrapf(err, "error adding %q to image %q", opts.Name, newImage.InputName)
+ names := strings.Split(name, ",")
+ if len(names) <= 1 {
+ newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(name)
+ if err != nil {
+ return nil, errors.Wrap(err, "image loaded but no additional tags were created")
+ }
+ if len(opts.Name) > 0 {
+ if err := newImage.TagImage(fmt.Sprintf("%s:%s", opts.Name, opts.Tag)); err != nil {
+ return nil, errors.Wrapf(err, "error adding %q to image %q", opts.Name, newImage.InputName)
+ }
}
}
- return &entities.ImageLoadReport{Name: name}, nil
+ return &entities.ImageLoadReport{Names: names}, nil
}
func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOptions) (*entities.ImageImportReport, error) {
@@ -475,3 +411,157 @@ func (ir *ImageEngine) Build(ctx context.Context, containerFiles []string, opts
}
return &entities.BuildReport{ID: id}, nil
}
+
+func (ir *ImageEngine) Tree(ctx context.Context, nameOrId string, opts entities.ImageTreeOptions) (*entities.ImageTreeReport, error) {
+ img, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId)
+ if err != nil {
+ return nil, err
+ }
+ results, err := img.GenerateTree(opts.WhatRequires)
+ if err != nil {
+ return nil, err
+ }
+ return &entities.ImageTreeReport{Tree: results}, nil
+}
+
+// Remove removes one or more images from local storage.
+func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entities.ImageRemoveOptions) (report *entities.ImageRemoveReport, finalError error) {
+ var (
+ // noSuchImageErrors indicates that at least one image was not found.
+ noSuchImageErrors bool
+ // inUseErrors indicates that at least one image is being used by a
+ // container.
+ inUseErrors bool
+ // otherErrors indicates that at least one error other than the two
+ // above occured.
+ otherErrors bool
+ // deleteError is a multierror to conveniently collect errors during
+ // removal. We really want to delete as many images as possible and not
+ // error out immediately.
+ deleteError *multierror.Error
+ )
+
+ report = &entities.ImageRemoveReport{}
+
+ // Set the removalCode and the error after all work is done.
+ defer func() {
+ switch {
+ // 2
+ case inUseErrors:
+ // One of the specified images has child images or is
+ // being used by a container.
+ report.ExitCode = 2
+ // 1
+ case noSuchImageErrors && !(otherErrors || inUseErrors):
+ // One of the specified images did not exist, and no other
+ // failures.
+ report.ExitCode = 1
+ // 0
+ default:
+ // Nothing to do.
+ }
+ if deleteError != nil {
+ // go-multierror has a trailing new line which we need to remove to normalize the string.
+ finalError = deleteError.ErrorOrNil()
+ finalError = errors.New(strings.TrimSpace(finalError.Error()))
+ }
+ }()
+
+ // deleteImage is an anonymous function to conveniently delete an image
+ // without having to pass all local data around.
+ deleteImage := func(img *image.Image) error {
+ results, err := ir.Libpod.RemoveImage(ctx, img, opts.Force)
+ switch errors.Cause(err) {
+ case nil:
+ break
+ case storage.ErrImageUsedByContainer:
+ inUseErrors = true // Important for exit codes in Podman.
+ return errors.New(
+ fmt.Sprintf("A container associated with containers/storage, i.e. via Buildah, CRI-O, etc., may be associated with this image: %-12.12s\n", img.ID()))
+ case define.ErrImageInUse:
+ inUseErrors = true
+ return err
+ default:
+ otherErrors = true // Important for exit codes in Podman.
+ return err
+ }
+
+ report.Deleted = append(report.Deleted, results.Deleted)
+ report.Untagged = append(report.Untagged, results.Untagged...)
+ return nil
+ }
+
+ // Delete all images from the local storage.
+ if opts.All {
+ previousImages := 0
+ // Remove all images one-by-one.
+ for {
+ storageImages, err := ir.Libpod.ImageRuntime().GetRWImages()
+ if err != nil {
+ deleteError = multierror.Append(deleteError,
+ errors.Wrapf(err, "unable to query local images"))
+ otherErrors = true // Important for exit codes in Podman.
+ return
+ }
+ // No images (left) to remove, so we're done.
+ if len(storageImages) == 0 {
+ return
+ }
+ // Prevent infinity loops by making a delete-progress check.
+ if previousImages == len(storageImages) {
+ otherErrors = true // Important for exit codes in Podman.
+ deleteError = multierror.Append(deleteError,
+ errors.New("unable to delete all images, check errors and re-run image removal if needed"))
+ break
+ }
+ previousImages = len(storageImages)
+ // Delete all "leaves" (i.e., images without child images).
+ for _, img := range storageImages {
+ isParent, err := img.IsParent(ctx)
+ if err != nil {
+ otherErrors = true // Important for exit codes in Podman.
+ deleteError = multierror.Append(deleteError, err)
+ }
+ // Skip parent images.
+ if isParent {
+ continue
+ }
+ if err := deleteImage(img); err != nil {
+ deleteError = multierror.Append(deleteError, err)
+ }
+ }
+ }
+
+ return
+ }
+
+ // Delete only the specified images.
+ for _, id := range images {
+ img, err := ir.Libpod.ImageRuntime().NewFromLocal(id)
+ switch errors.Cause(err) {
+ case nil:
+ break
+ case image.ErrNoSuchImage:
+ noSuchImageErrors = true // Important for exit codes in Podman.
+ fallthrough
+ default:
+ deleteError = multierror.Append(deleteError, err)
+ continue
+ }
+
+ err = deleteImage(img)
+ if err != nil {
+ otherErrors = true // Important for exit codes in Podman.
+ deleteError = multierror.Append(deleteError, err)
+ }
+ }
+
+ return
+}
+
+// Shutdown Libpod engine
+func (ir *ImageEngine) Shutdown(_ context.Context) {
+ shutdownSync.Do(func() {
+ _ = ir.Libpod.Shutdown(false)
+ })
+}
diff --git a/pkg/domain/infra/abi/images_list.go b/pkg/domain/infra/abi/images_list.go
index 68b961cb6..9add915ea 100644
--- a/pkg/domain/infra/abi/images_list.go
+++ b/pkg/domain/infra/abi/images_list.go
@@ -1,5 +1,3 @@
-// +build ABISupport
-
package abi
import (
diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go
new file mode 100644
index 000000000..88331f96c
--- /dev/null
+++ b/pkg/domain/infra/abi/manifest.go
@@ -0,0 +1,102 @@
+// +build ABISupport
+
+package abi
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "strings"
+
+ buildahUtil "github.com/containers/buildah/util"
+ "github.com/containers/image/v5/docker"
+ "github.com/containers/image/v5/transports/alltransports"
+ libpodImage "github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/util"
+
+ "github.com/pkg/errors"
+)
+
+// ManifestCreate implements logic for creating manifest lists via ImageEngine
+func (ir *ImageEngine) ManifestCreate(ctx context.Context, names, images []string, opts entities.ManifestCreateOptions) (string, error) {
+ fullNames, err := buildahUtil.ExpandNames(names, "", ir.Libpod.SystemContext(), ir.Libpod.GetStore())
+ if err != nil {
+ return "", errors.Wrapf(err, "error encountered while expanding image name %q", names)
+ }
+ imageID, err := libpodImage.CreateManifestList(ir.Libpod.ImageRuntime(), *ir.Libpod.SystemContext(), fullNames, images, opts.All)
+ if err != nil {
+ return imageID, err
+ }
+ return imageID, err
+}
+
+// ManifestInspect returns the content of a manifest list or image
+func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte, error) {
+ dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name())
+ _, err := alltransports.ParseImageName(name)
+ if err != nil {
+ _, err = alltransports.ParseImageName(dockerPrefix + name)
+ if err != nil {
+ return nil, errors.Errorf("invalid image reference %q", name)
+ }
+ }
+ image, err := ir.Libpod.ImageRuntime().New(ctx, name, "", "", nil, nil, libpodImage.SigningOptions{}, nil, util.PullImageMissing)
+ if err != nil {
+ return nil, errors.Wrapf(err, "reading image %q", name)
+ }
+
+ list, err := image.InspectManifest()
+ if err != nil {
+ return nil, errors.Wrapf(err, "loading manifest %q", name)
+ }
+ buf, err := json.MarshalIndent(list, "", " ")
+ if err != nil {
+ return buf, errors.Wrapf(err, "error rendering manifest for display")
+ }
+ return buf, nil
+}
+
+// ManifestAdd adds images to the manifest list
+func (ir *ImageEngine) ManifestAdd(ctx context.Context, opts entities.ManifestAddOptions) (string, error) {
+ imageSpec := opts.Images[0]
+ listImageSpec := opts.Images[1]
+ dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name())
+ _, err := alltransports.ParseImageName(imageSpec)
+ if err != nil {
+ _, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", dockerPrefix, imageSpec))
+ if err != nil {
+ return "", errors.Errorf("invalid image reference %q", imageSpec)
+ }
+ }
+ listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(listImageSpec)
+ if err != nil {
+ return "", errors.Wrapf(err, "error retriving local image from image name %s", listImageSpec)
+ }
+
+ manifestAddOpts := libpodImage.ManifestAddOpts{
+ All: opts.All,
+ Arch: opts.Arch,
+ Features: opts.Features,
+ Images: opts.Images,
+ OS: opts.OS,
+ OSVersion: opts.OSVersion,
+ Variant: opts.Variant,
+ }
+ if len(opts.Annotation) != 0 {
+ annotations := make(map[string]string)
+ for _, annotationSpec := range opts.Annotation {
+ spec := strings.SplitN(annotationSpec, "=", 2)
+ if len(spec) != 2 {
+ return "", errors.Errorf("no value given for annotation %q", spec[0])
+ }
+ annotations[spec[0]] = spec[1]
+ }
+ manifestAddOpts.Annotation = annotations
+ }
+ listID, err := listImage.AddManifest(*ir.Libpod.SystemContext(), manifestAddOpts)
+ if err != nil {
+ return listID, err
+ }
+ return listID, nil
+}
diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go
index 6b6e13e24..7c06f9a4e 100644
--- a/pkg/domain/infra/abi/pods.go
+++ b/pkg/domain/infra/abi/pods.go
@@ -1,5 +1,3 @@
-// +build ABISupport
-
package abi
import (
@@ -147,7 +145,7 @@ func (ic *ContainerEngine) PodStop(ctx context.Context, namesOrIds []string, opt
reports []*entities.PodStopReport
)
pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
- if err != nil {
+ if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchPod) {
return nil, err
}
for _, p := range pods {
@@ -182,6 +180,7 @@ func (ic *ContainerEngine) PodRestart(ctx context.Context, namesOrIds []string,
errs, err := p.Restart(ctx)
if err != nil {
report.Errs = []error{err}
+ reports = append(reports, &report)
continue
}
if len(errs) > 0 {
@@ -209,6 +208,7 @@ func (ic *ContainerEngine) PodStart(ctx context.Context, namesOrIds []string, op
errs, err := p.Start(ctx)
if err != nil {
report.Errs = []error{err}
+ reports = append(reports, &report)
continue
}
if len(errs) > 0 {
@@ -228,7 +228,7 @@ func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, optio
reports []*entities.PodRmReport
)
pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
- if err != nil {
+ if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchPod) {
return nil, err
}
for _, p := range pods {
@@ -236,6 +236,7 @@ func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, optio
err := ic.Libpod.RemovePod(ctx, p, true, options.Force)
if err != nil {
report.Err = err
+ reports = append(reports, &report)
continue
}
reports = append(reports, &report)
@@ -294,9 +295,12 @@ func (ic *ContainerEngine) PodTop(ctx context.Context, options entities.PodTopOp
func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOptions) ([]*entities.ListPodsReport, error) {
var (
+ err error
filters []libpod.PodFilter
+ pds []*libpod.Pod
reports []*entities.ListPodsReport
)
+
for k, v := range options.Filters {
for _, filter := range v {
f, err := lpfilters.GeneratePodFilterFunc(k, filter)
@@ -307,10 +311,19 @@ func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOpti
}
}
- pds, err := ic.Libpod.Pods(filters...)
- if err != nil {
- return nil, err
+ if options.Latest {
+ pod, err := ic.Libpod.GetLatestPod()
+ if err != nil {
+ return nil, err
+ }
+ pds = append(pds, pod)
+ } else {
+ pds, err = ic.Libpod.Pods(filters...)
+ if err != nil {
+ return nil, err
+ }
}
+
for _, p := range pds {
var lpcs []*entities.ListPodContainer
status, err := p.GetPodStatus()
diff --git a/pkg/domain/infra/abi/pods_stats.go b/pkg/domain/infra/abi/pods_stats.go
new file mode 100644
index 000000000..a41c01da0
--- /dev/null
+++ b/pkg/domain/infra/abi/pods_stats.go
@@ -0,0 +1,85 @@
+package abi
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/cgroups"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/rootless"
+ "github.com/docker/go-units"
+ "github.com/pkg/errors"
+)
+
+// PodStats implements printing stats about pods.
+func (ic *ContainerEngine) PodStats(ctx context.Context, namesOrIds []string, options entities.PodStatsOptions) ([]*entities.PodStatsReport, error) {
+ // Cgroups v2 check for rootless.
+ if rootless.IsRootless() {
+ unified, err := cgroups.IsCgroup2UnifiedMode()
+ if err != nil {
+ return nil, err
+ }
+ if !unified {
+ return nil, errors.New("pod stats is not supported in rootless mode without cgroups v2")
+ }
+ }
+ // Get the (running) pods and convert them to the entities format.
+ pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
+ if err != nil {
+ return nil, errors.Wrap(err, "unable to get list of pods")
+ }
+ return ic.podsToStatsReport(pods)
+}
+
+// podsToStatsReport converts a slice of pods into a corresponding slice of stats reports.
+func (ic *ContainerEngine) podsToStatsReport(pods []*libpod.Pod) ([]*entities.PodStatsReport, error) {
+ reports := []*entities.PodStatsReport{}
+ for i := range pods { // Access by index to prevent potential loop-variable leaks.
+ podStats, err := pods[i].GetPodStats(nil)
+ if err != nil {
+ return nil, err
+ }
+ podID := pods[i].ID()[:12]
+ for j := range podStats {
+ r := entities.PodStatsReport{
+ CPU: floatToPercentString(podStats[j].CPU),
+ MemUsage: combineHumanValues(podStats[j].MemUsage, podStats[j].MemLimit),
+ Mem: floatToPercentString(podStats[j].MemPerc),
+ NetIO: combineHumanValues(podStats[j].NetInput, podStats[j].NetOutput),
+ BlockIO: combineHumanValues(podStats[j].BlockInput, podStats[j].BlockOutput),
+ PIDS: pidsToString(podStats[j].PIDs),
+ CID: podStats[j].ContainerID[:12],
+ Name: podStats[j].Name,
+ Pod: podID,
+ }
+ reports = append(reports, &r)
+ }
+ }
+
+ return reports, nil
+}
+
+func combineHumanValues(a, b uint64) string {
+ if a == 0 && b == 0 {
+ return "-- / --"
+ }
+ return fmt.Sprintf("%s / %s", units.HumanSize(float64(a)), units.HumanSize(float64(b)))
+}
+
+func floatToPercentString(f float64) string {
+ strippedFloat, err := libpod.RemoveScientificNotationFromFloat(f)
+ if err != nil || strippedFloat == 0 {
+ // If things go bazinga, return a safe value
+ return "--"
+ }
+ return fmt.Sprintf("%.2f", strippedFloat) + "%"
+}
+
+func pidsToString(pid uint64) string {
+ if pid == 0 {
+ // If things go bazinga, return a safe value
+ return "--"
+ }
+ return fmt.Sprintf("%d", pid)
+}
diff --git a/pkg/domain/infra/abi/runtime.go b/pkg/domain/infra/abi/runtime.go
index b53fb6d3a..fba422d8e 100644
--- a/pkg/domain/infra/abi/runtime.go
+++ b/pkg/domain/infra/abi/runtime.go
@@ -1,8 +1,8 @@
-// +build ABISupport
-
package abi
import (
+ "sync"
+
"github.com/containers/libpod/libpod"
)
@@ -15,3 +15,5 @@ type ImageEngine struct {
type ContainerEngine struct {
Libpod *libpod.Runtime
}
+
+var shutdownSync sync.Once
diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go
index 67593b2dd..e5c109ee6 100644
--- a/pkg/domain/infra/abi/system.go
+++ b/pkg/domain/infra/abi/system.go
@@ -1,20 +1,15 @@
-// +build ABISupport
-
package abi
import (
"context"
"fmt"
"io/ioutil"
- "net"
"os"
"strconv"
- "strings"
"syscall"
"github.com/containers/common/pkg/config"
"github.com/containers/libpod/libpod/define"
- api "github.com/containers/libpod/pkg/api/server"
"github.com/containers/libpod/pkg/cgroups"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/rootless"
@@ -33,39 +28,6 @@ func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) {
return ic.Libpod.Info()
}
-func (ic *ContainerEngine) RestService(_ context.Context, opts entities.ServiceOptions) error {
- var (
- listener net.Listener
- err error
- )
-
- if opts.URI != "" {
- fields := strings.Split(opts.URI, ":")
- if len(fields) == 1 {
- return errors.Errorf("%s is an invalid socket destination", opts.URI)
- }
- address := strings.Join(fields[1:], ":")
- listener, err = net.Listen(fields[0], address)
- if err != nil {
- return errors.Wrapf(err, "unable to create socket %s", opts.URI)
- }
- }
-
- server, err := api.NewServerWithSettings(ic.Libpod, opts.Timeout, &listener)
- if err != nil {
- return err
- }
- defer func() {
- if err := server.Shutdown(); err != nil {
- logrus.Warnf("Error when stopping API service: %s", err)
- }
- }()
-
- err = server.Serve()
- _ = listener.Close()
- return err
-}
-
func (ic *ContainerEngine) VarlinkService(_ context.Context, opts entities.ServiceOptions) error {
var varlinkInterfaces = []*iopodman.VarlinkInterface{
iopodmanAPI.New(opts.Command, ic.Libpod),
diff --git a/pkg/domain/infra/abi/terminal/sigproxy_linux.go b/pkg/domain/infra/abi/terminal/sigproxy_linux.go
index d7f5853d8..b422e549e 100644
--- a/pkg/domain/infra/abi/terminal/sigproxy_linux.go
+++ b/pkg/domain/infra/abi/terminal/sigproxy_linux.go
@@ -1,5 +1,3 @@
-// +build ABISupport
-
package terminal
import (
diff --git a/pkg/domain/infra/abi/terminal/terminal.go b/pkg/domain/infra/abi/terminal/terminal.go
index f187bdd6b..0fc3af511 100644
--- a/pkg/domain/infra/abi/terminal/terminal.go
+++ b/pkg/domain/infra/abi/terminal/terminal.go
@@ -1,5 +1,3 @@
-// +build ABISupport
-
package terminal
import (
diff --git a/pkg/domain/infra/abi/terminal/terminal_linux.go b/pkg/domain/infra/abi/terminal/terminal_linux.go
index 664205df1..15701342f 100644
--- a/pkg/domain/infra/abi/terminal/terminal_linux.go
+++ b/pkg/domain/infra/abi/terminal/terminal_linux.go
@@ -1,5 +1,3 @@
-// +build ABISupport
-
package terminal
import (
diff --git a/pkg/domain/infra/runtime_libpod.go b/pkg/domain/infra/runtime_libpod.go
index dc59fec3d..7c9180d43 100644
--- a/pkg/domain/infra/runtime_libpod.go
+++ b/pkg/domain/infra/runtime_libpod.go
@@ -6,6 +6,7 @@ import (
"context"
"fmt"
"os"
+ "sync"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/cgroups"
@@ -18,6 +19,14 @@ import (
flag "github.com/spf13/pflag"
)
+var (
+ // runtimeSync only guards the non-specialized runtime
+ runtimeSync sync.Once
+ // The default GetRuntime() always returns the same object and error
+ runtimeLib *libpod.Runtime
+ runtimeErr error
+)
+
type engineOpts struct {
name string
renumber bool
@@ -63,13 +72,16 @@ func GetRuntimeRenumber(ctx context.Context, fs *flag.FlagSet, cfg *entities.Pod
// GetRuntime generates a new libpod runtime configured by command line options
func GetRuntime(ctx context.Context, flags *flag.FlagSet, cfg *entities.PodmanConfig) (*libpod.Runtime, error) {
- return getRuntime(ctx, flags, &engineOpts{
- renumber: false,
- migrate: false,
- noStore: false,
- withFDS: true,
- config: cfg,
+ runtimeSync.Do(func() {
+ runtimeLib, runtimeErr = getRuntime(ctx, flags, &engineOpts{
+ renumber: false,
+ migrate: false,
+ noStore: false,
+ withFDS: true,
+ config: cfg,
+ })
})
+ return runtimeLib, runtimeErr
}
// GetRuntimeNoStore generates a new libpod runtime configured by command line options
@@ -234,6 +246,18 @@ func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []strin
HostGIDMapping: true,
}
+ if mode.IsAuto() {
+ var err error
+ options.HostUIDMapping = false
+ options.HostGIDMapping = false
+ options.AutoUserNs = true
+ opts, err := mode.GetAutoOptions()
+ if err != nil {
+ return nil, err
+ }
+ options.AutoUserNsOpts = *opts
+ return &options, nil
+ }
if mode.IsKeepID() {
if len(uidMapSlice) > 0 || len(gidMapSlice) > 0 {
return nil, errors.New("cannot specify custom mappings with --userns=keep-id")
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index 679bb371b..32f9c4e36 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -115,11 +115,15 @@ func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []st
t := int(*options.Timeout)
timeout = &t
}
+
ctrs, err := getContainersByContext(ic.ClientCxt, options.All, namesOrIds)
if err != nil {
return nil, err
}
for _, c := range ctrs {
+ if options.Running && c.State != define.ContainerStateRunning.String() {
+ continue
+ }
reports = append(reports, &entities.RestartReport{
Id: c.ID,
Err: containers.Restart(ic.ClientCxt, c.ID, timeout),
@@ -371,3 +375,15 @@ func (ic *ContainerEngine) ContainerUnmount(ctx context.Context, nameOrIds []str
func (ic *ContainerEngine) Config(_ context.Context) (*config.Config, error) {
return config.Default()
}
+
+func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrId string, options entities.ContainerPortOptions) ([]*entities.ContainerPortReport, error) {
+ return nil, errors.New("not implemented")
+}
+
+func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, options entities.ContainerCpOptions) (*entities.ContainerCpReport, error) {
+ return nil, errors.New("not implemented")
+}
+
+// Shutdown Libpod engine
+func (ic *ContainerEngine) Shutdown(_ context.Context) {
+}
diff --git a/pkg/domain/infra/tunnel/events.go b/pkg/domain/infra/tunnel/events.go
index 46d88341a..93da3aeb4 100644
--- a/pkg/domain/infra/tunnel/events.go
+++ b/pkg/domain/infra/tunnel/events.go
@@ -4,7 +4,6 @@ import (
"context"
"strings"
- "github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/bindings/system"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/pkg/errors"
@@ -21,10 +20,10 @@ func (ic *ContainerEngine) Events(ctx context.Context, opts entities.EventsOptio
filters[split[0]] = append(filters[split[0]], strings.Join(split[1:], "="))
}
}
- binChan := make(chan handlers.Event)
+ binChan := make(chan entities.Event)
go func() {
for e := range binChan {
- opts.EventChan <- e.ToLibpodEvent()
+ opts.EventChan <- entities.ConvertToLibpodEvent(e)
}
}()
return system.Events(ic.ClientCxt, binChan, nil, &opts.Since, &opts.Until, filters)
diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go
index 6ea2bd9f2..66e4e6e3f 100644
--- a/pkg/domain/infra/tunnel/images.go
+++ b/pkg/domain/infra/tunnel/images.go
@@ -7,6 +7,7 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/image/v5/docker/reference"
+ "github.com/containers/libpod/pkg/bindings"
images "github.com/containers/libpod/pkg/bindings/images"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/domain/utils"
@@ -19,25 +20,8 @@ func (ir *ImageEngine) Exists(_ context.Context, nameOrId string) (*entities.Boo
return &entities.BoolReport{Value: found}, err
}
-func (ir *ImageEngine) Delete(ctx context.Context, nameOrId []string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) {
- report := entities.ImageDeleteReport{}
-
- for _, id := range nameOrId {
- results, err := images.Remove(ir.ClientCxt, id, &opts.Force)
- if err != nil {
- return nil, err
- }
- for _, e := range results {
- if a, ok := e["Deleted"]; ok {
- report.Deleted = append(report.Deleted, a)
- }
-
- if a, ok := e["Untagged"]; ok {
- report.Untagged = append(report.Untagged, a)
- }
- }
- }
- return &report, nil
+func (ir *ImageEngine) Remove(ctx context.Context, imagesArg []string, opts entities.ImageRemoveOptions) (*entities.ImageRemoveReport, error) {
+ return images.Remove(ir.ClientCxt, imagesArg, opts)
}
func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) ([]*entities.ImageSummary, error) {
@@ -126,6 +110,15 @@ func (ir *ImageEngine) Tag(ctx context.Context, nameOrId string, tags []string,
}
func (ir *ImageEngine) Untag(ctx context.Context, nameOrId string, tags []string, options entities.ImageUntagOptions) error {
+ // Remove all tags if none are provided
+ if len(tags) == 0 {
+ newImage, err := images.GetImage(ir.ClientCxt, nameOrId, &bindings.PFalse)
+ if err != nil {
+ return err
+ }
+ tags = newImage.NamesHistory
+ }
+
for _, newTag := range tags {
var (
tag, repo string
@@ -263,3 +256,11 @@ func (ir *ImageEngine) Config(_ context.Context) (*config.Config, error) {
func (ir *ImageEngine) Build(ctx context.Context, containerFiles []string, opts entities.BuildOptions) (*entities.BuildReport, error) {
return nil, errors.New("not implemented yet")
}
+
+func (ir *ImageEngine) Tree(ctx context.Context, nameOrId string, opts entities.ImageTreeOptions) (*entities.ImageTreeReport, error) {
+ return nil, errors.New("not implemented yet")
+}
+
+// Shutdown Libpod engine
+func (ir *ImageEngine) Shutdown(_ context.Context) {
+}
diff --git a/pkg/domain/infra/tunnel/manifest.go b/pkg/domain/infra/tunnel/manifest.go
new file mode 100644
index 000000000..18b400533
--- /dev/null
+++ b/pkg/domain/infra/tunnel/manifest.go
@@ -0,0 +1,64 @@
+package tunnel
+
+import (
+ "context"
+ "encoding/json"
+ "strings"
+
+ "github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/bindings/manifests"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+)
+
+// ManifestCreate implements manifest create via ImageEngine
+func (ir *ImageEngine) ManifestCreate(ctx context.Context, names, images []string, opts entities.ManifestCreateOptions) (string, error) {
+ imageID, err := manifests.Create(ir.ClientCxt, names, images, &opts.All)
+ if err != nil {
+ return imageID, errors.Wrapf(err, "error creating manifest")
+ }
+ return imageID, err
+}
+
+// ManifestInspect returns contents of manifest list with given name
+func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte, error) {
+ list, err := manifests.Inspect(ir.ClientCxt, name)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting content of manifest list or image %s", name)
+ }
+
+ buf, err := json.MarshalIndent(list, "", " ")
+ if err != nil {
+ return buf, errors.Wrapf(err, "error rendering manifest for display")
+ }
+ return buf, err
+}
+
+// ManifestAdd adds images to the manifest list
+func (ir *ImageEngine) ManifestAdd(ctx context.Context, opts entities.ManifestAddOptions) (string, error) {
+ manifestAddOpts := image.ManifestAddOpts{
+ All: opts.All,
+ Arch: opts.Arch,
+ Features: opts.Features,
+ Images: opts.Images,
+ OS: opts.OS,
+ OSVersion: opts.OSVersion,
+ Variant: opts.Variant,
+ }
+ if len(opts.Annotation) != 0 {
+ annotations := make(map[string]string)
+ for _, annotationSpec := range opts.Annotation {
+ spec := strings.SplitN(annotationSpec, "=", 2)
+ if len(spec) != 2 {
+ return "", errors.Errorf("no value given for annotation %q", spec[0])
+ }
+ annotations[spec[0]] = spec[1]
+ }
+ manifestAddOpts.Annotation = annotations
+ }
+ listID, err := manifests.Add(ctx, opts.Images[1], manifestAddOpts)
+ if err != nil {
+ return listID, errors.Wrapf(err, "error adding to manifest list %s", opts.Images[1])
+ }
+ return listID, nil
+}
diff --git a/pkg/domain/infra/tunnel/pods.go b/pkg/domain/infra/tunnel/pods.go
index e7641c077..c193c6752 100644
--- a/pkg/domain/infra/tunnel/pods.go
+++ b/pkg/domain/infra/tunnel/pods.go
@@ -211,3 +211,7 @@ func (ic *ContainerEngine) PodInspect(ctx context.Context, options entities.PodI
}
return pods.Inspect(ic.ClientCxt, options.NameOrID)
}
+
+func (ic *ContainerEngine) PodStats(ctx context.Context, namesOrIds []string, options entities.PodStatsOptions) ([]*entities.PodStatsReport, error) {
+ return pods.Stats(ic.ClientCxt, namesOrIds, options)
+}
diff --git a/pkg/domain/infra/tunnel/system.go b/pkg/domain/infra/tunnel/system.go
index f373525c5..97bf885e7 100644
--- a/pkg/domain/infra/tunnel/system.go
+++ b/pkg/domain/infra/tunnel/system.go
@@ -14,10 +14,6 @@ func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) {
return system.Info(ic.ClientCxt)
}
-func (ic *ContainerEngine) RestService(_ context.Context, _ entities.ServiceOptions) error {
- panic(errors.New("rest service is not supported when tunneling"))
-}
-
func (ic *ContainerEngine) VarlinkService(_ context.Context, _ entities.ServiceOptions) error {
panic(errors.New("varlink service is not supported when tunneling"))
}