summaryrefslogtreecommitdiff
path: root/pkg/domain
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/domain')
-rw-r--r--pkg/domain/entities/auto-update.go7
-rw-r--r--pkg/domain/entities/containers.go50
-rw-r--r--pkg/domain/entities/engine.go9
-rw-r--r--pkg/domain/entities/engine_container.go11
-rw-r--r--pkg/domain/entities/engine_image.go7
-rw-r--r--pkg/domain/entities/engine_system.go14
-rw-r--r--pkg/domain/entities/generate.go14
-rw-r--r--pkg/domain/entities/images.go24
-rw-r--r--pkg/domain/entities/manifest.go15
-rw-r--r--pkg/domain/entities/network.go52
-rw-r--r--pkg/domain/entities/play.go36
-rw-r--r--pkg/domain/entities/system.go57
-rw-r--r--pkg/domain/entities/types.go3
-rw-r--r--pkg/domain/infra/abi/auto-update.go13
-rw-r--r--pkg/domain/infra/abi/containers.go112
-rw-r--r--pkg/domain/infra/abi/containers_runlabel.go280
-rw-r--r--pkg/domain/infra/abi/generate.go85
-rw-r--r--pkg/domain/infra/abi/images.go115
-rw-r--r--pkg/domain/infra/abi/manifest.go110
-rw-r--r--pkg/domain/infra/abi/network.go258
-rw-r--r--pkg/domain/infra/abi/play.go544
-rw-r--r--pkg/domain/infra/abi/pods.go4
-rw-r--r--pkg/domain/infra/abi/pods_stats.go3
-rw-r--r--pkg/domain/infra/abi/runtime.go5
-rw-r--r--pkg/domain/infra/abi/system.go216
-rw-r--r--pkg/domain/infra/abi/trust.go171
-rw-r--r--pkg/domain/infra/abi/volumes.go6
-rw-r--r--pkg/domain/infra/runtime_abi.go31
-rw-r--r--pkg/domain/infra/runtime_abi_unsupported.go14
-rw-r--r--pkg/domain/infra/runtime_image_proxy.go21
-rw-r--r--pkg/domain/infra/runtime_proxy.go8
-rw-r--r--pkg/domain/infra/tunnel/auto-update.go12
-rw-r--r--pkg/domain/infra/tunnel/containers.go8
-rw-r--r--pkg/domain/infra/tunnel/generate.go5
-rw-r--r--pkg/domain/infra/tunnel/images.go4
-rw-r--r--pkg/domain/infra/tunnel/manifest.go46
-rw-r--r--pkg/domain/infra/tunnel/network.go23
-rw-r--r--pkg/domain/infra/tunnel/play.go12
-rw-r--r--pkg/domain/infra/tunnel/system.go9
-rw-r--r--pkg/domain/infra/tunnel/trust.go16
40 files changed, 2328 insertions, 102 deletions
diff --git a/pkg/domain/entities/auto-update.go b/pkg/domain/entities/auto-update.go
new file mode 100644
index 000000000..aef8fc46b
--- /dev/null
+++ b/pkg/domain/entities/auto-update.go
@@ -0,0 +1,7 @@
+package entities
+
+// AutoUpdateReport contains the results from running auto-update.
+type AutoUpdateReport struct {
+ // Units - the restarted systemd units during auto-update.
+ Units []string
+}
diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go
index e58258b75..e5330e1ab 100644
--- a/pkg/domain/entities/containers.go
+++ b/pkg/domain/entities/containers.go
@@ -6,11 +6,49 @@ import (
"os"
"time"
+ "github.com/containers/image/v5/types"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/specgen"
"github.com/cri-o/ocicni/pkg/ocicni"
)
+// ContainerRunlabelOptions are the options to execute container-runlabel.
+type ContainerRunlabelOptions struct {
+ // Authfile - path to an authentication file.
+ Authfile string
+ // CertDir - path to a directory containing TLS certifications and
+ // keys.
+ CertDir string
+ // Credentials - `user:password` to use when pulling an image.
+ Credentials string
+ // Display - do not execute but print the command.
+ Display bool
+ // Replace - replace an existing container with a new one from the
+ // image.
+ Replace bool
+ // Name - use this name when executing the runlabel container.
+ Name string
+ // Optional1 - fist optional parameter for install.
+ Optional1 string
+ // Optional2 - second optional parameter for install.
+ Optional2 string
+ // Optional3 - third optional parameter for install.
+ Optional3 string
+ // Pull - pull the specified image if it's not in the local storage.
+ Pull bool
+ // Quiet - suppress output when pulling images.
+ Quiet bool
+ // SignaturePolicy - path to a signature-policy file.
+ SignaturePolicy string
+ // SkipTLSVerify - skip HTTPS and certificate verifications when
+ // contacting registries.
+ SkipTLSVerify types.OptionalBool
+}
+
+// ContainerRunlabelReport contains the results from executing container-runlabel.
+type ContainerRunlabelReport struct {
+}
+
type WaitOptions struct {
Condition define.ContainerStatus
Interval time.Duration
@@ -227,6 +265,7 @@ type ContainerStartOptions struct {
// containers from the cli
type ContainerStartReport struct {
Id string
+ RawInput string
Err error
ExitCode int
}
@@ -366,3 +405,14 @@ type ContainerCpOptions struct {
// ContainerCpReport describes the output from a cp operation
type ContainerCpReport struct {
}
+
+// ContainerStatsOptions describes input options for getting
+// stats on containers
+type ContainerStatsOptions struct {
+ All bool
+ Format string
+ Latest bool
+ NoReset bool
+ NoStream bool
+ StatChan chan []*define.ContainerStats
+}
diff --git a/pkg/domain/entities/engine.go b/pkg/domain/entities/engine.go
index f45218d14..265c9f36f 100644
--- a/pkg/domain/entities/engine.go
+++ b/pkg/domain/entities/engine.go
@@ -12,9 +12,18 @@ import (
// EngineMode is the connection type podman is using to access libpod
type EngineMode string
+// EngineSetup calls out whether a "normal" or specialized engine should be created
+type EngineSetup string
+
const (
ABIMode = EngineMode("abi")
TunnelMode = EngineMode("tunnel")
+
+ MigrateMode = EngineSetup("migrate")
+ NoFDsMode = EngineSetup("disablefds")
+ NormalMode = EngineSetup("normal")
+ RenumberMode = EngineSetup("renumber")
+ ResetMode = EngineSetup("reset")
)
// Convert EngineMode to String
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
index eebf4c033..7c93e6802 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -10,6 +10,7 @@ import (
)
type ContainerEngine interface {
+ AutoUpdate(ctx context.Context) (*AutoUpdateReport, []error)
Config(ctx context.Context) (*config.Config, error)
ContainerAttach(ctx context.Context, nameOrId string, options AttachOptions) error
ContainerCheckpoint(ctx context.Context, namesOrIds []string, options CheckpointOptions) ([]*CheckpointReport, error)
@@ -34,7 +35,9 @@ type ContainerEngine interface {
ContainerRestore(ctx context.Context, namesOrIds []string, options RestoreOptions) ([]*RestoreReport, error)
ContainerRm(ctx context.Context, namesOrIds []string, options RmOptions) ([]*RmReport, error)
ContainerRun(ctx context.Context, opts ContainerRunOptions) (*ContainerRunReport, error)
+ ContainerRunlabel(ctx context.Context, label string, image string, args []string, opts ContainerRunlabelOptions) error
ContainerStart(ctx context.Context, namesOrIds []string, options ContainerStartOptions) ([]*ContainerStartReport, error)
+ ContainerStats(ctx context.Context, namesOrIds []string, options ContainerStatsOptions) error
ContainerStop(ctx context.Context, namesOrIds []string, options StopOptions) ([]*StopReport, error)
ContainerTop(ctx context.Context, options TopOptions) (*StringSliceReport, error)
ContainerUnmount(ctx context.Context, nameOrIds []string, options ContainerUnmountOptions) ([]*ContainerUnmountReport, error)
@@ -42,8 +45,15 @@ type ContainerEngine interface {
ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error)
Events(ctx context.Context, opts EventsOptions) error
GenerateSystemd(ctx context.Context, nameOrID string, opts GenerateSystemdOptions) (*GenerateSystemdReport, error)
+ GenerateKube(ctx context.Context, nameOrID string, opts GenerateKubeOptions) (*GenerateKubeReport, error)
+ SystemPrune(ctx context.Context, options SystemPruneOptions) (*SystemPruneReport, error)
HealthCheckRun(ctx context.Context, nameOrId string, options HealthCheckOptions) (*define.HealthCheckResults, error)
Info(ctx context.Context) (*define.Info, error)
+ NetworkCreate(ctx context.Context, name string, options NetworkCreateOptions) (*NetworkCreateReport, error)
+ NetworkInspect(ctx context.Context, namesOrIds []string, options NetworkInspectOptions) ([]NetworkInspectReport, error)
+ NetworkList(ctx context.Context, options NetworkListOptions) ([]*NetworkListReport, error)
+ NetworkRm(ctx context.Context, namesOrIds []string, options NetworkRmOptions) ([]*NetworkRmReport, error)
+ PlayKube(ctx context.Context, path string, opts PlayKubeOptions) (*PlayKubeReport, error)
PodCreate(ctx context.Context, opts PodCreateOptions) (*PodCreateReport, error)
PodExists(ctx context.Context, nameOrId string) (*BoolReport, error)
PodInspect(ctx context.Context, options PodInspectOptions) (*PodInspectReport, error)
@@ -60,6 +70,7 @@ type ContainerEngine interface {
PodUnpause(ctx context.Context, namesOrIds []string, options PodunpauseOptions) ([]*PodUnpauseReport, error)
SetupRootless(ctx context.Context, cmd *cobra.Command) error
Shutdown(ctx context.Context)
+ SystemDf(ctx context.Context, options SystemDfOptions) (*SystemDfReport, error)
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 46a96ca20..ffa71abd6 100644
--- a/pkg/domain/entities/engine_image.go
+++ b/pkg/domain/entities/engine_image.go
@@ -19,9 +19,11 @@ 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)
+ 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)
+ SetTrust(ctx context.Context, args []string, options SetTrustOptions) error
+ ShowTrust(ctx context.Context, args []string, options ShowTrustOptions) (*ShowTrustReport, 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)
@@ -29,4 +31,7 @@ type ImageEngine interface {
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)
+ ManifestAnnotate(ctx context.Context, names []string, opts ManifestAnnotateOptions) (string, error)
+ ManifestRemove(ctx context.Context, names []string) (string, error)
+ ManifestPush(ctx context.Context, names []string, manifestPushOpts ManifestPushOptions) error
}
diff --git a/pkg/domain/entities/engine_system.go b/pkg/domain/entities/engine_system.go
new file mode 100644
index 000000000..e2000f5cb
--- /dev/null
+++ b/pkg/domain/entities/engine_system.go
@@ -0,0 +1,14 @@
+package entities
+
+import (
+ "context"
+
+ "github.com/spf13/pflag"
+)
+
+type SystemEngine interface {
+ Renumber(ctx context.Context, flags *pflag.FlagSet, config *PodmanConfig) error
+ Migrate(ctx context.Context, flags *pflag.FlagSet, config *PodmanConfig, options SystemMigrateOptions) error
+ Reset(ctx context.Context, options SystemResetOptions) error
+ Shutdown(ctx context.Context)
+}
diff --git a/pkg/domain/entities/generate.go b/pkg/domain/entities/generate.go
index 6d65b52f8..edd217615 100644
--- a/pkg/domain/entities/generate.go
+++ b/pkg/domain/entities/generate.go
@@ -1,5 +1,7 @@
package entities
+import "io"
+
// GenerateSystemdOptions control the generation of systemd unit files.
type GenerateSystemdOptions struct {
// Files - generate files instead of printing to stdout.
@@ -20,3 +22,15 @@ type GenerateSystemdReport struct {
// entire content.
Output string
}
+
+// GenerateKubeOptions control the generation of Kubernetes YAML files.
+type GenerateKubeOptions struct {
+ // Service - generate YAML for a Kubernetes _service_ object.
+ Service bool
+}
+
+// GenerateKubeReport
+type GenerateKubeReport struct {
+ // Reader - the io.Reader to reader the generated YAML file.
+ Reader io.Reader
+}
diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go
index 74f27e25f..e116a90b9 100644
--- a/pkg/domain/entities/images.go
+++ b/pkg/domain/entities/images.go
@@ -7,6 +7,7 @@ import (
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/types"
"github.com/containers/libpod/pkg/inspect"
+ "github.com/containers/libpod/pkg/trust"
docker "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/opencontainers/go-digest"
@@ -285,3 +286,26 @@ type ImageTreeOptions struct {
type ImageTreeReport struct {
Tree string // TODO: Refactor move presentation work out of server
}
+
+// ShowTrustOptions are the cli options for showing trust
+type ShowTrustOptions struct {
+ JSON bool
+ PolicyPath string
+ Raw bool
+ RegistryPath string
+}
+
+// ShowTrustReport describes the results of show trust
+type ShowTrustReport struct {
+ Raw []byte
+ SystemRegistriesDirPath string
+ JSONOutput []byte
+ Policies []*trust.TrustPolicy
+}
+
+// SetTrustOptions describes the CLI options for setting trust
+type SetTrustOptions struct {
+ PolicyPath string
+ PubKeysFile []string
+ Type string
+}
diff --git a/pkg/domain/entities/manifest.go b/pkg/domain/entities/manifest.go
index 7316735b0..273052bb9 100644
--- a/pkg/domain/entities/manifest.go
+++ b/pkg/domain/entities/manifest.go
@@ -14,3 +14,18 @@ type ManifestAddOptions struct {
OSVersion string `json:"os_version" schema:"os_version"`
Variant string `json:"variant" schema:"variant"`
}
+
+type ManifestAnnotateOptions struct {
+ Annotation []string `json:"annotation"`
+ Arch string `json:"arch" schema:"arch"`
+ Features []string `json:"features" schema:"features"`
+ OS string `json:"os" schema:"os"`
+ OSFeatures []string `json:"os_features" schema:"os_features"`
+ OSVersion string `json:"os_version" schema:"os_version"`
+ Variant string `json:"variant" schema:"variant"`
+}
+
+type ManifestPushOptions struct {
+ Purge, Quiet, All, TlsVerify, RemoveSignatures bool
+ Authfile, CertDir, Creds, DigestFile, Format, SignBy string
+}
diff --git a/pkg/domain/entities/network.go b/pkg/domain/entities/network.go
new file mode 100644
index 000000000..cffd40899
--- /dev/null
+++ b/pkg/domain/entities/network.go
@@ -0,0 +1,52 @@
+package entities
+
+import (
+ "net"
+
+ "github.com/containernetworking/cni/libcni"
+)
+
+// NetworkListOptions describes options for listing networks in cli
+type NetworkListOptions struct {
+ Format string
+ Quiet bool
+}
+
+// NetworkListReport describes the results from listing networks
+type NetworkListReport struct {
+ *libcni.NetworkConfigList
+}
+
+// NetworkInspectOptions describes options for inspect networks
+type NetworkInspectOptions struct {
+}
+
+// NetworkInspectReport describes the results from inspect networks
+type NetworkInspectReport map[string]interface{}
+
+// NetworkRmOptions describes options for removing networks
+type NetworkRmOptions struct {
+ Force bool
+}
+
+//NetworkRmReport describes the results of network removal
+type NetworkRmReport struct {
+ Name string
+ Err error
+}
+
+// NetworkCreateOptions describes options to create a network
+type NetworkCreateOptions struct {
+ DisableDNS bool
+ Driver string
+ Gateway net.IP
+ Internal bool
+ MacVLAN string
+ Range net.IPNet
+ Subnet net.IPNet
+}
+
+// NetworkCreateReport describes a created network for the cli
+type NetworkCreateReport struct {
+ Filename string
+}
diff --git a/pkg/domain/entities/play.go b/pkg/domain/entities/play.go
new file mode 100644
index 000000000..93864c23b
--- /dev/null
+++ b/pkg/domain/entities/play.go
@@ -0,0 +1,36 @@
+package entities
+
+import "github.com/containers/image/v5/types"
+
+// PlayKubeOptions controls playing kube YAML files.
+type PlayKubeOptions struct {
+ // Authfile - path to an authentication file.
+ Authfile string
+ // CertDir - to a directory containing TLS certifications and keys.
+ CertDir string
+ // Credentials - `username:password` for authentication against a
+ // container registry.
+ Credentials string
+ // Network - name of the CNI network to connect to.
+ Network string
+ // Quiet - suppress output when pulling images.
+ Quiet bool
+ // SignaturePolicy - path to a signature-policy file.
+ SignaturePolicy string
+ // SkipTLSVerify - skip https and certificate validation when
+ // contacting container registries.
+ SkipTLSVerify types.OptionalBool
+ // SeccompProfileRoot - path to a directory containing seccomp
+ // profiles.
+ SeccompProfileRoot string
+}
+
+// PlayKubeReport contains the results of running play kube.
+type PlayKubeReport struct {
+ // Pod - the ID of the created pod.
+ Pod string
+ // Containers - the IDs of the containers running in the created pod.
+ Containers []string
+ // Logs - non-fatal erros and log messages while processing.
+ Logs []string
+}
diff --git a/pkg/domain/entities/system.go b/pkg/domain/entities/system.go
index de93a382f..c62f40025 100644
--- a/pkg/domain/entities/system.go
+++ b/pkg/domain/entities/system.go
@@ -26,3 +26,60 @@ type SystemPruneReport struct {
*ImagePruneReport
VolumePruneReport []*VolumePruneReport
}
+
+// SystemMigrateOptions describes the options needed for the
+// cli to migrate runtimes of containers
+type SystemMigrateOptions struct {
+ NewRuntime string
+}
+
+// SystemDfOptions describes the options for getting df information
+type SystemDfOptions struct {
+ Format string
+ Verbose bool
+}
+
+// SystemDfReport describes the response for df information
+type SystemDfReport struct {
+ Images []*SystemDfImageReport
+ Containers []*SystemDfContainerReport
+ Volumes []*SystemDfVolumeReport
+}
+
+// SystemDfImageReport describes an image for use with df
+type SystemDfImageReport struct {
+ Repository string
+ Tag string
+ ImageID string
+ Created time.Time
+ Size int64
+ SharedSize int64
+ UniqueSize int64
+ Containers int
+}
+
+// SystemDfContainerReport describes a container for use with df
+type SystemDfContainerReport struct {
+ ContainerID string
+ Image string
+ Command []string
+ LocalVolumes int
+ Size int64
+ RWSize int64
+ Created time.Time
+ Status string
+ Names string
+}
+
+// SystemDfVolumeReport describes a volume and its size
+type SystemDfVolumeReport struct {
+ VolumeName string
+ Links int
+ Size int64
+}
+
+// SystemResetOptions describes the options for resetting your
+// container runtime storage, etc
+type SystemResetOptions struct {
+ Force bool
+}
diff --git a/pkg/domain/entities/types.go b/pkg/domain/entities/types.go
index 9fbe04c9a..21ab025de 100644
--- a/pkg/domain/entities/types.go
+++ b/pkg/domain/entities/types.go
@@ -8,7 +8,6 @@ import (
"github.com/containers/libpod/libpod/events"
"github.com/containers/libpod/pkg/specgen"
"github.com/containers/storage/pkg/archive"
- "github.com/cri-o/ocicni/pkg/ocicni"
)
type Container struct {
@@ -40,7 +39,7 @@ type NetOptions struct {
DNSServers []net.IP
Network specgen.Namespace
NoHosts bool
- PublishPorts []ocicni.PortMapping
+ PublishPorts []specgen.PortMapping
StaticIP *net.IP
StaticMAC *net.HardwareAddr
}
diff --git a/pkg/domain/infra/abi/auto-update.go b/pkg/domain/infra/abi/auto-update.go
new file mode 100644
index 000000000..aa20664b4
--- /dev/null
+++ b/pkg/domain/infra/abi/auto-update.go
@@ -0,0 +1,13 @@
+package abi
+
+import (
+ "context"
+
+ "github.com/containers/libpod/pkg/autoupdate"
+ "github.com/containers/libpod/pkg/domain/entities"
+)
+
+func (ic *ContainerEngine) AutoUpdate(ctx context.Context) (*entities.AutoUpdateReport, []error) {
+ units, failures := autoupdate.AutoUpdate(ic.Libpod)
+ return &entities.AutoUpdateReport{Units: units}, failures
+}
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index 4c3389418..249e8147c 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -8,8 +8,7 @@ import (
"strconv"
"strings"
"sync"
-
- lpfilters "github.com/containers/libpod/libpod/filters"
+ "time"
"github.com/containers/buildah"
"github.com/containers/common/pkg/config"
@@ -17,8 +16,10 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/events"
+ lpfilters "github.com/containers/libpod/libpod/filters"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/libpod/logs"
+ "github.com/containers/libpod/pkg/cgroups"
"github.com/containers/libpod/pkg/checkpoint"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/domain/infra/abi/terminal"
@@ -32,9 +33,9 @@ import (
"github.com/sirupsen/logrus"
)
-// getContainersByContext gets pods whether all, latest, or a slice of names/ids
-// is specified.
-func getContainersByContext(all, latest bool, names []string, runtime *libpod.Runtime) (ctrs []*libpod.Container, err error) {
+// getContainersAndInputByContext gets containers whether all, latest, or a slice of names/ids
+// is specified. It also returns a list of the corresponding input name used to lookup each container.
+func getContainersAndInputByContext(all, latest bool, names []string, runtime *libpod.Runtime) (ctrs []*libpod.Container, rawInput []string, err error) {
var ctr *libpod.Container
ctrs = []*libpod.Container{}
@@ -43,6 +44,7 @@ func getContainersByContext(all, latest bool, names []string, runtime *libpod.Ru
ctrs, err = runtime.GetAllContainers()
case latest:
ctr, err = runtime.GetLatestContainer()
+ rawInput = append(rawInput, ctr.ID())
ctrs = append(ctrs, ctr)
default:
for _, n := range names {
@@ -54,6 +56,7 @@ func getContainersByContext(all, latest bool, names []string, runtime *libpod.Ru
err = e
}
} else {
+ rawInput = append(rawInput, n)
ctrs = append(ctrs, ctr)
}
}
@@ -61,6 +64,13 @@ func getContainersByContext(all, latest bool, names []string, runtime *libpod.Ru
return
}
+// getContainersByContext gets containers whether all, latest, or a slice of names/ids
+// is specified.
+func getContainersByContext(all, latest bool, names []string, runtime *libpod.Runtime) (ctrs []*libpod.Container, err error) {
+ ctrs, _, err = getContainersAndInputByContext(all, latest, names, runtime)
+ return
+}
+
// TODO: Should return *entities.ContainerExistsReport, error
func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrId string) (*entities.BoolReport, error) {
_, err := ic.Libpod.LookupContainer(nameOrId)
@@ -184,6 +194,10 @@ func (ic *ContainerEngine) ContainerPrune(ctx context.Context, options entities.
filterFuncs = append(filterFuncs, generatedFunc)
}
}
+ return ic.pruneContainersHelper(ctx, filterFuncs)
+}
+
+func (ic *ContainerEngine) pruneContainersHelper(ctx context.Context, filterFuncs []libpod.ContainerFilter) (*entities.ContainerPruneReport, error) {
prunedContainers, pruneErrors, err := ic.Libpod.PruneContainers(filterFuncs)
if err != nil {
return nil, err
@@ -514,7 +528,8 @@ func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrId string,
}
// If the container is in a pod, also set to recursively start dependencies
- if err := terminal.StartAttachCtr(ctx, ctr, options.Stdin, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, false, ctr.PodID() != ""); err != nil && errors.Cause(err) != define.ErrDetach {
+ err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, false, ctr.PodID() != "")
+ if err != nil && errors.Cause(err) != define.ErrDetach {
return errors.Wrapf(err, "error attaching to container %s", ctr.ID())
}
return nil
@@ -555,12 +570,14 @@ func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, o
func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) {
var reports []*entities.ContainerStartReport
var exitCode = define.ExecErrorCodeGeneric
- ctrs, err := getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod)
+ ctrs, rawInputs, err := getContainersAndInputByContext(false, options.Latest, namesOrIds, ic.Libpod)
if err != nil {
return nil, err
}
// There can only be one container if attach was used
- for _, ctr := range ctrs {
+ for i := range ctrs {
+ ctr := ctrs[i]
+ rawInput := rawInputs[i]
ctrState, err := ctr.State()
if err != nil {
return nil, err
@@ -574,6 +591,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
// Exit cleanly immediately
reports = append(reports, &entities.ContainerStartReport{
Id: ctr.ID(),
+ RawInput: rawInput,
Err: nil,
ExitCode: 0,
})
@@ -584,6 +602,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
logrus.Debugf("Deadlock error: %v", err)
reports = append(reports, &entities.ContainerStartReport{
Id: ctr.ID(),
+ RawInput: rawInput,
Err: err,
ExitCode: define.ExitCode(err),
})
@@ -593,6 +612,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
if ctrRunning {
reports = append(reports, &entities.ContainerStartReport{
Id: ctr.ID(),
+ RawInput: rawInput,
Err: nil,
ExitCode: 0,
})
@@ -602,6 +622,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
if err != nil {
reports = append(reports, &entities.ContainerStartReport{
Id: ctr.ID(),
+ RawInput: rawInput,
Err: err,
ExitCode: exitCode,
})
@@ -624,6 +645,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
}
reports = append(reports, &entities.ContainerStartReport{
Id: ctr.ID(),
+ RawInput: rawInput,
Err: err,
ExitCode: exitCode,
})
@@ -636,6 +658,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
// If the container is in a pod, also set to recursively start dependencies
report := &entities.ContainerStartReport{
Id: ctr.ID(),
+ RawInput: rawInput,
ExitCode: 125,
}
if err := ctr.Start(ctx, ctr.PodID() != ""); err != nil {
@@ -981,3 +1004,76 @@ func (ic *ContainerEngine) Shutdown(_ context.Context) {
_ = ic.Libpod.Shutdown(false)
})
}
+
+func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []string, options entities.ContainerStatsOptions) error {
+ containerFunc := ic.Libpod.GetRunningContainers
+ switch {
+ case len(namesOrIds) > 0:
+ containerFunc = func() ([]*libpod.Container, error) { return ic.Libpod.GetContainersByList(namesOrIds) }
+ case options.Latest:
+ containerFunc = func() ([]*libpod.Container, error) {
+ lastCtr, err := ic.Libpod.GetLatestContainer()
+ if err != nil {
+ return nil, err
+ }
+ return []*libpod.Container{lastCtr}, nil
+ }
+ case options.All:
+ containerFunc = ic.Libpod.GetAllContainers
+ }
+
+ ctrs, err := containerFunc()
+ if err != nil {
+ return errors.Wrapf(err, "unable to get list of containers")
+ }
+ containerStats := map[string]*define.ContainerStats{}
+ for _, ctr := range ctrs {
+ initialStats, err := ctr.GetContainerStats(&define.ContainerStats{})
+ if err != nil {
+ // when doing "all", don't worry about containers that are not running
+ cause := errors.Cause(err)
+ if options.All && (cause == define.ErrCtrRemoved || cause == define.ErrNoSuchCtr || cause == define.ErrCtrStateInvalid) {
+ continue
+ }
+ if cause == cgroups.ErrCgroupV1Rootless {
+ err = cause
+ }
+ return err
+ }
+ containerStats[ctr.ID()] = initialStats
+ }
+ for {
+ reportStats := []*define.ContainerStats{}
+ for _, ctr := range ctrs {
+ id := ctr.ID()
+ if _, ok := containerStats[ctr.ID()]; !ok {
+ initialStats, err := ctr.GetContainerStats(&define.ContainerStats{})
+ if errors.Cause(err) == define.ErrCtrRemoved || errors.Cause(err) == define.ErrNoSuchCtr || errors.Cause(err) == define.ErrCtrStateInvalid {
+ // skip dealing with a container that is gone
+ continue
+ }
+ if err != nil {
+ return err
+ }
+ containerStats[id] = initialStats
+ }
+ stats, err := ctr.GetContainerStats(containerStats[id])
+ if err != nil && errors.Cause(err) != define.ErrNoSuchCtr {
+ return err
+ }
+ // replace the previous measurement with the current one
+ containerStats[id] = stats
+ reportStats = append(reportStats, stats)
+ }
+ ctrs, err = containerFunc()
+ if err != nil {
+ return err
+ }
+ options.StatChan <- reportStats
+ if options.NoStream {
+ break
+ }
+ time.Sleep(time.Second)
+ }
+ return nil
+}
diff --git a/pkg/domain/infra/abi/containers_runlabel.go b/pkg/domain/infra/abi/containers_runlabel.go
new file mode 100644
index 000000000..41f4444d5
--- /dev/null
+++ b/pkg/domain/infra/abi/containers_runlabel.go
@@ -0,0 +1,280 @@
+package abi
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/containers/image/v5/types"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/domain/entities"
+ envLib "github.com/containers/libpod/pkg/env"
+ "github.com/containers/libpod/pkg/util"
+ "github.com/containers/libpod/utils"
+ "github.com/google/shlex"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+func (ic *ContainerEngine) ContainerRunlabel(ctx context.Context, label string, imageRef string, args []string, options entities.ContainerRunlabelOptions) error {
+ // First, get the image and pull it if needed.
+ img, err := ic.runlabelImage(ctx, label, imageRef, options)
+ if err != nil {
+ return err
+ }
+ // Extract the runlabel from the image.
+ runlabel, err := img.GetLabel(ctx, label)
+ if err != nil {
+ return err
+ }
+
+ cmd, env, err := generateRunlabelCommand(runlabel, img, args, options)
+ if err != nil {
+ return err
+ }
+
+ stdErr := os.Stderr
+ stdOut := os.Stdout
+ stdIn := os.Stdin
+ if options.Quiet {
+ stdErr = nil
+ stdOut = nil
+ stdIn = nil
+ }
+
+ // If container already exists && --replace given -- Nuke it
+ if options.Replace {
+ for i, entry := range cmd {
+ if entry == "--name" {
+ name := cmd[i+1]
+ ctr, err := ic.Libpod.LookupContainer(name)
+ if err != nil {
+ if errors.Cause(err) != define.ErrNoSuchCtr {
+ logrus.Debugf("Error occurred searching for container %s: %s", name, err.Error())
+ return err
+ }
+ } else {
+ logrus.Debugf("Runlabel --replace option given. Container %s will be deleted. The new container will be named %s", ctr.ID(), name)
+ if err := ic.Libpod.RemoveContainer(ctx, ctr, true, false); err != nil {
+ return err
+ }
+ }
+ break
+ }
+ }
+ }
+
+ return utils.ExecCmdWithStdStreams(stdIn, stdOut, stdErr, env, cmd[0], cmd[1:]...)
+}
+
+// runlabelImage returns an image based on the specified image AND options.
+func (ic *ContainerEngine) runlabelImage(ctx context.Context, label string, imageRef string, options entities.ContainerRunlabelOptions) (*image.Image, error) {
+ // First, look up the image locally. If we get an error and requested
+ // to pull, fallthrough and pull it.
+ img, err := ic.Libpod.ImageRuntime().NewFromLocal(imageRef)
+ switch {
+ case err == nil:
+ return img, nil
+ case !options.Pull:
+ return nil, err
+ default:
+ // Fallthrough and pull!
+ }
+
+ // Parse credentials if specified.
+ var credentials *types.DockerAuthConfig
+ if options.Credentials != "" {
+ credentials, err = util.ParseRegistryCreds(options.Credentials)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ // Suppress pull progress bars if requested.
+ pullOutput := os.Stdout
+ if options.Quiet {
+ pullOutput = nil // c/image/copy takes care of the rest
+ }
+
+ // Pull the image.
+ dockerRegistryOptions := image.DockerRegistryOptions{
+ DockerCertPath: options.CertDir,
+ DockerInsecureSkipTLSVerify: options.SkipTLSVerify,
+ DockerRegistryCreds: credentials,
+ }
+
+ return ic.Libpod.ImageRuntime().New(ctx, imageRef, options.SignaturePolicy, options.Authfile, pullOutput, &dockerRegistryOptions, image.SigningOptions{}, &label, util.PullImageMissing)
+}
+
+// generateRunlabelCommand generates the to-be-executed command as a string
+// slice along with a base environment.
+func generateRunlabelCommand(runlabel string, img *image.Image, args []string, options entities.ContainerRunlabelOptions) ([]string, []string, error) {
+ var (
+ err error
+ name, imageName string
+ globalOpts string
+ cmd, env []string
+ )
+
+ // TODO: How do we get global opts as done in v1?
+
+ // Extract the imageName (or ID).
+ imgNames := img.Names()
+ if len(imgNames) == 0 {
+ imageName = img.ID()
+ } else {
+ imageName = imgNames[0]
+ }
+
+ // Use the user-specified name or extract one from the image.
+ if options.Name != "" {
+ name = options.Name
+ } else {
+ name, err = image.GetImageBaseName(imageName)
+ if err != nil {
+ return nil, nil, err
+ }
+ }
+
+ // Append the user-specified arguments to the runlabel (command).
+ if len(args) > 0 {
+ runlabel = fmt.Sprintf("%s %s", runlabel, strings.Join(args, " "))
+ }
+
+ cmd, err = generateCommand(runlabel, imageName, name, globalOpts)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ env = generateRunEnvironment(name, imageName, options)
+ env = append(env, "PODMAN_RUNLABEL_NESTED=1")
+ envmap, err := envLib.ParseSlice(env)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ envmapper := func(k string) string {
+ switch k {
+ case "OPT1":
+ return envmap["OPT1"]
+ case "OPT2":
+ return envmap["OPT2"]
+ case "OPT3":
+ return envmap["OPT3"]
+ case "PWD":
+ // I would prefer to use os.getenv but it appears PWD is not in the os env list.
+ d, err := os.Getwd()
+ if err != nil {
+ logrus.Error("unable to determine current working directory")
+ return ""
+ }
+ return d
+ }
+ return ""
+ }
+ newS := os.Expand(strings.Join(cmd, " "), envmapper)
+ cmd, err = shlex.Split(newS)
+ if err != nil {
+ return nil, nil, err
+ }
+ return cmd, env, nil
+}
+
+// generateCommand takes a label (string) and converts it to an executable command
+func generateCommand(command, imageName, name, globalOpts string) ([]string, error) {
+ var (
+ newCommand []string
+ )
+ if name == "" {
+ name = imageName
+ }
+
+ cmd, err := shlex.Split(command)
+ if err != nil {
+ return nil, err
+ }
+
+ prog, err := substituteCommand(cmd[0])
+ if err != nil {
+ return nil, err
+ }
+ newCommand = append(newCommand, prog)
+
+ for _, arg := range cmd[1:] {
+ var newArg string
+ switch arg {
+ case "IMAGE":
+ newArg = imageName
+ case "$IMAGE":
+ newArg = imageName
+ case "IMAGE=IMAGE":
+ newArg = fmt.Sprintf("IMAGE=%s", imageName)
+ case "IMAGE=$IMAGE":
+ newArg = fmt.Sprintf("IMAGE=%s", imageName)
+ case "NAME":
+ newArg = name
+ case "NAME=NAME":
+ newArg = fmt.Sprintf("NAME=%s", name)
+ case "NAME=$NAME":
+ newArg = fmt.Sprintf("NAME=%s", name)
+ case "$NAME":
+ newArg = name
+ case "$GLOBAL_OPTS":
+ newArg = globalOpts
+ default:
+ newArg = arg
+ }
+ newCommand = append(newCommand, newArg)
+ }
+ return newCommand, nil
+}
+
+// GenerateRunEnvironment merges the current environment variables with optional
+// environment variables provided by the user
+func generateRunEnvironment(name, imageName string, options entities.ContainerRunlabelOptions) []string {
+ newEnv := os.Environ()
+ if options.Optional1 != "" {
+ newEnv = append(newEnv, fmt.Sprintf("OPT1=%s", options.Optional1))
+ }
+ if options.Optional2 != "" {
+ newEnv = append(newEnv, fmt.Sprintf("OPT2=%s", options.Optional2))
+ }
+ if options.Optional3 != "" {
+ newEnv = append(newEnv, fmt.Sprintf("OPT3=%s", options.Optional3))
+ }
+ return newEnv
+}
+
+func substituteCommand(cmd string) (string, error) {
+ var (
+ newCommand string
+ )
+
+ // Replace cmd with "/proc/self/exe" if "podman" or "docker" is being
+ // used. If "/usr/bin/docker" is provided, we also sub in podman.
+ // Otherwise, leave the command unchanged.
+ if cmd == "podman" || filepath.Base(cmd) == "docker" {
+ newCommand = "/proc/self/exe"
+ } else {
+ newCommand = cmd
+ }
+
+ // If cmd is an absolute or relative path, check if the file exists.
+ // Throw an error if it doesn't exist.
+ if strings.Contains(newCommand, "/") || strings.HasPrefix(newCommand, ".") {
+ res, err := filepath.Abs(newCommand)
+ if err != nil {
+ return "", err
+ }
+ if _, err := os.Stat(res); !os.IsNotExist(err) {
+ return res, nil
+ } else if err != nil {
+ return "", err
+ }
+ }
+
+ return newCommand, nil
+}
diff --git a/pkg/domain/infra/abi/generate.go b/pkg/domain/infra/abi/generate.go
index f69ba560e..be5d452bd 100644
--- a/pkg/domain/infra/abi/generate.go
+++ b/pkg/domain/infra/abi/generate.go
@@ -1,14 +1,18 @@
package abi
import (
+ "bytes"
"context"
"fmt"
"strings"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/systemd/generate"
+ "github.com/ghodss/yaml"
"github.com/pkg/errors"
+ k8sAPI "k8s.io/api/core/v1"
)
func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, options entities.GenerateSystemdOptions) (*entities.GenerateSystemdReport, error) {
@@ -172,3 +176,84 @@ func generateServiceName(ctr *libpod.Container, pod *libpod.Pod, options entitie
}
return ctrName, fmt.Sprintf("%s-%s", kind, name)
}
+
+func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrID string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) {
+ var (
+ pod *libpod.Pod
+ podYAML *k8sAPI.Pod
+ err error
+ ctr *libpod.Container
+ servicePorts []k8sAPI.ServicePort
+ serviceYAML k8sAPI.Service
+ )
+ // Get the container in question.
+ ctr, err = ic.Libpod.LookupContainer(nameOrID)
+ if err != nil {
+ pod, err = ic.Libpod.LookupPod(nameOrID)
+ if err != nil {
+ return nil, err
+ }
+ podYAML, servicePorts, err = pod.GenerateForKube()
+ } else {
+ if len(ctr.Dependencies()) > 0 {
+ return nil, errors.Wrapf(define.ErrNotImplemented, "containers with dependencies")
+ }
+ podYAML, err = ctr.GenerateForKube()
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ if options.Service {
+ serviceYAML = libpod.GenerateKubeServiceFromV1Pod(podYAML, servicePorts)
+ }
+
+ content, err := generateKubeOutput(podYAML, &serviceYAML)
+ if err != nil {
+ return nil, err
+ }
+
+ return &entities.GenerateKubeReport{Reader: bytes.NewReader(content)}, nil
+}
+
+func generateKubeOutput(podYAML *k8sAPI.Pod, serviceYAML *k8sAPI.Service) ([]byte, error) {
+ var (
+ output []byte
+ marshalledPod []byte
+ marshalledService []byte
+ err error
+ )
+
+ marshalledPod, err = yaml.Marshal(podYAML)
+ if err != nil {
+ return nil, err
+ }
+
+ if serviceYAML != nil {
+ marshalledService, err = yaml.Marshal(serviceYAML)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ header := `# Generation of Kubernetes YAML is still under development!
+#
+# Save the output of this file and use kubectl create -f to import
+# it into Kubernetes.
+#
+# Created with podman-%s
+`
+ podmanVersion, err := define.GetVersion()
+ if err != nil {
+ return nil, err
+ }
+
+ output = append(output, []byte(fmt.Sprintf(header, podmanVersion.Version))...)
+ output = append(output, marshalledPod...)
+ if serviceYAML != nil {
+ output = append(output, []byte("---\n")...)
+ output = append(output, marshalledService...)
+ }
+
+ return output, nil
+}
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index be788b2bf..7ab5131f0 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -21,7 +21,6 @@ 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,7 +35,11 @@ func (ir *ImageEngine) Exists(_ context.Context, nameOrId string) (*entities.Boo
}
func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) {
- results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, opts.Filter)
+ return ir.pruneImagesHelper(ctx, opts.All, opts.Filter)
+}
+
+func (ir *ImageEngine) pruneImagesHelper(ctx context.Context, all bool, filters []string) (*entities.ImagePruneReport, error) {
+ results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, all, filters)
if err != nil {
return nil, err
}
@@ -419,8 +422,10 @@ func (ir *ImageEngine) Tree(ctx context.Context, nameOrId string, opts entities.
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) {
+// removeErrorsToExitCode returns an exit code for the specified slice of
+// image-removal errors. The error codes are set according to the documented
+// behaviour in the Podman man pages.
+func removeErrorsToExitCode(rmErrors []error) int {
var (
// noSuchImageErrors indicates that at least one image was not found.
noSuchImageErrors bool
@@ -430,59 +435,53 @@ func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entitie
// 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{}
+ if len(rmErrors) == 0 {
+ return 0
+ }
- // 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
+ for _, e := range rmErrors {
+ switch errors.Cause(e) {
+ case define.ErrNoSuchImage:
+ noSuchImageErrors = true
+ case define.ErrImageInUse, storage.ErrImageUsedByContainer:
+ inUseErrors = true
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()))
+ otherErrors = true
}
+ }
+
+ switch {
+ case inUseErrors:
+ // One of the specified images has child images or is
+ // being used by a container.
+ return 2
+ case noSuchImageErrors && !(otherErrors || inUseErrors):
+ // One of the specified images did not exist, and no other
+ // failures.
+ return 1
+ default:
+ return 125
+ }
+}
+
+// Remove removes one or more images from local storage.
+func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entities.ImageRemoveOptions) (report *entities.ImageRemoveReport, rmErrors []error) {
+ report = &entities.ImageRemoveReport{}
+
+ // Set the exit code at very end.
+ defer func() {
+ report.ExitCode = removeErrorsToExitCode(rmErrors)
}()
// 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 define.ErrNoSuchImage:
- inUseErrors = true // ExitCode is expected
- 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.
+ if err != nil {
return err
}
-
report.Deleted = append(report.Deleted, results.Deleted)
report.Untagged = append(report.Untagged, results.Untagged...)
return nil
@@ -495,9 +494,7 @@ func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entitie
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.
+ rmErrors = append(rmErrors, err)
return
}
// No images (left) to remove, so we're done.
@@ -506,9 +503,7 @@ func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entitie
}
// 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"))
+ rmErrors = append(rmErrors, errors.New("unable to delete all images, check errors and re-run image removal if needed"))
break
}
previousImages = len(storageImages)
@@ -516,15 +511,15 @@ func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entitie
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)
+ rmErrors = append(rmErrors, err)
+ continue
}
// Skip parent images.
if isParent {
continue
}
if err := deleteImage(img); err != nil {
- deleteError = multierror.Append(deleteError, err)
+ rmErrors = append(rmErrors, err)
}
}
}
@@ -535,21 +530,13 @@ func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entitie
// 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, errors.Wrapf(err, "failed to remove image '%s'", id))
+ if err != nil {
+ rmErrors = append(rmErrors, err)
continue
}
-
err = deleteImage(img)
if err != nil {
- otherErrors = true // Important for exit codes in Podman.
- deleteError = multierror.Append(deleteError, err)
+ rmErrors = append(rmErrors, err)
}
}
diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go
index 88331f96c..fca34dda2 100644
--- a/pkg/domain/infra/abi/manifest.go
+++ b/pkg/domain/infra/abi/manifest.go
@@ -6,14 +6,21 @@ import (
"context"
"encoding/json"
"fmt"
+ "io/ioutil"
+ "os"
"strings"
+ "github.com/containers/buildah/manifests"
buildahUtil "github.com/containers/buildah/util"
+ cp "github.com/containers/image/v5/copy"
"github.com/containers/image/v5/docker"
+ "github.com/containers/image/v5/manifest"
"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/opencontainers/go-digest"
+ imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
@@ -71,7 +78,7 @@ func (ir *ImageEngine) ManifestAdd(ctx context.Context, opts entities.ManifestAd
}
listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(listImageSpec)
if err != nil {
- return "", errors.Wrapf(err, "error retriving local image from image name %s", listImageSpec)
+ return "", errors.Wrapf(err, "error retrieving local image from image name %s", listImageSpec)
}
manifestAddOpts := libpodImage.ManifestAddOpts{
@@ -100,3 +107,104 @@ func (ir *ImageEngine) ManifestAdd(ctx context.Context, opts entities.ManifestAd
}
return listID, nil
}
+
+// ManifestAnnotate updates an entry of the manifest list
+func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, names []string, opts entities.ManifestAnnotateOptions) (string, error) {
+ listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(names[0])
+ if err != nil {
+ return "", errors.Wrapf(err, "error retreiving local image from image name %s", names[0])
+ }
+ digest, err := digest.Parse(names[1])
+ if err != nil {
+ return "", errors.Errorf(`invalid image digest "%s": %v`, names[1], err)
+ }
+ manifestAnnotateOpts := libpodImage.ManifestAnnotateOpts{
+ Arch: opts.Arch,
+ Features: opts.Features,
+ OS: opts.OS,
+ OSFeatures: opts.OSFeatures,
+ 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]
+ }
+ manifestAnnotateOpts.Annotation = annotations
+ }
+ updatedListID, err := listImage.AnnotateManifest(*ir.Libpod.SystemContext(), digest, manifestAnnotateOpts)
+ if err == nil {
+ return fmt.Sprintf("%s: %s", updatedListID, digest.String()), nil
+ }
+ return "", err
+}
+
+// ManifestRemove removes specified digest from the specified manifest list
+func (ir *ImageEngine) ManifestRemove(ctx context.Context, names []string) (string, error) {
+ instanceDigest, err := digest.Parse(names[1])
+ if err != nil {
+ return "", errors.Errorf(`invalid image digest "%s": %v`, names[1], err)
+ }
+ listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(names[0])
+ if err != nil {
+ return "", errors.Wrapf(err, "error retriving local image from image name %s", names[0])
+ }
+ updatedListID, err := listImage.RemoveManifest(instanceDigest)
+ if err == nil {
+ return fmt.Sprintf("%s :%s\n", updatedListID, instanceDigest.String()), nil
+ }
+ return "", err
+}
+
+// ManifestPush pushes a manifest list or image index to the destination
+func (ir *ImageEngine) ManifestPush(ctx context.Context, names []string, opts entities.ManifestPushOptions) error {
+ listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(names[0])
+ if err != nil {
+ return errors.Wrapf(err, "error retriving local image from image name %s", names[0])
+ }
+ dest, err := alltransports.ParseImageName(names[1])
+ if err != nil {
+ return err
+ }
+ var manifestType string
+ if opts.Format != "" {
+ switch opts.Format {
+ case "oci":
+ manifestType = imgspecv1.MediaTypeImageManifest
+ case "v2s2", "docker":
+ manifestType = manifest.DockerV2Schema2MediaType
+ default:
+ return errors.Errorf("unknown format %q. Choose on of the supported formats: 'oci' or 'v2s2'", opts.Format)
+ }
+ }
+ options := manifests.PushOptions{
+ Store: ir.Libpod.GetStore(),
+ SystemContext: ir.Libpod.SystemContext(),
+ ImageListSelection: cp.CopySpecificImages,
+ Instances: nil,
+ RemoveSignatures: opts.RemoveSignatures,
+ SignBy: opts.SignBy,
+ ManifestType: manifestType,
+ }
+ if opts.All {
+ options.ImageListSelection = cp.CopyAllImages
+ }
+ if !opts.Quiet {
+ options.ReportWriter = os.Stderr
+ }
+ digest, err := listImage.PushManifest(dest, options)
+ if err == nil && opts.Purge {
+ _, err = ir.Libpod.GetStore().DeleteImage(listImage.ID(), true)
+ }
+ if opts.DigestFile != "" {
+ if err = ioutil.WriteFile(opts.DigestFile, []byte(digest.String()), 0644); err != nil {
+ return buildahUtil.GetFailureCause(err, errors.Wrapf(err, "failed to write digest to file %q", opts.DigestFile))
+ }
+ }
+ return err
+}
diff --git a/pkg/domain/infra/abi/network.go b/pkg/domain/infra/abi/network.go
new file mode 100644
index 000000000..5c39b5374
--- /dev/null
+++ b/pkg/domain/infra/abi/network.go
@@ -0,0 +1,258 @@
+package abi
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "path/filepath"
+
+ cniversion "github.com/containernetworking/cni/pkg/version"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/network"
+ "github.com/containers/libpod/pkg/util"
+ "github.com/pkg/errors"
+)
+
+func getCNIConfDir(r *libpod.Runtime) (string, error) {
+ config, err := r.GetConfig()
+ if err != nil {
+ return "", err
+ }
+ configPath := config.Network.NetworkConfigDir
+
+ if len(config.Network.NetworkConfigDir) < 1 {
+ configPath = network.CNIConfigDir
+ }
+ return configPath, nil
+}
+
+func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.NetworkListOptions) ([]*entities.NetworkListReport, error) {
+ var reports []*entities.NetworkListReport
+ cniConfigPath, err := getCNIConfDir(ic.Libpod)
+ if err != nil {
+ return nil, err
+ }
+ networks, err := network.LoadCNIConfsFromDir(cniConfigPath)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, n := range networks {
+ reports = append(reports, &entities.NetworkListReport{NetworkConfigList: n})
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.NetworkInspectOptions) ([]entities.NetworkInspectReport, error) {
+ var (
+ rawCNINetworks []entities.NetworkInspectReport
+ )
+ for _, name := range namesOrIds {
+ rawList, err := network.InspectNetwork(name)
+ if err != nil {
+ return nil, err
+ }
+ rawCNINetworks = append(rawCNINetworks, rawList)
+ }
+ return rawCNINetworks, nil
+}
+
+func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, options entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) {
+ var reports []*entities.NetworkRmReport
+ for _, name := range namesOrIds {
+ report := entities.NetworkRmReport{Name: name}
+ containers, err := ic.Libpod.GetAllContainers()
+ if err != nil {
+ return reports, err
+ }
+ // We need to iterate containers looking to see if they belong to the given network
+ for _, c := range containers {
+ if util.StringInSlice(name, c.Config().Networks) {
+ // if user passes force, we nuke containers
+ if !options.Force {
+ // Without the force option, we return an error
+ return reports, errors.Errorf("%q has associated containers with it. Use -f to forcibly delete containers", name)
+ }
+ if err := ic.Libpod.RemoveContainer(ctx, c, true, true); err != nil {
+ return reports, err
+ }
+ }
+ }
+ if err := network.RemoveNetwork(name); err != nil {
+ report.Err = err
+ }
+ reports = append(reports, &report)
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) NetworkCreate(ctx context.Context, name string, options entities.NetworkCreateOptions) (*entities.NetworkCreateReport, error) {
+ var (
+ err error
+ fileName string
+ )
+ if len(options.MacVLAN) > 0 {
+ fileName, err = createMacVLAN(ic.Libpod, name, options)
+ } else {
+ fileName, err = createBridge(ic.Libpod, name, options)
+ }
+ if err != nil {
+ return nil, err
+ }
+ return &entities.NetworkCreateReport{Filename: fileName}, nil
+}
+
+// createBridge creates a CNI network
+func createBridge(r *libpod.Runtime, name string, options entities.NetworkCreateOptions) (string, error) {
+ isGateway := true
+ ipMasq := true
+ subnet := &options.Subnet
+ ipRange := options.Range
+ runtimeConfig, err := r.GetConfig()
+ if err != nil {
+ return "", err
+ }
+ // if range is provided, make sure it is "in" network
+ if subnet.IP != nil {
+ // if network is provided, does it conflict with existing CNI or live networks
+ err = network.ValidateUserNetworkIsAvailable(subnet)
+ } else {
+ // if no network is provided, figure out network
+ subnet, err = network.GetFreeNetwork()
+ }
+ if err != nil {
+ return "", err
+ }
+ gateway := options.Gateway
+ if gateway == nil {
+ // if no gateway is provided, provide it as first ip of network
+ gateway = network.CalcGatewayIP(subnet)
+ }
+ // if network is provided and if gateway is provided, make sure it is "in" network
+ if options.Subnet.IP != nil && options.Gateway != nil {
+ if !subnet.Contains(gateway) {
+ return "", errors.Errorf("gateway %s is not in valid for subnet %s", gateway.String(), subnet.String())
+ }
+ }
+ if options.Internal {
+ isGateway = false
+ ipMasq = false
+ }
+
+ // if a range is given, we need to ensure it is "in" the network range.
+ if options.Range.IP != nil {
+ if options.Subnet.IP == nil {
+ return "", errors.New("you must define a subnet range to define an ip-range")
+ }
+ firstIP, err := network.FirstIPInSubnet(&options.Range)
+ if err != nil {
+ return "", err
+ }
+ lastIP, err := network.LastIPInSubnet(&options.Range)
+ if err != nil {
+ return "", err
+ }
+ if !subnet.Contains(firstIP) || !subnet.Contains(lastIP) {
+ return "", errors.Errorf("the ip range %s does not fall within the subnet range %s", options.Range.String(), subnet.String())
+ }
+ }
+ bridgeDeviceName, err := network.GetFreeDeviceName()
+ if err != nil {
+ return "", err
+ }
+
+ if len(name) > 0 {
+ netNames, err := network.GetNetworkNamesFromFileSystem()
+ if err != nil {
+ return "", err
+ }
+ if util.StringInSlice(name, netNames) {
+ return "", errors.Errorf("the network name %s is already used", name)
+ }
+ } else {
+ // If no name is given, we give the name of the bridge device
+ name = bridgeDeviceName
+ }
+
+ ncList := network.NewNcList(name, cniversion.Current())
+ var plugins []network.CNIPlugins
+ var routes []network.IPAMRoute
+
+ defaultRoute, err := network.NewIPAMDefaultRoute()
+ if err != nil {
+ return "", err
+ }
+ routes = append(routes, defaultRoute)
+ ipamConfig, err := network.NewIPAMHostLocalConf(subnet, routes, ipRange, gateway)
+ if err != nil {
+ return "", err
+ }
+
+ // TODO need to iron out the role of isDefaultGW and IPMasq
+ bridge := network.NewHostLocalBridge(bridgeDeviceName, isGateway, false, ipMasq, ipamConfig)
+ plugins = append(plugins, bridge)
+ plugins = append(plugins, network.NewPortMapPlugin())
+ plugins = append(plugins, network.NewFirewallPlugin())
+ // if we find the dnsname plugin, we add configuration for it
+ if network.HasDNSNamePlugin(runtimeConfig.Network.CNIPluginDirs) && !options.DisableDNS {
+ // Note: in the future we might like to allow for dynamic domain names
+ plugins = append(plugins, network.NewDNSNamePlugin(network.DefaultPodmanDomainName))
+ }
+ ncList["plugins"] = plugins
+ b, err := json.MarshalIndent(ncList, "", " ")
+ if err != nil {
+ return "", err
+ }
+ cniConfigPath, err := getCNIConfDir(r)
+ if err != nil {
+ return "", err
+ }
+ cniPathName := filepath.Join(cniConfigPath, fmt.Sprintf("%s.conflist", name))
+ err = ioutil.WriteFile(cniPathName, b, 0644)
+ return cniPathName, err
+}
+
+func createMacVLAN(r *libpod.Runtime, name string, options entities.NetworkCreateOptions) (string, error) {
+ var (
+ plugins []network.CNIPlugins
+ )
+ liveNetNames, err := network.GetLiveNetworkNames()
+ if err != nil {
+ return "", err
+ }
+ // Make sure the host-device exists
+ if !util.StringInSlice(options.MacVLAN, liveNetNames) {
+ return "", errors.Errorf("failed to find network interface %q", options.MacVLAN)
+ }
+ if len(name) > 0 {
+ netNames, err := network.GetNetworkNamesFromFileSystem()
+ if err != nil {
+ return "", err
+ }
+ if util.StringInSlice(name, netNames) {
+ return "", errors.Errorf("the network name %s is already used", name)
+ }
+ } else {
+ name, err = network.GetFreeDeviceName()
+ if err != nil {
+ return "", err
+ }
+ }
+ ncList := network.NewNcList(name, cniversion.Current())
+ macvlan := network.NewMacVLANPlugin(options.MacVLAN)
+ plugins = append(plugins, macvlan)
+ ncList["plugins"] = plugins
+ b, err := json.MarshalIndent(ncList, "", " ")
+ if err != nil {
+ return "", err
+ }
+ cniConfigPath, err := getCNIConfDir(r)
+ if err != nil {
+ return "", err
+ }
+ cniPathName := filepath.Join(cniConfigPath, fmt.Sprintf("%s.conflist", name))
+ err = ioutil.WriteFile(cniPathName, b, 0644)
+ return cniPathName, err
+}
diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go
new file mode 100644
index 000000000..cd7eec7e6
--- /dev/null
+++ b/pkg/domain/infra/abi/play.go
@@ -0,0 +1,544 @@
+package abi
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/containers/buildah/pkg/parse"
+ "github.com/containers/image/v5/types"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/image"
+ ann "github.com/containers/libpod/pkg/annotations"
+ "github.com/containers/libpod/pkg/domain/entities"
+ envLib "github.com/containers/libpod/pkg/env"
+ ns "github.com/containers/libpod/pkg/namespaces"
+ createconfig "github.com/containers/libpod/pkg/spec"
+ "github.com/containers/libpod/pkg/specgen/generate"
+ "github.com/containers/libpod/pkg/util"
+ "github.com/containers/storage"
+ "github.com/cri-o/ocicni/pkg/ocicni"
+ "github.com/docker/distribution/reference"
+ "github.com/ghodss/yaml"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ v1 "k8s.io/api/core/v1"
+)
+
+const (
+ // https://kubernetes.io/docs/concepts/storage/volumes/#hostpath
+ kubeDirectoryPermission = 0755
+ // https://kubernetes.io/docs/concepts/storage/volumes/#hostpath
+ kubeFilePermission = 0644
+)
+
+func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) {
+ var (
+ containers []*libpod.Container
+ pod *libpod.Pod
+ podOptions []libpod.PodCreateOption
+ podYAML v1.Pod
+ registryCreds *types.DockerAuthConfig
+ writer io.Writer
+ report entities.PlayKubeReport
+ )
+
+ content, err := ioutil.ReadFile(path)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := yaml.Unmarshal(content, &podYAML); err != nil {
+ return nil, errors.Wrapf(err, "unable to read %q as YAML", path)
+ }
+
+ if podYAML.Kind != "Pod" {
+ return nil, errors.Errorf("invalid YAML kind: %q. Pod is the only supported Kubernetes YAML kind", podYAML.Kind)
+ }
+
+ // check for name collision between pod and container
+ podName := podYAML.ObjectMeta.Name
+ if podName == "" {
+ return nil, errors.Errorf("pod does not have a name")
+ }
+ for _, n := range podYAML.Spec.Containers {
+ if n.Name == podName {
+ report.Logs = append(report.Logs,
+ fmt.Sprintf("a container exists with the same name (%q) as the pod in your YAML file; changing pod name to %s_pod\n", podName, podName))
+ podName = fmt.Sprintf("%s_pod", podName)
+ }
+ }
+
+ podOptions = append(podOptions, libpod.WithInfraContainer())
+ podOptions = append(podOptions, libpod.WithPodName(podName))
+ // TODO for now we just used the default kernel namespaces; we need to add/subtract this from yaml
+
+ hostname := podYAML.Spec.Hostname
+ if hostname == "" {
+ hostname = podName
+ }
+ podOptions = append(podOptions, libpod.WithPodHostname(hostname))
+
+ if podYAML.Spec.HostNetwork {
+ podOptions = append(podOptions, libpod.WithPodHostNetwork())
+ }
+
+ nsOptions, err := generate.GetNamespaceOptions(strings.Split(createconfig.DefaultKernelNamespaces, ","))
+ if err != nil {
+ return nil, err
+ }
+ podOptions = append(podOptions, nsOptions...)
+ podPorts := getPodPorts(podYAML.Spec.Containers)
+ podOptions = append(podOptions, libpod.WithInfraContainerPorts(podPorts))
+
+ if options.Network != "" {
+ switch strings.ToLower(options.Network) {
+ case "bridge", "host":
+ return nil, errors.Errorf("invalid value passed to --network: bridge or host networking must be configured in YAML")
+ case "":
+ return nil, errors.Errorf("invalid value passed to --network: must provide a comma-separated list of CNI networks")
+ default:
+ // We'll assume this is a comma-separated list of CNI
+ // networks.
+ networks := strings.Split(options.Network, ",")
+ logrus.Debugf("Pod joining CNI networks: %v", networks)
+ podOptions = append(podOptions, libpod.WithPodNetworks(networks))
+ }
+ }
+
+ // Create the Pod
+ pod, err = ic.Libpod.NewPod(ctx, podOptions...)
+ if err != nil {
+ return nil, err
+ }
+
+ podInfraID, err := pod.InfraContainerID()
+ if err != nil {
+ return nil, err
+ }
+ hasUserns := false
+ if podInfraID != "" {
+ podCtr, err := ic.Libpod.GetContainer(podInfraID)
+ if err != nil {
+ return nil, err
+ }
+ mappings, err := podCtr.IDMappings()
+ if err != nil {
+ return nil, err
+ }
+ hasUserns = len(mappings.UIDMap) > 0
+ }
+
+ namespaces := map[string]string{
+ // Disabled during code review per mheon
+ //"pid": fmt.Sprintf("container:%s", podInfraID),
+ "net": fmt.Sprintf("container:%s", podInfraID),
+ "ipc": fmt.Sprintf("container:%s", podInfraID),
+ "uts": fmt.Sprintf("container:%s", podInfraID),
+ }
+ if hasUserns {
+ namespaces["user"] = fmt.Sprintf("container:%s", podInfraID)
+ }
+ if !options.Quiet {
+ writer = os.Stderr
+ }
+
+ dockerRegistryOptions := image.DockerRegistryOptions{
+ DockerRegistryCreds: registryCreds,
+ DockerCertPath: options.CertDir,
+ DockerInsecureSkipTLSVerify: options.SkipTLSVerify,
+ }
+
+ // map from name to mount point
+ volumes := make(map[string]string)
+ for _, volume := range podYAML.Spec.Volumes {
+ hostPath := volume.VolumeSource.HostPath
+ if hostPath == nil {
+ return nil, errors.Errorf("HostPath is currently the only supported VolumeSource")
+ }
+ if hostPath.Type != nil {
+ switch *hostPath.Type {
+ case v1.HostPathDirectoryOrCreate:
+ if _, err := os.Stat(hostPath.Path); os.IsNotExist(err) {
+ if err := os.Mkdir(hostPath.Path, kubeDirectoryPermission); err != nil {
+ return nil, errors.Errorf("Error creating HostPath %s at %s", volume.Name, hostPath.Path)
+ }
+ }
+ // Label a newly created volume
+ if err := libpod.LabelVolumePath(hostPath.Path); err != nil {
+ return nil, errors.Wrapf(err, "Error giving %s a label", hostPath.Path)
+ }
+ case v1.HostPathFileOrCreate:
+ if _, err := os.Stat(hostPath.Path); os.IsNotExist(err) {
+ f, err := os.OpenFile(hostPath.Path, os.O_RDONLY|os.O_CREATE, kubeFilePermission)
+ if err != nil {
+ return nil, errors.Errorf("Error creating HostPath %s at %s", volume.Name, hostPath.Path)
+ }
+ if err := f.Close(); err != nil {
+ logrus.Warnf("Error in closing newly created HostPath file: %v", err)
+ }
+ }
+ // unconditionally label a newly created volume
+ if err := libpod.LabelVolumePath(hostPath.Path); err != nil {
+ return nil, errors.Wrapf(err, "Error giving %s a label", hostPath.Path)
+ }
+ case v1.HostPathDirectory:
+ case v1.HostPathFile:
+ case v1.HostPathUnset:
+ // do nothing here because we will verify the path exists in validateVolumeHostDir
+ break
+ default:
+ return nil, errors.Errorf("Directories are the only supported HostPath type")
+ }
+ }
+
+ if err := parse.ValidateVolumeHostDir(hostPath.Path); err != nil {
+ return nil, errors.Wrapf(err, "Error in parsing HostPath in YAML")
+ }
+ volumes[volume.Name] = hostPath.Path
+ }
+
+ seccompPaths, err := initializeSeccompPaths(podYAML.ObjectMeta.Annotations, options.SeccompProfileRoot)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, container := range podYAML.Spec.Containers {
+ pullPolicy := util.PullImageMissing
+ if len(container.ImagePullPolicy) > 0 {
+ pullPolicy, err = util.ValidatePullType(string(container.ImagePullPolicy))
+ if err != nil {
+ return nil, err
+ }
+ }
+ named, err := reference.ParseNormalizedNamed(container.Image)
+ if err != nil {
+ return nil, err
+ }
+ // In kube, if the image is tagged with latest, it should always pull
+ if tagged, isTagged := named.(reference.NamedTagged); isTagged {
+ if tagged.Tag() == image.LatestTag {
+ pullPolicy = util.PullImageAlways
+ }
+ }
+ newImage, err := ic.Libpod.ImageRuntime().New(ctx, container.Image, options.SignaturePolicy, options.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, nil, pullPolicy)
+ if err != nil {
+ return nil, err
+ }
+ conf, err := kubeContainerToCreateConfig(ctx, container, ic.Libpod, newImage, namespaces, volumes, pod.ID(), podInfraID, seccompPaths)
+ if err != nil {
+ return nil, err
+ }
+ ctr, err := createconfig.CreateContainerFromCreateConfig(ic.Libpod, conf, ctx, pod)
+ if err != nil {
+ return nil, err
+ }
+ containers = append(containers, ctr)
+ }
+
+ // start the containers
+ for _, ctr := range containers {
+ if err := ctr.Start(ctx, true); err != nil {
+ // Making this a hard failure here to avoid a mess
+ // the other containers are in created status
+ return nil, err
+ }
+ }
+
+ report.Pod = pod.ID()
+ for _, ctr := range containers {
+ report.Containers = append(report.Containers, ctr.ID())
+ }
+
+ return &report, nil
+}
+
+// getPodPorts converts a slice of kube container descriptions to an
+// array of ocicni portmapping descriptions usable in libpod
+func getPodPorts(containers []v1.Container) []ocicni.PortMapping {
+ var infraPorts []ocicni.PortMapping
+ for _, container := range containers {
+ for _, p := range container.Ports {
+ if p.HostPort != 0 && p.ContainerPort == 0 {
+ p.ContainerPort = p.HostPort
+ }
+ if p.Protocol == "" {
+ p.Protocol = "tcp"
+ }
+ portBinding := ocicni.PortMapping{
+ HostPort: p.HostPort,
+ ContainerPort: p.ContainerPort,
+ Protocol: strings.ToLower(string(p.Protocol)),
+ }
+ if p.HostIP != "" {
+ logrus.Debug("HostIP on port bindings is not supported")
+ }
+ // only hostPort is utilized in podman context, all container ports
+ // are accessible inside the shared network namespace
+ if p.HostPort != 0 {
+ infraPorts = append(infraPorts, portBinding)
+ }
+
+ }
+ }
+ return infraPorts
+}
+
+func setupSecurityContext(securityConfig *createconfig.SecurityConfig, userConfig *createconfig.UserConfig, containerYAML v1.Container) {
+ if containerYAML.SecurityContext == nil {
+ return
+ }
+ if containerYAML.SecurityContext.ReadOnlyRootFilesystem != nil {
+ securityConfig.ReadOnlyRootfs = *containerYAML.SecurityContext.ReadOnlyRootFilesystem
+ }
+ if containerYAML.SecurityContext.Privileged != nil {
+ securityConfig.Privileged = *containerYAML.SecurityContext.Privileged
+ }
+
+ if containerYAML.SecurityContext.AllowPrivilegeEscalation != nil {
+ securityConfig.NoNewPrivs = !*containerYAML.SecurityContext.AllowPrivilegeEscalation
+ }
+
+ if seopt := containerYAML.SecurityContext.SELinuxOptions; seopt != nil {
+ if seopt.User != "" {
+ securityConfig.SecurityOpts = append(securityConfig.SecurityOpts, fmt.Sprintf("label=user:%s", seopt.User))
+ securityConfig.LabelOpts = append(securityConfig.LabelOpts, fmt.Sprintf("user:%s", seopt.User))
+ }
+ if seopt.Role != "" {
+ securityConfig.SecurityOpts = append(securityConfig.SecurityOpts, fmt.Sprintf("label=role:%s", seopt.Role))
+ securityConfig.LabelOpts = append(securityConfig.LabelOpts, fmt.Sprintf("role:%s", seopt.Role))
+ }
+ if seopt.Type != "" {
+ securityConfig.SecurityOpts = append(securityConfig.SecurityOpts, fmt.Sprintf("label=type:%s", seopt.Type))
+ securityConfig.LabelOpts = append(securityConfig.LabelOpts, fmt.Sprintf("type:%s", seopt.Type))
+ }
+ if seopt.Level != "" {
+ securityConfig.SecurityOpts = append(securityConfig.SecurityOpts, fmt.Sprintf("label=level:%s", seopt.Level))
+ securityConfig.LabelOpts = append(securityConfig.LabelOpts, fmt.Sprintf("level:%s", seopt.Level))
+ }
+ }
+ if caps := containerYAML.SecurityContext.Capabilities; caps != nil {
+ for _, capability := range caps.Add {
+ securityConfig.CapAdd = append(securityConfig.CapAdd, string(capability))
+ }
+ for _, capability := range caps.Drop {
+ securityConfig.CapDrop = append(securityConfig.CapDrop, string(capability))
+ }
+ }
+ if containerYAML.SecurityContext.RunAsUser != nil {
+ userConfig.User = fmt.Sprintf("%d", *containerYAML.SecurityContext.RunAsUser)
+ }
+ if containerYAML.SecurityContext.RunAsGroup != nil {
+ if userConfig.User == "" {
+ userConfig.User = "0"
+ }
+ userConfig.User = fmt.Sprintf("%s:%d", userConfig.User, *containerYAML.SecurityContext.RunAsGroup)
+ }
+}
+
+// kubeContainerToCreateConfig takes a v1.Container and returns a createconfig describing a container
+func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container, runtime *libpod.Runtime, newImage *image.Image, namespaces map[string]string, volumes map[string]string, podID, infraID string, seccompPaths *kubeSeccompPaths) (*createconfig.CreateConfig, error) {
+ var (
+ containerConfig createconfig.CreateConfig
+ pidConfig createconfig.PidConfig
+ networkConfig createconfig.NetworkConfig
+ cgroupConfig createconfig.CgroupConfig
+ utsConfig createconfig.UtsConfig
+ ipcConfig createconfig.IpcConfig
+ userConfig createconfig.UserConfig
+ securityConfig createconfig.SecurityConfig
+ )
+
+ // The default for MemorySwappiness is -1, not 0
+ containerConfig.Resources.MemorySwappiness = -1
+
+ containerConfig.Image = containerYAML.Image
+ containerConfig.ImageID = newImage.ID()
+ containerConfig.Name = containerYAML.Name
+ containerConfig.Tty = containerYAML.TTY
+
+ containerConfig.Pod = podID
+
+ imageData, _ := newImage.Inspect(ctx)
+
+ userConfig.User = "0"
+ if imageData != nil {
+ userConfig.User = imageData.Config.User
+ }
+
+ setupSecurityContext(&securityConfig, &userConfig, containerYAML)
+
+ securityConfig.SeccompProfilePath = seccompPaths.findForContainer(containerConfig.Name)
+
+ containerConfig.Command = []string{}
+ if imageData != nil && imageData.Config != nil {
+ containerConfig.Command = append(containerConfig.Command, imageData.Config.Entrypoint...)
+ }
+ if len(containerYAML.Command) != 0 {
+ containerConfig.Command = append(containerConfig.Command, containerYAML.Command...)
+ } else if imageData != nil && imageData.Config != nil {
+ containerConfig.Command = append(containerConfig.Command, imageData.Config.Cmd...)
+ }
+ if imageData != nil && len(containerConfig.Command) == 0 {
+ return nil, errors.Errorf("No command specified in container YAML or as CMD or ENTRYPOINT in this image for %s", containerConfig.Name)
+ }
+
+ containerConfig.UserCommand = containerConfig.Command
+
+ containerConfig.StopSignal = 15
+
+ containerConfig.WorkDir = "/"
+ if imageData != nil {
+ // FIXME,
+ // we are currently ignoring imageData.Config.ExposedPorts
+ containerConfig.BuiltinImgVolumes = imageData.Config.Volumes
+ if imageData.Config.WorkingDir != "" {
+ containerConfig.WorkDir = imageData.Config.WorkingDir
+ }
+ containerConfig.Labels = imageData.Config.Labels
+ if imageData.Config.StopSignal != "" {
+ stopSignal, err := util.ParseSignal(imageData.Config.StopSignal)
+ if err != nil {
+ return nil, err
+ }
+ containerConfig.StopSignal = stopSignal
+ }
+ }
+
+ if containerYAML.WorkingDir != "" {
+ containerConfig.WorkDir = containerYAML.WorkingDir
+ }
+ // If the user does not pass in ID mappings, just set to basics
+ if userConfig.IDMappings == nil {
+ userConfig.IDMappings = &storage.IDMappingOptions{}
+ }
+
+ networkConfig.NetMode = ns.NetworkMode(namespaces["net"])
+ ipcConfig.IpcMode = ns.IpcMode(namespaces["ipc"])
+ utsConfig.UtsMode = ns.UTSMode(namespaces["uts"])
+ // disabled in code review per mheon
+ //containerConfig.PidMode = ns.PidMode(namespaces["pid"])
+ userConfig.UsernsMode = ns.UsernsMode(namespaces["user"])
+ if len(containerConfig.WorkDir) == 0 {
+ containerConfig.WorkDir = "/"
+ }
+
+ containerConfig.Pid = pidConfig
+ containerConfig.Network = networkConfig
+ containerConfig.Uts = utsConfig
+ containerConfig.Ipc = ipcConfig
+ containerConfig.Cgroup = cgroupConfig
+ containerConfig.User = userConfig
+ containerConfig.Security = securityConfig
+
+ annotations := make(map[string]string)
+ if infraID != "" {
+ annotations[ann.SandboxID] = infraID
+ annotations[ann.ContainerType] = ann.ContainerTypeContainer
+ }
+ containerConfig.Annotations = annotations
+
+ // Environment Variables
+ envs := map[string]string{}
+ if imageData != nil {
+ imageEnv, err := envLib.ParseSlice(imageData.Config.Env)
+ if err != nil {
+ return nil, errors.Wrap(err, "error parsing image environment variables")
+ }
+ envs = imageEnv
+ }
+ for _, e := range containerYAML.Env {
+ envs[e.Name] = e.Value
+ }
+ containerConfig.Env = envs
+
+ for _, volume := range containerYAML.VolumeMounts {
+ hostPath, exists := volumes[volume.Name]
+ if !exists {
+ return nil, errors.Errorf("Volume mount %s specified for container but not configured in volumes", volume.Name)
+ }
+ if err := parse.ValidateVolumeCtrDir(volume.MountPath); err != nil {
+ return nil, errors.Wrapf(err, "error in parsing MountPath")
+ }
+ containerConfig.Volumes = append(containerConfig.Volumes, fmt.Sprintf("%s:%s", hostPath, volume.MountPath))
+ }
+ return &containerConfig, nil
+}
+
+// kubeSeccompPaths holds information about a pod YAML's seccomp configuration
+// it holds both container and pod seccomp paths
+type kubeSeccompPaths struct {
+ containerPaths map[string]string
+ podPath string
+}
+
+// findForContainer checks whether a container has a seccomp path configured for it
+// if not, it returns the podPath, which should always have a value
+func (k *kubeSeccompPaths) findForContainer(ctrName string) string {
+ if path, ok := k.containerPaths[ctrName]; ok {
+ return path
+ }
+ return k.podPath
+}
+
+// initializeSeccompPaths takes annotations from the pod object metadata and finds annotations pertaining to seccomp
+// it parses both pod and container level
+// if the annotation is of the form "localhost/%s", the seccomp profile will be set to profileRoot/%s
+func initializeSeccompPaths(annotations map[string]string, profileRoot string) (*kubeSeccompPaths, error) {
+ seccompPaths := &kubeSeccompPaths{containerPaths: make(map[string]string)}
+ var err error
+ if annotations != nil {
+ for annKeyValue, seccomp := range annotations {
+ // check if it is prefaced with container.seccomp.security.alpha.kubernetes.io/
+ prefixAndCtr := strings.Split(annKeyValue, "/")
+ if prefixAndCtr[0]+"/" != v1.SeccompContainerAnnotationKeyPrefix {
+ continue
+ } else if len(prefixAndCtr) != 2 {
+ // this could be caused by a user inputting either of
+ // container.seccomp.security.alpha.kubernetes.io{,/}
+ // both of which are invalid
+ return nil, errors.Errorf("Invalid seccomp path: %s", prefixAndCtr[0])
+ }
+
+ path, err := verifySeccompPath(seccomp, profileRoot)
+ if err != nil {
+ return nil, err
+ }
+ seccompPaths.containerPaths[prefixAndCtr[1]] = path
+ }
+
+ podSeccomp, ok := annotations[v1.SeccompPodAnnotationKey]
+ if ok {
+ seccompPaths.podPath, err = verifySeccompPath(podSeccomp, profileRoot)
+ } else {
+ seccompPaths.podPath, err = libpod.DefaultSeccompPath()
+ }
+ if err != nil {
+ return nil, err
+ }
+ }
+ return seccompPaths, nil
+}
+
+// verifySeccompPath takes a path and checks whether it is a default, unconfined, or a path
+// the available options are parsed as defined in https://kubernetes.io/docs/concepts/policy/pod-security-policy/#seccomp
+func verifySeccompPath(path string, profileRoot string) (string, error) {
+ switch path {
+ case v1.DeprecatedSeccompProfileDockerDefault:
+ fallthrough
+ case v1.SeccompProfileRuntimeDefault:
+ return libpod.DefaultSeccompPath()
+ case "unconfined":
+ return path, nil
+ default:
+ parts := strings.Split(path, "/")
+ if parts[0] == "localhost" {
+ return filepath.Join(profileRoot, parts[1]), nil
+ }
+ return "", errors.Errorf("invalid seccomp path: %s", path)
+ }
+}
diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go
index b286bcf0d..16c222cbd 100644
--- a/pkg/domain/infra/abi/pods.go
+++ b/pkg/domain/infra/abi/pods.go
@@ -243,6 +243,10 @@ func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, optio
}
func (ic *ContainerEngine) PodPrune(ctx context.Context, options entities.PodPruneOptions) ([]*entities.PodPruneReport, error) {
+ return ic.prunePodHelper(ctx)
+}
+
+func (ic *ContainerEngine) prunePodHelper(ctx context.Context) ([]*entities.PodPruneReport, error) {
var (
reports []*entities.PodPruneReport
)
diff --git a/pkg/domain/infra/abi/pods_stats.go b/pkg/domain/infra/abi/pods_stats.go
index a41c01da0..c6befcf95 100644
--- a/pkg/domain/infra/abi/pods_stats.go
+++ b/pkg/domain/infra/abi/pods_stats.go
@@ -8,6 +8,7 @@ import (
"github.com/containers/libpod/pkg/cgroups"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/rootless"
+ "github.com/containers/libpod/utils"
"github.com/docker/go-units"
"github.com/pkg/errors"
)
@@ -68,7 +69,7 @@ func combineHumanValues(a, b uint64) string {
}
func floatToPercentString(f float64) string {
- strippedFloat, err := libpod.RemoveScientificNotationFromFloat(f)
+ strippedFloat, err := utils.RemoveScientificNotationFromFloat(f)
if err != nil || strippedFloat == 0 {
// If things go bazinga, return a safe value
return "--"
diff --git a/pkg/domain/infra/abi/runtime.go b/pkg/domain/infra/abi/runtime.go
index fba422d8e..b9020e9a5 100644
--- a/pkg/domain/infra/abi/runtime.go
+++ b/pkg/domain/infra/abi/runtime.go
@@ -16,4 +16,9 @@ type ContainerEngine struct {
Libpod *libpod.Runtime
}
+// Container-related runtime linked against libpod library
+type SystemEngine 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 e5c109ee6..24c62465f 100644
--- a/pkg/domain/infra/abi/system.go
+++ b/pkg/domain/infra/abi/system.go
@@ -5,6 +5,7 @@ import (
"fmt"
"io/ioutil"
"os"
+ "path/filepath"
"strconv"
"syscall"
@@ -18,9 +19,11 @@ import (
iopodmanAPI "github.com/containers/libpod/pkg/varlinkapi"
"github.com/containers/libpod/utils"
"github.com/containers/libpod/version"
+ "github.com/docker/distribution/reference"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
+ "github.com/spf13/pflag"
"github.com/varlink/go/varlink"
)
@@ -83,6 +86,7 @@ func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command)
}
}
}
+ return nil
}
pausePidPath, err := util.GetRootlessPauseProcessPidPath()
@@ -175,3 +179,215 @@ func setUMask() { // nolint:deadcode,unused
func checkInput() error { // nolint:deadcode,unused
return nil
}
+
+// SystemPrune removes unsed data from the system. Pruning pods, containers, volumes and images.
+func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.SystemPruneOptions) (*entities.SystemPruneReport, error) {
+ var systemPruneReport = new(entities.SystemPruneReport)
+ podPruneReport, err := ic.prunePodHelper(ctx)
+ if err != nil {
+ return nil, err
+ }
+ systemPruneReport.PodPruneReport = podPruneReport
+
+ containerPruneReport, err := ic.pruneContainersHelper(ctx, nil)
+ if err != nil {
+ return nil, err
+ }
+ systemPruneReport.ContainerPruneReport = containerPruneReport
+
+ results, err := ic.Libpod.ImageRuntime().PruneImages(ctx, options.All, nil)
+ if err != nil {
+ return nil, err
+ }
+ report := entities.ImagePruneReport{
+ Report: entities.Report{
+ Id: results,
+ Err: nil,
+ },
+ }
+
+ systemPruneReport.ImagePruneReport = &report
+
+ if options.Volume {
+ volumePruneReport, err := ic.pruneVolumesHelper(ctx)
+ if err != nil {
+ return nil, err
+ }
+ systemPruneReport.VolumePruneReport = volumePruneReport
+ }
+ return systemPruneReport, nil
+}
+
+func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.SystemDfOptions) (*entities.SystemDfReport, error) {
+ var (
+ dfImages []*entities.SystemDfImageReport
+ dfContainers []*entities.SystemDfContainerReport
+ dfVolumes []*entities.SystemDfVolumeReport
+ runningContainers []string
+ )
+
+ // Get Images and iterate them
+ imgs, err := ic.Libpod.ImageRuntime().GetImages()
+ if err != nil {
+ return nil, err
+ }
+ for _, i := range imgs {
+ var sharedSize uint64
+ cons, err := i.Containers()
+ if err != nil {
+ return nil, err
+ }
+ imageSize, err := i.Size(ctx)
+ if err != nil {
+ return nil, err
+ }
+ uniqueSize := *imageSize
+
+ parent, err := i.GetParent(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if parent != nil {
+ parentSize, err := parent.Size(ctx)
+ if err != nil {
+ return nil, err
+ }
+ uniqueSize = *parentSize - *imageSize
+ sharedSize = *imageSize - uniqueSize
+ }
+ var name, repository, tag string
+ for _, n := range i.Names() {
+ if len(n) > 0 {
+ name = n
+ break
+ }
+ }
+
+ named, err := reference.ParseNormalizedNamed(name)
+ if err != nil {
+ return nil, err
+ }
+ repository = named.Name()
+ if tagged, isTagged := named.(reference.NamedTagged); isTagged {
+ tag = tagged.Tag()
+ }
+
+ report := entities.SystemDfImageReport{
+ Repository: repository,
+ Tag: tag,
+ ImageID: i.ID(),
+ Created: i.Created(),
+ Size: int64(*imageSize),
+ SharedSize: int64(sharedSize),
+ UniqueSize: int64(uniqueSize),
+ Containers: len(cons),
+ }
+ dfImages = append(dfImages, &report)
+ }
+
+ // GetContainers and iterate them
+ cons, err := ic.Libpod.GetAllContainers()
+ if err != nil {
+ return nil, err
+ }
+ for _, c := range cons {
+ iid, _ := c.Image()
+ conSize, err := c.RootFsSize()
+ if err != nil {
+ return nil, err
+ }
+ state, err := c.State()
+ if err != nil {
+ return nil, err
+ }
+ rwsize, err := c.RWSize()
+ if err != nil {
+ return nil, err
+ }
+ report := entities.SystemDfContainerReport{
+ ContainerID: c.ID(),
+ Image: iid,
+ Command: c.Command(),
+ LocalVolumes: len(c.UserVolumes()),
+ RWSize: rwsize,
+ Size: conSize,
+ Created: c.CreatedTime(),
+ Status: state.String(),
+ Names: c.Name(),
+ }
+ dfContainers = append(dfContainers, &report)
+ }
+
+ // Get volumes and iterate them
+ vols, err := ic.Libpod.GetAllVolumes()
+ if err != nil {
+ return nil, err
+ }
+
+ running, err := ic.Libpod.GetRunningContainers()
+ if err != nil {
+ return nil, err
+ }
+ for _, c := range running {
+ runningContainers = append(runningContainers, c.ID())
+ }
+
+ for _, v := range vols {
+ var consInUse int
+ volSize, err := sizeOfPath(v.MountPoint())
+ if err != nil {
+ return nil, err
+ }
+ inUse, err := v.VolumesInUse()
+ if err != nil {
+ return nil, err
+ }
+ for _, viu := range inUse {
+ if util.StringInSlice(viu, runningContainers) {
+ consInUse += 1
+ }
+ }
+ report := entities.SystemDfVolumeReport{
+ VolumeName: v.Name(),
+ Links: consInUse,
+ Size: volSize,
+ }
+ dfVolumes = append(dfVolumes, &report)
+ }
+ return &entities.SystemDfReport{
+ Images: dfImages,
+ Containers: dfContainers,
+ Volumes: dfVolumes,
+ }, nil
+}
+
+// sizeOfPath determines the file usage of a given path. it was called volumeSize in v1
+// and now is made to be generic and take a path instead of a libpod volume
+func sizeOfPath(path string) (int64, error) {
+ var size int64
+ err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
+ if err == nil && !info.IsDir() {
+ size += info.Size()
+ }
+ return err
+ })
+ return size, err
+}
+
+func (se *SystemEngine) Reset(ctx context.Context, options entities.SystemResetOptions) error {
+ return se.Libpod.Reset(ctx)
+}
+
+func (se *SystemEngine) Renumber(ctx context.Context, flags *pflag.FlagSet, config *entities.PodmanConfig) error {
+ return nil
+}
+
+func (s SystemEngine) Migrate(ctx context.Context, flags *pflag.FlagSet, config *entities.PodmanConfig, options entities.SystemMigrateOptions) error {
+ return nil
+}
+
+func (s SystemEngine) Shutdown(ctx context.Context) {
+ if err := s.Libpod.Shutdown(false); err != nil {
+ logrus.Error(err)
+ }
+}
diff --git a/pkg/domain/infra/abi/trust.go b/pkg/domain/infra/abi/trust.go
new file mode 100644
index 000000000..5b89c91d9
--- /dev/null
+++ b/pkg/domain/infra/abi/trust.go
@@ -0,0 +1,171 @@
+package abi
+
+import (
+ "context"
+ "encoding/json"
+ "io/ioutil"
+ "os"
+ "strings"
+
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/trust"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+func (ir *ImageEngine) ShowTrust(ctx context.Context, args []string, options entities.ShowTrustOptions) (*entities.ShowTrustReport, error) {
+ var (
+ err error
+ report entities.ShowTrustReport
+ )
+ policyPath := trust.DefaultPolicyPath(ir.Libpod.SystemContext())
+ if len(options.PolicyPath) > 0 {
+ policyPath = options.PolicyPath
+ }
+ report.Raw, err = ioutil.ReadFile(policyPath)
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to read %s", policyPath)
+ }
+ if options.Raw {
+ return &report, nil
+ }
+ report.SystemRegistriesDirPath = trust.RegistriesDirPath(ir.Libpod.SystemContext())
+ if len(options.RegistryPath) > 0 {
+ report.SystemRegistriesDirPath = options.RegistryPath
+ }
+ policyContentStruct, err := trust.GetPolicy(policyPath)
+ if err != nil {
+ return nil, errors.Wrapf(err, "could not read trust policies")
+ }
+ report.Policies, err = getPolicyShowOutput(policyContentStruct, report.SystemRegistriesDirPath)
+ if err != nil {
+ return nil, errors.Wrapf(err, "could not show trust policies")
+ }
+ return &report, nil
+}
+
+func (ir *ImageEngine) SetTrust(ctx context.Context, args []string, options entities.SetTrustOptions) error {
+ var (
+ policyContentStruct trust.PolicyContent
+ newReposContent []trust.RepoContent
+ )
+ trustType := options.Type
+ if trustType == "accept" {
+ trustType = "insecureAcceptAnything"
+ }
+
+ pubkeysfile := options.PubKeysFile
+ if len(pubkeysfile) == 0 && trustType == "signedBy" {
+ return errors.Errorf("At least one public key must be defined for type 'signedBy'")
+ }
+
+ policyPath := trust.DefaultPolicyPath(ir.Libpod.SystemContext())
+ if len(options.PolicyPath) > 0 {
+ policyPath = options.PolicyPath
+ }
+ _, err := os.Stat(policyPath)
+ if !os.IsNotExist(err) {
+ policyContent, err := ioutil.ReadFile(policyPath)
+ if err != nil {
+ return errors.Wrapf(err, "unable to read %s", policyPath)
+ }
+ if err := json.Unmarshal(policyContent, &policyContentStruct); err != nil {
+ return errors.Errorf("could not read trust policies")
+ }
+ }
+ if len(pubkeysfile) != 0 {
+ for _, filepath := range pubkeysfile {
+ newReposContent = append(newReposContent, trust.RepoContent{Type: trustType, KeyType: "GPGKeys", KeyPath: filepath})
+ }
+ } else {
+ newReposContent = append(newReposContent, trust.RepoContent{Type: trustType})
+ }
+ if args[0] == "default" {
+ policyContentStruct.Default = newReposContent
+ } else {
+ if len(policyContentStruct.Default) == 0 {
+ return errors.Errorf("Default trust policy must be set.")
+ }
+ registryExists := false
+ for transport, transportval := range policyContentStruct.Transports {
+ _, registryExists = transportval[args[0]]
+ if registryExists {
+ policyContentStruct.Transports[transport][args[0]] = newReposContent
+ break
+ }
+ }
+ if !registryExists {
+ if policyContentStruct.Transports == nil {
+ policyContentStruct.Transports = make(map[string]trust.RepoMap)
+ }
+ if policyContentStruct.Transports["docker"] == nil {
+ policyContentStruct.Transports["docker"] = make(map[string][]trust.RepoContent)
+ }
+ policyContentStruct.Transports["docker"][args[0]] = append(policyContentStruct.Transports["docker"][args[0]], newReposContent...)
+ }
+ }
+
+ data, err := json.MarshalIndent(policyContentStruct, "", " ")
+ if err != nil {
+ return errors.Wrapf(err, "error setting trust policy")
+ }
+ return ioutil.WriteFile(policyPath, data, 0644)
+}
+
+func getPolicyShowOutput(policyContentStruct trust.PolicyContent, systemRegistriesDirPath string) ([]*trust.TrustPolicy, error) {
+ var output []*trust.TrustPolicy
+
+ registryConfigs, err := trust.LoadAndMergeConfig(systemRegistriesDirPath)
+ if err != nil {
+ return nil, err
+ }
+
+ if len(policyContentStruct.Default) > 0 {
+ defaultPolicyStruct := trust.TrustPolicy{
+ Name: "* (default)",
+ RepoName: "default",
+ Type: trustTypeDescription(policyContentStruct.Default[0].Type),
+ }
+ output = append(output, &defaultPolicyStruct)
+ }
+ for _, transval := range policyContentStruct.Transports {
+ for repo, repoval := range transval {
+ tempTrustShowOutput := trust.TrustPolicy{
+ Name: repo,
+ RepoName: repo,
+ Type: repoval[0].Type,
+ }
+ // TODO - keyarr is not used and I don't know its intent; commenting out for now for someone to fix later
+ //keyarr := []string{}
+ uids := []string{}
+ for _, repoele := range repoval {
+ if len(repoele.KeyPath) > 0 {
+ //keyarr = append(keyarr, repoele.KeyPath)
+ uids = append(uids, trust.GetGPGIdFromKeyPath(repoele.KeyPath)...)
+ }
+ if len(repoele.KeyData) > 0 {
+ //keyarr = append(keyarr, string(repoele.KeyData))
+ uids = append(uids, trust.GetGPGIdFromKeyData(repoele.KeyData)...)
+ }
+ }
+ tempTrustShowOutput.GPGId = strings.Join(uids, ", ")
+
+ registryNamespace := trust.HaveMatchRegistry(repo, registryConfigs)
+ if registryNamespace != nil {
+ tempTrustShowOutput.SignatureStore = registryNamespace.SigStore
+ }
+ output = append(output, &tempTrustShowOutput)
+ }
+ }
+ return output, nil
+}
+
+var typeDescription = map[string]string{"insecureAcceptAnything": "accept", "signedBy": "signed", "reject": "reject"}
+
+func trustTypeDescription(trustType string) string {
+ trustDescription, exist := typeDescription[trustType]
+ if !exist {
+ logrus.Warnf("invalid trust type %s", trustType)
+ }
+ return trustDescription
+}
diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go
index bdae4359d..91b2440df 100644
--- a/pkg/domain/infra/abi/volumes.go
+++ b/pkg/domain/infra/abi/volumes.go
@@ -1,5 +1,3 @@
-// +build ABISupport
-
package abi
import (
@@ -113,6 +111,10 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin
}
func (ic *ContainerEngine) VolumePrune(ctx context.Context, opts entities.VolumePruneOptions) ([]*entities.VolumePruneReport, error) {
+ return ic.pruneVolumesHelper(ctx)
+}
+
+func (ic *ContainerEngine) pruneVolumesHelper(ctx context.Context) ([]*entities.VolumePruneReport, error) {
var (
reports []*entities.VolumePruneReport
)
diff --git a/pkg/domain/infra/runtime_abi.go b/pkg/domain/infra/runtime_abi.go
index 7aa6986a7..67c1cd534 100644
--- a/pkg/domain/infra/runtime_abi.go
+++ b/pkg/domain/infra/runtime_abi.go
@@ -6,8 +6,10 @@ import (
"context"
"fmt"
+ "github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/bindings"
"github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/domain/infra/abi"
"github.com/containers/libpod/pkg/domain/infra/tunnel"
)
@@ -36,3 +38,32 @@ func NewImageEngine(facts *entities.PodmanConfig) (entities.ImageEngine, error)
}
return nil, fmt.Errorf("runtime mode '%v' is not supported", facts.EngineMode)
}
+
+// NewSystemEngine factory provides a libpod runtime for specialized system operations
+func NewSystemEngine(setup entities.EngineSetup, facts *entities.PodmanConfig) (entities.SystemEngine, error) {
+ switch facts.EngineMode {
+ case entities.ABIMode:
+ var r *libpod.Runtime
+ var err error
+ switch setup {
+ case entities.NormalMode:
+ r, err = GetRuntime(context.Background(), facts.FlagSet, facts)
+ case entities.RenumberMode:
+ r, err = GetRuntimeRenumber(context.Background(), facts.FlagSet, facts)
+ case entities.ResetMode:
+ r, err = GetRuntimeRenumber(context.Background(), facts.FlagSet, facts)
+ case entities.MigrateMode:
+ name, flagErr := facts.FlagSet.GetString("new-runtime")
+ if flagErr != nil {
+ return nil, flagErr
+ }
+ r, err = GetRuntimeMigrate(context.Background(), facts.FlagSet, facts, name)
+ case entities.NoFDsMode:
+ r, err = GetRuntimeDisableFDs(context.Background(), facts.FlagSet, facts)
+ }
+ return &abi.SystemEngine{Libpod: r}, err
+ case entities.TunnelMode:
+ return nil, fmt.Errorf("tunnel system runtime not supported")
+ }
+ return nil, fmt.Errorf("runtime mode '%v' is not supported", facts.EngineMode)
+}
diff --git a/pkg/domain/infra/runtime_abi_unsupported.go b/pkg/domain/infra/runtime_abi_unsupported.go
new file mode 100644
index 000000000..c4e25e990
--- /dev/null
+++ b/pkg/domain/infra/runtime_abi_unsupported.go
@@ -0,0 +1,14 @@
+// +build !ABISupport
+
+package infra
+
+import (
+ "errors"
+
+ "github.com/containers/libpod/pkg/domain/entities"
+)
+
+// NewSystemEngine factory provides a libpod runtime for specialized system operations
+func NewSystemEngine(setup entities.EngineSetup, facts *entities.PodmanConfig) (entities.SystemEngine, error) {
+ return nil, errors.New("not implemented")
+}
diff --git a/pkg/domain/infra/runtime_image_proxy.go b/pkg/domain/infra/runtime_image_proxy.go
deleted file mode 100644
index ea5d0e6f2..000000000
--- a/pkg/domain/infra/runtime_image_proxy.go
+++ /dev/null
@@ -1,21 +0,0 @@
-// +build ABISupport
-
-package infra
-
-import (
- "context"
-
- "github.com/containers/libpod/pkg/domain/entities"
- "github.com/containers/libpod/pkg/domain/infra/abi"
- "github.com/spf13/pflag"
-)
-
-// ContainerEngine Image Proxy will be EOL'ed after podman is separated from libpod repo
-
-func NewLibpodImageRuntime(flags *pflag.FlagSet, opts *entities.PodmanConfig) (entities.ImageEngine, error) {
- r, err := GetRuntime(context.Background(), flags, opts)
- if err != nil {
- return nil, err
- }
- return &abi.ImageEngine{Libpod: r}, nil
-}
diff --git a/pkg/domain/infra/runtime_proxy.go b/pkg/domain/infra/runtime_proxy.go
index 41193fd89..e7002e20f 100644
--- a/pkg/domain/infra/runtime_proxy.go
+++ b/pkg/domain/infra/runtime_proxy.go
@@ -19,3 +19,11 @@ func NewLibpodRuntime(flags *flag.FlagSet, opts *entities.PodmanConfig) (entitie
}
return &abi.ContainerEngine{Libpod: r}, nil
}
+
+func NewLibpodImageRuntime(flags *flag.FlagSet, opts *entities.PodmanConfig) (entities.ImageEngine, error) {
+ r, err := GetRuntime(context.Background(), flags, opts)
+ if err != nil {
+ return nil, err
+ }
+ return &abi.ImageEngine{Libpod: r}, nil
+}
diff --git a/pkg/domain/infra/tunnel/auto-update.go b/pkg/domain/infra/tunnel/auto-update.go
new file mode 100644
index 000000000..fac033050
--- /dev/null
+++ b/pkg/domain/infra/tunnel/auto-update.go
@@ -0,0 +1,12 @@
+package tunnel
+
+import (
+ "context"
+
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+)
+
+func (ic *ContainerEngine) AutoUpdate(ctx context.Context) (*entities.AutoUpdateReport, []error) {
+ return nil, []error{errors.New("not implemented")}
+}
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index 32f9c4e36..49a3069d6 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -14,6 +14,10 @@ import (
"github.com/pkg/errors"
)
+func (ic *ContainerEngine) ContainerRunlabel(ctx context.Context, label string, image string, args []string, options entities.ContainerRunlabelOptions) error {
+ return errors.New("not implemented")
+}
+
func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrId string) (*entities.BoolReport, error) {
exists, err := containers.Exists(ic.ClientCxt, nameOrId)
return &entities.BoolReport{Value: exists}, err
@@ -387,3 +391,7 @@ func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string,
// Shutdown Libpod engine
func (ic *ContainerEngine) Shutdown(_ context.Context) {
}
+
+func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []string, options entities.ContainerStatsOptions) error {
+ return errors.New("not implemented")
+}
diff --git a/pkg/domain/infra/tunnel/generate.go b/pkg/domain/infra/tunnel/generate.go
index 3cd483053..eb5587f89 100644
--- a/pkg/domain/infra/tunnel/generate.go
+++ b/pkg/domain/infra/tunnel/generate.go
@@ -3,6 +3,7 @@ package tunnel
import (
"context"
+ "github.com/containers/libpod/pkg/bindings/generate"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/pkg/errors"
)
@@ -10,3 +11,7 @@ import (
func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, options entities.GenerateSystemdOptions) (*entities.GenerateSystemdReport, error) {
return nil, errors.New("not implemented for tunnel")
}
+
+func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrID string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) {
+ return generate.GenerateKube(ic.ClientCxt, nameOrID, options)
+}
diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go
index dcc5fc3e7..00893194c 100644
--- a/pkg/domain/infra/tunnel/images.go
+++ b/pkg/domain/infra/tunnel/images.go
@@ -20,8 +20,8 @@ func (ir *ImageEngine) Exists(_ context.Context, nameOrId string) (*entities.Boo
return &entities.BoolReport{Value: found}, err
}
-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) Remove(ctx context.Context, imagesArg []string, opts entities.ImageRemoveOptions) (*entities.ImageRemoveReport, []error) {
+ return images.BatchRemove(ir.ClientCxt, imagesArg, opts)
}
func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) ([]*entities.ImageSummary, error) {
diff --git a/pkg/domain/infra/tunnel/manifest.go b/pkg/domain/infra/tunnel/manifest.go
index 18b400533..9c1f5349a 100644
--- a/pkg/domain/infra/tunnel/manifest.go
+++ b/pkg/domain/infra/tunnel/manifest.go
@@ -3,6 +3,7 @@ package tunnel
import (
"context"
"encoding/json"
+ "fmt"
"strings"
"github.com/containers/libpod/libpod/image"
@@ -62,3 +63,48 @@ func (ir *ImageEngine) ManifestAdd(ctx context.Context, opts entities.ManifestAd
}
return listID, nil
}
+
+// FIXME There is no endpoint for annotate and therefor this code is currently invalid
+// ManifestAnnotate updates an entry of the manifest list
+func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, names []string, opts entities.ManifestAnnotateOptions) (string, error) {
+ return "", errors.New("not implemented")
+ // manifestAnnotateOpts := image.ManifestAnnotateOpts{
+ // Arch: opts.Arch,
+ // Features: opts.Features,
+ // OS: opts.OS,
+ // OSFeatures: opts.OSFeatures,
+ // 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]
+ // }
+ // manifestAnnotateOpts.Annotation = annotations
+ // }
+ // updatedListID, err := manifests.Annotate(ctx, names[0], names[1], manifestAnnotateOpts)
+ // if err != nil {
+ // return updatedListID, errors.Wrapf(err, "error annotating %s of manifest list %s", names[1], names[0])
+ // }
+ // return fmt.Sprintf("%s :%s", updatedListID, names[1]), nil
+}
+
+// ManifestRemove removes the digest from manifest list
+func (ir *ImageEngine) ManifestRemove(ctx context.Context, names []string) (string, error) {
+ updatedListID, err := manifests.Remove(ctx, names[0], names[1])
+ if err != nil {
+ return updatedListID, errors.Wrapf(err, "error removing from manifest %s", names[0])
+ }
+ return fmt.Sprintf("%s :%s\n", updatedListID, names[1]), nil
+}
+
+// ManifestPush pushes a manifest list or image index to the destination
+func (ir *ImageEngine) ManifestPush(ctx context.Context, names []string, opts entities.ManifestPushOptions) error {
+ _, err := manifests.Push(ctx, names[0], &names[1], &opts.All)
+ return err
+}
diff --git a/pkg/domain/infra/tunnel/network.go b/pkg/domain/infra/tunnel/network.go
new file mode 100644
index 000000000..4ff72dcfc
--- /dev/null
+++ b/pkg/domain/infra/tunnel/network.go
@@ -0,0 +1,23 @@
+package tunnel
+
+import (
+ "context"
+ "errors"
+
+ "github.com/containers/libpod/pkg/domain/entities"
+)
+
+func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.NetworkListOptions) ([]*entities.NetworkListReport, error) {
+ return nil, errors.New("not implemented")
+}
+
+func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.NetworkInspectOptions) ([]entities.NetworkInspectReport, error) {
+ return nil, errors.New("not implemented")
+}
+func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, options entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) {
+ return nil, errors.New("not implemented")
+}
+
+func (ic *ContainerEngine) NetworkCreate(ctx context.Context, name string, options entities.NetworkCreateOptions) (*entities.NetworkCreateReport, error) {
+ return nil, errors.New("not implemented")
+}
diff --git a/pkg/domain/infra/tunnel/play.go b/pkg/domain/infra/tunnel/play.go
new file mode 100644
index 000000000..15383a703
--- /dev/null
+++ b/pkg/domain/infra/tunnel/play.go
@@ -0,0 +1,12 @@
+package tunnel
+
+import (
+ "context"
+
+ "github.com/containers/libpod/pkg/bindings/play"
+ "github.com/containers/libpod/pkg/domain/entities"
+)
+
+func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) {
+ return play.PlayKube(ic.ClientCxt, path, options)
+}
diff --git a/pkg/domain/infra/tunnel/system.go b/pkg/domain/infra/tunnel/system.go
index 97bf885e7..448fbed1f 100644
--- a/pkg/domain/infra/tunnel/system.go
+++ b/pkg/domain/infra/tunnel/system.go
@@ -21,3 +21,12 @@ func (ic *ContainerEngine) VarlinkService(_ context.Context, _ entities.ServiceO
func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command) error {
panic(errors.New("rootless engine mode is not supported when tunneling"))
}
+
+// SystemPrune prunes unused data from the system.
+func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.SystemPruneOptions) (*entities.SystemPruneReport, error) {
+ return system.Prune(ic.ClientCxt, &options.All, &options.Volume)
+}
+
+func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.SystemDfOptions) (*entities.SystemDfReport, error) {
+ panic(errors.New("system df is not supported on remote clients"))
+}
diff --git a/pkg/domain/infra/tunnel/trust.go b/pkg/domain/infra/tunnel/trust.go
new file mode 100644
index 000000000..a976bfdc2
--- /dev/null
+++ b/pkg/domain/infra/tunnel/trust.go
@@ -0,0 +1,16 @@
+package tunnel
+
+import (
+ "context"
+ "errors"
+
+ "github.com/containers/libpod/pkg/domain/entities"
+)
+
+func (ir *ImageEngine) ShowTrust(ctx context.Context, args []string, options entities.ShowTrustOptions) (*entities.ShowTrustReport, error) {
+ return nil, errors.New("not implemented")
+}
+
+func (ir *ImageEngine) SetTrust(ctx context.Context, args []string, options entities.SetTrustOptions) error {
+ return errors.New("not implemented")
+}