summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml2
-rw-r--r--Makefile6
-rw-r--r--RELEASE_NOTES.md11
-rw-r--r--changelog.txt18
-rw-r--r--cmd/podman/autoupdate.go56
-rw-r--r--cmd/podman/cliconfig/config.go13
-rw-r--r--cmd/podman/commands.go1
-rw-r--r--cmd/podman/pod_ps.go126
-rw-r--r--cmd/podman/shared/create.go59
-rw-r--r--cmd/podman/shared/pod.go126
-rw-r--r--cmd/podmanV2/README.md113
-rw-r--r--cmd/podmanV2/containers/container.go33
-rw-r--r--cmd/podmanV2/containers/inspect.go42
-rw-r--r--cmd/podmanV2/containers/list.go34
-rw-r--r--cmd/podmanV2/containers/ps.go29
-rw-r--r--cmd/podmanV2/images/history.go79
-rw-r--r--cmd/podmanV2/images/image.go33
-rw-r--r--cmd/podmanV2/images/images.go46
-rw-r--r--cmd/podmanV2/images/inspect.go124
-rw-r--r--cmd/podmanV2/images/list.go33
-rw-r--r--cmd/podmanV2/main.go84
-rw-r--r--cmd/podmanV2/networks/network.go33
-rw-r--r--cmd/podmanV2/pods/pod.go33
-rw-r--r--cmd/podmanV2/pods/ps.go32
-rw-r--r--cmd/podmanV2/registry/registry.go96
-rw-r--r--cmd/podmanV2/report/templates.go61
-rw-r--r--cmd/podmanV2/root.go35
-rw-r--r--cmd/podmanV2/system/system.go33
-rw-r--r--cmd/podmanV2/volumes/volume.go33
-rw-r--r--completions/bash/podman1
-rw-r--r--contrib/spec/podman.spec.in2
-rw-r--r--contrib/systemd/auto-update/podman-auto-update.service11
-rw-r--r--contrib/systemd/auto-update/podman-auto-update.timer9
-rw-r--r--docs/source/markdown/podman-auto-update.1.md46
-rw-r--r--docs/source/markdown/podman.1.md1
-rw-r--r--go.mod2
-rw-r--r--go.sum4
-rwxr-xr-xhack/man-page-checker24
-rwxr-xr-xhack/podman-commands.sh3
-rwxr-xr-xhack/xref-helpmsgs-manpages307
-rw-r--r--libpod/container.go14
-rw-r--r--libpod/events/config.go2
-rw-r--r--libpod/healthcheck_linux.go44
-rw-r--r--libpod/options.go4
-rw-r--r--libpod/runtime_ctr.go1
-rw-r--r--libpod/runtime_pod.go10
-rw-r--r--libpod/runtime_pod_infra_linux.go6
-rw-r--r--pkg/adapter/autoupdate.go11
-rw-r--r--pkg/adapter/autoupdate_remote.go11
-rw-r--r--pkg/adapter/pods.go26
-rw-r--r--pkg/adapter/pods_remote.go7
-rw-r--r--pkg/api/handlers/compat/containers.go2
-rw-r--r--pkg/api/handlers/compat/images_history.go2
-rw-r--r--pkg/api/handlers/libpod/pods.go7
-rw-r--r--pkg/api/handlers/libpod/swagger.go29
-rw-r--r--pkg/api/handlers/utils/containers.go2
-rw-r--r--pkg/api/handlers/utils/pods.go45
-rw-r--r--pkg/api/server/register_manifest.go12
-rw-r--r--pkg/api/server/register_swagger.go15
-rw-r--r--pkg/api/server/server.go91
-rw-r--r--pkg/autoupdate/autoupdate.go280
-rw-r--r--pkg/autoupdate/autoupdate_test.go50
-rw-r--r--pkg/bindings/connection.go2
-rw-r--r--pkg/bindings/test/common_test.go2
-rw-r--r--pkg/bindings/test/pods_test.go67
-rw-r--r--pkg/bindings/test/volumes_test.go5
-rw-r--r--pkg/domain/entities/containers.go1
-rw-r--r--pkg/domain/entities/engine.go97
-rw-r--r--pkg/domain/entities/engine_container.go26
-rw-r--r--pkg/domain/entities/engine_image.go12
-rw-r--r--pkg/domain/entities/filters.go150
-rw-r--r--pkg/domain/entities/images.go151
-rw-r--r--pkg/domain/entities/types.go25
-rw-r--r--pkg/domain/infra/abi/images.go131
-rw-r--r--pkg/domain/infra/abi/images_test.go37
-rw-r--r--pkg/domain/infra/abi/runtime.go19
-rw-r--r--pkg/domain/infra/runtime_abi.go39
-rw-r--r--pkg/domain/infra/runtime_image_proxy.go25
-rw-r--r--pkg/domain/infra/runtime_libpod.go331
-rw-r--r--pkg/domain/infra/runtime_proxy.go54
-rw-r--r--pkg/domain/infra/runtime_tunnel.go35
-rw-r--r--pkg/domain/infra/tunnel/images.go81
-rw-r--r--pkg/domain/infra/tunnel/runtime.go45
-rw-r--r--pkg/domain/utils/utils.go41
-rw-r--r--pkg/rootless/rootless_linux.c17
-rw-r--r--pkg/spec/createconfig.go3
-rw-r--r--pkg/specgen/create.go2
-rw-r--r--pkg/specgen/specgen.go4
-rw-r--r--pkg/systemd/dbus.go47
-rw-r--r--pkg/systemd/generate/systemdgen.go9
-rw-r--r--pkg/systemd/generate/systemdgen_test.go7
-rw-r--r--pkg/varlinkapi/system.go2
-rw-r--r--test/e2e/search_test.go7
-rw-r--r--vendor/github.com/containerd/containerd/log/context.go90
-rw-r--r--vendor/github.com/containerd/containerd/platforms/compare.go229
-rw-r--r--vendor/github.com/containerd/containerd/platforms/cpuinfo.go117
-rw-r--r--vendor/github.com/containerd/containerd/platforms/database.go114
-rw-r--r--vendor/github.com/containerd/containerd/platforms/defaults.go38
-rw-r--r--vendor/github.com/containerd/containerd/platforms/defaults_unix.go24
-rw-r--r--vendor/github.com/containerd/containerd/platforms/defaults_windows.go31
-rw-r--r--vendor/github.com/containerd/containerd/platforms/platforms.go279
-rw-r--r--vendor/github.com/containers/buildah/CHANGELOG.md16
-rw-r--r--vendor/github.com/containers/buildah/buildah.go2
-rw-r--r--vendor/github.com/containers/buildah/changelog.txt16
-rw-r--r--vendor/github.com/containers/buildah/go.mod4
-rw-r--r--vendor/github.com/containers/buildah/go.sum12
-rw-r--r--vendor/github.com/openshift/imagebuilder/builder.go6
-rw-r--r--vendor/github.com/openshift/imagebuilder/dispatchers.go84
-rw-r--r--vendor/github.com/openshift/imagebuilder/vendor.conf5
-rw-r--r--vendor/modules.txt6
-rw-r--r--version/version.go2
111 files changed, 4723 insertions, 331 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index 5ec35cccb..c44277b05 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -137,7 +137,7 @@ gating_task:
- 'cd $GOSRC && ./hack/podman-commands.sh |& ${TIMESTAMP}'
# N/B: need 'clean' so some committed files are re-generated.
- '/usr/local/bin/entrypoint.sh clean podman-remote |& ${TIMESTAMP}'
- - '/usr/local/bin/entrypoint.sh clean podman BUILDTAGS="exclude_graphdriver_devicemapper selinux seccomp" |& ${TIMESTAMP}'
+ - '/usr/local/bin/entrypoint.sh clean podman xref_helpmsgs_manpages BUILDTAGS="exclude_graphdriver_devicemapper selinux seccomp" |& ${TIMESTAMP}'
- '/usr/local/bin/entrypoint.sh local-cross |& ${TIMESTAMP}'
- '/usr/local/bin/entrypoint.sh podman-remote-darwin |& ${TIMESTAMP}'
- '/usr/local/bin/entrypoint.sh podman-remote-windows |& ${TIMESTAMP}'
diff --git a/Makefile b/Makefile
index 6104c444c..d0f7d9382 100644
--- a/Makefile
+++ b/Makefile
@@ -383,6 +383,10 @@ docdir:
.PHONY: docs
docs: .install.md2man docdir $(MANPAGES) ## Generate documentation
+.PHONE: xref_helpmsgs_manpages
+xref_helpmsgs_manpages:
+ ./hack/xref-helpmsgs-manpages
+
install-podman-remote-%-docs: podman-remote docs $(MANPAGES)
rm -rf docs/build/remote
mkdir -p docs/build/remote
@@ -430,7 +434,7 @@ podman-remote-release-%.zip:
cp release.txt "$(TMPDIR)/"
cp ./bin/podman-remote-$*$(BINSFX) "$(TMPDIR)/$(SUBDIR)/podman$(BINSFX)"
cp -r ./docs/build/remote/$* "$(TMPDIR)/$(SUBDIR)/docs/"
- cd "$(TMPDIR)" && \
+ cd "$(TMPDIR)/$(SUBDIR)" && \
zip --recurse-paths "$(CURDIR)/$@" "./release.txt" "./"
-rm -rf "$(TMPDIR)"
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index c9c676ccb..6578f40fd 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -2,19 +2,26 @@
## 1.8.2
+### Features
+- Initial support for automatically updating containers managed via Systemd unit files has been merged. This allows containers to automatically upgrade if a newer version of their image becomes available
+
### Bugfixes
-- Fixed a bug where unit files generates by `podman generate systemd --new` would not force containers to detach, causing the unit to time out when trying to start
+- Fixed a bug where unit files generated by `podman generate systemd --new` would not force containers to detach, causing the unit to time out when trying to start
- Fixed a bug where `podman system reset` could delete important system directories if run as rootless on installations created by older Podman ([#4831](https://github.com/containers/libpod/issues/4831))
- Fixed a bug where image built by `podman build` would not properly set the OS and Architecture they were built with ([#5503](https://github.com/containers/libpod/issues/5503))
- Fixed a bug where attached `podman run` with `--sig-proxy` enabled (the default), when built with Go 1.14, would repeatedly send signal 23 to the process in the container and could generate errors when the container stopped ([#5483](https://github.com/containers/libpod/issues/5483))
- Fixed a bug where rootless `podman run` commands could hang when forwarding ports
+- Fixed a bug where rootless Podman would not work when `/proc` was mounted with the `hidepid` option set
+- Fixed a bug where the `podman system service` command would use large amounts of CPU when `--timeout` was set to 0 ([#5531](https://github.com/containers/libpod/issues/5531))
### HTTP API
- Initial support for Libpod endpoints related to creating and operating on image manifest lists has been added
- The Libpod Healthcheck and Events API endpoints are now supported
+- The Swagger endpoint can now handle cases where no Swagger documentation has been generated
### Misc
-- Updated vendored containers/storage to v1.16.5
+- Updated Buildah to v1.14.3
+- Updated containers/storage to v1.16.5
- Several performance improvements have been made to creating containers, which should somewhat improve the performance of `podman create` and `podman run`
## 1.8.1
diff --git a/changelog.txt b/changelog.txt
index e1f65fe9b..1e08a8419 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,3 +1,21 @@
+- Changelog for v1.8.2 (2020-03-19)
+ * fix reported compat issues
+ * Don't include SUBDIR in windows.zip
+ * rootless: fix usage with hidepid=1
+ * V2 podman command
+ * serve swagger when present
+ * swagger: more consistency fixes
+ * Vendor in containers/buildah v1.14.3
+ * Reduce CPU usage when --timeout=0
+ * New test: man page cross-ref against --help
+ * podman: avoid conmon zombie on exec
+ * Filter pods through pod list api
+ * Bump to v1.8.2-dev
+ * Fix vendoring on master
+ * fix timeout file flake
+ * auto updates
+ * pkg/systemd: add dbus support
+
- Changelog for v1.8.2-rc1 (2020-03-17)
* Update release notes for v1.8.2-rc1
* Fix vendoring on master
diff --git a/cmd/podman/autoupdate.go b/cmd/podman/autoupdate.go
new file mode 100644
index 000000000..2cc1ae72e
--- /dev/null
+++ b/cmd/podman/autoupdate.go
@@ -0,0 +1,56 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podman/cliconfig"
+ "github.com/containers/libpod/pkg/adapter"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ autoUpdateCommand cliconfig.AutoUpdateValues
+ autoUpdateDescription = `Auto update containers according to their auto-update policy.
+
+Auto-update policies are specified with the "io.containers.autoupdate" label.`
+ _autoUpdateCommand = &cobra.Command{
+ Use: "auto-update [flags]",
+ Short: "Auto update containers according to their auto-update policy",
+ Args: noSubArgs,
+ Long: autoUpdateDescription,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ restartCommand.InputArgs = args
+ restartCommand.GlobalFlags = MainGlobalOpts
+ return autoUpdateCmd(&restartCommand)
+ },
+ Example: `podman auto-update`,
+ }
+)
+
+func init() {
+ autoUpdateCommand.Command = _autoUpdateCommand
+ autoUpdateCommand.SetHelpTemplate(HelpTemplate())
+ autoUpdateCommand.SetUsageTemplate(UsageTemplate())
+}
+
+func autoUpdateCmd(c *cliconfig.RestartValues) error {
+ runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand)
+ if err != nil {
+ return errors.Wrapf(err, "error creating libpod runtime")
+ }
+ defer runtime.DeferredShutdown(false)
+
+ units, failures := runtime.AutoUpdate()
+ for _, unit := range units {
+ fmt.Println(unit)
+ }
+ var finalErr error
+ if len(failures) > 0 {
+ finalErr = failures[0]
+ for _, e := range failures[1:] {
+ finalErr = errors.Errorf("%v\n%v", finalErr, e)
+ }
+ }
+ return finalErr
+}
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go
index 79917946a..94a7b2091 100644
--- a/cmd/podman/cliconfig/config.go
+++ b/cmd/podman/cliconfig/config.go
@@ -54,6 +54,10 @@ type AttachValues struct {
SigProxy bool
}
+type AutoUpdateValues struct {
+ PodmanCommand
+}
+
type ImagesValues struct {
PodmanCommand
All bool
@@ -470,10 +474,11 @@ type RefreshValues struct {
type RestartValues struct {
PodmanCommand
- All bool
- Latest bool
- Running bool
- Timeout uint
+ All bool
+ AutoUpdate bool
+ Latest bool
+ Running bool
+ Timeout uint
}
type RestoreValues struct {
diff --git a/cmd/podman/commands.go b/cmd/podman/commands.go
index d6018a6f4..dfa04315e 100644
--- a/cmd/podman/commands.go
+++ b/cmd/podman/commands.go
@@ -11,6 +11,7 @@ const remoteclient = false
// Commands that the local client implements
func getMainCommands() []*cobra.Command {
rootCommands := []*cobra.Command{
+ _autoUpdateCommand,
_cpCommand,
_playCommand,
_loginCommand,
diff --git a/cmd/podman/pod_ps.go b/cmd/podman/pod_ps.go
index d7731e983..7acbd6888 100644
--- a/cmd/podman/pod_ps.go
+++ b/cmd/podman/pod_ps.go
@@ -4,7 +4,6 @@ import (
"fmt"
"reflect"
"sort"
- "strconv"
"strings"
"time"
@@ -13,7 +12,6 @@ import (
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/adapter"
- "github.com/containers/libpod/pkg/util"
"github.com/docker/go-units"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -29,8 +27,6 @@ const (
NUM_CTR_INFO = 10
)
-type PodFilter func(*adapter.Pod) bool
-
var (
bc_opts shared.PsOptions
)
@@ -174,29 +170,23 @@ func podPsCmd(c *cliconfig.PodPsValues) error {
opts.Format = genPodPsFormat(c)
- var filterFuncs []PodFilter
- if c.Filter != "" {
- filters := strings.Split(c.Filter, ",")
- for _, f := range filters {
- filterSplit := strings.Split(f, "=")
- if len(filterSplit) < 2 {
- return errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
- }
- generatedFunc, err := generatePodFilterFuncs(filterSplit[0], filterSplit[1])
- if err != nil {
- return errors.Wrapf(err, "invalid filter")
- }
- filterFuncs = append(filterFuncs, generatedFunc)
- }
- }
-
var pods []*adapter.Pod
+
+ // If latest is set true filters are ignored.
if c.Latest {
pod, err := runtime.GetLatestPod()
if err != nil {
return err
}
pods = append(pods, pod)
+ return generatePodPsOutput(pods, opts)
+ }
+
+ if c.Filter != "" {
+ pods, err = runtime.GetPodsWithFilters(c.Filter)
+ if err != nil {
+ return err
+ }
} else {
pods, err = runtime.GetAllPods()
if err != nil {
@@ -204,19 +194,7 @@ func podPsCmd(c *cliconfig.PodPsValues) error {
}
}
- podsFiltered := make([]*adapter.Pod, 0, len(pods))
- for _, pod := range pods {
- include := true
- for _, filter := range filterFuncs {
- include = include && filter(pod)
- }
-
- if include {
- podsFiltered = append(podsFiltered, pod)
- }
- }
-
- return generatePodPsOutput(podsFiltered, opts)
+ return generatePodPsOutput(pods, opts)
}
// podPsCheckFlagsPassed checks if mutually exclusive flags are passed together
@@ -235,88 +213,6 @@ func podPsCheckFlagsPassed(c *cliconfig.PodPsValues) error {
return nil
}
-func generatePodFilterFuncs(filter, filterValue string) (func(pod *adapter.Pod) bool, error) {
- switch filter {
- case "ctr-ids":
- return func(p *adapter.Pod) bool {
- ctrIds, err := p.AllContainersByID()
- if err != nil {
- return false
- }
- return util.StringInSlice(filterValue, ctrIds)
- }, nil
- case "ctr-names":
- return func(p *adapter.Pod) bool {
- ctrs, err := p.AllContainers()
- if err != nil {
- return false
- }
- for _, ctr := range ctrs {
- if filterValue == ctr.Name() {
- return true
- }
- }
- return false
- }, nil
- case "ctr-number":
- return func(p *adapter.Pod) bool {
- ctrIds, err := p.AllContainersByID()
- if err != nil {
- return false
- }
-
- fVint, err2 := strconv.Atoi(filterValue)
- if err2 != nil {
- return false
- }
- return len(ctrIds) == fVint
- }, nil
- case "ctr-status":
- if !util.StringInSlice(filterValue, []string{"created", "restarting", "running", "paused", "exited", "unknown"}) {
- return nil, errors.Errorf("%s is not a valid status", filterValue)
- }
- return func(p *adapter.Pod) bool {
- ctr_statuses, err := p.Status()
- if err != nil {
- return false
- }
- for _, ctr_status := range ctr_statuses {
- state := ctr_status.String()
- if ctr_status == define.ContainerStateConfigured {
- state = "created"
- }
- if state == filterValue {
- return true
- }
- }
- return false
- }, nil
- case "id":
- return func(p *adapter.Pod) bool {
- return strings.Contains(p.ID(), filterValue)
- }, nil
- case "name":
- return func(p *adapter.Pod) bool {
- return strings.Contains(p.Name(), filterValue)
- }, nil
- case "status":
- if !util.StringInSlice(filterValue, []string{"stopped", "running", "paused", "exited", "dead", "created"}) {
- return nil, errors.Errorf("%s is not a valid pod status", filterValue)
- }
- return func(p *adapter.Pod) bool {
- status, err := p.GetPodStatus()
- if err != nil {
- return false
- }
- if strings.ToLower(status) == filterValue {
- return true
- }
- return false
- }, nil
- }
- return nil, errors.Errorf("%s is an invalid filter", filter)
-}
-
// generate the template based on conditions given
func genPodPsFormat(c *cliconfig.PodPsValues) string {
format := ""
diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go
index 8968f10e8..cec837af6 100644
--- a/cmd/podman/shared/create.go
+++ b/cmd/podman/shared/create.go
@@ -18,6 +18,7 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image"
ann "github.com/containers/libpod/pkg/annotations"
+ "github.com/containers/libpod/pkg/autoupdate"
envLib "github.com/containers/libpod/pkg/env"
"github.com/containers/libpod/pkg/errorhandling"
"github.com/containers/libpod/pkg/inspect"
@@ -25,6 +26,7 @@ import (
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/seccomp"
cc "github.com/containers/libpod/pkg/spec"
+ systemdGen "github.com/containers/libpod/pkg/systemd/generate"
"github.com/containers/libpod/pkg/util"
"github.com/docker/go-connections/nat"
"github.com/docker/go-units"
@@ -69,6 +71,7 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.
}
imageName := ""
+ rawImageName := ""
var imageData *inspect.ImageData = nil
// Set the storage if there is no rootfs specified
@@ -78,9 +81,8 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.
writer = os.Stderr
}
- name := ""
if len(c.InputArgs) != 0 {
- name = c.InputArgs[0]
+ rawImageName = c.InputArgs[0]
} else {
return nil, nil, errors.Errorf("error, image name not provided")
}
@@ -97,7 +99,7 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.
ArchitectureChoice: overrideArch,
}
- newImage, err := runtime.ImageRuntime().New(ctx, name, rtc.SignaturePolicyPath, c.String("authfile"), writer, &dockerRegistryOptions, image.SigningOptions{}, nil, pullType)
+ newImage, err := runtime.ImageRuntime().New(ctx, rawImageName, rtc.SignaturePolicyPath, c.String("authfile"), writer, &dockerRegistryOptions, image.SigningOptions{}, nil, pullType)
if err != nil {
return nil, nil, err
}
@@ -174,11 +176,32 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.
}
}
- createConfig, err := ParseCreateOpts(ctx, c, runtime, imageName, imageData)
+ createConfig, err := ParseCreateOpts(ctx, c, runtime, imageName, rawImageName, imageData)
if err != nil {
return nil, nil, err
}
+ // (VR): Ideally we perform the checks _before_ pulling the image but that
+ // would require some bigger code refactoring of `ParseCreateOpts` and the
+ // logic here. But as the creation code will be consolidated in the future
+ // and given auto updates are experimental, we can live with that for now.
+ // In the end, the user may only need to correct the policy or the raw image
+ // name.
+ autoUpdatePolicy, autoUpdatePolicySpecified := createConfig.Labels[autoupdate.Label]
+ if autoUpdatePolicySpecified {
+ if _, err := autoupdate.LookupPolicy(autoUpdatePolicy); err != nil {
+ return nil, nil, err
+ }
+ // Now we need to make sure we're having a fully-qualified image reference.
+ if rootfs != "" {
+ return nil, nil, errors.Errorf("auto updates do not work with --rootfs")
+ }
+ // Make sure the input image is a docker.
+ if err := autoupdate.ValidateImageReference(rawImageName); err != nil {
+ return nil, nil, err
+ }
+ }
+
// Because parseCreateOpts does derive anything from the image, we add health check
// at this point. The rest is done by WithOptions.
createConfig.HealthCheck = healthCheck
@@ -270,7 +293,7 @@ func configurePod(c *GenericCLIResults, runtime *libpod.Runtime, namespaces map[
// Parses CLI options related to container creation into a config which can be
// parsed into an OCI runtime spec
-func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.Runtime, imageName string, data *inspect.ImageData) (*cc.CreateConfig, error) {
+func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.Runtime, imageName string, rawImageName string, data *inspect.ImageData) (*cc.CreateConfig, error) {
var (
inputCommand, command []string
memoryLimit, memoryReservation, memorySwap, memoryKernel int64
@@ -481,12 +504,15 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
"container": "podman",
}
+ // First transform the os env into a map. We need it for the labels later in
+ // any case.
+ osEnv, err := envLib.ParseSlice(os.Environ())
+ if err != nil {
+ return nil, errors.Wrap(err, "error parsing host environment variables")
+ }
+
// Start with env-host
if c.Bool("env-host") {
- osEnv, err := envLib.ParseSlice(os.Environ())
- if err != nil {
- return nil, errors.Wrap(err, "error parsing host environment variables")
- }
env = envLib.Join(env, osEnv)
}
@@ -534,6 +560,10 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
}
}
+ if systemdUnit, exists := osEnv[systemdGen.EnvVariable]; exists {
+ labels[systemdGen.EnvVariable] = systemdUnit
+ }
+
// ANNOTATIONS
annotations := make(map[string]string)
@@ -764,11 +794,12 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
Entrypoint: entrypoint,
Env: env,
// ExposedPorts: ports,
- Init: c.Bool("init"),
- InitPath: c.String("init-path"),
- Image: imageName,
- ImageID: imageID,
- Interactive: c.Bool("interactive"),
+ Init: c.Bool("init"),
+ InitPath: c.String("init-path"),
+ Image: imageName,
+ RawImageName: rawImageName,
+ ImageID: imageID,
+ Interactive: c.Bool("interactive"),
// IP6Address: c.String("ipv6"), // Not implemented yet - needs CNI support for static v6
Labels: labels,
// LinkLocalIP: c.StringSlice("link-local-ip"), // Not implemented yet
diff --git a/cmd/podman/shared/pod.go b/cmd/podman/shared/pod.go
index 7b0b497fc..3046953b5 100644
--- a/cmd/podman/shared/pod.go
+++ b/cmd/podman/shared/pod.go
@@ -2,9 +2,11 @@ package shared
import (
"strconv"
+ "strings"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/util"
"github.com/cri-o/ocicni/pkg/ocicni"
"github.com/docker/go-connections/nat"
"github.com/pkg/errors"
@@ -134,4 +136,128 @@ func CreatePortBindings(ports []string) ([]ocicni.PortMapping, error) {
return portBindings, nil
}
+// GetPodsWithFilters uses the cliconfig to categorize if the latest pod is required.
+func GetPodsWithFilters(r *libpod.Runtime, filters string) ([]*libpod.Pod, error) {
+ filterFuncs, err := GenerateFilterFunction(r, strings.Split(filters, ","))
+ if err != nil {
+ return nil, err
+ }
+ return FilterAllPodsWithFilterFunc(r, filterFuncs...)
+}
+
+// FilterAllPodsWithFilterFunc retrieves all pods
+// Filters can be provided which will determine which pods are included in the
+// output. Multiple filters are handled by ANDing their output, so only pods
+// matching all filters are returned
+func FilterAllPodsWithFilterFunc(r *libpod.Runtime, filters ...libpod.PodFilter) ([]*libpod.Pod, error) {
+ pods, err := r.Pods(filters...)
+ if err != nil {
+ return nil, err
+ }
+ return pods, nil
+}
+
+// GenerateFilterFunction basically gets the filters based on the input by the user
+// and filter the pod list based on the criteria.
+func GenerateFilterFunction(r *libpod.Runtime, filters []string) ([]libpod.PodFilter, error) {
+ var filterFuncs []libpod.PodFilter
+ for _, f := range filters {
+ filterSplit := strings.Split(f, "=")
+ if len(filterSplit) < 2 {
+ return nil, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
+ }
+ generatedFunc, err := generatePodFilterFuncs(filterSplit[0], filterSplit[1])
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid filter")
+ }
+ filterFuncs = append(filterFuncs, generatedFunc)
+ }
+
+ return filterFuncs, nil
+}
+func generatePodFilterFuncs(filter, filterValue string) (
+ func(pod *libpod.Pod) bool, error) {
+ switch filter {
+ case "ctr-ids":
+ return func(p *libpod.Pod) bool {
+ ctrIds, err := p.AllContainersByID()
+ if err != nil {
+ return false
+ }
+ return util.StringInSlice(filterValue, ctrIds)
+ }, nil
+ case "ctr-names":
+ return func(p *libpod.Pod) bool {
+ ctrs, err := p.AllContainers()
+ if err != nil {
+ return false
+ }
+ for _, ctr := range ctrs {
+ if filterValue == ctr.Name() {
+ return true
+ }
+ }
+ return false
+ }, nil
+ case "ctr-number":
+ return func(p *libpod.Pod) bool {
+ ctrIds, err := p.AllContainersByID()
+ if err != nil {
+ return false
+ }
+
+ fVint, err2 := strconv.Atoi(filterValue)
+ if err2 != nil {
+ return false
+ }
+ return len(ctrIds) == fVint
+ }, nil
+ case "ctr-status":
+ if !util.StringInSlice(filterValue,
+ []string{"created", "restarting", "running", "paused",
+ "exited", "unknown"}) {
+ return nil, errors.Errorf("%s is not a valid status", filterValue)
+ }
+ return func(p *libpod.Pod) bool {
+ ctr_statuses, err := p.Status()
+ if err != nil {
+ return false
+ }
+ for _, ctr_status := range ctr_statuses {
+ state := ctr_status.String()
+ if ctr_status == define.ContainerStateConfigured {
+ state = "created"
+ }
+ if state == filterValue {
+ return true
+ }
+ }
+ return false
+ }, nil
+ case "id":
+ return func(p *libpod.Pod) bool {
+ return strings.Contains(p.ID(), filterValue)
+ }, nil
+ case "name":
+ return func(p *libpod.Pod) bool {
+ return strings.Contains(p.Name(), filterValue)
+ }, nil
+ case "status":
+ if !util.StringInSlice(filterValue, []string{"stopped", "running", "paused", "exited", "dead", "created"}) {
+ return nil, errors.Errorf("%s is not a valid pod status", filterValue)
+ }
+ return func(p *libpod.Pod) bool {
+ status, err := p.GetPodStatus()
+ if err != nil {
+ return false
+ }
+ if strings.ToLower(status) == filterValue {
+ return true
+ }
+ return false
+ }, nil
+ }
+ return nil, errors.Errorf("%s is an invalid filter", filter)
+}
+
var DefaultKernelNamespaces = "cgroup,ipc,net,uts"
diff --git a/cmd/podmanV2/README.md b/cmd/podmanV2/README.md
new file mode 100644
index 000000000..a17e6f850
--- /dev/null
+++ b/cmd/podmanV2/README.md
@@ -0,0 +1,113 @@
+# Adding a podman V2 commands
+
+## Build podman V2
+
+```shell script
+$ cd $GOPATH/src/github.com/containers/libpod/cmd/podmanV2
+```
+If you wish to include the libpod library in your program,
+```shell script
+$ go build -tags 'ABISupport' .
+```
+The `--remote` flag may be used to connect to the Podman service using the API.
+Otherwise, direct calls will be made to the Libpod library.
+```shell script
+$ go build -tags '!ABISupport' .
+```
+The Libpod library is not linked into the executable.
+All calls are made via the API and `--remote=False` is an error condition.
+
+## Adding a new command `podman manifests`
+```shell script
+$ mkdir -p $GOPATH/src/github.com/containers/libpod/cmd/podmanV2/manifests
+```
+Create the file ```$GOPATH/src/github.com/containers/libpod/cmd/podmanV2/manifests/manifest.go```
+```go
+package manifests
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ // podman _manifests_
+ manifestCmd = &cobra.Command{
+ Use: "manifest",
+ Short: "Manage manifests",
+ Long: "Manage manifests",
+ Example: "podman manifests IMAGE",
+ TraverseChildren: true,
+ PersistentPreRunE: preRunE,
+ RunE: registry.SubCommandExists, // Report error if there is no sub command given
+ }
+)
+func init() {
+ // Subscribe command to podman
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ // _podman manifest_ will support both ABIMode and TunnelMode
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ // The definition for this command
+ Command: manifestCmd,
+ })
+ // Setup cobra templates, sub commands will inherit
+ manifestCmd.SetHelpTemplate(registry.HelpTemplate())
+ manifestCmd.SetUsageTemplate(registry.UsageTemplate())
+}
+
+// preRunE populates the image engine for sub commands
+func preRunE(cmd *cobra.Command, args []string) error {
+ _, err := registry.NewImageEngine(cmd, args)
+ return err
+}
+```
+To "wire" in the `manifest` command, edit the file ```$GOPATH/src/github.com/containers/libpod/cmd/podmanV2/main.go``` to add:
+```go
+package main
+
+import _ "github.com/containers/libpod/cmd/podmanV2/manifests"
+```
+
+## Adding a new sub command `podman manifests list`
+Create the file ```$GOPATH/src/github.com/containers/libpod/cmd/podmanV2/manifests/inspect.go```
+```go
+package manifests
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ // podman manifests _inspect_
+ inspectCmd = &cobra.Command{
+ Use: "inspect IMAGE",
+ Short: "Display manifest from image",
+ Long: "Displays the low-level information on a manifest identified by image name or ID",
+ RunE: inspect,
+ Example: "podman manifest DEADBEEF",
+ }
+)
+
+func init() {
+ // Subscribe inspect sub command to manifest command
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ // _podman manifest inspect_ will support both ABIMode and TunnelMode
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ // The definition for this command
+ Command: inspectCmd,
+ Parent: manifestCmd,
+ })
+
+ // This is where you would configure the cobra flags using inspectCmd.Flags()
+}
+
+// Business logic: cmd is inspectCmd, args is the positional arguments from os.Args
+func inspect(cmd *cobra.Command, args []string) error {
+ // Business logic using registry.ImageEngine
+ // Do not pull from libpod directly use the domain objects and types
+ return nil
+}
+```
diff --git a/cmd/podmanV2/containers/container.go b/cmd/podmanV2/containers/container.go
new file mode 100644
index 000000000..6b44f2a3e
--- /dev/null
+++ b/cmd/podmanV2/containers/container.go
@@ -0,0 +1,33 @@
+package containers
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ // Command: podman _container_
+ containerCmd = &cobra.Command{
+ Use: "container",
+ Short: "Manage containers",
+ Long: "Manage containers",
+ TraverseChildren: true,
+ PersistentPreRunE: preRunE,
+ RunE: registry.SubCommandExists,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: containerCmd,
+ })
+ containerCmd.SetHelpTemplate(registry.HelpTemplate())
+ containerCmd.SetUsageTemplate(registry.UsageTemplate())
+}
+
+func preRunE(cmd *cobra.Command, args []string) error {
+ _, err := registry.NewContainerEngine(cmd, args)
+ return err
+}
diff --git a/cmd/podmanV2/containers/inspect.go b/cmd/podmanV2/containers/inspect.go
new file mode 100644
index 000000000..635be4789
--- /dev/null
+++ b/cmd/podmanV2/containers/inspect.go
@@ -0,0 +1,42 @@
+package containers
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ // podman container _inspect_
+ inspectCmd = &cobra.Command{
+ Use: "inspect [flags] CONTAINER",
+ Short: "Display the configuration of a container",
+ Long: `Displays the low-level information on a container identified by name or ID.`,
+ PreRunE: inspectPreRunE,
+ RunE: inspect,
+ Example: `podman container inspect myCtr
+ podman container inspect -l --format '{{.Id}} {{.Config.Labels}}'`,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: inspectCmd,
+ Parent: containerCmd,
+ })
+}
+
+func inspectPreRunE(cmd *cobra.Command, args []string) (err error) {
+ err = preRunE(cmd, args)
+ if err != nil {
+ return
+ }
+
+ _, err = registry.NewImageEngine(cmd, args)
+ return err
+}
+
+func inspect(cmd *cobra.Command, args []string) error {
+ return nil
+}
diff --git a/cmd/podmanV2/containers/list.go b/cmd/podmanV2/containers/list.go
new file mode 100644
index 000000000..630d9bbc7
--- /dev/null
+++ b/cmd/podmanV2/containers/list.go
@@ -0,0 +1,34 @@
+package containers
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ // podman container _list_
+ listCmd = &cobra.Command{
+ Use: "list",
+ Aliases: []string{"ls"},
+ Args: cobra.NoArgs,
+ Short: "List containers",
+ Long: "Prints out information about the containers",
+ RunE: containers,
+ Example: `podman container list -a
+ podman container list -a --format "{{.ID}} {{.Image}} {{.Labels}} {{.Mounts}}"
+ podman container list --size --sort names`,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: listCmd,
+ Parent: containerCmd,
+ })
+}
+
+func containers(cmd *cobra.Command, args []string) error {
+ return nil
+}
diff --git a/cmd/podmanV2/containers/ps.go b/cmd/podmanV2/containers/ps.go
new file mode 100644
index 000000000..ce3d66c51
--- /dev/null
+++ b/cmd/podmanV2/containers/ps.go
@@ -0,0 +1,29 @@
+package containers
+
+import (
+ "strings"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ // podman _ps_
+ psCmd = &cobra.Command{
+ Use: "ps",
+ Args: cobra.NoArgs,
+ Short: listCmd.Short,
+ Long: listCmd.Long,
+ PersistentPreRunE: preRunE,
+ RunE: containers,
+ Example: strings.Replace(listCmd.Example, "container list", "ps", -1),
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: psCmd,
+ })
+}
diff --git a/cmd/podmanV2/images/history.go b/cmd/podmanV2/images/history.go
new file mode 100644
index 000000000..c75ae6ddc
--- /dev/null
+++ b/cmd/podmanV2/images/history.go
@@ -0,0 +1,79 @@
+package images
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "text/tabwriter"
+ "text/template"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/cmd/podmanV2/report"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ long = `Displays the history of an image.
+
+ The information can be printed out in an easy to read, or user specified format, and can be truncated.`
+
+ // podman _history_
+ historyCmd = &cobra.Command{
+ Use: "history [flags] IMAGE",
+ Short: "Show history of a specified image",
+ Long: long,
+ Example: "podman history quay.io/fedora/fedora",
+ Args: cobra.ExactArgs(1),
+ PersistentPreRunE: preRunE,
+ RunE: history,
+ }
+)
+
+var cmdFlags = struct {
+ Human bool
+ NoTrunc bool
+ Quiet bool
+ Format string
+}{}
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: historyCmd,
+ })
+
+ historyCmd.SetHelpTemplate(registry.HelpTemplate())
+ historyCmd.SetUsageTemplate(registry.UsageTemplate())
+ flags := historyCmd.Flags()
+ flags.StringVar(&cmdFlags.Format, "format", "", "Change the output to JSON or a Go template")
+ flags.BoolVarP(&cmdFlags.Human, "human", "H", true, "Display sizes and dates in human readable format")
+ flags.BoolVar(&cmdFlags.NoTrunc, "no-trunc", false, "Do not truncate the output")
+ flags.BoolVar(&cmdFlags.NoTrunc, "notruncate", false, "Do not truncate the output")
+ flags.BoolVarP(&cmdFlags.Quiet, "quiet", "q", false, "Display the numeric IDs only")
+}
+
+func history(cmd *cobra.Command, args []string) error {
+ results, err := registry.ImageEngine().History(context.Background(), args[0], entities.ImageHistoryOptions{})
+ if err != nil {
+ return err
+ }
+
+ row := "{{slice $x.ID 0 12}}\t{{toRFC3339 $x.Created}}\t{{ellipsis $x.CreatedBy 45}}\t{{$x.Size}}\t{{$x.Comment}}\n"
+ if cmdFlags.Human {
+ row = "{{slice $x.ID 0 12}}\t{{toHumanDuration $x.Created}}\t{{ellipsis $x.CreatedBy 45}}\t{{toHumanSize $x.Size}}\t{{$x.Comment}}\n"
+ }
+ format := "{{range $y, $x := . }}" + row + "{{end}}"
+
+ tmpl := template.Must(template.New("report").Funcs(report.PodmanTemplateFuncs()).Parse(format))
+ w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
+
+ _, _ = w.Write(report.ReportHeader("id", "created", "created by", "size", "comment"))
+ err = tmpl.Execute(w, results.Layers)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, errors.Wrapf(err, "Failed to print report"))
+ }
+ w.Flush()
+ return nil
+}
diff --git a/cmd/podmanV2/images/image.go b/cmd/podmanV2/images/image.go
new file mode 100644
index 000000000..a15c3e826
--- /dev/null
+++ b/cmd/podmanV2/images/image.go
@@ -0,0 +1,33 @@
+package images
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ // Command: podman _image_
+ imageCmd = &cobra.Command{
+ Use: "image",
+ Short: "Manage images",
+ Long: "Manage images",
+ TraverseChildren: true,
+ PersistentPreRunE: preRunE,
+ RunE: registry.SubCommandExists,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: imageCmd,
+ })
+ imageCmd.SetHelpTemplate(registry.HelpTemplate())
+ imageCmd.SetUsageTemplate(registry.UsageTemplate())
+}
+
+func preRunE(cmd *cobra.Command, args []string) error {
+ _, err := registry.NewImageEngine(cmd, args)
+ return err
+}
diff --git a/cmd/podmanV2/images/images.go b/cmd/podmanV2/images/images.go
new file mode 100644
index 000000000..a1e56396a
--- /dev/null
+++ b/cmd/podmanV2/images/images.go
@@ -0,0 +1,46 @@
+package images
+
+import (
+ "strings"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ // podman _images_
+ imagesCmd = &cobra.Command{
+ Use: strings.Replace(listCmd.Use, "list", "images", 1),
+ Short: listCmd.Short,
+ Long: listCmd.Long,
+ PersistentPreRunE: preRunE,
+ RunE: images,
+ Example: strings.Replace(listCmd.Example, "podman image list", "podman images", -1),
+ }
+
+ imagesOpts = entities.ImageListOptions{}
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: imagesCmd,
+ })
+ imagesCmd.SetHelpTemplate(registry.HelpTemplate())
+ imagesCmd.SetUsageTemplate(registry.UsageTemplate())
+
+ flags := imagesCmd.Flags()
+ flags.BoolVarP(&imagesOpts.All, "all", "a", false, "Show all images (default hides intermediate images)")
+ flags.BoolVar(&imagesOpts.Digests, "digests", false, "Show digests")
+ flags.StringSliceVarP(&imagesOpts.Filter, "filter", "f", []string{}, "Filter output based on conditions provided (default [])")
+ flags.StringVar(&imagesOpts.Format, "format", "", "Change the output format to JSON or a Go template")
+ flags.BoolVarP(&imagesOpts.Noheading, "noheading", "n", false, "Do not print column headings")
+ // TODO Need to learn how to deal with second name being a string instead of a char.
+ // This needs to be "no-trunc, notruncate"
+ flags.BoolVar(&imagesOpts.NoTrunc, "no-trunc", false, "Do not truncate output")
+ flags.BoolVar(&imagesOpts.NoTrunc, "notruncate", false, "Do not truncate output")
+ flags.BoolVarP(&imagesOpts.Quiet, "quiet", "q", false, "Display only image IDs")
+ flags.StringVar(&imagesOpts.Sort, "sort", "created", "Sort by created, id, repository, size, or tag")
+ flags.BoolVarP(&imagesOpts.History, "history", "", false, "Display the image name history")
+}
diff --git a/cmd/podmanV2/images/inspect.go b/cmd/podmanV2/images/inspect.go
new file mode 100644
index 000000000..9c44cea35
--- /dev/null
+++ b/cmd/podmanV2/images/inspect.go
@@ -0,0 +1,124 @@
+package images
+
+import (
+ "strings"
+
+ "github.com/containers/buildah/pkg/formats"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/util"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ inspectOpts = entities.ImageInspectOptions{}
+
+ // Command: podman image _inspect_
+ inspectCmd = &cobra.Command{
+ Use: "inspect [flags] IMAGE",
+ Short: "Display the configuration of an image",
+ Long: `Displays the low-level information on an image identified by name or ID.`,
+ PreRunE: populateEngines,
+ RunE: imageInspect,
+ Example: `podman image inspect alpine`,
+ }
+
+ containerEngine entities.ContainerEngine
+)
+
+// Inspect is unique in that it needs both an ImageEngine and a ContainerEngine
+func populateEngines(cmd *cobra.Command, args []string) (err error) {
+ // Populate registry.ImageEngine
+ err = preRunE(cmd, args)
+ if err != nil {
+ return
+ }
+
+ // Populate registry.ContainerEngine
+ containerEngine, err = registry.NewContainerEngine(cmd, args)
+ return
+}
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: inspectCmd,
+ Parent: imageCmd,
+ })
+
+ flags := inspectCmd.Flags()
+ flags.BoolVarP(&inspectOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ flags.BoolVarP(&inspectOpts.Size, "size", "s", false, "Display total file size")
+ flags.StringVarP(&inspectOpts.Format, "format", "f", "", "Change the output format to a Go template")
+
+ if registry.GlobalFlags.EngineMode == entities.ABIMode {
+ // TODO: This is the same as V1. We could skip creating the flag altogether in V2...
+ _ = flags.MarkHidden("latest")
+ }
+}
+
+const (
+ inspectTypeContainer = "container"
+ inspectTypeImage = "image"
+ inspectAll = "all"
+)
+
+func imageInspect(cmd *cobra.Command, args []string) error {
+ inspectType := inspectTypeImage
+ latestContainer := inspectOpts.Latest
+
+ if len(args) == 0 && !latestContainer {
+ return errors.Errorf("container or image name must be specified: podman inspect [options [...]] name")
+ }
+
+ if len(args) > 0 && latestContainer {
+ return errors.Errorf("you cannot provide additional arguments with --latest")
+ }
+
+ if !util.StringInSlice(inspectType, []string{inspectTypeContainer, inspectTypeImage, inspectAll}) {
+ return errors.Errorf("the only recognized types are %q, %q, and %q", inspectTypeContainer, inspectTypeImage, inspectAll)
+ }
+
+ outputFormat := inspectOpts.Format
+ if strings.Contains(outputFormat, "{{.Id}}") {
+ outputFormat = strings.Replace(outputFormat, "{{.Id}}", formats.IDString, -1)
+ }
+ // These fields were renamed, so we need to provide backward compat for
+ // the old names.
+ if strings.Contains(outputFormat, ".Src") {
+ outputFormat = strings.Replace(outputFormat, ".Src", ".Source", -1)
+ }
+ if strings.Contains(outputFormat, ".Dst") {
+ outputFormat = strings.Replace(outputFormat, ".Dst", ".Destination", -1)
+ }
+ if strings.Contains(outputFormat, ".ImageID") {
+ outputFormat = strings.Replace(outputFormat, ".ImageID", ".Image", -1)
+ }
+ _ = outputFormat
+ // if latestContainer {
+ // lc, err := ctnrRuntime.GetLatestContainer()
+ // if err != nil {
+ // return err
+ // }
+ // args = append(args, lc.ID())
+ // inspectType = inspectTypeContainer
+ // }
+
+ // inspectedObjects, iterateErr := iterateInput(getContext(), c.Size, args, runtime, inspectType)
+ // if iterateErr != nil {
+ // return iterateErr
+ // }
+ //
+ // var out formats.Writer
+ // if outputFormat != "" && outputFormat != formats.JSONString {
+ // // template
+ // out = formats.StdoutTemplateArray{Output: inspectedObjects, Template: outputFormat}
+ // } else {
+ // // default is json output
+ // out = formats.JSONStructArray{Output: inspectedObjects}
+ // }
+ //
+ // return out.Out()
+ return nil
+}
diff --git a/cmd/podmanV2/images/list.go b/cmd/podmanV2/images/list.go
new file mode 100644
index 000000000..cfdfaaed2
--- /dev/null
+++ b/cmd/podmanV2/images/list.go
@@ -0,0 +1,33 @@
+package images
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ // Command: podman image _list_
+ listCmd = &cobra.Command{
+ Use: "list [flag] [IMAGE]",
+ Aliases: []string{"ls"},
+ Short: "List images in local storage",
+ Long: "Lists images previously pulled to the system or created on the system.",
+ RunE: images,
+ Example: `podman image list --format json
+ podman image list --sort repository --format "table {{.ID}} {{.Repository}} {{.Tag}}"
+ podman image list --filter dangling=true`,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: listCmd,
+ Parent: imageCmd,
+ })
+}
+
+func images(cmd *cobra.Command, args []string) error {
+ return nil
+}
diff --git a/cmd/podmanV2/main.go b/cmd/podmanV2/main.go
new file mode 100644
index 000000000..0df086352
--- /dev/null
+++ b/cmd/podmanV2/main.go
@@ -0,0 +1,84 @@
+package main
+
+import (
+ "fmt"
+ "os"
+ "reflect"
+ "runtime"
+
+ _ "github.com/containers/libpod/cmd/podmanV2/containers"
+ _ "github.com/containers/libpod/cmd/podmanV2/images"
+ _ "github.com/containers/libpod/cmd/podmanV2/networks"
+ _ "github.com/containers/libpod/cmd/podmanV2/pods"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ _ "github.com/containers/libpod/cmd/podmanV2/volumes"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/sirupsen/logrus"
+ "github.com/spf13/cobra"
+)
+
+func init() {
+ if err := libpod.SetXdgDirs(); err != nil {
+ logrus.Errorf(err.Error())
+ os.Exit(1)
+ }
+ initCobra()
+}
+
+func initCobra() {
+ switch runtime.GOOS {
+ case "darwin":
+ fallthrough
+ case "windows":
+ registry.GlobalFlags.EngineMode = entities.TunnelMode
+ case "linux":
+ registry.GlobalFlags.EngineMode = entities.ABIMode
+ default:
+ logrus.Errorf("%s is not a supported OS", runtime.GOOS)
+ os.Exit(1)
+ }
+
+ // TODO: Is there a Cobra way to "peek" at os.Args?
+ if ok := Contains("--remote", os.Args); ok {
+ registry.GlobalFlags.EngineMode = entities.TunnelMode
+ }
+
+ cobra.OnInitialize(func() {})
+}
+
+func main() {
+ fmt.Fprintf(os.Stderr, "Number of commands: %d\n", len(registry.Commands))
+ for _, c := range registry.Commands {
+ if Contains(registry.GlobalFlags.EngineMode, c.Mode) {
+ parent := rootCmd
+ if c.Parent != nil {
+ parent = c.Parent
+ }
+ parent.AddCommand(c.Command)
+ }
+ }
+
+ Execute()
+ os.Exit(0)
+}
+
+func Contains(item interface{}, slice interface{}) bool {
+ s := reflect.ValueOf(slice)
+
+ switch s.Kind() {
+ case reflect.Array:
+ fallthrough
+ case reflect.Slice:
+ break
+ default:
+ return false
+ }
+
+ for i := 0; i < s.Len(); i++ {
+ if s.Index(i).Interface() == item {
+ return true
+ }
+ }
+ return false
+}
diff --git a/cmd/podmanV2/networks/network.go b/cmd/podmanV2/networks/network.go
new file mode 100644
index 000000000..fc92d2321
--- /dev/null
+++ b/cmd/podmanV2/networks/network.go
@@ -0,0 +1,33 @@
+package images
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ // Command: podman _network_
+ cmd = &cobra.Command{
+ Use: "network",
+ Short: "Manage networks",
+ Long: "Manage networks",
+ TraverseChildren: true,
+ PersistentPreRunE: preRunE,
+ RunE: registry.SubCommandExists,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: cmd,
+ })
+ cmd.SetHelpTemplate(registry.HelpTemplate())
+ cmd.SetUsageTemplate(registry.UsageTemplate())
+}
+
+func preRunE(cmd *cobra.Command, args []string) error {
+ _, err := registry.NewContainerEngine(cmd, args)
+ return err
+}
diff --git a/cmd/podmanV2/pods/pod.go b/cmd/podmanV2/pods/pod.go
new file mode 100644
index 000000000..81c0d33e1
--- /dev/null
+++ b/cmd/podmanV2/pods/pod.go
@@ -0,0 +1,33 @@
+package pods
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ // Command: podman _pod_
+ podCmd = &cobra.Command{
+ Use: "pod",
+ Short: "Manage pods",
+ Long: "Manage pods",
+ TraverseChildren: true,
+ PersistentPreRunE: preRunE,
+ RunE: registry.SubCommandExists,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: podCmd,
+ })
+ podCmd.SetHelpTemplate(registry.HelpTemplate())
+ podCmd.SetUsageTemplate(registry.UsageTemplate())
+}
+
+func preRunE(cmd *cobra.Command, args []string) error {
+ _, err := registry.NewContainerEngine(cmd, args)
+ return err
+}
diff --git a/cmd/podmanV2/pods/ps.go b/cmd/podmanV2/pods/ps.go
new file mode 100644
index 000000000..d4c625b2e
--- /dev/null
+++ b/cmd/podmanV2/pods/ps.go
@@ -0,0 +1,32 @@
+package pods
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ psDescription = "List all pods on system including their names, ids and current state."
+
+ // Command: podman pod _ps_
+ psCmd = &cobra.Command{
+ Use: "ps",
+ Aliases: []string{"ls", "list"},
+ Short: "list pods",
+ Long: psDescription,
+ RunE: pods,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: psCmd,
+ Parent: podCmd,
+ })
+}
+
+func pods(cmd *cobra.Command, args []string) error {
+ return nil
+}
diff --git a/cmd/podmanV2/registry/registry.go b/cmd/podmanV2/registry/registry.go
new file mode 100644
index 000000000..fa51d6535
--- /dev/null
+++ b/cmd/podmanV2/registry/registry.go
@@ -0,0 +1,96 @@
+package registry
+
+import (
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/domain/infra"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+type CliCommand struct {
+ Mode []entities.EngineMode
+ Command *cobra.Command
+ Parent *cobra.Command
+}
+
+var (
+ Commands []CliCommand
+ GlobalFlags entities.EngineFlags
+ imageEngine entities.ImageEngine
+ containerEngine entities.ContainerEngine
+ PodmanTunnel bool
+)
+
+// HelpTemplate returns the help template for podman commands
+// This uses the short and long options.
+// command should not use this.
+func HelpTemplate() string {
+ return `{{.Short}}
+
+Description:
+ {{.Long}}
+
+{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`
+}
+
+// UsageTemplate returns the usage template for podman commands
+// This blocks the displaying of the global options. The main podman
+// command should not use this.
+func UsageTemplate() string {
+ return `Usage(v2):{{if (and .Runnable (not .HasAvailableSubCommands))}}
+ {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
+ {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
+
+Aliases:
+ {{.NameAndAliases}}{{end}}{{if .HasExample}}
+
+Examples:
+ {{.Example}}{{end}}{{if .HasAvailableSubCommands}}
+
+Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
+ {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
+
+Flags:
+{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
+{{end}}
+`
+}
+
+func ImageEngine() entities.ImageEngine {
+ return imageEngine
+}
+
+// NewImageEngine is a wrapper for building an ImageEngine to be used for PreRunE functions
+func NewImageEngine(cmd *cobra.Command, args []string) (entities.ImageEngine, error) {
+ if imageEngine == nil {
+ engine, err := infra.NewImageEngine(GlobalFlags.EngineMode, entities.EngineOptions{})
+ if err != nil {
+ return nil, err
+ }
+ imageEngine = engine
+ }
+ return imageEngine, nil
+}
+
+func ContainerEngine() entities.ContainerEngine {
+ return containerEngine
+}
+
+// NewContainerEngine is a wrapper for building an ContainerEngine to be used for PreRunE functions
+func NewContainerEngine(cmd *cobra.Command, args []string) (entities.ContainerEngine, error) {
+ if containerEngine == nil {
+ engine, err := infra.NewContainerEngine(GlobalFlags.EngineMode, entities.EngineOptions{})
+ if err != nil {
+ return nil, err
+ }
+ containerEngine = engine
+ }
+ return containerEngine, nil
+}
+
+func SubCommandExists(cmd *cobra.Command, args []string) error {
+ if len(args) > 0 {
+ return errors.Errorf("unrecognized command `%[1]s %[2]s`\nTry '%[1]s --help' for more information.", cmd.CommandPath(), args[0])
+ }
+ return errors.Errorf("missing command '%[1]s COMMAND'\nTry '%[1]s --help' for more information.", cmd.CommandPath())
+}
diff --git a/cmd/podmanV2/report/templates.go b/cmd/podmanV2/report/templates.go
new file mode 100644
index 000000000..dc43d4f9b
--- /dev/null
+++ b/cmd/podmanV2/report/templates.go
@@ -0,0 +1,61 @@
+package report
+
+import (
+ "strings"
+ "text/template"
+ "time"
+
+ "github.com/docker/go-units"
+)
+
+var defaultFuncMap = template.FuncMap{
+ "ellipsis": func(s string, length int) string {
+ if len(s) > length {
+ return s[:length-3] + "..."
+ }
+ return s
+ },
+ // TODO: Remove on Go 1.14 port
+ "slice": func(s string, i, j int) string {
+ if i > j || len(s) < i {
+ return s
+ }
+ if len(s) < j {
+ return s[i:]
+ }
+ return s[i:j]
+ },
+ "toRFC3339": func(t int64) string {
+ return time.Unix(t, 0).Format(time.RFC3339)
+ },
+ "toHumanDuration": func(t int64) string {
+ return units.HumanDuration(time.Since(time.Unix(t, 0))) + " ago"
+ },
+ "toHumanSize": func(sz int64) string {
+ return units.HumanSize(float64(sz))
+ },
+}
+
+func ReportHeader(columns ...string) []byte {
+ hdr := make([]string, len(columns))
+ for i, h := range columns {
+ hdr[i] = strings.ToUpper(h)
+ }
+ return []byte(strings.Join(hdr, "\t") + "\n")
+}
+
+func AppendFuncMap(funcMap template.FuncMap) template.FuncMap {
+ merged := PodmanTemplateFuncs()
+ for k, v := range funcMap {
+ merged[k] = v
+ }
+ return merged
+}
+
+func PodmanTemplateFuncs() template.FuncMap {
+ merged := make(template.FuncMap)
+ for k, v := range defaultFuncMap {
+ merged[k] = v
+ }
+ return merged
+}
diff --git a/cmd/podmanV2/root.go b/cmd/podmanV2/root.go
new file mode 100644
index 000000000..778184f28
--- /dev/null
+++ b/cmd/podmanV2/root.go
@@ -0,0 +1,35 @@
+package main
+
+import (
+ "fmt"
+ "os"
+ "path"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/version"
+ "github.com/spf13/cobra"
+)
+
+var rootCmd = &cobra.Command{
+ Use: path.Base(os.Args[0]),
+ Long: "Manage pods, containers and images",
+ SilenceUsage: true,
+ SilenceErrors: true,
+ TraverseChildren: true,
+ RunE: registry.SubCommandExists,
+ Version: version.Version,
+}
+
+func init() {
+ // Override default --help information of `--version` global flag}
+ var dummyVersion bool
+ rootCmd.PersistentFlags().BoolVarP(&dummyVersion, "version", "v", false, "Version of podman")
+ rootCmd.PersistentFlags().BoolVarP(&registry.PodmanTunnel, "remote", "r", false, "Access service via SSH tunnel")
+}
+
+func Execute() {
+ if err := rootCmd.Execute(); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+}
diff --git a/cmd/podmanV2/system/system.go b/cmd/podmanV2/system/system.go
new file mode 100644
index 000000000..30ed328e8
--- /dev/null
+++ b/cmd/podmanV2/system/system.go
@@ -0,0 +1,33 @@
+package images
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ // Command: podman _system_
+ cmd = &cobra.Command{
+ Use: "system",
+ Short: "Manage podman",
+ Long: "Manage podman",
+ TraverseChildren: true,
+ PersistentPreRunE: preRunE,
+ RunE: registry.SubCommandExists,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: cmd,
+ })
+ cmd.SetHelpTemplate(registry.HelpTemplate())
+ cmd.SetUsageTemplate(registry.UsageTemplate())
+}
+
+func preRunE(cmd *cobra.Command, args []string) error {
+ _, err := registry.NewContainerEngine(cmd, args)
+ return err
+}
diff --git a/cmd/podmanV2/volumes/volume.go b/cmd/podmanV2/volumes/volume.go
new file mode 100644
index 000000000..245c06da0
--- /dev/null
+++ b/cmd/podmanV2/volumes/volume.go
@@ -0,0 +1,33 @@
+package images
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ // Command: podman _volume_
+ cmd = &cobra.Command{
+ Use: "volume",
+ Short: "Manage volumes",
+ Long: "Volumes are created in and can be shared between containers",
+ TraverseChildren: true,
+ PersistentPreRunE: preRunE,
+ RunE: registry.SubCommandExists,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: cmd,
+ })
+ cmd.SetHelpTemplate(registry.HelpTemplate())
+ cmd.SetUsageTemplate(registry.UsageTemplate())
+}
+
+func preRunE(cmd *cobra.Command, args []string) error {
+ _, err := registry.NewContainerEngine(cmd, args)
+ return err
+}
diff --git a/completions/bash/podman b/completions/bash/podman
index 895659fe5..8a6fc2073 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -3334,6 +3334,7 @@ _podman_podman() {
"
commands="
attach
+ auto-update
build
commit
container
diff --git a/contrib/spec/podman.spec.in b/contrib/spec/podman.spec.in
index 0222be7ba..635de0c7e 100644
--- a/contrib/spec/podman.spec.in
+++ b/contrib/spec/podman.spec.in
@@ -48,7 +48,7 @@ Epoch: 99
%else
Epoch: 0
%endif
-Version: 1.8.2
+Version: 1.8.3
Release: #COMMITDATE#.git%{shortcommit0}%{?dist}
Summary: Manage Pods, Containers and Container Images
License: ASL 2.0
diff --git a/contrib/systemd/auto-update/podman-auto-update.service b/contrib/systemd/auto-update/podman-auto-update.service
new file mode 100644
index 000000000..b63f24230
--- /dev/null
+++ b/contrib/systemd/auto-update/podman-auto-update.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Podman auto-update service
+Documentation=man:podman-auto-update(1)
+Wants=network.target
+After=network-online.target
+
+[Service]
+ExecStart=/usr/bin/podman auto-update
+
+[Install]
+WantedBy=multi-user.target default.target
diff --git a/contrib/systemd/auto-update/podman-auto-update.timer b/contrib/systemd/auto-update/podman-auto-update.timer
new file mode 100644
index 000000000..3e50ffa9b
--- /dev/null
+++ b/contrib/systemd/auto-update/podman-auto-update.timer
@@ -0,0 +1,9 @@
+[Unit]
+Description=Podman auto-update timer
+
+[Timer]
+OnCalendar=daily
+Persistent=true
+
+[Install]
+WantedBy=timers.target
diff --git a/docs/source/markdown/podman-auto-update.1.md b/docs/source/markdown/podman-auto-update.1.md
new file mode 100644
index 000000000..93ad22f76
--- /dev/null
+++ b/docs/source/markdown/podman-auto-update.1.md
@@ -0,0 +1,46 @@
+% podman-auto-update(1)
+
+## NAME
+podman-auto-update - Auto update containers according to their auto-update policy
+
+## SYNOPSIS
+**podman auto-update**
+
+## DESCRIPTION
+`podman auto-update` looks up containers with a specified "io.containers.autoupdate" label (i.e., the auto-update policy).
+
+If the label is present and set to "image", Podman reaches out to the corresponding registry to check if the image has been updated.
+An image is considered updated if the digest in the local storage is different than the one of the remote image.
+If an image must be updated, Podman pulls it down and restarts the systemd unit executing the container.
+
+At container-creation time, Podman looks up the "PODMAN_SYSTEMD_UNIT" environment variables and stores it verbatim in the container's label.
+This variable is now set by all systemd units generated by `podman-generate-systemd` and is set to `%n` (i.e., the name of systemd unit starting the container).
+This data is then being used in the auto-update sequence to instruct systemd (via DBUS) to restart the unit and hence to restart the container.
+
+Note that `podman auto-update` relies on systemd and requires a fully-qualified image reference (e.g., quay.io/podman/stable:latest) to be used to create the container.
+This enforcement is necessary to know which image to actually check and pull.
+If an image ID was used, Podman would not know which image to check/pull anymore.
+
+## EXAMPLES
+
+```
+# Start a container
+$ podman run -d busybox:latest top
+bc219740a210455fa27deacc96d50a9e20516492f1417507c13ce1533dbdcd9d
+
+# Generate a systemd unit for this container
+$ podman generate systemd --new --files bc219740a210455fa27deacc96d50a9e20516492f1417507c13ce1533dbdcd9d
+/home/user/containers/libpod/container-bc219740a210455fa27deacc96d50a9e20516492f1417507c13ce1533dbdcd9d.service
+
+# Load the new systemd unit and start it
+$ mv ./container-bc219740a210455fa27deacc96d50a9e20516492f1417507c13ce1533dbdcd9d.service ~/.config/systemd/user
+$ systemctl --user daemon-reload
+$ systemctl --user start container-bc219740a210455fa27deacc96d50a9e20516492f1417507c13ce1533dbdcd9d.service
+
+# Auto-update the container
+$ podman auto-update
+container-bc219740a210455fa27deacc96d50a9e20516492f1417507c13ce1533dbdcd9d.service
+```
+
+## SEE ALSO
+podman(1), podman-generate-systemd(1), podman-run(1), systemd.unit(5)
diff --git a/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md
index 86d246e87..5797535f7 100644
--- a/docs/source/markdown/podman.1.md
+++ b/docs/source/markdown/podman.1.md
@@ -154,6 +154,7 @@ the exit codes follow the `chroot` standard, see below:
| Command | Description |
| ------------------------------------------------ | --------------------------------------------------------------------------- |
| [podman-attach(1)](podman-attach.1.md) | Attach to a running container. |
+| [podman-auto-update(1)](podman-auto-update.1.md) | Auto update containers according to their auto-update policy |
| [podman-build(1)](podman-build.1.md) | Build a container image using a Containerfile. |
| [podman-commit(1)](podman-commit.1.md) | Create new image based on the changed container. |
| [podman-container(1)](podman-container.1.md) | Manage containers. |
diff --git a/go.mod b/go.mod
index 653b3c49d..b079fbf65 100644
--- a/go.mod
+++ b/go.mod
@@ -9,7 +9,7 @@ require (
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect
github.com/containernetworking/cni v0.7.2-0.20200304161608-4fae32b84921
github.com/containernetworking/plugins v0.8.5
- github.com/containers/buildah v1.14.3-0.20200313154200-d26f437b2a46
+ github.com/containers/buildah v1.14.3
github.com/containers/common v0.5.0
github.com/containers/conmon v2.0.10+incompatible
github.com/containers/image/v5 v5.2.1
diff --git a/go.sum b/go.sum
index abe1852e6..08d321a9f 100644
--- a/go.sum
+++ b/go.sum
@@ -71,6 +71,8 @@ github.com/containers/buildah v1.14.3-0.20200313093807-c0e60d444696 h1:TCJsENYev
github.com/containers/buildah v1.14.3-0.20200313093807-c0e60d444696/go.mod h1:OCorIy7yUrQ2hIZY5z/LhJuPiH8bT8GUwC+9CarZK5o=
github.com/containers/buildah v1.14.3-0.20200313154200-d26f437b2a46 h1:Zw8xYI3HATHra5Csm1k5GOXNCietwGR6D2kQVP5zw2w=
github.com/containers/buildah v1.14.3-0.20200313154200-d26f437b2a46/go.mod h1:OCorIy7yUrQ2hIZY5z/LhJuPiH8bT8GUwC+9CarZK5o=
+github.com/containers/buildah v1.14.3 h1:GYH/o3ME76CI0bvjFp++Fr9mOTrqvK5FLGtLkCBrKic=
+github.com/containers/buildah v1.14.3/go.mod h1:OmsmT+HR5i1o2U/0qm81fvI+m71hjd57gyf02z1Q7YI=
github.com/containers/common v0.4.2 h1:O5d1gj/xdpQdZi0MEivRQ/7AeRaVeHdbSP/bvShw458=
github.com/containers/common v0.4.2/go.mod h1:m62kenckrWi5rZx32kaLje2Og0hpf6NsaTBn6+b+Oys=
github.com/containers/common v0.5.0 h1:ZAef7h3oO46PcbTyfooZf8XLHrYad+GkhSu3EhH6P24=
@@ -371,6 +373,8 @@ github.com/openshift/api v0.0.0-20200106203948-7ab22a2c8316 h1:enQG2QUGwug4fR1yM
github.com/openshift/api v0.0.0-20200106203948-7ab22a2c8316/go.mod h1:dv+J0b/HWai0QnMVb37/H0v36klkLBi2TNpPeWDxX10=
github.com/openshift/imagebuilder v1.1.1 h1:KAUR31p8UBJdfVO42azWgb+LeMAed2zaKQ19e0C0X2I=
github.com/openshift/imagebuilder v1.1.1/go.mod h1:9aJRczxCH0mvT6XQ+5STAQaPWz7OsWcU5/mRkt8IWeo=
+github.com/openshift/imagebuilder v1.1.2 h1:vCO8hZQR/4uzo+j0PceBH5aKFcvCDM43UzUGOYQN+Go=
+github.com/openshift/imagebuilder v1.1.2/go.mod h1:9aJRczxCH0mvT6XQ+5STAQaPWz7OsWcU5/mRkt8IWeo=
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913 h1:TnbXhKzrTOyuvWrjI8W6pcoI9XPbLHFXCdN2dtUw7Rw=
diff --git a/hack/man-page-checker b/hack/man-page-checker
index 99d280539..17d85d65d 100755
--- a/hack/man-page-checker
+++ b/hack/man-page-checker
@@ -1,16 +1,6 @@
#!/bin/bash
#
-# man-page-name-checker - validate and cross-reference man page names
-#
-# FIXME as of 2019-03-20 there are still four files with inconsistent names:
-#
-# podman-logs.1.md NAME= podman-container-logs
-# podman-info.1.md NAME= podman-system-info
-# podman-rm.1.md NAME= podman-container-rm
-# podman-rmi.1.md NAME= podman-image-rm
-#
-# If those four get renamed (with suitable symlink fixes), this script
-# can be enabled in CI to prevent future inconsistencies.
+# man-page-checker - validate and cross-reference man page names
#
die() {
@@ -49,6 +39,12 @@ for md in $(ls -1 *-*.1.md | grep -v remote);do
# podman.1.md has a two-column table; podman-*.1.md all have three.
parent=$(echo $md | sed -e 's/^\(.*\)-.*$/\1.1.md/')
+ if [[ $parent =~ "podman-auto" ]]; then
+ # podman-auto-update.1.md is special cased as it's structure differs
+ # from that of other man pages where main and sub-commands split by
+ # dashes.
+ parent="podman.1.md"
+ fi
x=3
if expr -- "$parent" : ".*-" >/dev/null; then
x=4
@@ -90,6 +86,12 @@ for md in *.1.md;do
# Get the command name, and confirm that it matches the md file name.
cmd=$(echo "$synopsis" | sed -e 's/\(.*\)\*\*.*/\1/' | tr -d \*)
md_nodash=$(basename "$md" .1.md | tr '-' ' ')
+ if [[ $md_nodash = 'podman auto update' ]]; then
+ # podman-auto-update.1.md is special cased as it's structure differs
+ # from that of other man pages where main and sub-commands split by
+ # dashes.
+ md_nodash='podman auto-update'
+ fi
if [ "$cmd" != "$md_nodash" -a "$cmd" != "podman-remote" ]; then
echo
printf "Inconsistent program name in SYNOPSIS in %s:\n" $md
diff --git a/hack/podman-commands.sh b/hack/podman-commands.sh
index 32f94fc7b..da4d446aa 100755
--- a/hack/podman-commands.sh
+++ b/hack/podman-commands.sh
@@ -38,6 +38,9 @@ function podman_man() {
# Special case: there is no podman-help man page, nor need for such.
echo "help"
+ # Auto-update differs from other commands as it's a single command, not
+ # a main and sub-command split by a dash.
+ echo "auto-update"
elif [ "$@" = "podman-image-trust" ]; then
# Special case: set and show aren't actually in a table in the man page
echo set
diff --git a/hack/xref-helpmsgs-manpages b/hack/xref-helpmsgs-manpages
new file mode 100755
index 000000000..00db3c8de
--- /dev/null
+++ b/hack/xref-helpmsgs-manpages
@@ -0,0 +1,307 @@
+#!/usr/bin/perl
+#
+# xref-helpmsgs-manpages - cross-reference --help options against man pages
+#
+package LibPod::CI::XrefHelpmsgsManpages;
+
+use v5.14;
+use utf8;
+
+use strict;
+use warnings;
+
+(our $ME = $0) =~ s|.*/||;
+our $VERSION = '0.1';
+
+# For debugging, show data structures using DumpTree($var)
+#use Data::TreeDumper; $Data::TreeDumper::Displayaddress = 0;
+
+###############################################################################
+# BEGIN user-customizable section
+
+# Path to podman executable
+my $Default_Podman = './bin/podman';
+my $PODMAN = $ENV{PODMAN} || $Default_Podman;
+
+# Path to podman markdown source files (of the form podman-*.1.md)
+my $Markdown_Path = 'docs/source/markdown';
+
+# END user-customizable section
+###############################################################################
+
+use FindBin;
+
+###############################################################################
+# BEGIN boilerplate args checking, usage messages
+
+sub usage {
+ print <<"END_USAGE";
+Usage: $ME [OPTIONS]
+
+$ME recursively runs 'podman --help' against
+all subcommands; and recursively reads podman-*.1.md files
+in $Markdown_Path, then cross-references that each --help
+option is listed in the appropriate man page and vice-versa.
+
+$ME invokes '\$PODMAN' (default: $Default_Podman).
+
+Exit status is zero if no inconsistencies found, one otherwise
+
+OPTIONS:
+
+ -v, --verbose show verbose progress indicators
+ -n, --dry-run make no actual changes
+
+ --help display this message
+ --version display program name and version
+END_USAGE
+
+ exit;
+}
+
+# Command-line options. Note that this operates directly on @ARGV !
+our $debug = 0;
+our $verbose = 0;
+sub handle_opts {
+ use Getopt::Long;
+ GetOptions(
+ 'debug!' => \$debug,
+ 'verbose|v' => \$verbose,
+
+ help => \&usage,
+ version => sub { print "$ME version $VERSION\n"; exit 0 },
+ ) or die "Try `$ME --help' for help\n";
+}
+
+# END boilerplate args checking, usage messages
+###############################################################################
+
+############################## CODE BEGINS HERE ###############################
+
+# The term is "modulino".
+__PACKAGE__->main() unless caller();
+
+# Main code.
+sub main {
+ # Note that we operate directly on @ARGV, not on function parameters.
+ # This is deliberate: it's because Getopt::Long only operates on @ARGV
+ # and there's no clean way to make it use @_.
+ handle_opts(); # will set package globals
+
+ # Fetch command-line arguments. Barf if too many.
+ die "$ME: Too many arguments; try $ME --help\n" if @ARGV;
+
+ my $help = podman_help();
+ my $man = podman_man('podman');
+
+ my $retval = xref_by_help($help, $man)
+ + xref_by_man($help, $man);
+
+ exit !!$retval;
+}
+
+##################
+# xref_by_help # Find keys in '--help' but not in man
+##################
+sub xref_by_help {
+ my ($help, $man, @subcommand) = @_;
+ my $errs = 0;
+
+ for my $k (sort keys %$help) {
+ if (exists $man->{$k}) {
+ if (ref $help->{$k}) {
+ $errs += xref_by_help($help->{$k}, $man->{$k}, @subcommand, $k);
+ }
+ # Otherwise, non-ref is leaf node such as a --option
+ }
+ else {
+ my $man = $man->{_path} || 'man';
+ warn "$ME: podman @subcommand --help lists $k, but $k not in $man\n";
+ ++$errs;
+ }
+ }
+
+ return $errs;
+}
+
+#################
+# xref_by_man # Find keys in man pages but not in --help
+#################
+#
+# In an ideal world we could share the functionality in one function; but
+# there are just too many special cases in man pages.
+#
+sub xref_by_man {
+ my ($help, $man, @subcommand) = @_;
+
+ my $errs = 0;
+
+ # FIXME: this generates way too much output
+ for my $k (grep { $_ ne '_path' } sort keys %$man) {
+ if (exists $help->{$k}) {
+ if (ref $man->{$k}) {
+ $errs += xref_by_man($help->{$k}, $man->{$k}, @subcommand, $k);
+ }
+ }
+ elsif ($k ne '--help' && $k ne '-h') {
+ my $man = $man->{_path} || 'man';
+
+ # Special case: podman-inspect serves dual purpose (image, ctr)
+ my %ignore = map { $_ => 1 } qw(-l -s -t --latest --size --type);
+ next if $man =~ /-inspect/ && $ignore{$k};
+
+ # Special case: the 'trust' man page is a mess
+ next if $man =~ /-trust/;
+
+ # Special case: '--net' is an undocumented shortcut
+ next if $k eq '--net' && $help->{'--network'};
+
+ # Special case: these are actually global options
+ next if $k =~ /^--(cni-config-dir|runtime)$/ && $man =~ /-build/;
+
+ # Special case: weirdness with Cobra and global/local options
+ next if $k eq '--namespace' && $man =~ /-ps/;
+
+ # Special case: these require compiling with 'varlink' tag,
+ # which doesn't happen in CI gating task.
+ next if $k eq 'varlink';
+ next if "@subcommand" eq 'system' && $k eq 'service';
+
+ warn "$ME: podman @subcommand: $k in $man, but not --help\n";
+ ++$errs;
+ }
+ }
+
+ return $errs;
+}
+
+
+#################
+# podman_help # Parse output of 'podman [subcommand] --help'
+#################
+sub podman_help {
+ my %help;
+ open my $fh, '-|', $PODMAN, @_, '--help'
+ or die "$ME: Cannot fork: $!\n";
+ my $section = '';
+ while (my $line = <$fh>) {
+ # Cobra is blessedly consistent in its output:
+ # Usage: ...
+ # Available Commands:
+ # ....
+ # Flags:
+ # ....
+ #
+ # Start by identifying the section we're in...
+ if ($line =~ /^Available\s+(Commands):/) {
+ $section = lc $1;
+ }
+ elsif ($line =~ /^(Flags):/) {
+ $section = lc $1;
+ }
+
+ # ...then track commands and options. For subcommands, recurse.
+ elsif ($section eq 'commands') {
+ if ($line =~ /^\s{1,4}(\S+)\s/) {
+ my $subcommand = $1;
+ print "> podman @_ $subcommand\n" if $debug;
+ $help{$subcommand} = podman_help(@_, $subcommand)
+ unless $subcommand eq 'help'; # 'help' not in man
+ }
+ }
+ elsif ($section eq 'flags') {
+ # Handle '--foo' or '-f, --foo'
+ if ($line =~ /^\s{1,10}(--\S+)\s/) {
+ print "> podman @_ $1\n" if $debug;
+ $help{$1} = 1;
+ }
+ elsif ($line =~ /^\s{1,10}(-\S),\s+(--\S+)\s/) {
+ print "> podman @_ $1, $2\n" if $debug;
+ $help{$1} = $help{$2} = 1;
+ }
+ }
+ }
+ close $fh
+ or die "$ME: Error running 'podman @_ --help'\n";
+
+ return \%help;
+}
+
+
+################
+# podman_man # Parse contents of podman-*.1.md
+################
+sub podman_man {
+ my $command = shift;
+ my $subpath = "$Markdown_Path/$command.1.md";
+ my $manpath = "$FindBin::Bin/../$subpath";
+ print "** $subpath \n" if $debug;
+
+ my %man = (_path => $subpath);
+ open my $fh, '<', $manpath
+ or die "$ME: Cannot read $manpath: $!\n";
+ my $section = '';
+ my @most_recent_flags;
+ while (my $line = <$fh>) {
+ chomp $line;
+ next unless $line; # skip empty lines
+
+ # .md files designate sections with leading double hash
+ if ($line =~ /^##\s*(GLOBAL\s+)?OPTIONS/) {
+ $section = 'flags';
+ }
+ elsif ($line =~ /^\#\#\s+(SUB)?COMMANDS/) {
+ $section = 'commands';
+ }
+ elsif ($line =~ /^\#\#/) {
+ $section = '';
+ }
+
+ # This will be a table containing subcommand names, links to man pages.
+ # The format is slightly different between podman.1.md and subcommands.
+ elsif ($section eq 'commands') {
+ # In podman.1.md
+ if ($line =~ /^\|\s*\[podman-(\S+?)\(\d\)\]/) {
+ $man{$1} = podman_man("podman-$1");
+ }
+
+ # In podman-<subcommand>.1.md
+ elsif ($line =~ /^\|\s+(\S+)\s+\|\s+\[\S+\]\((\S+)\.1\.md\)/) {
+ $man{$1} = podman_man($2);
+ }
+ }
+
+ # Flags should always be of the form '**-f**' or '**--flag**',
+ # possibly separated by comma-space.
+ elsif ($section eq 'flags') {
+ # e.g. 'podman run --ip6', documented in man page, but nonexistent
+ if ($line =~ /^not\s+implemented/i) {
+ delete $man{$_} for @most_recent_flags;
+ }
+
+ @most_recent_flags = ();
+ # Handle any variation of '**--foo**, **-f**'
+ while ($line =~ s/^\*\*((--[a-z0-9-]+)|(-.))\*\*(,\s+)?//g) {
+ $man{$1} = 1;
+
+ # Keep track of them, in case we see 'Not implemented' below
+ push @most_recent_flags, $1;
+ }
+ }
+ }
+ close $fh;
+
+ # Special case: the 'image trust' man page tries hard to cover both set
+ # and show, which means it ends up not being machine-readable.
+ if ($command eq 'podman-image-trust') {
+ my %set = %man;
+ my %show = %man;
+ $show{$_} = 1 for qw(--raw -j --json);
+ return +{ set => \%set, show => \%show }
+ }
+
+ return \%man;
+}
+
+
+1;
diff --git a/libpod/container.go b/libpod/container.go
index 89cf83707..e59fb9fe8 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -236,6 +236,12 @@ type ContainerConfig struct {
// container has been created with.
CreateCommand []string `json:"CreateCommand,omitempty"`
+ // RawImageName is the raw and unprocessed name of the image when creating
+ // the container (as specified by the user). May or may not be set. One
+ // use case to store this data are auto-updates where we need the _exact_
+ // name and not some normalized instance of it.
+ RawImageName string `json:"RawImageName,omitempty"`
+
// TODO consider breaking these subsections up into smaller structs
// UID/GID mappings used by the storage
@@ -500,11 +506,17 @@ func (c *Container) Namespace() string {
return c.config.Namespace
}
-// Image returns the ID and name of the image used as the container's rootfs
+// Image returns the ID and name of the image used as the container's rootfs.
func (c *Container) Image() (string, string) {
return c.config.RootfsImageID, c.config.RootfsImageName
}
+// RawImageName returns the unprocessed and not-normalized user-specified image
+// name.
+func (c *Container) RawImageName() string {
+ return c.config.RawImageName
+}
+
// ShmDir returns the sources path to be mounted on /dev/shm in container
func (c *Container) ShmDir() string {
return c.config.ShmDir
diff --git a/libpod/events/config.go b/libpod/events/config.go
index 20c01baff..8fe551c5d 100644
--- a/libpod/events/config.go
+++ b/libpod/events/config.go
@@ -98,6 +98,8 @@ const (
// Attach ...
Attach Status = "attach"
+ // AutoUpdate ...
+ AutoUpdate Status = "auto-update"
// Checkpoint ...
Checkpoint Status = "checkpoint"
// Cleanup ...
diff --git a/libpod/healthcheck_linux.go b/libpod/healthcheck_linux.go
index 5da2d311b..42dba6610 100644
--- a/libpod/healthcheck_linux.go
+++ b/libpod/healthcheck_linux.go
@@ -4,50 +4,14 @@ import (
"fmt"
"os"
"os/exec"
- "path/filepath"
- "strconv"
"strings"
"github.com/containers/libpod/pkg/rootless"
- "github.com/coreos/go-systemd/v22/dbus"
- godbus "github.com/godbus/dbus/v5"
+ "github.com/containers/libpod/pkg/systemd"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
-func dbusAuthRootlessConnection(createBus func(opts ...godbus.ConnOption) (*godbus.Conn, error)) (*godbus.Conn, error) {
- conn, err := createBus()
- if err != nil {
- return nil, err
- }
-
- methods := []godbus.Auth{godbus.AuthExternal(strconv.Itoa(rootless.GetRootlessUID()))}
-
- err = conn.Auth(methods)
- if err != nil {
- conn.Close()
- return nil, err
- }
-
- return conn, nil
-}
-
-func newRootlessConnection() (*dbus.Conn, error) {
- return dbus.NewConnection(func() (*godbus.Conn, error) {
- return dbusAuthRootlessConnection(func(opts ...godbus.ConnOption) (*godbus.Conn, error) {
- path := filepath.Join(os.Getenv("XDG_RUNTIME_DIR"), "systemd/private")
- return godbus.Dial(fmt.Sprintf("unix:path=%s", path))
- })
- })
-}
-
-func getConnection() (*dbus.Conn, error) {
- if rootless.IsRootless() {
- return newRootlessConnection()
- }
- return dbus.NewSystemdConnection()
-}
-
// createTimer systemd timers for healthchecks of a container
func (c *Container) createTimer() error {
if c.disableHealthCheckSystemd() {
@@ -64,7 +28,7 @@ func (c *Container) createTimer() error {
}
cmd = append(cmd, "--unit", c.ID(), fmt.Sprintf("--on-unit-inactive=%s", c.HealthCheckConfig().Interval.String()), "--timer-property=AccuracySec=1s", podman, "healthcheck", "run", c.ID())
- conn, err := getConnection()
+ conn, err := systemd.ConnectToDBUS()
if err != nil {
return errors.Wrapf(err, "unable to get systemd connection to add healthchecks")
}
@@ -83,7 +47,7 @@ func (c *Container) startTimer() error {
if c.disableHealthCheckSystemd() {
return nil
}
- conn, err := getConnection()
+ conn, err := systemd.ConnectToDBUS()
if err != nil {
return errors.Wrapf(err, "unable to get systemd connection to start healthchecks")
}
@@ -98,7 +62,7 @@ func (c *Container) removeTimer() error {
if c.disableHealthCheckSystemd() {
return nil
}
- conn, err := getConnection()
+ conn, err := systemd.ConnectToDBUS()
if err != nil {
return errors.Wrapf(err, "unable to get systemd connection to remove healthchecks")
}
diff --git a/libpod/options.go b/libpod/options.go
index 98de71af2..9b61d7947 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -593,7 +593,7 @@ func WithUser(user string) CtrCreateOption {
// other configuration from the image will be added to the config.
// TODO: Replace image name and ID with a libpod.Image struct when that is
// finished.
-func WithRootFSFromImage(imageID string, imageName string) CtrCreateOption {
+func WithRootFSFromImage(imageID, imageName, rawImageName string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
@@ -601,7 +601,7 @@ func WithRootFSFromImage(imageID string, imageName string) CtrCreateOption {
ctr.config.RootfsImageID = imageID
ctr.config.RootfsImageName = imageName
-
+ ctr.config.RawImageName = rawImageName
return nil
}
}
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index b8dafc8a1..0b18436ca 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -131,6 +131,7 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ..
return nil, errors.Wrapf(err, "error running container create option")
}
}
+
return r.setupContainer(ctx, ctr)
}
diff --git a/libpod/runtime_pod.go b/libpod/runtime_pod.go
index e1dc31391..be566e211 100644
--- a/libpod/runtime_pod.go
+++ b/libpod/runtime_pod.go
@@ -90,18 +90,10 @@ func (r *Runtime) LookupPod(idOrName string) (*Pod, error) {
// output. Multiple filters are handled by ANDing their output, so only pods
// matching all filters are returned
func (r *Runtime) Pods(filters ...PodFilter) ([]*Pod, error) {
- r.lock.RLock()
- defer r.lock.RUnlock()
-
- if !r.valid {
- return nil, define.ErrRuntimeStopped
- }
-
- pods, err := r.state.AllPods()
+ pods, err := r.GetAllPods()
if err != nil {
return nil, err
}
-
podsFiltered := make([]*Pod, 0, len(pods))
for _, pod := range pods {
include := true
diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go
index 279cafa39..155ac83d9 100644
--- a/libpod/runtime_pod_infra_linux.go
+++ b/libpod/runtime_pod_infra_linux.go
@@ -23,7 +23,7 @@ const (
IDTruncLength = 12
)
-func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, imgID string, config *v1.ImageConfig) (*Container, error) {
+func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawImageName, imgID string, config *v1.ImageConfig) (*Container, error) {
// Set up generator for infra container defaults
g, err := generate.New("linux")
@@ -127,7 +127,7 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, imgID
containerName := p.ID()[:IDTruncLength] + "-infra"
options = append(options, r.WithPod(p))
- options = append(options, WithRootFSFromImage(imgID, imgName))
+ options = append(options, WithRootFSFromImage(imgID, imgName, rawImageName))
options = append(options, WithName(containerName))
options = append(options, withIsInfra())
@@ -154,5 +154,5 @@ func (r *Runtime) createInfraContainer(ctx context.Context, p *Pod) (*Container,
imageName := newImage.Names()[0]
imageID := data.ID
- return r.makeInfraContainer(ctx, p, imageName, imageID, data.Config)
+ return r.makeInfraContainer(ctx, p, imageName, r.config.InfraImage, imageID, data.Config)
}
diff --git a/pkg/adapter/autoupdate.go b/pkg/adapter/autoupdate.go
new file mode 100644
index 000000000..01f7a29c5
--- /dev/null
+++ b/pkg/adapter/autoupdate.go
@@ -0,0 +1,11 @@
+// +build !remoteclient
+
+package adapter
+
+import (
+ "github.com/containers/libpod/pkg/autoupdate"
+)
+
+func (r *LocalRuntime) AutoUpdate() ([]string, []error) {
+ return autoupdate.AutoUpdate(r.Runtime)
+}
diff --git a/pkg/adapter/autoupdate_remote.go b/pkg/adapter/autoupdate_remote.go
new file mode 100644
index 000000000..a2a82d0d4
--- /dev/null
+++ b/pkg/adapter/autoupdate_remote.go
@@ -0,0 +1,11 @@
+// +build remoteclient
+
+package adapter
+
+import (
+ "github.com/containers/libpod/libpod/define"
+)
+
+func (r *LocalRuntime) AutoUpdate() ([]string, []error) {
+ return nil, []error{define.ErrNotImplemented}
+}
diff --git a/pkg/adapter/pods.go b/pkg/adapter/pods.go
index dc856cc8d..1417bd2b9 100644
--- a/pkg/adapter/pods.go
+++ b/pkg/adapter/pods.go
@@ -122,19 +122,31 @@ func (r *LocalRuntime) GetLatestPod() (*Pod, error) {
return &pod, err
}
+// GetPodsWithFilters gets the filtered list of pods based on the filter parameters provided.
+func (r *LocalRuntime) GetPodsWithFilters(filters string) ([]*Pod, error) {
+ pods, err := shared.GetPodsWithFilters(r.Runtime, filters)
+ if err != nil {
+ return nil, err
+ }
+ return r.podstoAdapterPods(pods)
+}
+
+func (r *LocalRuntime) podstoAdapterPods(pod []*libpod.Pod) ([]*Pod, error) {
+ var pods []*Pod
+ for _, i := range pod {
+
+ pods = append(pods, &Pod{i})
+ }
+ return pods, nil
+}
+
// GetAllPods gets all pods and wraps it in an adapter pod
func (r *LocalRuntime) GetAllPods() ([]*Pod, error) {
- var pods []*Pod
allPods, err := r.Runtime.GetAllPods()
if err != nil {
return nil, err
}
- for _, p := range allPods {
- pod := Pod{}
- pod.Pod = p
- pods = append(pods, &pod)
- }
- return pods, nil
+ return r.podstoAdapterPods(allPods)
}
// LookupPod gets a pod by name or id and wraps it in an adapter pod
diff --git a/pkg/adapter/pods_remote.go b/pkg/adapter/pods_remote.go
index 20f089628..6b8f22f15 100644
--- a/pkg/adapter/pods_remote.go
+++ b/pkg/adapter/pods_remote.go
@@ -10,7 +10,7 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/shared"
- "github.com/containers/libpod/cmd/podman/varlink"
+ iopodman "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/varlinkapi"
@@ -208,6 +208,11 @@ func (r *LocalRuntime) GetAllPods() ([]*Pod, error) {
return pods, nil
}
+// This is a empty implementation stating remoteclient not yet implemented
+func (r *LocalRuntime) GetPodsWithFilters(filters string) ([]*Pod, error) {
+ return nil, define.ErrNotImplemented
+}
+
// GetPodsByStatus returns a slice of pods filtered by a libpod status
func (r *LocalRuntime) GetPodsByStatus(statuses []string) ([]*Pod, error) {
podIDs, err := iopodman.GetPodsByStatus().Call(r.Conn, statuses)
diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go
index 1298e7fa4..e7031f471 100644
--- a/pkg/api/handlers/compat/containers.go
+++ b/pkg/api/handlers/compat/containers.go
@@ -87,7 +87,7 @@ func ListContainers(w http.ResponseWriter, r *http.Request) {
utils.InternalServerError(w, err)
return
}
- if _, found := r.URL.Query()["limit"]; found {
+ if _, found := r.URL.Query()["limit"]; found && query.Limit != -1 {
last := query.Limit
if len(containers) > last {
containers = containers[len(containers)-last:]
diff --git a/pkg/api/handlers/compat/images_history.go b/pkg/api/handlers/compat/images_history.go
index 04304caa4..afadf4c48 100644
--- a/pkg/api/handlers/compat/images_history.go
+++ b/pkg/api/handlers/compat/images_history.go
@@ -28,7 +28,7 @@ func HistoryImage(w http.ResponseWriter, r *http.Request) {
for _, h := range history {
l := handlers.HistoryResponse{
ID: h.ID,
- Created: h.Created.UnixNano(),
+ Created: h.Created.Unix(),
CreatedBy: h.CreatedBy,
Tags: h.Tags,
Size: h.Size,
diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go
index f93c8f8d5..27ec64d89 100644
--- a/pkg/api/handlers/libpod/pods.go
+++ b/pkg/api/handlers/libpod/pods.go
@@ -103,7 +103,6 @@ func PodCreate(w http.ResponseWriter, r *http.Request) {
func Pods(w http.ResponseWriter, r *http.Request) {
var (
- runtime = r.Context().Value("runtime").(*libpod.Runtime)
podInspectData []*libpod.PodInspect
)
decoder := r.Context().Value("decoder").(*schema.Decoder)
@@ -118,12 +117,8 @@ func Pods(w http.ResponseWriter, r *http.Request) {
return
}
- if len(query.Filters) > 0 {
- utils.Error(w, "filters are not implemented yet", http.StatusInternalServerError, define.ErrNotImplemented)
- return
- }
+ pods, err := utils.GetPods(w, r)
- pods, err := runtime.GetAllPods()
if err != nil {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
diff --git a/pkg/api/handlers/libpod/swagger.go b/pkg/api/handlers/libpod/swagger.go
index f6a26134b..149fa10dc 100644
--- a/pkg/api/handlers/libpod/swagger.go
+++ b/pkg/api/handlers/libpod/swagger.go
@@ -1,6 +1,16 @@
package libpod
-import "github.com/containers/image/v5/manifest"
+import (
+ "net/http"
+ "os"
+
+ "github.com/containers/image/v5/manifest"
+ "github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/pkg/errors"
+)
+
+// DefaultPodmanSwaggerSpec provides the default path to the podman swagger spec file
+const DefaultPodmanSwaggerSpec = "/usr/share/containers/podman/swagger.yaml"
// List Containers
// swagger:response ListContainers
@@ -15,3 +25,20 @@ type swagInspectManifestResponse struct {
// in:body
Body manifest.List
}
+
+func ServeSwagger(w http.ResponseWriter, r *http.Request) {
+ path := DefaultPodmanSwaggerSpec
+ if p, found := os.LookupEnv("PODMAN_SWAGGER_SPEC"); found {
+ path = p
+ }
+ if _, err := os.Stat(path); err != nil {
+ if os.IsNotExist(err) {
+ utils.InternalServerError(w, errors.Errorf("file %q does not exist", path))
+ return
+ }
+ utils.InternalServerError(w, err)
+ return
+ }
+ w.Header().Set("Content-Type", "text/yaml")
+ http.ServeFile(w, r, path)
+}
diff --git a/pkg/api/handlers/utils/containers.go b/pkg/api/handlers/utils/containers.go
index d5a79bdc8..bbe4cee3c 100644
--- a/pkg/api/handlers/utils/containers.go
+++ b/pkg/api/handlers/utils/containers.go
@@ -16,7 +16,7 @@ import (
// ContainerCreateResponse is the response struct for creating a container
type ContainerCreateResponse struct {
// ID of the container created
- ID string `json:"id"`
+ ID string `json:"Id"`
// Warnings during container creation
Warnings []string `json:"Warnings"`
}
diff --git a/pkg/api/handlers/utils/pods.go b/pkg/api/handlers/utils/pods.go
new file mode 100644
index 000000000..266ad9a4b
--- /dev/null
+++ b/pkg/api/handlers/utils/pods.go
@@ -0,0 +1,45 @@
+package utils
+
+import (
+ "fmt"
+ "net/http"
+
+ "github.com/containers/libpod/cmd/podman/shared"
+ "github.com/containers/libpod/libpod"
+ "github.com/gorilla/schema"
+)
+
+func GetPods(w http.ResponseWriter, r *http.Request) ([]*libpod.Pod, error) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+
+ query := struct {
+ All bool
+ Filters map[string][]string `schema:"filters"`
+ Digests bool
+ }{}
+
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ return nil, err
+ }
+ var filters = []string{}
+ if _, found := r.URL.Query()["digests"]; found && query.Digests {
+ UnSupportedParameter("digests")
+ }
+
+ if len(query.Filters) > 0 {
+ for k, v := range query.Filters {
+ for _, val := range v {
+ filters = append(filters, fmt.Sprintf("%s=%s", k, val))
+ }
+ }
+ filterFuncs, err := shared.GenerateFilterFunction(runtime, filters)
+ if err != nil {
+ return nil, err
+ }
+ return shared.FilterAllPodsWithFilterFunc(runtime, filterFuncs...)
+ }
+
+ return runtime.GetAllPods()
+
+}
diff --git a/pkg/api/server/register_manifest.go b/pkg/api/server/register_manifest.go
index ccfc2192d..8fd84f205 100644
--- a/pkg/api/server/register_manifest.go
+++ b/pkg/api/server/register_manifest.go
@@ -38,7 +38,7 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
// 500:
// $ref: "#/responses/InternalError"
r.Handle(VersionedPath("/libpod/manifests/create"), s.APIHandler(libpod.ManifestCreate)).Methods(http.MethodPost)
- // swagger:operation GET /libpod/manifests/{name}/json manifests Inspect
+ // swagger:operation GET /libpod/manifests/{name:.*}/json manifests Inspect
// ---
// summary: Inspect
// description: Display a manifest list
@@ -46,7 +46,7 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
// - application/json
// parameters:
// - in: path
- // name: name
+ // name: name:.*
// type: string
// required: true
// description: the name or ID of the manifest
@@ -58,14 +58,14 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
// 500:
// $ref: "#/responses/InternalError"
r.Handle(VersionedPath("/libpod/manifests/{name:.*}/json"), s.APIHandler(libpod.ManifestInspect)).Methods(http.MethodGet)
- // swagger:operation POST /libpod/manifests/{name}/add manifests AddManifest
+ // swagger:operation POST /libpod/manifests/{name:.*}/add manifests AddManifest
// ---
// description: Add an image to a manifest list
// produces:
// - application/json
// parameters:
// - in: path
- // name: name
+ // name: name:.*
// type: string
// required: true
// description: the name or ID of the manifest
@@ -84,7 +84,7 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
// 500:
// $ref: "#/responses/InternalError"
r.Handle(VersionedPath("/libpod/manifests/{name:.*}/add"), s.APIHandler(libpod.ManifestAdd)).Methods(http.MethodPost)
- // swagger:operation DELETE /libpod/manifests/{name} manifests RemoveManifest
+ // swagger:operation DELETE /libpod/manifests/{name:.*} manifests RemoveManifest
// ---
// summary: Remove
// description: Remove an image from a manifest list
@@ -92,7 +92,7 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
// - application/json
// parameters:
// - in: path
- // name: name
+ // name: name:.*
// type: string
// required: true
// description: the image associated with the manifest
diff --git a/pkg/api/server/register_swagger.go b/pkg/api/server/register_swagger.go
index 5564ec096..9048c1951 100644
--- a/pkg/api/server/register_swagger.go
+++ b/pkg/api/server/register_swagger.go
@@ -2,25 +2,14 @@ package server
import (
"net/http"
- "os"
+ "github.com/containers/libpod/pkg/api/handlers/libpod"
"github.com/gorilla/mux"
)
-// DefaultPodmanSwaggerSpec provides the default path to the podman swagger spec file
-const DefaultPodmanSwaggerSpec = "/usr/share/containers/podman/swagger.yaml"
-
// RegisterSwaggerHandlers maps the swagger endpoint for the server
func (s *APIServer) RegisterSwaggerHandlers(r *mux.Router) error {
// This handler does _*NOT*_ provide an UI rather just a swagger spec that an UI could render
- r.PathPrefix("/swagger/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- path := DefaultPodmanSwaggerSpec
- if p, found := os.LookupEnv("PODMAN_SWAGGER_SPEC"); found {
- path = p
- }
- w.Header().Set("Content-Type", "text/yaml")
-
- http.ServeFile(w, r, path)
- })
+ r.HandleFunc(VersionedPath("/libpod/swagger"), s.APIHandler(libpod.ServeSwagger)).Methods(http.MethodGet)
return nil
}
diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go
index 8496cd11c..59f1f95cb 100644
--- a/pkg/api/server/server.go
+++ b/pkg/api/server/server.go
@@ -83,10 +83,6 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
ConnectionCh: make(chan int),
}
- server.Timer = time.AfterFunc(server.Duration, func() {
- server.ConnectionCh <- NOOPHandler
- })
-
router.NotFoundHandler = http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
// We can track user errors...
@@ -109,6 +105,7 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
server.registerPingHandlers,
server.registerPluginsHandlers,
server.registerPodsHandlers,
+ server.RegisterSwaggerHandlers,
server.registerSwarmHandlers,
server.registerSystemHandlers,
server.registerVersionHandlers,
@@ -139,36 +136,15 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
// Serve starts responding to HTTP requests
func (s *APIServer) Serve() error {
- // stalker to count the connections. Should the timer expire it will shutdown the service.
- go func() {
- for delta := range s.ConnectionCh {
- switch delta {
- case EnterHandler:
- s.Timer.Stop()
- s.ActiveConnections += 1
- s.TotalConnections += 1
- case ExitHandler:
- s.Timer.Stop()
- s.ActiveConnections -= 1
- if s.ActiveConnections == 0 {
- // Server will be shutdown iff the timer expires before being reset or stopped
- s.Timer = time.AfterFunc(s.Duration, func() {
- if err := s.Shutdown(); err != nil {
- logrus.Errorf("Failed to shutdown APIServer: %v", err)
- os.Exit(1)
- }
- })
- } else {
- s.Timer.Reset(s.Duration)
- }
- case NOOPHandler:
- // push the check out another duration...
- s.Timer.Reset(s.Duration)
- default:
- logrus.Errorf("ConnectionCh received unsupported input %d", delta)
- }
- }
- }()
+ // This is initialized here as Timer is not needed until Serve'ing
+ if s.Duration > 0 {
+ s.Timer = time.AfterFunc(s.Duration, func() {
+ s.ConnectionCh <- NOOPHandler
+ })
+ go s.ReadChannelWithTimeout()
+ } else {
+ go s.ReadChannelNoTimeout()
+ }
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
@@ -193,6 +169,53 @@ func (s *APIServer) Serve() error {
return nil
}
+func (s *APIServer) ReadChannelWithTimeout() {
+ // stalker to count the connections. Should the timer expire it will shutdown the service.
+ for delta := range s.ConnectionCh {
+ switch delta {
+ case EnterHandler:
+ s.Timer.Stop()
+ s.ActiveConnections += 1
+ s.TotalConnections += 1
+ case ExitHandler:
+ s.Timer.Stop()
+ s.ActiveConnections -= 1
+ if s.ActiveConnections == 0 {
+ // Server will be shutdown iff the timer expires before being reset or stopped
+ s.Timer = time.AfterFunc(s.Duration, func() {
+ if err := s.Shutdown(); err != nil {
+ logrus.Errorf("Failed to shutdown APIServer: %v", err)
+ os.Exit(1)
+ }
+ })
+ } else {
+ s.Timer.Reset(s.Duration)
+ }
+ case NOOPHandler:
+ // push the check out another duration...
+ s.Timer.Reset(s.Duration)
+ default:
+ logrus.Warnf("ConnectionCh received unsupported input %d", delta)
+ }
+ }
+}
+
+func (s *APIServer) ReadChannelNoTimeout() {
+ // stalker to count the connections.
+ for delta := range s.ConnectionCh {
+ switch delta {
+ case EnterHandler:
+ s.ActiveConnections += 1
+ s.TotalConnections += 1
+ case ExitHandler:
+ s.ActiveConnections -= 1
+ case NOOPHandler:
+ default:
+ logrus.Warnf("ConnectionCh received unsupported input %d", delta)
+ }
+ }
+}
+
// Shutdown is a clean shutdown waiting on existing clients
func (s *APIServer) Shutdown() error {
// Duration == 0 flags no auto-shutdown of the server
diff --git a/pkg/autoupdate/autoupdate.go b/pkg/autoupdate/autoupdate.go
new file mode 100644
index 000000000..7c243eb00
--- /dev/null
+++ b/pkg/autoupdate/autoupdate.go
@@ -0,0 +1,280 @@
+package autoupdate
+
+import (
+ "context"
+ "os"
+ "sort"
+
+ "github.com/containers/image/v5/docker"
+ "github.com/containers/image/v5/docker/reference"
+ "github.com/containers/image/v5/manifest"
+ "github.com/containers/image/v5/transports/alltransports"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/systemd"
+ systemdGen "github.com/containers/libpod/pkg/systemd/generate"
+ "github.com/containers/libpod/pkg/util"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+// Label denotes the container/pod label key to specify auto-update policies in
+// container labels.
+const Label = "io.containers.autoupdate"
+
+// Policy represents an auto-update policy.
+type Policy string
+
+const (
+ // PolicyDefault is the default policy denoting no auto updates.
+ PolicyDefault Policy = "disabled"
+ // PolicyNewImage is the policy to update as soon as there's a new image found.
+ PolicyNewImage = "image"
+)
+
+// Map for easy lookups of supported policies.
+var supportedPolicies = map[string]Policy{
+ "": PolicyDefault,
+ "disabled": PolicyDefault,
+ "image": PolicyNewImage,
+}
+
+// LookupPolicy looksup the corresponding Policy for the specified
+// string. If none is found, an errors is returned including the list of
+// supported policies.
+//
+// Note that an empty string resolved to PolicyDefault.
+func LookupPolicy(s string) (Policy, error) {
+ policy, exists := supportedPolicies[s]
+ if exists {
+ return policy, nil
+ }
+
+ // Sort the keys first as maps are non-deterministic.
+ keys := []string{}
+ for k := range supportedPolicies {
+ if k != "" {
+ keys = append(keys, k)
+ }
+ }
+ sort.Strings(keys)
+
+ return "", errors.Errorf("invalid auto-update policy %q: valid policies are %+q", s, keys)
+}
+
+// ValidateImageReference checks if the specified imageName is a fully-qualified
+// image reference to the docker transport (without digest). Such a reference
+// includes a domain, name and tag (e.g., quay.io/podman/stable:latest). The
+// reference may also be prefixed with "docker://" explicitly indicating that
+// it's a reference to the docker transport.
+func ValidateImageReference(imageName string) error {
+ // Make sure the input image is a docker.
+ imageRef, err := alltransports.ParseImageName(imageName)
+ if err == nil && imageRef.Transport().Name() != docker.Transport.Name() {
+ return errors.Errorf("auto updates require the docker image transport but image is of transport %q", imageRef.Transport().Name())
+ } else if err != nil {
+ repo, err := reference.Parse(imageName)
+ if err != nil {
+ return errors.Wrap(err, "error enforcing fully-qualified docker transport reference for auto updates")
+ }
+ if _, ok := repo.(reference.NamedTagged); !ok {
+ return errors.Errorf("auto updates require fully-qualified image references (no tag): %q", imageName)
+ }
+ if _, ok := repo.(reference.Digested); ok {
+ return errors.Errorf("auto updates require fully-qualified image references without digest: %q", imageName)
+ }
+ }
+ return nil
+}
+
+// AutoUpdate looks up containers with a specified auto-update policy and acts
+// accordingly. If the policy is set to PolicyNewImage, it checks if the image
+// on the remote registry is different than the local one. If the image digests
+// differ, it pulls the remote image and restarts the systemd unit running the
+// container.
+//
+// It returns a slice of successfully restarted systemd units and a slice of
+// errors encountered during auto update.
+func AutoUpdate(runtime *libpod.Runtime) ([]string, []error) {
+ // Create a map from `image ID -> []*Container`.
+ containerMap, errs := imageContainersMap(runtime)
+ if len(containerMap) == 0 {
+ return nil, errs
+ }
+
+ // Create a map from `image ID -> *image.Image` for image lookups.
+ imagesSlice, err := runtime.ImageRuntime().GetImages()
+ if err != nil {
+ return nil, []error{err}
+ }
+ imageMap := make(map[string]*image.Image)
+ for i := range imagesSlice {
+ imageMap[imagesSlice[i].ID()] = imagesSlice[i]
+ }
+
+ // Connect to DBUS.
+ conn, err := systemd.ConnectToDBUS()
+ if err != nil {
+ logrus.Errorf(err.Error())
+ return nil, []error{err}
+ }
+ defer conn.Close()
+
+ // Update images.
+ containersToRestart := []*libpod.Container{}
+ updatedRawImages := make(map[string]bool)
+ for imageID, containers := range containerMap {
+ image, exists := imageMap[imageID]
+ if !exists {
+ errs = append(errs, errors.Errorf("container image ID %q not found in local storage", imageID))
+ return nil, errs
+ }
+ // Now we have to check if the image of any containers must be updated.
+ // Note that the image ID is NOT enough for this check as a given image
+ // may have multiple tags.
+ for i, ctr := range containers {
+ rawImageName := ctr.RawImageName()
+ if rawImageName == "" {
+ errs = append(errs, errors.Errorf("error auto-updating container %q: raw-image name is empty", ctr.ID()))
+ }
+ needsUpdate, err := newerImageAvailable(runtime, image, rawImageName)
+ if err != nil {
+ errs = append(errs, errors.Wrapf(err, "error auto-updating container %q: image check for %q failed", ctr.ID(), rawImageName))
+ continue
+ }
+ if !needsUpdate {
+ continue
+ }
+ logrus.Infof("Auto-updating container %q using image %q", ctr.ID(), rawImageName)
+ if _, updated := updatedRawImages[rawImageName]; !updated {
+ _, err = updateImage(runtime, rawImageName)
+ if err != nil {
+ errs = append(errs, errors.Wrapf(err, "error auto-updating container %q: image update for %q failed", ctr.ID(), rawImageName))
+ continue
+ }
+ updatedRawImages[rawImageName] = true
+ }
+ containersToRestart = append(containersToRestart, containers[i])
+ }
+ }
+
+ // Restart containers.
+ updatedUnits := []string{}
+ for _, ctr := range containersToRestart {
+ labels := ctr.Labels()
+ unit, exists := labels[systemdGen.EnvVariable]
+ if !exists {
+ // Shouldn't happen but let's be sure of it.
+ errs = append(errs, errors.Errorf("error auto-updating container %q: no %s label found", ctr.ID(), systemdGen.EnvVariable))
+ continue
+ }
+ _, err := conn.RestartUnit(unit, "replace", nil)
+ if err != nil {
+ errs = append(errs, errors.Wrapf(err, "error auto-updating container %q: restarting systemd unit %q failed", ctr.ID(), unit))
+ continue
+ }
+ logrus.Infof("Successfully restarted systemd unit %q", unit)
+ updatedUnits = append(updatedUnits, unit)
+ }
+
+ return updatedUnits, errs
+}
+
+// imageContainersMap generates a map[image ID] -> [containers using the image]
+// of all containers with a valid auto-update policy.
+func imageContainersMap(runtime *libpod.Runtime) (map[string][]*libpod.Container, []error) {
+ allContainers, err := runtime.GetAllContainers()
+ if err != nil {
+ return nil, []error{err}
+ }
+
+ errors := []error{}
+ imageMap := make(map[string][]*libpod.Container)
+ for i, ctr := range allContainers {
+ state, err := ctr.State()
+ if err != nil {
+ errors = append(errors, err)
+ continue
+ }
+ // Only update running containers.
+ if state != define.ContainerStateRunning {
+ continue
+ }
+ // Only update containers with the specific label/policy set.
+ labels := ctr.Labels()
+ if value, exists := labels[Label]; exists {
+ policy, err := LookupPolicy(value)
+ if err != nil {
+ errors = append(errors, err)
+ continue
+ }
+ if policy != PolicyNewImage {
+ continue
+ }
+ }
+ // Now we know that `ctr` is configured for auto updates.
+ id, _ := ctr.Image()
+ imageMap[id] = append(imageMap[id], allContainers[i])
+ }
+
+ return imageMap, errors
+}
+
+// newerImageAvailable returns true if there corresponding image on the remote
+// registry is newer.
+func newerImageAvailable(runtime *libpod.Runtime, img *image.Image, origName string) (bool, error) {
+ remoteRef, err := docker.ParseReference("//" + origName)
+ if err != nil {
+ return false, err
+ }
+
+ remoteImg, err := remoteRef.NewImage(context.Background(), runtime.SystemContext())
+ if err != nil {
+ return false, err
+ }
+
+ rawManifest, _, err := remoteImg.Manifest(context.Background())
+ if err != nil {
+ return false, err
+ }
+
+ remoteDigest, err := manifest.Digest(rawManifest)
+ if err != nil {
+ return false, err
+ }
+
+ return img.Digest().String() != remoteDigest.String(), nil
+}
+
+// updateImage pulls the specified image.
+func updateImage(runtime *libpod.Runtime, name string) (*image.Image, error) {
+ sys := runtime.SystemContext()
+ registryOpts := image.DockerRegistryOptions{}
+ signaturePolicyPath := ""
+ authFilePath := ""
+
+ if sys != nil {
+ registryOpts.OSChoice = sys.OSChoice
+ registryOpts.ArchitectureChoice = sys.OSChoice
+ registryOpts.DockerCertPath = sys.DockerCertPath
+
+ signaturePolicyPath = sys.SignaturePolicyPath
+ authFilePath = sys.AuthFilePath
+ }
+
+ newImage, err := runtime.ImageRuntime().New(context.Background(),
+ docker.Transport.Name()+"://"+name,
+ signaturePolicyPath,
+ authFilePath,
+ os.Stderr,
+ &registryOpts,
+ image.SigningOptions{},
+ nil,
+ util.PullImageAlways,
+ )
+ if err != nil {
+ return nil, err
+ }
+ return newImage, nil
+}
diff --git a/pkg/autoupdate/autoupdate_test.go b/pkg/autoupdate/autoupdate_test.go
new file mode 100644
index 000000000..7a5da5bb0
--- /dev/null
+++ b/pkg/autoupdate/autoupdate_test.go
@@ -0,0 +1,50 @@
+package autoupdate
+
+import (
+ "testing"
+)
+
+func TestValidateImageReference(t *testing.T) {
+ tests := []struct {
+ input string
+ valid bool
+ }{
+ { // Fully-qualified reference
+ input: "quay.io/foo/bar:tag",
+ valid: true,
+ },
+ { // Fully-qualified reference in transport notation
+ input: "docker://quay.io/foo/bar:tag",
+ valid: true,
+ },
+ { // Fully-qualified reference but with digest
+ input: "quay.io/foo/bar@sha256:c9b1b535fdd91a9855fb7f82348177e5f019329a58c53c47272962dd60f71fc9",
+ valid: false,
+ },
+ { // Reference with missing tag
+ input: "quay.io/foo/bar",
+ valid: false,
+ },
+ { // Short name
+ input: "alpine",
+ valid: false,
+ },
+ { // Short name with repo
+ input: "library/alpine",
+ valid: false,
+ },
+ { // Wrong transport
+ input: "docker-archive:/some/path.tar",
+ valid: false,
+ },
+ }
+
+ for _, test := range tests {
+ err := ValidateImageReference(test.input)
+ if test.valid && err != nil {
+ t.Fatalf("parsing %q should have succeeded: %v", test.input, err)
+ } else if !test.valid && err == nil {
+ t.Fatalf("parsing %q should have failed", test.input)
+ }
+ }
+}
diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go
index ba5f9c3aa..289debd8c 100644
--- a/pkg/bindings/connection.go
+++ b/pkg/bindings/connection.go
@@ -109,7 +109,7 @@ func NewConnection(ctx context.Context, uri string, identity ...string) (context
}
client, err = tcpClient(_url)
default:
- return nil, errors.Errorf("%s is not a support schema", _url.Scheme)
+ return nil, errors.Errorf("'%s' is not a supported schema", _url.Scheme)
}
if err != nil {
return nil, errors.Wrapf(err, "Failed to create %sClient", _url.Scheme)
diff --git a/pkg/bindings/test/common_test.go b/pkg/bindings/test/common_test.go
index a4d065a14..5cd8f7e4f 100644
--- a/pkg/bindings/test/common_test.go
+++ b/pkg/bindings/test/common_test.go
@@ -152,7 +152,7 @@ func (b *bindingTest) startAPIService() *gexec.Session {
var (
cmd []string
)
- cmd = append(cmd, "--log-level=debug", "system", "service", "--timeout=999999", b.sock)
+ cmd = append(cmd, "--log-level=debug", "system", "service", "--timeout=0", b.sock)
return b.runPodman(cmd)
}
diff --git a/pkg/bindings/test/pods_test.go b/pkg/bindings/test/pods_test.go
index 7e29265b7..f6c4d465d 100644
--- a/pkg/bindings/test/pods_test.go
+++ b/pkg/bindings/test/pods_test.go
@@ -76,15 +76,66 @@ var _ = Describe("Podman pods", func() {
}
Expect(StringInSlice(newpod, names)).To(BeTrue())
Expect(StringInSlice("newpod2", names)).To(BeTrue())
+ })
- // TODO not working Because: code to list based on filter
- // "not yet implemented",
- // Validate list pod with filters
- //filters := make(map[string][]string)
- //filters["name"] = []string{newpod}
- //filteredPods, err := pods.List(bt.conn, filters)
- //Expect(err).To(BeNil())
- //Expect(len(filteredPods)).To(BeNumerically("==", 1))
+ // The test validates the list pod endpoint with passing filters as the params.
+ It("List pods with filters", func() {
+ var newpod2 string = "newpod2"
+ bt.Podcreate(&newpod2)
+ _, err = bt.RunTopContainer(nil, &trueFlag, &newpod)
+ Expect(err).To(BeNil())
+
+ // Expected err with invalid filter params
+ filters := make(map[string][]string)
+ filters["dummy"] = []string{"dummy"}
+ filteredPods, err := pods.List(bt.conn, filters)
+ Expect(err).ToNot(BeNil())
+ code, _ := bindings.CheckResponseCode(err)
+ Expect(code).To(BeNumerically("==", http.StatusInternalServerError))
+
+ // Expected empty response with invalid filters
+ filters = make(map[string][]string)
+ filters["name"] = []string{"dummy"}
+ filteredPods, err = pods.List(bt.conn, filters)
+ Expect(err).To(BeNil())
+ Expect(len(filteredPods)).To(BeNumerically("==", 0))
+
+ // Validate list pod with name filter
+ filters = make(map[string][]string)
+ filters["name"] = []string{newpod2}
+ filteredPods, err = pods.List(bt.conn, filters)
+ Expect(err).To(BeNil())
+ Expect(len(filteredPods)).To(BeNumerically("==", 1))
+ var names []string
+ for _, i := range filteredPods {
+ names = append(names, i.Config.Name)
+ }
+ Expect(StringInSlice("newpod2", names)).To(BeTrue())
+
+ // Validate list pod with id filter
+ filters = make(map[string][]string)
+ response, err := pods.Inspect(bt.conn, newpod)
+ id := response.Config.ID
+ filters["id"] = []string{id}
+ filteredPods, err = pods.List(bt.conn, filters)
+ Expect(err).To(BeNil())
+ Expect(len(filteredPods)).To(BeNumerically("==", 1))
+ names = names[:0]
+ for _, i := range filteredPods {
+ names = append(names, i.Config.Name)
+ }
+ Expect(StringInSlice("newpod", names)).To(BeTrue())
+
+ // Using multiple filters
+ filters["name"] = []string{newpod}
+ filteredPods, err = pods.List(bt.conn, filters)
+ Expect(err).To(BeNil())
+ Expect(len(filteredPods)).To(BeNumerically("==", 1))
+ names = names[:0]
+ for _, i := range filteredPods {
+ names = append(names, i.Config.Name)
+ }
+ Expect(StringInSlice("newpod", names)).To(BeTrue())
})
// The test validates if the exists responds
diff --git a/pkg/bindings/test/volumes_test.go b/pkg/bindings/test/volumes_test.go
index c8940d46e..b1a742c43 100644
--- a/pkg/bindings/test/volumes_test.go
+++ b/pkg/bindings/test/volumes_test.go
@@ -3,11 +3,12 @@ package test_bindings
import (
"context"
"fmt"
+ "net/http"
+ "time"
+
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/bindings/containers"
"github.com/containers/libpod/pkg/bindings/volumes"
- "net/http"
- "time"
"github.com/containers/libpod/pkg/bindings"
. "github.com/onsi/ginkgo"
diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go
new file mode 100644
index 000000000..1899b98f7
--- /dev/null
+++ b/pkg/domain/entities/containers.go
@@ -0,0 +1 @@
+package entities
diff --git a/pkg/domain/entities/engine.go b/pkg/domain/entities/engine.go
new file mode 100644
index 000000000..a1096f1f1
--- /dev/null
+++ b/pkg/domain/entities/engine.go
@@ -0,0 +1,97 @@
+package entities
+
+import (
+ "net/url"
+ "os/user"
+ "path/filepath"
+
+ "github.com/containers/libpod/libpod/define"
+ "github.com/spf13/pflag"
+)
+
+type EngineMode string
+
+const (
+ ABIMode = EngineMode("abi")
+ TunnelMode = EngineMode("tunnel")
+)
+
+func (m EngineMode) String() string {
+ return string(m)
+}
+
+type EngineOptions struct {
+ Uri *url.URL
+ Identities []string
+ FlagSet pflag.FlagSet
+ Flags EngineFlags
+}
+
+type EngineFlags struct {
+ CGroupManager string
+ CniConfigDir string
+ ConmonPath string
+ DefaultMountsFile string
+ EventsBackend string
+ HooksDir []string
+ MaxWorks int
+ Namespace string
+ Root string
+ Runroot string
+ Runtime string
+ StorageDriver string
+ StorageOpts []string
+ Syslog bool
+ Trace bool
+ NetworkCmdPath string
+
+ Config string
+ CpuProfile string
+ LogLevel string
+ TmpDir string
+
+ RemoteUserName string
+ RemoteHost string
+ VarlinkAddress string
+ ConnectionName string
+ RemoteConfigFilePath string
+ Port int
+ IdentityFile string
+ IgnoreHosts bool
+
+ EngineMode EngineMode
+}
+
+func NewEngineOptions() (EngineFlags, error) {
+ u, _ := user.Current()
+ return EngineFlags{
+ CGroupManager: define.SystemdCgroupsManager,
+ CniConfigDir: "",
+ Config: "",
+ ConmonPath: filepath.Join("usr", "bin", "conmon"),
+ ConnectionName: "",
+ CpuProfile: "",
+ DefaultMountsFile: "",
+ EventsBackend: "",
+ HooksDir: nil,
+ IdentityFile: "",
+ IgnoreHosts: false,
+ LogLevel: "",
+ MaxWorks: 0,
+ Namespace: "",
+ NetworkCmdPath: "",
+ Port: 0,
+ RemoteConfigFilePath: "",
+ RemoteHost: "",
+ RemoteUserName: "",
+ Root: "",
+ Runroot: filepath.Join("run", "user", u.Uid),
+ Runtime: "",
+ StorageDriver: "overlayfs",
+ StorageOpts: nil,
+ Syslog: false,
+ TmpDir: filepath.Join("run", "user", u.Uid, "libpod", "tmp"),
+ Trace: false,
+ VarlinkAddress: "",
+ }, nil
+}
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
new file mode 100644
index 000000000..d08f37d44
--- /dev/null
+++ b/pkg/domain/entities/engine_container.go
@@ -0,0 +1,26 @@
+package entities
+
+import (
+ "context"
+)
+
+type ContainerEngine interface {
+ ContainerRuntime
+ PodRuntime
+ VolumeRuntime
+}
+
+type ContainerRuntime interface {
+ ContainerDelete(ctx context.Context, opts ContainerDeleteOptions) (*ContainerDeleteReport, error)
+ ContainerPrune(ctx context.Context) (*ContainerPruneReport, error)
+}
+
+type PodRuntime interface {
+ PodDelete(ctx context.Context, opts PodPruneOptions) (*PodDeleteReport, error)
+ PodPrune(ctx context.Context) (*PodPruneReport, error)
+}
+
+type VolumeRuntime interface {
+ VolumeDelete(ctx context.Context, opts VolumeDeleteOptions) (*VolumeDeleteReport, error)
+ VolumePrune(ctx context.Context) (*VolumePruneReport, error)
+}
diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go
new file mode 100644
index 000000000..27676d781
--- /dev/null
+++ b/pkg/domain/entities/engine_image.go
@@ -0,0 +1,12 @@
+package entities
+
+import (
+ "context"
+)
+
+type ImageEngine interface {
+ Delete(ctx context.Context, nameOrId string, opts ImageDeleteOptions) (*ImageDeleteReport, error)
+ History(ctx context.Context, nameOrId string, opts ImageHistoryOptions) (*ImageHistoryReport, error)
+ List(ctx context.Context, opts ImageListOptions) (*ImageListReport, error)
+ Prune(ctx context.Context, opts ImagePruneOptions) (*ImagePruneReport, error)
+}
diff --git a/pkg/domain/entities/filters.go b/pkg/domain/entities/filters.go
new file mode 100644
index 000000000..c7e227244
--- /dev/null
+++ b/pkg/domain/entities/filters.go
@@ -0,0 +1,150 @@
+package entities
+
+import (
+ "net/url"
+ "strings"
+)
+
+// Identifier interface allows filters to access ID() of object
+type Identifier interface {
+ Id() string
+}
+
+// Named interface allows filters to access Name() of object
+type Named interface {
+ Name() string
+}
+
+// Named interface allows filters to access Name() of object
+type Names interface {
+ Names() []string
+}
+
+// IdOrName interface allows filters to access ID() or Name() of object
+type IdOrNamed interface {
+ Identifier
+ Named
+}
+
+// IdOrName interface allows filters to access ID() or Names() of object
+type IdOrNames interface {
+ Identifier
+ Names
+}
+
+type ImageFilter func(Image) bool
+type VolumeFilter func(Volume) bool
+type ContainerFilter func(Container) bool
+
+func CompileImageFilters(filters url.Values) ImageFilter {
+ var fns []interface{}
+
+ for name, targets := range filters {
+ switch name {
+ case "id":
+ fns = append(fns, FilterIdFn(targets))
+ case "name":
+ fns = append(fns, FilterNamesFn(targets))
+ case "idOrName":
+ fns = append(fns, FilterIdOrNameFn(targets))
+ }
+ }
+
+ return func(image Image) bool {
+ for _, fn := range fns {
+ if !fn.(ImageFilter)(image) {
+ return false
+ }
+ }
+ return true
+ }
+}
+
+func CompileContainerFilters(filters url.Values) ContainerFilter {
+ var fns []interface{}
+
+ for name, targets := range filters {
+ switch name {
+ case "id":
+ fns = append(fns, FilterIdFn(targets))
+ case "name":
+ fns = append(fns, FilterNameFn(targets))
+ case "idOrName":
+ fns = append(fns, FilterIdOrNameFn(targets))
+ }
+ }
+
+ return func(ctnr Container) bool {
+ for _, fn := range fns {
+ if !fn.(ContainerFilter)(ctnr) {
+ return false
+ }
+ }
+ return true
+ }
+}
+
+func CompileVolumeFilters(filters url.Values) VolumeFilter {
+ var fns []interface{}
+
+ for name, targets := range filters {
+ if name == "id" {
+ fns = append(fns, FilterIdFn(targets))
+ }
+ }
+
+ return func(volume Volume) bool {
+ for _, fn := range fns {
+ if !fn.(VolumeFilter)(volume) {
+ return false
+ }
+ }
+ return true
+ }
+}
+
+func FilterIdFn(id []string) func(Identifier) bool {
+ return func(obj Identifier) bool {
+ for _, v := range id {
+ if strings.Contains(obj.Id(), v) {
+ return true
+ }
+ }
+ return false
+ }
+}
+
+func FilterNameFn(name []string) func(Named) bool {
+ return func(obj Named) bool {
+ for _, v := range name {
+ if strings.Contains(obj.Name(), v) {
+ return true
+ }
+ }
+ return false
+ }
+}
+
+func FilterNamesFn(name []string) func(Names) bool {
+ return func(obj Names) bool {
+ for _, v := range name {
+ for _, n := range obj.Names() {
+ if strings.Contains(n, v) {
+ return true
+ }
+ }
+ }
+ return false
+ }
+}
+
+func FilterIdOrNameFn(id []string) func(IdOrNamed) bool {
+ return func(obj IdOrNamed) bool {
+ for _, v := range id {
+ if strings.Contains(obj.Id(), v) || strings.Contains(obj.Name(), v) {
+ return true
+ }
+ }
+ return false
+ }
+}
diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go
new file mode 100644
index 000000000..c84ed5351
--- /dev/null
+++ b/pkg/domain/entities/images.go
@@ -0,0 +1,151 @@
+package entities
+
+import (
+ "net/url"
+
+ "github.com/containers/image/v5/manifest"
+ docker "github.com/docker/docker/api/types"
+ "github.com/docker/docker/api/types/container"
+ "github.com/opencontainers/go-digest"
+ v1 "github.com/opencontainers/image-spec/specs-go/v1"
+)
+
+type Image struct {
+ IdOrNamed
+ ID string `json:"Id"`
+ RepoTags []string `json:",omitempty"`
+ RepoDigests []string `json:",omitempty"`
+ Parent string `json:",omitempty"`
+ Comment string `json:",omitempty"`
+ Created string `json:",omitempty"`
+ Container string `json:",omitempty"`
+ ContainerConfig *container.Config `json:",omitempty"`
+ DockerVersion string `json:",omitempty"`
+ Author string `json:",omitempty"`
+ Config *container.Config `json:",omitempty"`
+ Architecture string `json:",omitempty"`
+ Variant string `json:",omitempty"`
+ Os string `json:",omitempty"`
+ OsVersion string `json:",omitempty"`
+ Size int64 `json:",omitempty"`
+ VirtualSize int64 `json:",omitempty"`
+ GraphDriver docker.GraphDriverData `json:",omitempty"`
+ RootFS docker.RootFS `json:",omitempty"`
+ Metadata docker.ImageMetadata `json:",omitempty"`
+
+ // Podman extensions
+ Digest digest.Digest `json:",omitempty"`
+ PodmanVersion string `json:",omitempty"`
+ ManifestType string `json:",omitempty"`
+ User string `json:",omitempty"`
+ History []v1.History `json:",omitempty"`
+ NamesHistory []string `json:",omitempty"`
+ HealthCheck *manifest.Schema2HealthConfig `json:",omitempty"`
+}
+
+func (i *Image) Id() string {
+ return i.ID
+}
+
+type ImageSummary struct {
+ Identifier
+ ID string `json:"Id"`
+ ParentId string `json:",omitempty"`
+ RepoTags []string `json:",omitempty"`
+ Created int `json:",omitempty"`
+ Size int `json:",omitempty"`
+ SharedSize int `json:",omitempty"`
+ VirtualSize int `json:",omitempty"`
+ Labels string `json:",omitempty"`
+ Containers int `json:",omitempty"`
+ ReadOnly bool `json:",omitempty"`
+ Dangling bool `json:",omitempty"`
+
+ // Podman extensions
+ Digest digest.Digest `json:",omitempty"`
+ ConfigDigest digest.Digest `json:",omitempty"`
+}
+
+func (i *ImageSummary) Id() string {
+ return i.ID
+}
+
+func (i *ImageSummary) IsReadOnly() bool {
+ return i.ReadOnly
+}
+
+func (i *ImageSummary) IsDangling() bool {
+ return i.Dangling
+}
+
+type ImageOptions struct {
+ All bool
+ Digests bool
+ Filter []string
+ Format string
+ Noheading bool
+ NoTrunc bool
+ Quiet bool
+ Sort string
+ History bool
+}
+
+type ImageDeleteOptions struct {
+ Force bool
+}
+
+// ImageDeleteResponse is the response for removing an image from storage and containers
+// what was untagged vs actually removed
+type ImageDeleteReport struct {
+ Untagged []string `json:"untagged"`
+ Deleted string `json:"deleted"`
+}
+
+type ImageHistoryOptions struct{}
+
+type ImageHistoryLayer struct {
+ ID string `json:"Id"`
+ Created int64 `json:"Created,omitempty"`
+ CreatedBy string `json:",omitempty"`
+ Tags []string `json:",omitempty"`
+ Size int64 `json:",omitempty"`
+ Comment string `json:",omitempty"`
+}
+
+type ImageHistoryReport struct {
+ Layers []ImageHistoryLayer
+}
+
+type ImageInspectOptions struct {
+ TypeObject string `json:",omitempty"`
+ Format string `json:",omitempty"`
+ Size bool `json:",omitempty"`
+ Latest bool `json:",omitempty"`
+}
+
+type ImageListOptions struct {
+ All bool `json:"all" schema:"all"`
+ Digests bool `json:"digests" schema:"digests"`
+ Filter []string `json:",omitempty"`
+ Filters url.Values `json:"filters" schema:"filters"`
+ Format string `json:",omitempty"`
+ History bool `json:",omitempty"`
+ Noheading bool `json:",omitempty"`
+ NoTrunc bool `json:",omitempty"`
+ Quiet bool `json:",omitempty"`
+ Sort string `json:",omitempty"`
+}
+
+type ImageListReport struct {
+ Images []ImageSummary
+}
+
+type ImagePruneOptions struct {
+ All bool
+ Filter ImageFilter
+}
+
+type ImagePruneReport struct {
+ Report Report
+ Size int64
+}
diff --git a/pkg/domain/entities/types.go b/pkg/domain/entities/types.go
new file mode 100644
index 000000000..6f947dc4d
--- /dev/null
+++ b/pkg/domain/entities/types.go
@@ -0,0 +1,25 @@
+package entities
+
+type Container struct {
+ IdOrNamed
+}
+
+type Volume struct {
+ Identifier
+}
+
+type Report struct {
+ Id []string
+ Err map[string]error
+}
+
+type ContainerDeleteOptions struct{}
+type ContainerDeleteReport struct{ Report }
+type ContainerPruneReport struct{ Report }
+
+type PodDeleteReport struct{ Report }
+type PodPruneOptions struct{}
+type PodPruneReport struct{ Report }
+type VolumeDeleteOptions struct{}
+type VolumeDeleteReport struct{ Report }
+type VolumePruneReport struct{ Report }
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
new file mode 100644
index 000000000..2db08f259
--- /dev/null
+++ b/pkg/domain/infra/abi/images.go
@@ -0,0 +1,131 @@
+// +build ABISupport
+
+package abi
+
+import (
+ "context"
+
+ libpodImage "github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/domain/utils"
+)
+
+func (ir *ImageEngine) Delete(ctx context.Context, nameOrId string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) {
+ image, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId)
+ if err != nil {
+ return nil, err
+ }
+
+ results, err := ir.Libpod.RemoveImage(ctx, image, opts.Force)
+ if err != nil {
+ return nil, err
+ }
+
+ report := entities.ImageDeleteReport{}
+ if err := utils.DeepCopy(&report, results); err != nil {
+ return nil, err
+ }
+ return &report, nil
+}
+
+func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) {
+ results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, []string{})
+ if err != nil {
+ return nil, err
+ }
+
+ report := entities.ImagePruneReport{}
+ copy(report.Report.Id, results)
+ return &report, nil
+}
+
+func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) (*entities.ImageListReport, error) {
+ var (
+ images []*libpodImage.Image
+ err error
+ )
+
+ filters := utils.ToLibpodFilters(opts.Filters)
+ if len(filters) > 0 {
+ images, err = ir.Libpod.ImageRuntime().GetImagesWithFilters(filters)
+ } else {
+ images, err = ir.Libpod.ImageRuntime().GetImages()
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ report := entities.ImageListReport{
+ Images: make([]entities.ImageSummary, len(images)),
+ }
+ for i, img := range images {
+ hold := entities.ImageSummary{}
+ if err := utils.DeepCopy(&hold, img); err != nil {
+ return nil, err
+ }
+ report.Images[i] = hold
+ }
+ return &report, nil
+}
+
+func (ir *ImageEngine) History(ctx context.Context, nameOrId string, opts entities.ImageHistoryOptions) (*entities.ImageHistoryReport, error) {
+ image, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId)
+ if err != nil {
+ return nil, err
+ }
+ results, err := image.History(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ history := entities.ImageHistoryReport{
+ Layers: make([]entities.ImageHistoryLayer, len(results)),
+ }
+
+ for i, layer := range results {
+ history.Layers[i] = ToDomainHistoryLayer(layer)
+ }
+ return &history, nil
+}
+
+func ToDomainHistoryLayer(layer *libpodImage.History) entities.ImageHistoryLayer {
+ l := entities.ImageHistoryLayer{}
+ l.ID = layer.ID
+ l.Created = layer.Created.Unix()
+ l.CreatedBy = layer.CreatedBy
+ copy(l.Tags, layer.Tags)
+ l.Size = layer.Size
+ l.Comment = layer.Comment
+ return l
+}
+
+// func (r *imageRuntime) Delete(ctx context.Context, nameOrId string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) {
+// image, err := r.libpod.ImageEngine().NewFromLocal(nameOrId)
+// if err != nil {
+// return nil, err
+// }
+//
+// results, err := r.libpod.RemoveImage(ctx, image, opts.Force)
+// if err != nil {
+// return nil, err
+// }
+//
+// report := entities.ImageDeleteReport{}
+// if err := utils.DeepCopy(&report, results); err != nil {
+// return nil, err
+// }
+// return &report, nil
+// }
+//
+// func (r *imageRuntime) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) {
+// // TODO: map FilterOptions
+// id, err := r.libpod.ImageEngine().PruneImages(ctx, opts.All, []string{})
+// if err != nil {
+// return nil, err
+// }
+//
+// // TODO: Determine Size
+// report := entities.ImagePruneReport{}
+// copy(report.Report.Id, id)
+// return &report, nil
+// }
diff --git a/pkg/domain/infra/abi/images_test.go b/pkg/domain/infra/abi/images_test.go
new file mode 100644
index 000000000..20ef1b150
--- /dev/null
+++ b/pkg/domain/infra/abi/images_test.go
@@ -0,0 +1,37 @@
+package abi
+
+//
+// import (
+// "context"
+// "testing"
+//
+// "github.com/stretchr/testify/mock"
+// )
+//
+// type MockImageRuntime struct {
+// mock.Mock
+// }
+//
+// func (m *MockImageRuntime) Delete(ctx context.Context, renderer func() interface{}, name string) error {
+// _ = m.Called(ctx, renderer, name)
+// return nil
+// }
+//
+// func TestImageSuccess(t *testing.T) {
+// actual := func() interface{} { return nil }
+//
+// m := new(MockImageRuntime)
+// m.On(
+// "Delete",
+// mock.AnythingOfType("*context.emptyCtx"),
+// mock.AnythingOfType("func() interface {}"),
+// "fedora").
+// Return(nil)
+//
+// r := DirectImageRuntime{m}
+// err := r.Delete(context.TODO(), actual, "fedora")
+// if err != nil {
+// t.Errorf("error should be nil, got: %v", err)
+// }
+// m.AssertExpectations(t)
+// }
diff --git a/pkg/domain/infra/abi/runtime.go b/pkg/domain/infra/abi/runtime.go
new file mode 100644
index 000000000..479a69586
--- /dev/null
+++ b/pkg/domain/infra/abi/runtime.go
@@ -0,0 +1,19 @@
+// +build ABISupport
+
+package abi
+
+import (
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/domain/entities"
+)
+
+// Image-related runtime linked against libpod library
+type ImageEngine struct {
+ Libpod *libpod.Runtime
+}
+
+// Container-related runtime linked against libpod library
+type ContainerEngine struct {
+ entities.ContainerEngine
+ Libpod *libpod.Runtime
+}
diff --git a/pkg/domain/infra/runtime_abi.go b/pkg/domain/infra/runtime_abi.go
new file mode 100644
index 000000000..de996f567
--- /dev/null
+++ b/pkg/domain/infra/runtime_abi.go
@@ -0,0 +1,39 @@
+// +build ABISupport
+
+package infra
+
+import (
+ "context"
+ "fmt"
+
+ "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"
+)
+
+// NewContainerEngine factory provides a libpod runtime for container-related operations
+func NewContainerEngine(mode entities.EngineMode, opts entities.EngineOptions) (entities.ContainerEngine, error) {
+ switch mode {
+ case entities.ABIMode:
+ r, err := NewLibpodRuntime(opts.FlagSet, opts.Flags)
+ return &abi.ContainerEngine{ContainerEngine: r}, err
+ case entities.TunnelMode:
+ ctx, err := bindings.NewConnection(context.Background(), opts.Uri.String(), opts.Identities...)
+ return &tunnel.ContainerEngine{ClientCxt: ctx}, err
+ }
+ return nil, fmt.Errorf("runtime mode '%v' is not supported", mode)
+}
+
+// NewContainerEngine factory provides a libpod runtime for image-related operations
+func NewImageEngine(mode entities.EngineMode, opts entities.EngineOptions) (entities.ImageEngine, error) {
+ switch mode {
+ case entities.ABIMode:
+ r, err := NewLibpodImageRuntime(opts.FlagSet, opts.Flags)
+ return r, err
+ case entities.TunnelMode:
+ ctx, err := bindings.NewConnection(context.Background(), opts.Uri.String(), opts.Identities...)
+ return &tunnel.ImageEngine{ClientCxt: ctx}, err
+ }
+ return nil, fmt.Errorf("runtime mode '%v' is not supported", mode)
+}
diff --git a/pkg/domain/infra/runtime_image_proxy.go b/pkg/domain/infra/runtime_image_proxy.go
new file mode 100644
index 000000000..480e7c8d7
--- /dev/null
+++ b/pkg/domain/infra/runtime_image_proxy.go
@@ -0,0 +1,25 @@
+// +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 podmanV2 is separated from libpod repo
+
+func NewLibpodImageRuntime(flags pflag.FlagSet, opts entities.EngineFlags) (entities.ImageEngine, error) {
+ r, err := GetRuntime(context.Background(), flags, opts)
+ if err != nil {
+ return nil, err
+ }
+ return &abi.ImageEngine{Libpod: r}, nil
+}
+
+func (ir *runtime) ShutdownImageRuntime(force bool) error {
+ return ir.Libpod.Shutdown(force)
+}
diff --git a/pkg/domain/infra/runtime_libpod.go b/pkg/domain/infra/runtime_libpod.go
new file mode 100644
index 000000000..b42c52b6e
--- /dev/null
+++ b/pkg/domain/infra/runtime_libpod.go
@@ -0,0 +1,331 @@
+package infra
+
+import (
+ "context"
+ "fmt"
+ "os"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/cgroups"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/namespaces"
+ "github.com/containers/libpod/pkg/rootless"
+ "github.com/containers/storage"
+ "github.com/containers/storage/pkg/idtools"
+ "github.com/pkg/errors"
+ flag "github.com/spf13/pflag"
+)
+
+type engineOpts struct {
+ name string
+ renumber bool
+ migrate bool
+ noStore bool
+ withFDS bool
+ flags entities.EngineFlags
+}
+
+// GetRuntimeMigrate gets a libpod runtime that will perform a migration of existing containers
+func GetRuntimeMigrate(ctx context.Context, fs flag.FlagSet, ef entities.EngineFlags, newRuntime string) (*libpod.Runtime, error) {
+ return getRuntime(ctx, fs, &engineOpts{
+ name: newRuntime,
+ renumber: false,
+ migrate: true,
+ noStore: false,
+ withFDS: true,
+ flags: ef,
+ })
+}
+
+// GetRuntimeDisableFDs gets a libpod runtime that will disable sd notify
+func GetRuntimeDisableFDs(ctx context.Context, fs flag.FlagSet, ef entities.EngineFlags) (*libpod.Runtime, error) {
+ return getRuntime(ctx, fs, &engineOpts{
+ renumber: false,
+ migrate: false,
+ noStore: false,
+ withFDS: false,
+ flags: ef,
+ })
+}
+
+// GetRuntimeRenumber gets a libpod runtime that will perform a lock renumber
+func GetRuntimeRenumber(ctx context.Context, fs flag.FlagSet, ef entities.EngineFlags) (*libpod.Runtime, error) {
+ return getRuntime(ctx, fs, &engineOpts{
+ renumber: true,
+ migrate: false,
+ noStore: false,
+ withFDS: true,
+ flags: ef,
+ })
+}
+
+// GetRuntime generates a new libpod runtime configured by command line options
+func GetRuntime(ctx context.Context, flags flag.FlagSet, ef entities.EngineFlags) (*libpod.Runtime, error) {
+ return getRuntime(ctx, flags, &engineOpts{
+ renumber: false,
+ migrate: false,
+ noStore: false,
+ withFDS: true,
+ flags: ef,
+ })
+}
+
+// GetRuntimeNoStore generates a new libpod runtime configured by command line options
+func GetRuntimeNoStore(ctx context.Context, fs flag.FlagSet, ef entities.EngineFlags) (*libpod.Runtime, error) {
+ return getRuntime(ctx, fs, &engineOpts{
+ renumber: false,
+ migrate: false,
+ noStore: true,
+ withFDS: true,
+ flags: ef,
+ })
+}
+
+func getRuntime(ctx context.Context, fs flag.FlagSet, opts *engineOpts) (*libpod.Runtime, error) {
+ options := []libpod.RuntimeOption{}
+ storageOpts := storage.StoreOptions{}
+ storageSet := false
+
+ uidmapFlag := fs.Lookup("uidmap")
+ gidmapFlag := fs.Lookup("gidmap")
+ subuidname := fs.Lookup("subuidname")
+ subgidname := fs.Lookup("subgidname")
+ if (uidmapFlag != nil && gidmapFlag != nil && subuidname != nil && subgidname != nil) &&
+ (uidmapFlag.Changed || gidmapFlag.Changed || subuidname.Changed || subgidname.Changed) {
+ userns, _ := fs.GetString("userns")
+ uidmapVal, _ := fs.GetStringSlice("uidmap")
+ gidmapVal, _ := fs.GetStringSlice("gidmap")
+ subuidVal, _ := fs.GetString("subuidname")
+ subgidVal, _ := fs.GetString("subgidname")
+ mappings, err := ParseIDMapping(namespaces.UsernsMode(userns), uidmapVal, gidmapVal, subuidVal, subgidVal)
+ if err != nil {
+ return nil, err
+ }
+ storageOpts.UIDMap = mappings.UIDMap
+ storageOpts.GIDMap = mappings.GIDMap
+
+ storageSet = true
+ }
+
+ if fs.Changed("root") {
+ storageSet = true
+ storageOpts.GraphRoot = opts.flags.Root
+ }
+ if fs.Changed("runroot") {
+ storageSet = true
+ storageOpts.RunRoot = opts.flags.Runroot
+ }
+ if len(storageOpts.RunRoot) > 50 {
+ return nil, errors.New("the specified runroot is longer than 50 characters")
+ }
+ if fs.Changed("storage-driver") {
+ storageSet = true
+ storageOpts.GraphDriverName = opts.flags.StorageDriver
+ // Overriding the default storage driver caused GraphDriverOptions from storage.conf to be ignored
+ storageOpts.GraphDriverOptions = []string{}
+ }
+ // This should always be checked after storage-driver is checked
+ if len(opts.flags.StorageOpts) > 0 {
+ storageSet = true
+ storageOpts.GraphDriverOptions = opts.flags.StorageOpts
+ }
+ if opts.migrate {
+ options = append(options, libpod.WithMigrate())
+ if opts.name != "" {
+ options = append(options, libpod.WithMigrateRuntime(opts.name))
+ }
+ }
+
+ if opts.renumber {
+ options = append(options, libpod.WithRenumber())
+ }
+
+ // Only set this if the user changes storage config on the command line
+ if storageSet {
+ options = append(options, libpod.WithStorageConfig(storageOpts))
+ }
+
+ if !storageSet && opts.noStore {
+ options = append(options, libpod.WithNoStore())
+ }
+ // TODO CLI flags for image config?
+ // TODO CLI flag for signature policy?
+
+ if len(opts.flags.Namespace) > 0 {
+ options = append(options, libpod.WithNamespace(opts.flags.Namespace))
+ }
+
+ if fs.Changed("runtime") {
+ options = append(options, libpod.WithOCIRuntime(opts.flags.Runtime))
+ }
+
+ if fs.Changed("conmon") {
+ options = append(options, libpod.WithConmonPath(opts.flags.ConmonPath))
+ }
+ if fs.Changed("tmpdir") {
+ options = append(options, libpod.WithTmpDir(opts.flags.TmpDir))
+ }
+ if fs.Changed("network-cmd-path") {
+ options = append(options, libpod.WithNetworkCmdPath(opts.flags.NetworkCmdPath))
+ }
+
+ if fs.Changed("events-backend") {
+ options = append(options, libpod.WithEventsLogger(opts.flags.EventsBackend))
+ }
+
+ if fs.Changed("cgroup-manager") {
+ options = append(options, libpod.WithCgroupManager(opts.flags.CGroupManager))
+ } else {
+ unified, err := cgroups.IsCgroup2UnifiedMode()
+ if err != nil {
+ return nil, err
+ }
+ if rootless.IsRootless() && !unified {
+ options = append(options, libpod.WithCgroupManager("cgroupfs"))
+ }
+ }
+
+ // TODO flag to set libpod static dir?
+ // TODO flag to set libpod tmp dir?
+
+ if fs.Changed("cni-config-dir") {
+ options = append(options, libpod.WithCNIConfigDir(opts.flags.CniConfigDir))
+ }
+ if fs.Changed("default-mounts-file") {
+ options = append(options, libpod.WithDefaultMountsFile(opts.flags.DefaultMountsFile))
+ }
+ if fs.Changed("hooks-dir") {
+ options = append(options, libpod.WithHooksDir(opts.flags.HooksDir...))
+ }
+
+ // TODO flag to set CNI plugins dir?
+
+ // TODO I don't think these belong here?
+ // Will follow up with a different PR to address
+ //
+ // Pod create options
+
+ infraImageFlag := fs.Lookup("infra-image")
+ if infraImageFlag != nil && infraImageFlag.Changed {
+ infraImage, _ := fs.GetString("infra-image")
+ options = append(options, libpod.WithDefaultInfraImage(infraImage))
+ }
+
+ infraCommandFlag := fs.Lookup("infra-command")
+ if infraCommandFlag != nil && infraImageFlag.Changed {
+ infraCommand, _ := fs.GetString("infra-command")
+ options = append(options, libpod.WithDefaultInfraCommand(infraCommand))
+ }
+
+ if !opts.withFDS {
+ options = append(options, libpod.WithEnableSDNotify())
+ }
+ if fs.Changed("config") {
+ return libpod.NewRuntimeFromConfig(ctx, opts.flags.Config, options...)
+ }
+ return libpod.NewRuntime(ctx, options...)
+}
+
+// ParseIDMapping takes idmappings and subuid and subgid maps and returns a storage mapping
+func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []string, subUIDMap, subGIDMap string) (*storage.IDMappingOptions, error) {
+ options := storage.IDMappingOptions{
+ HostUIDMapping: true,
+ HostGIDMapping: true,
+ }
+
+ if mode.IsKeepID() {
+ if len(uidMapSlice) > 0 || len(gidMapSlice) > 0 {
+ return nil, errors.New("cannot specify custom mappings with --userns=keep-id")
+ }
+ if len(subUIDMap) > 0 || len(subGIDMap) > 0 {
+ return nil, errors.New("cannot specify subuidmap or subgidmap with --userns=keep-id")
+ }
+ if rootless.IsRootless() {
+ min := func(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+ }
+
+ uid := rootless.GetRootlessUID()
+ gid := rootless.GetRootlessGID()
+
+ uids, gids, err := rootless.GetConfiguredMappings()
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot read mappings")
+ }
+ maxUID, maxGID := 0, 0
+ for _, u := range uids {
+ maxUID += u.Size
+ }
+ for _, g := range gids {
+ maxGID += g.Size
+ }
+
+ options.UIDMap, options.GIDMap = nil, nil
+
+ options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(uid, maxUID)})
+ options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid, HostID: 0, Size: 1})
+ if maxUID > uid {
+ options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid + 1, HostID: uid + 1, Size: maxUID - uid})
+ }
+
+ options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(gid, maxGID)})
+ options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid, HostID: 0, Size: 1})
+ if maxGID > gid {
+ options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid + 1, HostID: gid + 1, Size: maxGID - gid})
+ }
+
+ options.HostUIDMapping = false
+ options.HostGIDMapping = false
+ }
+ // Simply ignore the setting and do not setup an inner namespace for root as it is a no-op
+ return &options, nil
+ }
+
+ if subGIDMap == "" && subUIDMap != "" {
+ subGIDMap = subUIDMap
+ }
+ if subUIDMap == "" && subGIDMap != "" {
+ subUIDMap = subGIDMap
+ }
+ if len(gidMapSlice) == 0 && len(uidMapSlice) != 0 {
+ gidMapSlice = uidMapSlice
+ }
+ if len(uidMapSlice) == 0 && len(gidMapSlice) != 0 {
+ uidMapSlice = gidMapSlice
+ }
+ if len(uidMapSlice) == 0 && subUIDMap == "" && os.Getuid() != 0 {
+ uidMapSlice = []string{fmt.Sprintf("0:%d:1", os.Getuid())}
+ }
+ if len(gidMapSlice) == 0 && subGIDMap == "" && os.Getuid() != 0 {
+ gidMapSlice = []string{fmt.Sprintf("0:%d:1", os.Getgid())}
+ }
+
+ if subUIDMap != "" && subGIDMap != "" {
+ mappings, err := idtools.NewIDMappings(subUIDMap, subGIDMap)
+ if err != nil {
+ return nil, err
+ }
+ options.UIDMap = mappings.UIDs()
+ options.GIDMap = mappings.GIDs()
+ }
+ parsedUIDMap, err := idtools.ParseIDMap(uidMapSlice, "UID")
+ if err != nil {
+ return nil, err
+ }
+ parsedGIDMap, err := idtools.ParseIDMap(gidMapSlice, "GID")
+ if err != nil {
+ return nil, err
+ }
+ options.UIDMap = append(options.UIDMap, parsedUIDMap...)
+ options.GIDMap = append(options.GIDMap, parsedGIDMap...)
+ if len(options.UIDMap) > 0 {
+ options.HostUIDMapping = false
+ }
+ if len(options.GIDMap) > 0 {
+ options.HostGIDMapping = false
+ }
+ return &options, nil
+}
diff --git a/pkg/domain/infra/runtime_proxy.go b/pkg/domain/infra/runtime_proxy.go
new file mode 100644
index 000000000..d17b8efa1
--- /dev/null
+++ b/pkg/domain/infra/runtime_proxy.go
@@ -0,0 +1,54 @@
+// +build ABISupport
+
+package infra
+
+import (
+ "context"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/domain/entities"
+ flag "github.com/spf13/pflag"
+)
+
+// ContainerEngine Proxy will be EOL'ed after podmanV2 is separated from libpod repo
+
+type runtime struct {
+ entities.ContainerEngine
+ Libpod *libpod.Runtime
+}
+
+func NewLibpodRuntime(flags flag.FlagSet, opts entities.EngineFlags) (entities.ContainerEngine, error) {
+ r, err := GetRuntime(context.Background(), flags, opts)
+ if err != nil {
+ return nil, err
+ }
+ return &runtime{Libpod: r}, nil
+}
+
+func (r *runtime) ShutdownRuntime(force bool) error {
+ return r.Libpod.Shutdown(force)
+}
+
+func (r *runtime) ContainerDelete(ctx context.Context, opts entities.ContainerDeleteOptions) (*entities.ContainerDeleteReport, error) {
+ panic("implement me")
+}
+
+func (r *runtime) ContainerPrune(ctx context.Context) (*entities.ContainerPruneReport, error) {
+ panic("implement me")
+}
+
+func (r *runtime) PodDelete(ctx context.Context, opts entities.PodPruneOptions) (*entities.PodDeleteReport, error) {
+ panic("implement me")
+}
+
+func (r *runtime) PodPrune(ctx context.Context) (*entities.PodPruneReport, error) {
+ panic("implement me")
+}
+
+func (r *runtime) VolumeDelete(ctx context.Context, opts entities.VolumeDeleteOptions) (*entities.VolumeDeleteReport, error) {
+ panic("implement me")
+}
+
+func (r *runtime) VolumePrune(ctx context.Context) (*entities.VolumePruneReport, error) {
+ panic("implement me")
+}
diff --git a/pkg/domain/infra/runtime_tunnel.go b/pkg/domain/infra/runtime_tunnel.go
new file mode 100644
index 000000000..8a606deaf
--- /dev/null
+++ b/pkg/domain/infra/runtime_tunnel.go
@@ -0,0 +1,35 @@
+// +build !ABISupport
+
+package infra
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/containers/libpod/pkg/bindings"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/domain/infra/tunnel"
+)
+
+func NewContainerEngine(mode entities.EngineMode, opts entities.EngineOptions) (entities.ContainerEngine, error) {
+ switch mode {
+ case entities.ABIMode:
+ return nil, fmt.Errorf("direct runtime not supported")
+ case entities.TunnelMode:
+ ctx, err := bindings.NewConnection(context.Background(), opts.Uri.String(), opts.Identities...)
+ return &tunnel.ContainerEngine{ClientCxt: ctx}, err
+ }
+ return nil, fmt.Errorf("runtime mode '%v' is not supported", mode)
+}
+
+// NewImageEngine factory provides a libpod runtime for image-related operations
+func NewImageEngine(mode entities.EngineMode, opts entities.EngineOptions) (entities.ImageEngine, error) {
+ switch mode {
+ case entities.ABIMode:
+ return nil, fmt.Errorf("direct image runtime not supported")
+ case entities.TunnelMode:
+ ctx, err := bindings.NewConnection(context.Background(), opts.Uri.String(), opts.Identities...)
+ return &tunnel.ImageEngine{ClientCxt: ctx}, err
+ }
+ return nil, fmt.Errorf("runtime mode '%v' is not supported", mode)
+}
diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go
new file mode 100644
index 000000000..718685e57
--- /dev/null
+++ b/pkg/domain/infra/tunnel/images.go
@@ -0,0 +1,81 @@
+package tunnel
+
+import (
+ "context"
+ "net/url"
+
+ images "github.com/containers/libpod/pkg/bindings/images"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/domain/utils"
+)
+
+func (ir *ImageEngine) Delete(ctx context.Context, nameOrId string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) {
+ results, err := images.Remove(ir.ClientCxt, nameOrId, &opts.Force)
+ if err != nil {
+ return nil, err
+ }
+
+ report := entities.ImageDeleteReport{
+ Untagged: nil,
+ Deleted: "",
+ }
+
+ for _, e := range results {
+ if a, ok := e["Deleted"]; ok {
+ report.Deleted = a
+ }
+
+ if a, ok := e["Untagged"]; ok {
+ report.Untagged = append(report.Untagged, a)
+ }
+ }
+ return &report, err
+}
+
+func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) (*entities.ImageListReport, error) {
+ images, err := images.List(ir.ClientCxt, &opts.All, opts.Filters)
+ if err != nil {
+ return nil, err
+ }
+
+ report := entities.ImageListReport{
+ Images: make([]entities.ImageSummary, len(images)),
+ }
+ for i, img := range images {
+ hold := entities.ImageSummary{}
+ if err := utils.DeepCopy(&hold, img); err != nil {
+ return nil, err
+ }
+ report.Images[i] = hold
+ }
+ return &report, nil
+}
+
+func (ir *ImageEngine) History(ctx context.Context, nameOrId string, opts entities.ImageHistoryOptions) (*entities.ImageHistoryReport, error) {
+ results, err := images.History(ir.ClientCxt, nameOrId)
+ if err != nil {
+ return nil, err
+ }
+
+ history := entities.ImageHistoryReport{
+ Layers: make([]entities.ImageHistoryLayer, len(results)),
+ }
+
+ for i, layer := range results {
+ hold := entities.ImageHistoryLayer{}
+ _ = utils.DeepCopy(&hold, layer)
+ history.Layers[i] = hold
+ }
+ return &history, nil
+}
+
+func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) {
+ results, err := images.Prune(ir.ClientCxt, url.Values{})
+ if err != nil {
+ return nil, err
+ }
+
+ report := entities.ImagePruneReport{}
+ copy(report.Report.Id, results)
+ return &report, nil
+}
diff --git a/pkg/domain/infra/tunnel/runtime.go b/pkg/domain/infra/tunnel/runtime.go
new file mode 100644
index 000000000..af433a6d9
--- /dev/null
+++ b/pkg/domain/infra/tunnel/runtime.go
@@ -0,0 +1,45 @@
+package tunnel
+
+import (
+ "context"
+
+ "github.com/containers/libpod/pkg/domain/entities"
+)
+
+// Image-related runtime using an ssh-tunnel to utilize Podman service
+type ImageEngine struct {
+ ClientCxt context.Context
+}
+
+// Container-related runtime using an ssh-tunnel to utilize Podman service
+type ContainerEngine struct {
+ ClientCxt context.Context
+}
+
+func (r *ContainerEngine) Shutdown(force bool) error {
+ return nil
+}
+
+func (r *ContainerEngine) ContainerDelete(ctx context.Context, opts entities.ContainerDeleteOptions) (*entities.ContainerDeleteReport, error) {
+ panic("implement me")
+}
+
+func (r *ContainerEngine) ContainerPrune(ctx context.Context) (*entities.ContainerPruneReport, error) {
+ panic("implement me")
+}
+
+func (r *ContainerEngine) PodDelete(ctx context.Context, opts entities.PodPruneOptions) (*entities.PodDeleteReport, error) {
+ panic("implement me")
+}
+
+func (r *ContainerEngine) PodPrune(ctx context.Context) (*entities.PodPruneReport, error) {
+ panic("implement me")
+}
+
+func (r *ContainerEngine) VolumeDelete(ctx context.Context, opts entities.VolumeDeleteOptions) (*entities.VolumeDeleteReport, error) {
+ panic("implement me")
+}
+
+func (r *ContainerEngine) VolumePrune(ctx context.Context) (*entities.VolumePruneReport, error) {
+ panic("implement me")
+}
diff --git a/pkg/domain/utils/utils.go b/pkg/domain/utils/utils.go
new file mode 100644
index 000000000..c17769f62
--- /dev/null
+++ b/pkg/domain/utils/utils.go
@@ -0,0 +1,41 @@
+package utils
+
+import (
+ "net/url"
+ "strings"
+
+ jsoniter "github.com/json-iterator/go"
+)
+
+var json = jsoniter.ConfigCompatibleWithStandardLibrary
+
+// DeepCopy does a deep copy of a structure
+// Error checking of parameters delegated to json engine
+var DeepCopy = func(dst interface{}, src interface{}) error {
+ payload, err := json.Marshal(src)
+ if err != nil {
+ return err
+ }
+
+ err = json.Unmarshal(payload, dst)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func ToLibpodFilters(f url.Values) (filters []string) {
+ for k, v := range f {
+ filters = append(filters, k+"="+v[0])
+ }
+ return
+}
+
+func ToUrlValues(f []string) (filters url.Values) {
+ filters = make(url.Values)
+ for _, v := range f {
+ t := strings.SplitN(v, "=", 2)
+ filters.Add(t[0], t[1])
+ }
+ return
+}
diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c
index db898e706..6643bfbbf 100644
--- a/pkg/rootless/rootless_linux.c
+++ b/pkg/rootless/rootless_linux.c
@@ -108,10 +108,9 @@ do_pause ()
}
static char **
-get_cmd_line_args (pid_t pid)
+get_cmd_line_args ()
{
int fd;
- char path[PATH_MAX];
char *buffer;
size_t allocated;
size_t used = 0;
@@ -119,11 +118,7 @@ get_cmd_line_args (pid_t pid)
int i, argc = 0;
char **argv;
- if (pid)
- sprintf (path, "/proc/%d/cmdline", pid);
- else
- strcpy (path, "/proc/self/cmdline");
- fd = open (path, O_RDONLY);
+ fd = open ("/proc/self/cmdline", O_RDONLY);
if (fd < 0)
return NULL;
@@ -196,7 +191,7 @@ can_use_shortcut ()
return false;
#endif
- argv = get_cmd_line_args (0);
+ argv = get_cmd_line_args ();
if (argv == NULL)
return false;
@@ -542,7 +537,6 @@ create_pause_process (const char *pause_pid_file_path, char **argv)
int
reexec_userns_join (int userns, int mountns, char *pause_pid_file_path)
{
- pid_t ppid = getpid ();
char uid[16];
char gid[16];
char **argv;
@@ -559,7 +553,7 @@ reexec_userns_join (int userns, int mountns, char *pause_pid_file_path)
sprintf (uid, "%d", geteuid ());
sprintf (gid, "%d", getegid ());
- argv = get_cmd_line_args (ppid);
+ argv = get_cmd_line_args ();
if (argv == NULL)
{
fprintf (stderr, "cannot read argv: %s\n", strerror (errno));
@@ -724,7 +718,6 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re
int ret;
pid_t pid;
char b;
- pid_t ppid = getpid ();
char **argv;
char uid[16];
char gid[16];
@@ -801,7 +794,7 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re
_exit (EXIT_FAILURE);
}
- argv = get_cmd_line_args (ppid);
+ argv = get_cmd_line_args ();
if (argv == NULL)
{
fprintf (stderr, "cannot read argv: %s\n", strerror (errno));
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index 9b2255d61..12dfed8c3 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -144,6 +144,7 @@ type CreateConfig struct {
InitPath string //init-path
Image string
ImageID string
+ RawImageName string
BuiltinImgVolumes map[string]struct{} // volumes defined in the image config
ImageVolumeType string // how to handle the image volume, either bind, tmpfs, or ignore
Interactive bool //interactive
@@ -348,7 +349,7 @@ func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *l
options = append(options, nsOpts...)
// Gather up the options for NewContainer which consist of With... funcs
- options = append(options, libpod.WithRootFSFromImage(c.ImageID, c.Image))
+ options = append(options, libpod.WithRootFSFromImage(c.ImageID, c.Image, c.RawImageName))
options = append(options, libpod.WithConmonPidFile(c.ConmonPidFile))
options = append(options, libpod.WithLabels(c.Labels))
options = append(options, libpod.WithShmSize(c.Resources.ShmSize))
diff --git a/pkg/specgen/create.go b/pkg/specgen/create.go
index 99a99083b..aefbe7405 100644
--- a/pkg/specgen/create.go
+++ b/pkg/specgen/create.go
@@ -36,7 +36,7 @@ func (s *SpecGenerator) MakeContainer(rt *libpod.Runtime) (*libpod.Container, er
return nil, err
}
- options = append(options, libpod.WithRootFSFromImage(newImage.ID(), s.Image))
+ options = append(options, libpod.WithRootFSFromImage(newImage.ID(), s.Image, s.RawImageName))
runtimeSpec, err := s.toOCISpec(rt, newImage)
if err != nil {
diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go
index e1dfe4dc5..7a430652a 100644
--- a/pkg/specgen/specgen.go
+++ b/pkg/specgen/specgen.go
@@ -143,6 +143,10 @@ type ContainerStorageConfig struct {
// Conflicts with Rootfs.
// At least one of Image or Rootfs must be specified.
Image string `json:"image"`
+ // RawImageName is the unprocessed and not-normalized user-specified image
+ // name. One use case for having this data at hand are auto-updates where
+ // the _exact_ user input is needed in order to look-up the correct image.
+ RawImageName string `json:"raw_image_name,omitempty"`
// Rootfs is the path to a directory that will be used as the
// container's root filesystem. No modification will be made to the
// directory, it will be directly mounted into the container as root.
diff --git a/pkg/systemd/dbus.go b/pkg/systemd/dbus.go
new file mode 100644
index 000000000..df24667a1
--- /dev/null
+++ b/pkg/systemd/dbus.go
@@ -0,0 +1,47 @@
+package systemd
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "strconv"
+
+ "github.com/containers/libpod/pkg/rootless"
+ "github.com/coreos/go-systemd/v22/dbus"
+ godbus "github.com/godbus/dbus/v5"
+)
+
+func dbusAuthRootlessConnection(createBus func(opts ...godbus.ConnOption) (*godbus.Conn, error)) (*godbus.Conn, error) {
+ conn, err := createBus()
+ if err != nil {
+ return nil, err
+ }
+
+ methods := []godbus.Auth{godbus.AuthExternal(strconv.Itoa(rootless.GetRootlessUID()))}
+
+ err = conn.Auth(methods)
+ if err != nil {
+ conn.Close()
+ return nil, err
+ }
+
+ return conn, nil
+}
+
+func newRootlessConnection() (*dbus.Conn, error) {
+ return dbus.NewConnection(func() (*godbus.Conn, error) {
+ return dbusAuthRootlessConnection(func(opts ...godbus.ConnOption) (*godbus.Conn, error) {
+ path := filepath.Join(os.Getenv("XDG_RUNTIME_DIR"), "systemd/private")
+ return godbus.Dial(fmt.Sprintf("unix:path=%s", path))
+ })
+ })
+}
+
+// ConnectToDBUS returns a DBUS connection. It works both as root and non-root
+// users.
+func ConnectToDBUS() (*dbus.Conn, error) {
+ if rootless.IsRootless() {
+ return newRootlessConnection()
+ }
+ return dbus.NewSystemdConnection()
+}
diff --git a/pkg/systemd/generate/systemdgen.go b/pkg/systemd/generate/systemdgen.go
index 4cd7745c0..eb15d4927 100644
--- a/pkg/systemd/generate/systemdgen.go
+++ b/pkg/systemd/generate/systemdgen.go
@@ -16,6 +16,10 @@ import (
"github.com/sirupsen/logrus"
)
+// EnvVariable "PODMAN_SYSTEMD_UNIT" is set in all generated systemd units and
+// is set to the unit's (unique) name.
+const EnvVariable = "PODMAN_SYSTEMD_UNIT"
+
// ContainerInfo contains data required for generating a container's systemd
// unit file.
type ContainerInfo struct {
@@ -57,6 +61,8 @@ type ContainerInfo struct {
// RunCommand is a post-processed variant of CreateCommand and used for
// the ExecStart field in generic unit files.
RunCommand string
+ // EnvVariable is generate.EnvVariable and must not be set.
+ EnvVariable string
}
var restartPolicies = []string{"no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", "always"}
@@ -94,6 +100,7 @@ Before={{- range $index, $value := .RequiredServices -}}{{if $index}} {{end}}{{
{{- end}}
[Service]
+Environment={{.EnvVariable}}=%n
Restart={{.RestartPolicy}}
{{- if .New}}
ExecStartPre=/usr/bin/rm -f %t/%n-pid %t/%n-cid
@@ -138,6 +145,8 @@ func CreateContainerSystemdUnit(info *ContainerInfo, opts Options) (string, erro
info.Executable = executable
}
+ info.EnvVariable = EnvVariable
+
// Assemble the ExecStart command when creating a new container.
//
// Note that we cannot catch all corner cases here such that users
diff --git a/pkg/systemd/generate/systemdgen_test.go b/pkg/systemd/generate/systemdgen_test.go
index bbdccdcf8..3269405a6 100644
--- a/pkg/systemd/generate/systemdgen_test.go
+++ b/pkg/systemd/generate/systemdgen_test.go
@@ -44,6 +44,7 @@ Wants=network.target
After=network-online.target
[Service]
+Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=always
ExecStart=/usr/bin/podman start 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401
ExecStop=/usr/bin/podman stop -t 10 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401
@@ -64,6 +65,7 @@ Wants=network.target
After=network-online.target
[Service]
+Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=always
ExecStart=/usr/bin/podman start foobar
ExecStop=/usr/bin/podman stop -t 10 foobar
@@ -88,6 +90,7 @@ BindsTo=a.service b.service c.service pod.service
After=a.service b.service c.service pod.service
[Service]
+Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=always
ExecStart=/usr/bin/podman start foobar
ExecStop=/usr/bin/podman stop -t 10 foobar
@@ -110,6 +113,7 @@ Requires=container-1.service container-2.service
Before=container-1.service container-2.service
[Service]
+Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=always
ExecStart=/usr/bin/podman start jadda-jadda-infra
ExecStop=/usr/bin/podman stop -t 10 jadda-jadda-infra
@@ -130,6 +134,7 @@ Wants=network.target
After=network-online.target
[Service]
+Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=always
ExecStartPre=/usr/bin/rm -f %t/%n-pid %t/%n-cid
ExecStart=/usr/bin/podman run --conmon-pidfile %t/%n-pid --cidfile %t/%n-cid --cgroups=no-conmon -d --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN
@@ -152,6 +157,7 @@ Wants=network.target
After=network-online.target
[Service]
+Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=always
ExecStartPre=/usr/bin/rm -f %t/%n-pid %t/%n-cid
ExecStart=/usr/bin/podman run --conmon-pidfile %t/%n-pid --cidfile %t/%n-cid --cgroups=no-conmon --detach --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN
@@ -174,6 +180,7 @@ Wants=network.target
After=network-online.target
[Service]
+Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=always
ExecStartPre=/usr/bin/rm -f %t/%n-pid %t/%n-cid
ExecStart=/usr/bin/podman run --conmon-pidfile %t/%n-pid --cidfile %t/%n-cid --cgroups=no-conmon -d awesome-image:latest
diff --git a/pkg/varlinkapi/system.go b/pkg/varlinkapi/system.go
index 50aaaaa44..e88d010c5 100644
--- a/pkg/varlinkapi/system.go
+++ b/pkg/varlinkapi/system.go
@@ -10,7 +10,7 @@ import (
"time"
"github.com/containers/image/v5/pkg/sysregistriesv2"
- "github.com/containers/libpod/cmd/podman/varlink"
+ iopodman "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod/define"
"github.com/sirupsen/logrus"
)
diff --git a/test/e2e/search_test.go b/test/e2e/search_test.go
index 6d762d338..9ba0241fe 100644
--- a/test/e2e/search_test.go
+++ b/test/e2e/search_test.go
@@ -5,13 +5,14 @@ package integration
import (
"bytes"
"fmt"
- . "github.com/containers/libpod/test/utils"
- . "github.com/onsi/ginkgo"
- . "github.com/onsi/gomega"
"io/ioutil"
"os"
"strconv"
"text/template"
+
+ . "github.com/containers/libpod/test/utils"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
)
type endpoint struct {
diff --git a/vendor/github.com/containerd/containerd/log/context.go b/vendor/github.com/containerd/containerd/log/context.go
new file mode 100644
index 000000000..31f1a3ac0
--- /dev/null
+++ b/vendor/github.com/containerd/containerd/log/context.go
@@ -0,0 +1,90 @@
+/*
+ Copyright The containerd Authors.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+package log
+
+import (
+ "context"
+ "sync/atomic"
+
+ "github.com/sirupsen/logrus"
+)
+
+var (
+ // G is an alias for GetLogger.
+ //
+ // We may want to define this locally to a package to get package tagged log
+ // messages.
+ G = GetLogger
+
+ // L is an alias for the standard logger.
+ L = logrus.NewEntry(logrus.StandardLogger())
+)
+
+type (
+ loggerKey struct{}
+)
+
+// TraceLevel is the log level for tracing. Trace level is lower than debug level,
+// and is usually used to trace detailed behavior of the program.
+const TraceLevel = logrus.Level(uint32(logrus.DebugLevel + 1))
+
+// RFC3339NanoFixed is time.RFC3339Nano with nanoseconds padded using zeros to
+// ensure the formatted time is always the same number of characters.
+const RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"
+
+// ParseLevel takes a string level and returns the Logrus log level constant.
+// It supports trace level.
+func ParseLevel(lvl string) (logrus.Level, error) {
+ if lvl == "trace" {
+ return TraceLevel, nil
+ }
+ return logrus.ParseLevel(lvl)
+}
+
+// WithLogger returns a new context with the provided logger. Use in
+// combination with logger.WithField(s) for great effect.
+func WithLogger(ctx context.Context, logger *logrus.Entry) context.Context {
+ return context.WithValue(ctx, loggerKey{}, logger)
+}
+
+// GetLogger retrieves the current logger from the context. If no logger is
+// available, the default logger is returned.
+func GetLogger(ctx context.Context) *logrus.Entry {
+ logger := ctx.Value(loggerKey{})
+
+ if logger == nil {
+ return L
+ }
+
+ return logger.(*logrus.Entry)
+}
+
+// Trace logs a message at level Trace with the log entry passed-in.
+func Trace(e *logrus.Entry, args ...interface{}) {
+ level := logrus.Level(atomic.LoadUint32((*uint32)(&e.Logger.Level)))
+ if level >= TraceLevel {
+ e.Debug(args...)
+ }
+}
+
+// Tracef logs a message at level Trace with the log entry passed-in.
+func Tracef(e *logrus.Entry, format string, args ...interface{}) {
+ level := logrus.Level(atomic.LoadUint32((*uint32)(&e.Logger.Level)))
+ if level >= TraceLevel {
+ e.Debugf(format, args...)
+ }
+}
diff --git a/vendor/github.com/containerd/containerd/platforms/compare.go b/vendor/github.com/containerd/containerd/platforms/compare.go
new file mode 100644
index 000000000..3ad22a10d
--- /dev/null
+++ b/vendor/github.com/containerd/containerd/platforms/compare.go
@@ -0,0 +1,229 @@
+/*
+ Copyright The containerd Authors.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+package platforms
+
+import specs "github.com/opencontainers/image-spec/specs-go/v1"
+
+// MatchComparer is able to match and compare platforms to
+// filter and sort platforms.
+type MatchComparer interface {
+ Matcher
+
+ Less(specs.Platform, specs.Platform) bool
+}
+
+// Only returns a match comparer for a single platform
+// using default resolution logic for the platform.
+//
+// For ARMv8, will also match ARMv7, ARMv6 and ARMv5 (for 32bit runtimes)
+// For ARMv7, will also match ARMv6 and ARMv5
+// For ARMv6, will also match ARMv5
+func Only(platform specs.Platform) MatchComparer {
+ platform = Normalize(platform)
+ if platform.Architecture == "arm" {
+ if platform.Variant == "v8" {
+ return orderedPlatformComparer{
+ matchers: []Matcher{
+ &matcher{
+ Platform: platform,
+ },
+ &matcher{
+ Platform: specs.Platform{
+ Architecture: platform.Architecture,
+ OS: platform.OS,
+ OSVersion: platform.OSVersion,
+ OSFeatures: platform.OSFeatures,
+ Variant: "v7",
+ },
+ },
+ &matcher{
+ Platform: specs.Platform{
+ Architecture: platform.Architecture,
+ OS: platform.OS,
+ OSVersion: platform.OSVersion,
+ OSFeatures: platform.OSFeatures,
+ Variant: "v6",
+ },
+ },
+ &matcher{
+ Platform: specs.Platform{
+ Architecture: platform.Architecture,
+ OS: platform.OS,
+ OSVersion: platform.OSVersion,
+ OSFeatures: platform.OSFeatures,
+ Variant: "v5",
+ },
+ },
+ },
+ }
+ }
+ if platform.Variant == "v7" {
+ return orderedPlatformComparer{
+ matchers: []Matcher{
+ &matcher{
+ Platform: platform,
+ },
+ &matcher{
+ Platform: specs.Platform{
+ Architecture: platform.Architecture,
+ OS: platform.OS,
+ OSVersion: platform.OSVersion,
+ OSFeatures: platform.OSFeatures,
+ Variant: "v6",
+ },
+ },
+ &matcher{
+ Platform: specs.Platform{
+ Architecture: platform.Architecture,
+ OS: platform.OS,
+ OSVersion: platform.OSVersion,
+ OSFeatures: platform.OSFeatures,
+ Variant: "v5",
+ },
+ },
+ },
+ }
+ }
+ if platform.Variant == "v6" {
+ return orderedPlatformComparer{
+ matchers: []Matcher{
+ &matcher{
+ Platform: platform,
+ },
+ &matcher{
+ Platform: specs.Platform{
+ Architecture: platform.Architecture,
+ OS: platform.OS,
+ OSVersion: platform.OSVersion,
+ OSFeatures: platform.OSFeatures,
+ Variant: "v5",
+ },
+ },
+ },
+ }
+ }
+ }
+
+ return singlePlatformComparer{
+ Matcher: &matcher{
+ Platform: platform,
+ },
+ }
+}
+
+// Ordered returns a platform MatchComparer which matches any of the platforms
+// but orders them in order they are provided.
+func Ordered(platforms ...specs.Platform) MatchComparer {
+ matchers := make([]Matcher, len(platforms))
+ for i := range platforms {
+ matchers[i] = NewMatcher(platforms[i])
+ }
+ return orderedPlatformComparer{
+ matchers: matchers,
+ }
+}
+
+// Any returns a platform MatchComparer which matches any of the platforms
+// with no preference for ordering.
+func Any(platforms ...specs.Platform) MatchComparer {
+ matchers := make([]Matcher, len(platforms))
+ for i := range platforms {
+ matchers[i] = NewMatcher(platforms[i])
+ }
+ return anyPlatformComparer{
+ matchers: matchers,
+ }
+}
+
+// All is a platform MatchComparer which matches all platforms
+// with preference for ordering.
+var All MatchComparer = allPlatformComparer{}
+
+type singlePlatformComparer struct {
+ Matcher
+}
+
+func (c singlePlatformComparer) Less(p1, p2 specs.Platform) bool {
+ return c.Match(p1) && !c.Match(p2)
+}
+
+type orderedPlatformComparer struct {
+ matchers []Matcher
+}
+
+func (c orderedPlatformComparer) Match(platform specs.Platform) bool {
+ for _, m := range c.matchers {
+ if m.Match(platform) {
+ return true
+ }
+ }
+ return false
+}
+
+func (c orderedPlatformComparer) Less(p1 specs.Platform, p2 specs.Platform) bool {
+ for _, m := range c.matchers {
+ p1m := m.Match(p1)
+ p2m := m.Match(p2)
+ if p1m && !p2m {
+ return true
+ }
+ if p1m || p2m {
+ return false
+ }
+ }
+ return false
+}
+
+type anyPlatformComparer struct {
+ matchers []Matcher
+}
+
+func (c anyPlatformComparer) Match(platform specs.Platform) bool {
+ for _, m := range c.matchers {
+ if m.Match(platform) {
+ return true
+ }
+ }
+ return false
+}
+
+func (c anyPlatformComparer) Less(p1, p2 specs.Platform) bool {
+ var p1m, p2m bool
+ for _, m := range c.matchers {
+ if !p1m && m.Match(p1) {
+ p1m = true
+ }
+ if !p2m && m.Match(p2) {
+ p2m = true
+ }
+ if p1m && p2m {
+ return false
+ }
+ }
+ // If one matches, and the other does, sort match first
+ return p1m && !p2m
+}
+
+type allPlatformComparer struct{}
+
+func (allPlatformComparer) Match(specs.Platform) bool {
+ return true
+}
+
+func (allPlatformComparer) Less(specs.Platform, specs.Platform) bool {
+ return false
+}
diff --git a/vendor/github.com/containerd/containerd/platforms/cpuinfo.go b/vendor/github.com/containerd/containerd/platforms/cpuinfo.go
new file mode 100644
index 000000000..69b336d67
--- /dev/null
+++ b/vendor/github.com/containerd/containerd/platforms/cpuinfo.go
@@ -0,0 +1,117 @@
+/*
+ Copyright The containerd Authors.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+package platforms
+
+import (
+ "bufio"
+ "os"
+ "runtime"
+ "strings"
+
+ "github.com/containerd/containerd/errdefs"
+ "github.com/containerd/containerd/log"
+ "github.com/pkg/errors"
+)
+
+// Present the ARM instruction set architecture, eg: v7, v8
+var cpuVariant string
+
+func init() {
+ if isArmArch(runtime.GOARCH) {
+ cpuVariant = getCPUVariant()
+ } else {
+ cpuVariant = ""
+ }
+}
+
+// For Linux, the kernel has already detected the ABI, ISA and Features.
+// So we don't need to access the ARM registers to detect platform information
+// by ourselves. We can just parse these information from /proc/cpuinfo
+func getCPUInfo(pattern string) (info string, err error) {
+ if !isLinuxOS(runtime.GOOS) {
+ return "", errors.Wrapf(errdefs.ErrNotImplemented, "getCPUInfo for OS %s", runtime.GOOS)
+ }
+
+ cpuinfo, err := os.Open("/proc/cpuinfo")
+ if err != nil {
+ return "", err
+ }
+ defer cpuinfo.Close()
+
+ // Start to Parse the Cpuinfo line by line. For SMP SoC, we parse
+ // the first core is enough.
+ scanner := bufio.NewScanner(cpuinfo)
+ for scanner.Scan() {
+ newline := scanner.Text()
+ list := strings.Split(newline, ":")
+
+ if len(list) > 1 && strings.EqualFold(strings.TrimSpace(list[0]), pattern) {
+ return strings.TrimSpace(list[1]), nil
+ }
+ }
+
+ // Check whether the scanner encountered errors
+ err = scanner.Err()
+ if err != nil {
+ return "", err
+ }
+
+ return "", errors.Wrapf(errdefs.ErrNotFound, "getCPUInfo for pattern: %s", pattern)
+}
+
+func getCPUVariant() string {
+ if runtime.GOOS == "windows" {
+ // Windows only supports v7 for ARM32 and v8 for ARM64 and so we can use
+ // runtime.GOARCH to determine the variants
+ var variant string
+ switch runtime.GOARCH {
+ case "arm64":
+ variant = "v8"
+ case "arm":
+ variant = "v7"
+ default:
+ variant = "unknown"
+ }
+
+ return variant
+ }
+
+ variant, err := getCPUInfo("Cpu architecture")
+ if err != nil {
+ log.L.WithError(err).Error("failure getting variant")
+ return ""
+ }
+
+ switch variant {
+ case "8", "AArch64":
+ variant = "v8"
+ case "7", "7M", "?(12)", "?(13)", "?(14)", "?(15)", "?(16)", "?(17)":
+ variant = "v7"
+ case "6", "6TEJ":
+ variant = "v6"
+ case "5", "5T", "5TE", "5TEJ":
+ variant = "v5"
+ case "4", "4T":
+ variant = "v4"
+ case "3":
+ variant = "v3"
+ default:
+ variant = "unknown"
+ }
+
+ return variant
+}
diff --git a/vendor/github.com/containerd/containerd/platforms/database.go b/vendor/github.com/containerd/containerd/platforms/database.go
new file mode 100644
index 000000000..6ede94061
--- /dev/null
+++ b/vendor/github.com/containerd/containerd/platforms/database.go
@@ -0,0 +1,114 @@
+/*
+ Copyright The containerd Authors.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+package platforms
+
+import (
+ "runtime"
+ "strings"
+)
+
+// isLinuxOS returns true if the operating system is Linux.
+//
+// The OS value should be normalized before calling this function.
+func isLinuxOS(os string) bool {
+ return os == "linux"
+}
+
+// These function are generated from https://golang.org/src/go/build/syslist.go.
+//
+// We use switch statements because they are slightly faster than map lookups
+// and use a little less memory.
+
+// isKnownOS returns true if we know about the operating system.
+//
+// The OS value should be normalized before calling this function.
+func isKnownOS(os string) bool {
+ switch os {
+ case "aix", "android", "darwin", "dragonfly", "freebsd", "hurd", "illumos", "js", "linux", "nacl", "netbsd", "openbsd", "plan9", "solaris", "windows", "zos":
+ return true
+ }
+ return false
+}
+
+// isArmArch returns true if the architecture is ARM.
+//
+// The arch value should be normalized before being passed to this function.
+func isArmArch(arch string) bool {
+ switch arch {
+ case "arm", "arm64":
+ return true
+ }
+ return false
+}
+
+// isKnownArch returns true if we know about the architecture.
+//
+// The arch value should be normalized before being passed to this function.
+func isKnownArch(arch string) bool {
+ switch arch {
+ case "386", "amd64", "amd64p32", "arm", "armbe", "arm64", "arm64be", "ppc64", "ppc64le", "mips", "mipsle", "mips64", "mips64le", "mips64p32", "mips64p32le", "ppc", "riscv", "riscv64", "s390", "s390x", "sparc", "sparc64", "wasm":
+ return true
+ }
+ return false
+}
+
+func normalizeOS(os string) string {
+ if os == "" {
+ return runtime.GOOS
+ }
+ os = strings.ToLower(os)
+
+ switch os {
+ case "macos":
+ os = "darwin"
+ }
+ return os
+}
+
+// normalizeArch normalizes the architecture.
+func normalizeArch(arch, variant string) (string, string) {
+ arch, variant = strings.ToLower(arch), strings.ToLower(variant)
+ switch arch {
+ case "i386":
+ arch = "386"
+ variant = ""
+ case "x86_64", "x86-64":
+ arch = "amd64"
+ variant = ""
+ case "aarch64", "arm64":
+ arch = "arm64"
+ switch variant {
+ case "8", "v8":
+ variant = ""
+ }
+ case "armhf":
+ arch = "arm"
+ variant = "v7"
+ case "armel":
+ arch = "arm"
+ variant = "v6"
+ case "arm":
+ switch variant {
+ case "", "7":
+ variant = "v7"
+ case "5", "6", "8":
+ variant = "v" + variant
+ }
+ }
+
+ return arch, variant
+}
diff --git a/vendor/github.com/containerd/containerd/platforms/defaults.go b/vendor/github.com/containerd/containerd/platforms/defaults.go
new file mode 100644
index 000000000..a14d80e58
--- /dev/null
+++ b/vendor/github.com/containerd/containerd/platforms/defaults.go
@@ -0,0 +1,38 @@
+/*
+ Copyright The containerd Authors.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+package platforms
+
+import (
+ "runtime"
+
+ specs "github.com/opencontainers/image-spec/specs-go/v1"
+)
+
+// DefaultString returns the default string specifier for the platform.
+func DefaultString() string {
+ return Format(DefaultSpec())
+}
+
+// DefaultSpec returns the current platform's default platform specification.
+func DefaultSpec() specs.Platform {
+ return specs.Platform{
+ OS: runtime.GOOS,
+ Architecture: runtime.GOARCH,
+ // The Variant field will be empty if arch != ARM.
+ Variant: cpuVariant,
+ }
+}
diff --git a/vendor/github.com/containerd/containerd/platforms/defaults_unix.go b/vendor/github.com/containerd/containerd/platforms/defaults_unix.go
new file mode 100644
index 000000000..e8a7d5ffa
--- /dev/null
+++ b/vendor/github.com/containerd/containerd/platforms/defaults_unix.go
@@ -0,0 +1,24 @@
+// +build !windows
+
+/*
+ Copyright The containerd Authors.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+package platforms
+
+// Default returns the default matcher for the platform.
+func Default() MatchComparer {
+ return Only(DefaultSpec())
+}
diff --git a/vendor/github.com/containerd/containerd/platforms/defaults_windows.go b/vendor/github.com/containerd/containerd/platforms/defaults_windows.go
new file mode 100644
index 000000000..0defbd36c
--- /dev/null
+++ b/vendor/github.com/containerd/containerd/platforms/defaults_windows.go
@@ -0,0 +1,31 @@
+// +build windows
+
+/*
+ Copyright The containerd Authors.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+package platforms
+
+import (
+ specs "github.com/opencontainers/image-spec/specs-go/v1"
+)
+
+// Default returns the default matcher for the platform.
+func Default() MatchComparer {
+ return Ordered(DefaultSpec(), specs.Platform{
+ OS: "linux",
+ Architecture: "amd64",
+ })
+}
diff --git a/vendor/github.com/containerd/containerd/platforms/platforms.go b/vendor/github.com/containerd/containerd/platforms/platforms.go
new file mode 100644
index 000000000..d2b73ac3d
--- /dev/null
+++ b/vendor/github.com/containerd/containerd/platforms/platforms.go
@@ -0,0 +1,279 @@
+/*
+ Copyright The containerd Authors.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+// Package platforms provides a toolkit for normalizing, matching and
+// specifying container platforms.
+//
+// Centered around OCI platform specifications, we define a string-based
+// specifier syntax that can be used for user input. With a specifier, users
+// only need to specify the parts of the platform that are relevant to their
+// context, providing an operating system or architecture or both.
+//
+// How do I use this package?
+//
+// The vast majority of use cases should simply use the match function with
+// user input. The first step is to parse a specifier into a matcher:
+//
+// m, err := Parse("linux")
+// if err != nil { ... }
+//
+// Once you have a matcher, use it to match against the platform declared by a
+// component, typically from an image or runtime. Since extracting an images
+// platform is a little more involved, we'll use an example against the
+// platform default:
+//
+// if ok := m.Match(Default()); !ok { /* doesn't match */ }
+//
+// This can be composed in loops for resolving runtimes or used as a filter for
+// fetch and select images.
+//
+// More details of the specifier syntax and platform spec follow.
+//
+// Declaring Platform Support
+//
+// Components that have strict platform requirements should use the OCI
+// platform specification to declare their support. Typically, this will be
+// images and runtimes that should make these declaring which platform they
+// support specifically. This looks roughly as follows:
+//
+// type Platform struct {
+// Architecture string
+// OS string
+// Variant string
+// }
+//
+// Most images and runtimes should at least set Architecture and OS, according
+// to their GOARCH and GOOS values, respectively (follow the OCI image
+// specification when in doubt). ARM should set variant under certain
+// discussions, which are outlined below.
+//
+// Platform Specifiers
+//
+// While the OCI platform specifications provide a tool for components to
+// specify structured information, user input typically doesn't need the full
+// context and much can be inferred. To solve this problem, we introduced
+// "specifiers". A specifier has the format
+// `<os>|<arch>|<os>/<arch>[/<variant>]`. The user can provide either the
+// operating system or the architecture or both.
+//
+// An example of a common specifier is `linux/amd64`. If the host has a default
+// of runtime that matches this, the user can simply provide the component that
+// matters. For example, if a image provides amd64 and arm64 support, the
+// operating system, `linux` can be inferred, so they only have to provide
+// `arm64` or `amd64`. Similar behavior is implemented for operating systems,
+// where the architecture may be known but a runtime may support images from
+// different operating systems.
+//
+// Normalization
+//
+// Because not all users are familiar with the way the Go runtime represents
+// platforms, several normalizations have been provided to make this package
+// easier to user.
+//
+// The following are performed for architectures:
+//
+// Value Normalized
+// aarch64 arm64
+// armhf arm
+// armel arm/v6
+// i386 386
+// x86_64 amd64
+// x86-64 amd64
+//
+// We also normalize the operating system `macos` to `darwin`.
+//
+// ARM Support
+//
+// To qualify ARM architecture, the Variant field is used to qualify the arm
+// version. The most common arm version, v7, is represented without the variant
+// unless it is explicitly provided. This is treated as equivalent to armhf. A
+// previous architecture, armel, will be normalized to arm/v6.
+//
+// While these normalizations are provided, their support on arm platforms has
+// not yet been fully implemented and tested.
+package platforms
+
+import (
+ "regexp"
+ "runtime"
+ "strconv"
+ "strings"
+
+ "github.com/containerd/containerd/errdefs"
+ specs "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/pkg/errors"
+)
+
+var (
+ specifierRe = regexp.MustCompile(`^[A-Za-z0-9_-]+$`)
+)
+
+// Matcher matches platforms specifications, provided by an image or runtime.
+type Matcher interface {
+ Match(platform specs.Platform) bool
+}
+
+// NewMatcher returns a simple matcher based on the provided platform
+// specification. The returned matcher only looks for equality based on os,
+// architecture and variant.
+//
+// One may implement their own matcher if this doesn't provide the required
+// functionality.
+//
+// Applications should opt to use `Match` over directly parsing specifiers.
+func NewMatcher(platform specs.Platform) Matcher {
+ return &matcher{
+ Platform: Normalize(platform),
+ }
+}
+
+type matcher struct {
+ specs.Platform
+}
+
+func (m *matcher) Match(platform specs.Platform) bool {
+ normalized := Normalize(platform)
+ return m.OS == normalized.OS &&
+ m.Architecture == normalized.Architecture &&
+ m.Variant == normalized.Variant
+}
+
+func (m *matcher) String() string {
+ return Format(m.Platform)
+}
+
+// Parse parses the platform specifier syntax into a platform declaration.
+//
+// Platform specifiers are in the format `<os>|<arch>|<os>/<arch>[/<variant>]`.
+// The minimum required information for a platform specifier is the operating
+// system or architecture. If there is only a single string (no slashes), the
+// value will be matched against the known set of operating systems, then fall
+// back to the known set of architectures. The missing component will be
+// inferred based on the local environment.
+func Parse(specifier string) (specs.Platform, error) {
+ if strings.Contains(specifier, "*") {
+ // TODO(stevvooe): need to work out exact wildcard handling
+ return specs.Platform{}, errors.Wrapf(errdefs.ErrInvalidArgument, "%q: wildcards not yet supported", specifier)
+ }
+
+ parts := strings.Split(specifier, "/")
+
+ for _, part := range parts {
+ if !specifierRe.MatchString(part) {
+ return specs.Platform{}, errors.Wrapf(errdefs.ErrInvalidArgument, "%q is an invalid component of %q: platform specifier component must match %q", part, specifier, specifierRe.String())
+ }
+ }
+
+ var p specs.Platform
+ switch len(parts) {
+ case 1:
+ // in this case, we will test that the value might be an OS, then look
+ // it up. If it is not known, we'll treat it as an architecture. Since
+ // we have very little information about the platform here, we are
+ // going to be a little more strict if we don't know about the argument
+ // value.
+ p.OS = normalizeOS(parts[0])
+ if isKnownOS(p.OS) {
+ // picks a default architecture
+ p.Architecture = runtime.GOARCH
+ if p.Architecture == "arm" {
+ // TODO(stevvooe): Resolve arm variant, if not v6 (default)
+ return specs.Platform{}, errors.Wrapf(errdefs.ErrNotImplemented, "arm support not fully implemented")
+ }
+
+ return p, nil
+ }
+
+ p.Architecture, p.Variant = normalizeArch(parts[0], "")
+ if p.Architecture == "arm" && p.Variant == "v7" {
+ p.Variant = ""
+ }
+ if isKnownArch(p.Architecture) {
+ p.OS = runtime.GOOS
+ return p, nil
+ }
+
+ return specs.Platform{}, errors.Wrapf(errdefs.ErrInvalidArgument, "%q: unknown operating system or architecture", specifier)
+ case 2:
+ // In this case, we treat as a regular os/arch pair. We don't care
+ // about whether or not we know of the platform.
+ p.OS = normalizeOS(parts[0])
+ p.Architecture, p.Variant = normalizeArch(parts[1], "")
+ if p.Architecture == "arm" && p.Variant == "v7" {
+ p.Variant = ""
+ }
+
+ return p, nil
+ case 3:
+ // we have a fully specified variant, this is rare
+ p.OS = normalizeOS(parts[0])
+ p.Architecture, p.Variant = normalizeArch(parts[1], parts[2])
+ if p.Architecture == "arm64" && p.Variant == "" {
+ p.Variant = "v8"
+ }
+
+ return p, nil
+ }
+
+ return specs.Platform{}, errors.Wrapf(errdefs.ErrInvalidArgument, "%q: cannot parse platform specifier", specifier)
+}
+
+// MustParse is like Parses but panics if the specifier cannot be parsed.
+// Simplifies initialization of global variables.
+func MustParse(specifier string) specs.Platform {
+ p, err := Parse(specifier)
+ if err != nil {
+ panic("platform: Parse(" + strconv.Quote(specifier) + "): " + err.Error())
+ }
+ return p
+}
+
+// Format returns a string specifier from the provided platform specification.
+func Format(platform specs.Platform) string {
+ if platform.OS == "" {
+ return "unknown"
+ }
+
+ return joinNotEmpty(platform.OS, platform.Architecture, platform.Variant)
+}
+
+func joinNotEmpty(s ...string) string {
+ var ss []string
+ for _, s := range s {
+ if s == "" {
+ continue
+ }
+
+ ss = append(ss, s)
+ }
+
+ return strings.Join(ss, "/")
+}
+
+// Normalize validates and translate the platform to the canonical value.
+//
+// For example, if "Aarch64" is encountered, we change it to "arm64" or if
+// "x86_64" is encountered, it becomes "amd64".
+func Normalize(platform specs.Platform) specs.Platform {
+ platform.OS = normalizeOS(platform.OS)
+ platform.Architecture, platform.Variant = normalizeArch(platform.Architecture, platform.Variant)
+
+ // these fields are deprecated, remove them
+ platform.OSFeatures = nil
+ platform.OSVersion = ""
+
+ return platform
+}
diff --git a/vendor/github.com/containers/buildah/CHANGELOG.md b/vendor/github.com/containers/buildah/CHANGELOG.md
index 3bf97a522..8d1944c65 100644
--- a/vendor/github.com/containers/buildah/CHANGELOG.md
+++ b/vendor/github.com/containers/buildah/CHANGELOG.md
@@ -2,6 +2,22 @@
# Changelog
+## v1.14.3 (2020-03-17)
+ Update containers/storage to v1.16.5
+ Bump github.com/containers/storage from 1.16.2 to 1.16.4
+ Bump github.com/openshift/imagebuilder from 1.1.1 to 1.1.2
+ Update github.com/openshift/imagebuilder vendoring
+ Update unshare man page to fix script example
+ Fix compilation errors on non linux platforms
+ Bump containers/common and opencontainers/selinux versions
+ Add tests for volume ownership
+ Preserve volume uid and gid through subsequent commands
+ Fix FORWARD_NULL errors found by Coverity
+ Bump github.com/containers/storage from 1.16.1 to 1.16.2
+ Fix errors found by codespell
+ Bump back to v1.15.0-dev
+ Add Pull Request Template
+
## v1.14.2 (2020-03-03)
Add Buildah pull request template
Bump to containers/storage v1.16.1
diff --git a/vendor/github.com/containers/buildah/buildah.go b/vendor/github.com/containers/buildah/buildah.go
index 2ece11acd..0fee906e5 100644
--- a/vendor/github.com/containers/buildah/buildah.go
+++ b/vendor/github.com/containers/buildah/buildah.go
@@ -27,7 +27,7 @@ const (
Package = "buildah"
// Version for the Package. Bump version in contrib/rpm/buildah.spec
// too.
- Version = "1.15.0-dev"
+ Version = "1.14.3"
// The value we use to identify what type of information, currently a
// serialized Builder structure, we are using as per-container state.
// This should only be changed when we make incompatible changes to
diff --git a/vendor/github.com/containers/buildah/changelog.txt b/vendor/github.com/containers/buildah/changelog.txt
index b4c71bf6a..900accf10 100644
--- a/vendor/github.com/containers/buildah/changelog.txt
+++ b/vendor/github.com/containers/buildah/changelog.txt
@@ -1,3 +1,19 @@
+- Changelog for v1.14.3 (2020-03-17)
+ * Update containers/storage to v1.16.5
+ * Bump github.com/containers/storage from 1.16.2 to 1.16.4
+ * Bump github.com/openshift/imagebuilder from 1.1.1 to 1.1.2
+ * Update github.com/openshift/imagebuilder vendoring
+ * Update unshare man page to fix script example
+ * Fix compilation errors on non linux platforms
+ * Bump containers/common and opencontainers/selinux versions
+ * Add tests for volume ownership
+ * Preserve volume uid and gid through subsequent commands
+ * Fix FORWARD_NULL errors found by Coverity
+ * Bump github.com/containers/storage from 1.16.1 to 1.16.2
+ * Fix errors found by codespell
+ * Bump back to v1.15.0-dev
+ * Add Pull Request Template
+
- Changelog for v1.14.2 (2020-03-03)
* Add Buildah pull request template
* Bump to containers/storage v1.16.1
diff --git a/vendor/github.com/containers/buildah/go.mod b/vendor/github.com/containers/buildah/go.mod
index 97b2eeae8..862c7e1a2 100644
--- a/vendor/github.com/containers/buildah/go.mod
+++ b/vendor/github.com/containers/buildah/go.mod
@@ -6,7 +6,7 @@ require (
github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784
github.com/containers/common v0.5.0
github.com/containers/image/v5 v5.2.1
- github.com/containers/storage v1.16.2
+ github.com/containers/storage v1.16.5
github.com/cyphar/filepath-securejoin v0.2.2
github.com/docker/distribution v2.7.1+incompatible
github.com/docker/go-metrics v0.0.1 // indirect
@@ -27,7 +27,7 @@ require (
github.com/opencontainers/runtime-tools v0.9.0
github.com/opencontainers/selinux v1.4.0
github.com/openshift/api v0.0.0-20200106203948-7ab22a2c8316
- github.com/openshift/imagebuilder v1.1.1
+ github.com/openshift/imagebuilder v1.1.2
github.com/pkg/errors v0.9.1
github.com/seccomp/containers-golang v0.0.0-20190312124753-8ca8945ccf5f
github.com/seccomp/libseccomp-golang v0.9.1
diff --git a/vendor/github.com/containers/buildah/go.sum b/vendor/github.com/containers/buildah/go.sum
index 191eb1f11..17ea81042 100644
--- a/vendor/github.com/containers/buildah/go.sum
+++ b/vendor/github.com/containers/buildah/go.sum
@@ -159,6 +159,12 @@ github.com/containers/storage v1.16.1 h1:gVLVqbqaoyopLJbcQ9PQdsnm8SzVy6Vw24fofwM
github.com/containers/storage v1.16.1/go.mod h1:toFp72SLn/iyJ6YbrnrZ0bW63aH2Qw3dA8JVwL4ADPo=
github.com/containers/storage v1.16.2 h1:S77Y+lmJcnGoPEZB2OOrTrRGyjT8viDCGyhVNNz78h8=
github.com/containers/storage v1.16.2/go.mod h1:/RNmsK01ajCL+VtMSi3W8kHzpBwN+Q5gLYWgfw5wlMg=
+github.com/containers/storage v1.16.3 h1:bctiz1I+0TIivtXbrVmy02ZYlOA+IjKIJMzAMTBifj8=
+github.com/containers/storage v1.16.3/go.mod h1:dNTv0+BaebIAOGgH34dPtwGPR+Km2fObcfOlFxYFwA0=
+github.com/containers/storage v1.16.4 h1:+pEL9A1i11qy1j/MYvh8Y5vs79BBfA+hslyJq1iPOGc=
+github.com/containers/storage v1.16.4/go.mod h1:SdysZeLKJOvfHYysUWg9OZUC3gdZWi5b2b7NC18VpPE=
+github.com/containers/storage v1.16.5 h1:eHeWEhUEWX3VMIG1Vn1rEjfRoLHUQev3cwtA5zd89wk=
+github.com/containers/storage v1.16.5/go.mod h1:SdysZeLKJOvfHYysUWg9OZUC3gdZWi5b2b7NC18VpPE=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
@@ -380,10 +386,14 @@ github.com/klauspost/compress v1.10.0 h1:92XGj1AcYzA6UrVdd4qIIBrT8OroryvRvdmg/If
github.com/klauspost/compress v1.10.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.10.2 h1:Znfn6hXZAHaLPNnlqUYRrBSReFHYybslgv4PTiyz6P0=
github.com/klauspost/compress v1.10.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eTO0Q8=
+github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w=
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/pgzip v1.2.1 h1:oIPZROsWuPHpOdMVWLuJZXwgjhrW8r1yEX8UqMyeNHM=
github.com/klauspost/pgzip v1.2.1/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
+github.com/klauspost/pgzip v1.2.2 h1:8d4I0LDiieuGngsqlqOih9ker/NS0LX4V0i+EhiFWg0=
+github.com/klauspost/pgzip v1.2.2/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
@@ -516,6 +526,8 @@ github.com/openshift/imagebuilder v1.1.0 h1:oT704SkwMEzmIMU/+Uv1Wmvt+p10q3v2WuYM
github.com/openshift/imagebuilder v1.1.0/go.mod h1:9aJRczxCH0mvT6XQ+5STAQaPWz7OsWcU5/mRkt8IWeo=
github.com/openshift/imagebuilder v1.1.1 h1:KAUR31p8UBJdfVO42azWgb+LeMAed2zaKQ19e0C0X2I=
github.com/openshift/imagebuilder v1.1.1/go.mod h1:9aJRczxCH0mvT6XQ+5STAQaPWz7OsWcU5/mRkt8IWeo=
+github.com/openshift/imagebuilder v1.1.2 h1:vCO8hZQR/4uzo+j0PceBH5aKFcvCDM43UzUGOYQN+Go=
+github.com/openshift/imagebuilder v1.1.2/go.mod h1:9aJRczxCH0mvT6XQ+5STAQaPWz7OsWcU5/mRkt8IWeo=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913 h1:TnbXhKzrTOyuvWrjI8W6pcoI9XPbLHFXCdN2dtUw7Rw=
github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913/go.mod h1:J6OG6YJVEWopen4avK3VNQSnALmmjvniMmni/YFYAwc=
diff --git a/vendor/github.com/openshift/imagebuilder/builder.go b/vendor/github.com/openshift/imagebuilder/builder.go
index 5a2d0d539..81d7b8421 100644
--- a/vendor/github.com/openshift/imagebuilder/builder.go
+++ b/vendor/github.com/openshift/imagebuilder/builder.go
@@ -288,8 +288,12 @@ func NewBuilder(args map[string]string) *Builder {
for k, v := range builtinAllowedBuildArgs {
allowed[k] = v
}
+ provided := make(map[string]string)
+ for k, v := range args {
+ provided[k] = v
+ }
return &Builder{
- Args: args,
+ Args: provided,
AllowedArgs: allowed,
}
}
diff --git a/vendor/github.com/openshift/imagebuilder/dispatchers.go b/vendor/github.com/openshift/imagebuilder/dispatchers.go
index ff365848a..e7f2f97bf 100644
--- a/vendor/github.com/openshift/imagebuilder/dispatchers.go
+++ b/vendor/github.com/openshift/imagebuilder/dispatchers.go
@@ -19,6 +19,7 @@ import (
docker "github.com/fsouza/go-dockerclient"
+ "github.com/containerd/containerd/platforms"
"github.com/openshift/imagebuilder/signal"
"github.com/openshift/imagebuilder/strslice"
)
@@ -27,6 +28,27 @@ var (
obRgex = regexp.MustCompile(`(?i)^\s*ONBUILD\s*`)
)
+var localspec = platforms.DefaultSpec()
+
+// https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope
+var builtinBuildArgs = map[string]string{
+ "TARGETPLATFORM": localspec.OS + "/" + localspec.Architecture,
+ "TARGETOS": localspec.OS,
+ "TARGETARCH": localspec.Architecture,
+ "TARGETVARIANT": localspec.Variant,
+ "BUILDPLATFORM": localspec.OS + "/" + localspec.Architecture,
+ "BUILDOS": localspec.OS,
+ "BUILDARCH": localspec.Architecture,
+ "BUILDVARIANT": localspec.Variant,
+}
+
+func init() {
+ if localspec.Variant != "" {
+ builtinBuildArgs["TARGETPLATFORM"] = builtinBuildArgs["TARGETPLATFORM"] + "/" + localspec.Variant
+ builtinBuildArgs["BUILDPLATFORM"] = builtinBuildArgs["BUILDPLATFORM"] + "/" + localspec.Variant
+ }
+}
+
// ENV foo bar
//
// Sets the environment variable foo to bar, also makes interpolation
@@ -131,14 +153,16 @@ func add(b *Builder, args []string, attributes map[string]bool, flagArgs []strin
var chown string
last := len(args) - 1
dest := makeAbsolute(args[last], b.RunConfig.WorkingDir)
- if len(flagArgs) > 0 {
- for _, arg := range flagArgs {
- switch {
- case strings.HasPrefix(arg, "--chown="):
- chown = strings.TrimPrefix(arg, "--chown=")
- default:
- return fmt.Errorf("ADD only supports the --chown=<uid:gid> flag")
- }
+ for _, a := range flagArgs {
+ arg, err := ProcessWord(a, b.Env)
+ if err != nil {
+ return err
+ }
+ switch {
+ case strings.HasPrefix(arg, "--chown="):
+ chown = strings.TrimPrefix(arg, "--chown=")
+ default:
+ return fmt.Errorf("ADD only supports the --chown=<uid:gid> flag")
}
}
b.PendingCopies = append(b.PendingCopies, Copy{Src: args[0:last], Dest: dest, Download: true, Chown: chown})
@@ -157,16 +181,18 @@ func dispatchCopy(b *Builder, args []string, attributes map[string]bool, flagArg
dest := makeAbsolute(args[last], b.RunConfig.WorkingDir)
var chown string
var from string
- if len(flagArgs) > 0 {
- for _, arg := range flagArgs {
- switch {
- case strings.HasPrefix(arg, "--chown="):
- chown = strings.TrimPrefix(arg, "--chown=")
- case strings.HasPrefix(arg, "--from="):
- from = strings.TrimPrefix(arg, "--from=")
- default:
- return fmt.Errorf("COPY only supports the --chown=<uid:gid> and the --from=<image|stage> flags")
- }
+ for _, a := range flagArgs {
+ arg, err := ProcessWord(a, b.Env)
+ if err != nil {
+ return err
+ }
+ switch {
+ case strings.HasPrefix(arg, "--chown="):
+ chown = strings.TrimPrefix(arg, "--chown=")
+ case strings.HasPrefix(arg, "--from="):
+ from = strings.TrimPrefix(arg, "--from=")
+ default:
+ return fmt.Errorf("COPY only supports the --chown=<uid:gid> and the --from=<image|stage> flags")
}
}
b.PendingCopies = append(b.PendingCopies, Copy{From: from, Src: args[0:last], Dest: dest, Download: false, Chown: chown})
@@ -516,6 +542,8 @@ func healthcheck(b *Builder, args []string, attributes map[string]bool, flagArgs
return nil
}
+var targetArgs = []string{"TARGETOS", "TARGETARCH", "TARGETVARIANT"}
+
// ARG name[=value]
//
// Adds the variable foo to the trusted list of variables that can be passed
@@ -543,6 +571,26 @@ func arg(b *Builder, args []string, attributes map[string]bool, flagArgs []strin
name = parts[0]
value = parts[1]
hasDefault = true
+ if name == "TARGETPLATFORM" {
+ p, err := platforms.Parse(value)
+ if err != nil {
+ return fmt.Errorf("error parsing TARGETPLATFORM argument")
+ }
+ for _, val := range targetArgs {
+ b.AllowedArgs[val] = true
+ }
+ b.Args["TARGETPLATFORM"] = p.OS + "/" + p.Architecture
+ b.Args["TARGETOS"] = p.OS
+ b.Args["TARGETARCH"] = p.Architecture
+ b.Args["TARGETVARIANT"] = p.Variant
+ if p.Variant != "" {
+ b.Args["TARGETPLATFORM"] = b.Args["TARGETPLATFORM"] + "/" + p.Variant
+ }
+ }
+ } else if val, ok := builtinBuildArgs[arg]; ok {
+ name = arg
+ value = val
+ hasDefault = true
} else {
name = arg
hasDefault = false
diff --git a/vendor/github.com/openshift/imagebuilder/vendor.conf b/vendor/github.com/openshift/imagebuilder/vendor.conf
index c3f7d1a6b..8074ce80a 100644
--- a/vendor/github.com/openshift/imagebuilder/vendor.conf
+++ b/vendor/github.com/openshift/imagebuilder/vendor.conf
@@ -1,4 +1,5 @@
github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109
+github.com/containerd/containerd v1.3.0
github.com/containers/storage v1.2
github.com/docker/docker b68221c37ee597950364788204546f9c9d0e46a1
github.com/docker/go-connections 97c2040d34dfae1d1b1275fa3a78dbdd2f41cf7e
@@ -18,3 +19,7 @@ golang.org/x/crypto ff983b9c42bc9fbf91556e191cc8efb585c16908
golang.org/x/net 45ffb0cd1ba084b73e26dee67e667e1be5acce83
golang.org/x/sys 7fbe1cd0fcc20051e1fcb87fbabec4a1bacaaeba
k8s.io/klog 8e90cee79f823779174776412c13478955131846
+google.golang.org/grpc 6eaf6f47437a6b4e2153a190160ef39a92c7eceb # v1.23.0
+github.com/golang/protobuf v1.2.0
+google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
+
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 2b93c89f2..1ad73e1bc 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -44,6 +44,8 @@ github.com/checkpoint-restore/go-criu/rpc
github.com/containerd/cgroups/stats/v1
# github.com/containerd/containerd v1.3.0
github.com/containerd/containerd/errdefs
+github.com/containerd/containerd/log
+github.com/containerd/containerd/platforms
# github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc
github.com/containerd/continuity/fs
github.com/containerd/continuity/syscallx
@@ -62,7 +64,7 @@ github.com/containernetworking/plugins/pkg/ns
github.com/containernetworking/plugins/pkg/utils/hwaddr
github.com/containernetworking/plugins/plugins/ipam/host-local/backend
github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator
-# github.com/containers/buildah v1.14.3-0.20200313154200-d26f437b2a46
+# github.com/containers/buildah v1.14.3
github.com/containers/buildah
github.com/containers/buildah/bind
github.com/containers/buildah/chroot
@@ -413,7 +415,7 @@ github.com/opencontainers/selinux/go-selinux/label
github.com/opencontainers/selinux/pkg/pwalk
# github.com/openshift/api v0.0.0-20200106203948-7ab22a2c8316
github.com/openshift/api/config/v1
-# github.com/openshift/imagebuilder v1.1.1
+# github.com/openshift/imagebuilder v1.1.2
github.com/openshift/imagebuilder
github.com/openshift/imagebuilder/dockerfile/command
github.com/openshift/imagebuilder/dockerfile/parser
diff --git a/version/version.go b/version/version.go
index 5a7b4a36e..0d0b0b422 100644
--- a/version/version.go
+++ b/version/version.go
@@ -4,7 +4,7 @@ package version
// NOTE: remember to bump the version at the top
// of the top-level README.md file when this is
// bumped.
-const Version = "1.8.2-dev"
+const Version = "1.8.3-dev"
// RemoteAPIVersion is the version for the remote
// client API. It is used to determine compatibility