summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml2
-rw-r--r--README.md2
-rw-r--r--cmd/podman/shared/create.go27
-rw-r--r--cmd/podmanV2/Makefile2
-rw-r--r--cmd/podmanV2/common/create.go534
-rw-r--r--cmd/podmanV2/common/create_opts.go103
-rw-r--r--cmd/podmanV2/common/createparse.go51
-rw-r--r--cmd/podmanV2/common/default.go121
-rw-r--r--cmd/podmanV2/common/ports.go126
-rw-r--r--cmd/podmanV2/common/specgen.go647
-rw-r--r--cmd/podmanV2/containers/attach.go60
-rw-r--r--cmd/podmanV2/containers/checkpoint.go79
-rw-r--r--cmd/podmanV2/containers/create.go102
-rw-r--r--cmd/podmanV2/containers/exec.go93
-rw-r--r--cmd/podmanV2/containers/export.go57
-rw-r--r--cmd/podmanV2/containers/restore.go104
-rw-r--r--cmd/podmanV2/images/import.go87
-rw-r--r--cmd/podmanV2/images/load.go61
-rw-r--r--cmd/podmanV2/images/push.go127
-rw-r--r--cmd/podmanV2/images/save.go87
-rw-r--r--cmd/podmanV2/main.go7
-rw-r--r--cmd/podmanV2/parse/common.go50
-rw-r--r--cmd/podmanV2/parse/net.go (renamed from cmd/podmanV2/parse/parse.go)45
-rw-r--r--cmd/podmanV2/parse/net_test.go (renamed from cmd/podmanV2/parse/parse_test.go)0
-rw-r--r--cmd/podmanV2/system/version.go119
-rw-r--r--go.mod6
-rw-r--r--go.sum12
-rwxr-xr-xhack/podmanv2-retry37
-rw-r--r--libpod/container_api.go24
-rw-r--r--libpod/container_exec.go4
-rw-r--r--libpod/container_top_linux.go2
-rw-r--r--libpod/define/config.go31
-rw-r--r--libpod/healthcheck.go2
-rw-r--r--libpod/image/image.go6
-rw-r--r--libpod/image/image_test.go2
-rw-r--r--libpod/oci.go4
-rw-r--r--libpod/oci_attach_linux.go14
-rw-r--r--libpod/oci_attach_unsupported.go4
-rw-r--r--libpod/oci_conmon_linux.go49
-rw-r--r--libpod/options.go2
-rw-r--r--pkg/adapter/containers.go5
-rw-r--r--pkg/adapter/terminal_linux.go5
-rw-r--r--pkg/adapter/terminal_unsupported.go2
-rw-r--r--pkg/api/handlers/compat/containers_export.go42
-rw-r--r--pkg/api/handlers/compat/images_push.go80
-rw-r--r--pkg/api/handlers/libpod/containers.go131
-rw-r--r--pkg/api/handlers/libpod/containers_create.go7
-rw-r--r--pkg/api/handlers/libpod/images.go179
-rw-r--r--pkg/api/handlers/swagger.go4
-rw-r--r--pkg/api/handlers/types.go4
-rw-r--r--pkg/api/handlers/utils/handler.go7
-rw-r--r--pkg/api/handlers/utils/images.go41
-rw-r--r--pkg/api/server/register_containers.go140
-rw-r--r--pkg/api/server/register_images.go74
-rw-r--r--pkg/bindings/containers/checkpoint.go79
-rw-r--r--pkg/bindings/containers/containers.go20
-rw-r--r--pkg/bindings/images/images.go53
-rw-r--r--pkg/bindings/test/images_test.go4
-rw-r--r--pkg/checkpoint/checkpoint_restore.go (renamed from pkg/adapter/checkpoint_restore.go)8
-rw-r--r--pkg/domain/entities/containers.go69
-rw-r--r--pkg/domain/entities/engine_container.go7
-rw-r--r--pkg/domain/entities/engine_image.go4
-rw-r--r--pkg/domain/entities/images.go69
-rw-r--r--pkg/domain/infra/abi/containers.go202
-rw-r--r--pkg/domain/infra/abi/images.go98
-rw-r--r--pkg/domain/infra/abi/terminal/sigproxy_linux.go47
-rw-r--r--pkg/domain/infra/abi/terminal/terminal.go103
-rw-r--r--pkg/domain/infra/abi/terminal/terminal_linux.go123
-rw-r--r--pkg/domain/infra/tunnel/containers.go105
-rw-r--r--pkg/domain/infra/tunnel/images.go84
-rw-r--r--pkg/domain/infra/tunnel/system.go1
-rw-r--r--pkg/spec/spec.go16
-rw-r--r--pkg/spec/storage.go78
-rw-r--r--pkg/specgen/config_linux.go93
-rw-r--r--pkg/specgen/config_linux_cgo.go1
-rw-r--r--pkg/specgen/container_validate.go4
-rw-r--r--pkg/specgen/generate/container.go168
-rw-r--r--pkg/specgen/generate/container_create.go (renamed from pkg/specgen/container_create.go)19
-rw-r--r--pkg/specgen/namespaces.go7
-rw-r--r--pkg/specgen/oci.go6
-rw-r--r--pkg/specgen/security.go165
-rw-r--r--pkg/specgen/specgen.go13
-rw-r--r--pkg/specgen/storage.go885
-rw-r--r--pkg/util/mountOpts.go24
-rw-r--r--pkg/util/mountOpts_linux.go23
-rw-r--r--pkg/util/mountOpts_other.go7
-rw-r--r--pkg/varlinkapi/attach.go8
-rw-r--r--test/e2e/build/basicalpine/Containerfile.path2
-rw-r--r--test/e2e/build_test.go20
-rw-r--r--test/e2e/checkpoint_test.go2
-rw-r--r--test/e2e/exec_test.go12
-rw-r--r--test/e2e/run_volume_test.go84
-rw-r--r--utils/utils.go1
-rw-r--r--vendor/github.com/containers/buildah/.gitignore1
-rw-r--r--vendor/github.com/containers/buildah/CHANGELOG.md14
-rw-r--r--vendor/github.com/containers/buildah/buildah.go2
-rw-r--r--vendor/github.com/containers/buildah/changelog.txt14
-rw-r--r--vendor/github.com/containers/buildah/chroot/run.go2
-rw-r--r--vendor/github.com/containers/buildah/common.go2
-rw-r--r--vendor/github.com/containers/buildah/go.mod14
-rw-r--r--vendor/github.com/containers/buildah/go.sum62
-rw-r--r--vendor/github.com/containers/buildah/imagebuildah/executor.go100
-rw-r--r--vendor/github.com/containers/buildah/imagebuildah/stage_executor.go103
-rw-r--r--vendor/github.com/containers/buildah/imagebuildah/util.go8
-rw-r--r--vendor/github.com/containers/buildah/info.go2
-rw-r--r--vendor/github.com/containers/buildah/install.md2
-rw-r--r--vendor/github.com/containers/buildah/pkg/overlay/overlay.go2
-rw-r--r--vendor/github.com/containers/buildah/pkg/parse/parse_unix.go2
-rw-r--r--vendor/github.com/containers/buildah/run_linux.go6
-rw-r--r--vendor/github.com/containers/common/pkg/apparmor/apparmor.go21
-rw-r--r--vendor/github.com/containers/common/pkg/apparmor/apparmor_linux.go289
-rw-r--r--vendor/github.com/containers/common/pkg/apparmor/apparmor_linux_template.go49
-rw-r--r--vendor/github.com/containers/common/pkg/apparmor/apparmor_unsupported.go31
-rw-r--r--vendor/github.com/containers/common/pkg/capabilities/capabilities.go4
-rw-r--r--vendor/github.com/containers/common/pkg/cgroupv2/cgroups_linux.go27
-rw-r--r--vendor/github.com/containers/common/pkg/cgroupv2/cgroups_unsupported.go8
-rw-r--r--vendor/github.com/containers/common/pkg/config/config.go36
-rw-r--r--vendor/github.com/containers/common/pkg/config/containers.conf15
-rw-r--r--vendor/github.com/containers/common/pkg/config/default.go136
-rw-r--r--vendor/github.com/containers/common/pkg/config/default_linux.go14
-rw-r--r--vendor/github.com/containers/common/pkg/config/libpodConfig.go15
-rw-r--r--vendor/github.com/containers/common/pkg/config/nosystemd.go11
-rw-r--r--vendor/github.com/containers/common/pkg/config/systemd.go10
-rw-r--r--vendor/github.com/containers/common/pkg/config/util_supported.go2
-rw-r--r--vendor/github.com/containers/common/pkg/sysinfo/README.md1
-rw-r--r--vendor/github.com/containers/common/pkg/sysinfo/numcpu.go12
-rw-r--r--vendor/github.com/containers/common/pkg/sysinfo/numcpu_linux.go44
-rw-r--r--vendor/github.com/containers/common/pkg/sysinfo/numcpu_windows.go37
-rw-r--r--vendor/github.com/containers/common/pkg/sysinfo/sysinfo.go153
-rw-r--r--vendor/github.com/containers/common/pkg/sysinfo/sysinfo_linux.go261
-rw-r--r--vendor/github.com/containers/common/pkg/sysinfo/sysinfo_solaris.go122
-rw-r--r--vendor/github.com/containers/common/pkg/sysinfo/sysinfo_unix.go9
-rw-r--r--vendor/github.com/containers/common/pkg/sysinfo/sysinfo_windows.go9
-rw-r--r--vendor/github.com/containers/storage/VERSION2
-rw-r--r--vendor/github.com/containers/storage/go.mod4
-rw-r--r--vendor/github.com/containers/storage/go.sum36
-rw-r--r--vendor/github.com/containers/storage/layers.go2
-rw-r--r--vendor/github.com/containers/storage/pkg/config/config.go12
-rw-r--r--vendor/github.com/containers/storage/pkg/unshare/getenv_linux_cgo.go (renamed from vendor/github.com/containers/common/pkg/unshare/getenv_linux_cgo.go)0
-rw-r--r--vendor/github.com/containers/storage/pkg/unshare/getenv_linux_nocgo.go (renamed from vendor/github.com/containers/common/pkg/unshare/getenv_linux_nocgo.go)0
-rw-r--r--vendor/github.com/containers/storage/pkg/unshare/unshare.c (renamed from vendor/github.com/containers/common/pkg/unshare/unshare.c)4
-rw-r--r--vendor/github.com/containers/storage/pkg/unshare/unshare.go (renamed from vendor/github.com/containers/common/pkg/unshare/unshare.go)0
-rw-r--r--vendor/github.com/containers/storage/pkg/unshare/unshare_cgo.go (renamed from vendor/github.com/containers/common/pkg/unshare/unshare_cgo.go)0
-rw-r--r--vendor/github.com/containers/storage/pkg/unshare/unshare_gccgo.go (renamed from vendor/github.com/containers/common/pkg/unshare/unshare_gccgo.go)0
-rw-r--r--vendor/github.com/containers/storage/pkg/unshare/unshare_linux.go (renamed from vendor/github.com/containers/common/pkg/unshare/unshare_linux.go)6
-rw-r--r--vendor/github.com/containers/storage/pkg/unshare/unshare_unsupported.go (renamed from vendor/github.com/containers/common/pkg/unshare/unshare_unsupported.go)0
-rw-r--r--vendor/github.com/containers/storage/pkg/unshare/unshare_unsupported_cgo.go10
-rw-r--r--vendor/github.com/containers/storage/storage.conf18
-rw-r--r--vendor/github.com/containers/storage/store.go131
-rw-r--r--vendor/github.com/containers/storage/userns.go457
-rw-r--r--vendor/github.com/openshift/imagebuilder/builder.go55
-rw-r--r--vendor/github.com/openshift/imagebuilder/dispatchers.go14
-rw-r--r--vendor/modules.txt13
153 files changed, 8161 insertions, 582 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index a0340dbbd..01cecd5a8 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -139,8 +139,6 @@ gating_task:
- '/usr/local/bin/entrypoint.sh clean podman-remote |& ${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}'
# Verify some aspects of ci/related scripts
ci_script:
diff --git a/README.md b/README.md
index ea10454be..25d1432e0 100644
--- a/README.md
+++ b/README.md
@@ -49,7 +49,7 @@ and
tracking system.
There is also a [mailing list](https://lists.podman.io/archives/) at `lists.podman.io`.
-You can subscribe by sending a message to `podman@lists.podman.io` with the subject `subscribe`.
+You can subscribe by sending a message to `podman-join@lists.podman.io` with the subject `subscribe`.
## Rootless
Podman can be easily run as a normal user, without requiring a setuid binary.
diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go
index 5fa8d6c0b..68a36d967 100644
--- a/cmd/podman/shared/create.go
+++ b/cmd/podman/shared/create.go
@@ -538,14 +538,16 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
}
}
- // env overrides any previous variables
- cmdlineEnv := c.StringSlice("env")
- if len(cmdlineEnv) > 0 {
- parsedEnv, err := envLib.ParseSlice(cmdlineEnv)
- if err != nil {
- return nil, err
+ if c.IsSet("env") {
+ // env overrides any previous variables
+ cmdlineEnv := c.StringSlice("env")
+ if len(cmdlineEnv) > 0 {
+ parsedEnv, err := envLib.ParseSlice(cmdlineEnv)
+ if err != nil {
+ return nil, err
+ }
+ env = envLib.Join(env, parsedEnv)
}
- env = envLib.Join(env, parsedEnv)
}
// LABEL VARIABLES
@@ -636,10 +638,13 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
if err != nil {
return nil, errors.Wrapf(err, "unable to translate --shm-size")
}
- // Verify the additional hosts are in correct format
- for _, host := range c.StringSlice("add-host") {
- if _, err := parse.ValidateExtraHost(host); err != nil {
- return nil, err
+
+ if c.IsSet("add-host") {
+ // Verify the additional hosts are in correct format
+ for _, host := range c.StringSlice("add-host") {
+ if _, err := parse.ValidateExtraHost(host); err != nil {
+ return nil, err
+ }
}
}
diff --git a/cmd/podmanV2/Makefile b/cmd/podmanV2/Makefile
index 147a78d9c..b847a9385 100644
--- a/cmd/podmanV2/Makefile
+++ b/cmd/podmanV2/Makefile
@@ -1,2 +1,2 @@
all:
- GO111MODULE=off go build -tags 'ABISupport'
+ CGO_ENABLED=1 GO111MODULE=off go build -tags 'ABISupport systemd seccomp'
diff --git a/cmd/podmanV2/common/create.go b/cmd/podmanV2/common/create.go
new file mode 100644
index 000000000..f81d021c8
--- /dev/null
+++ b/cmd/podmanV2/common/create.go
@@ -0,0 +1,534 @@
+package common
+
+import (
+ "fmt"
+ "os"
+
+ buildahcli "github.com/containers/buildah/pkg/cli"
+ "github.com/containers/common/pkg/config"
+ "github.com/containers/libpod/cmd/podman/cliconfig"
+ "github.com/sirupsen/logrus"
+ "github.com/spf13/pflag"
+)
+
+const (
+ sizeWithUnitFormat = "(format: `<number>[<unit>]`, where unit = b (bytes), k (kilobytes), m (megabytes), or g (gigabytes))"
+)
+
+var (
+ defaultContainerConfig = getDefaultContainerConfig()
+)
+
+func getDefaultContainerConfig() *config.Config {
+ defaultContainerConfig, err := config.Default()
+ if err != nil {
+ logrus.Error(err)
+ os.Exit(1)
+ }
+ return defaultContainerConfig
+}
+
+func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet {
+ //createFlags := c.Flags()
+ createFlags := pflag.FlagSet{}
+ createFlags.StringSliceVar(
+ &cf.Annotation,
+ "annotation", []string{},
+ "Add annotations to container (key:value)",
+ )
+ createFlags.StringSliceVarP(
+ &cf.Attach,
+ "attach", "a", []string{},
+ "Attach to STDIN, STDOUT or STDERR",
+ )
+ createFlags.StringVar(
+ &cf.Authfile,
+ "authfile", buildahcli.GetDefaultAuthFile(),
+ "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override",
+ )
+ createFlags.StringVar(
+ &cf.BlkIOWeight,
+ "blkio-weight", "",
+ "Block IO weight (relative weight) accepts a weight value between 10 and 1000.",
+ )
+ createFlags.StringSliceVar(
+ &cf.BlkIOWeightDevice,
+ "blkio-weight-device", []string{},
+ "Block IO weight (relative device weight, format: `DEVICE_NAME:WEIGHT`)",
+ )
+ createFlags.StringSliceVar(
+ &cf.CapAdd,
+ "cap-add", []string{},
+ "Add capabilities to the container",
+ )
+ createFlags.StringSliceVar(
+ &cf.CapDrop,
+ "cap-drop", []string{},
+ "Drop capabilities from the container",
+ )
+ createFlags.StringVar(
+ &cf.CGroupsNS,
+ "cgroupns", getDefaultCgroupNS(),
+ "cgroup namespace to use",
+ )
+ createFlags.StringVar(
+ &cf.CGroups,
+ "cgroups", "enabled",
+ `control container cgroup configuration ("enabled"|"disabled"|"no-conmon")`,
+ )
+ createFlags.StringVar(
+ &cf.CGroupParent,
+ "cgroup-parent", "",
+ "Optional parent cgroup for the container",
+ )
+ createFlags.StringVar(
+ &cf.CIDFile,
+ "cidfile", "",
+ "Write the container ID to the file",
+ )
+ createFlags.StringVar(
+ &cf.ConmonPIDFile,
+ "conmon-pidfile", "",
+ "Path to the file that will receive the PID of conmon",
+ )
+ createFlags.Uint64Var(
+ &cf.CPUPeriod,
+ "cpu-period", 0,
+ "Limit the CPU CFS (Completely Fair Scheduler) period",
+ )
+ createFlags.Int64Var(
+ &cf.CPUQuota,
+ "cpu-quota", 0,
+ "Limit the CPU CFS (Completely Fair Scheduler) quota",
+ )
+ createFlags.Uint64Var(
+ &cf.CPURTPeriod,
+ "cpu-rt-period", 0,
+ "Limit the CPU real-time period in microseconds",
+ )
+ createFlags.Int64Var(
+ &cf.CPURTRuntime,
+ "cpu-rt-runtime", 0,
+ "Limit the CPU real-time runtime in microseconds",
+ )
+ createFlags.Uint64Var(
+ &cf.CPUShares,
+ "cpu-shares", 0,
+ "CPU shares (relative weight)",
+ )
+ createFlags.Float64Var(
+ &cf.CPUS,
+ "cpus", 0,
+ "Number of CPUs. The default is 0.000 which means no limit",
+ )
+ createFlags.StringVar(
+ &cf.CPUSetCPUs,
+ "cpuset-cpus", "",
+ "CPUs in which to allow execution (0-3, 0,1)",
+ )
+ createFlags.StringVar(
+ &cf.CPUSetMems,
+ "cpuset-mems", "",
+ "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.",
+ )
+ createFlags.BoolVarP(
+ &cf.Detach,
+ "detach", "d", false,
+ "Run container in background and print container ID",
+ )
+ createFlags.StringVar(
+ &cf.DetachKeys,
+ "detach-keys", GetDefaultDetachKeys(),
+ "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-cf`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`",
+ )
+ createFlags.StringSliceVar(
+ &cf.Device,
+ "device", getDefaultDevices(),
+ fmt.Sprintf("Add a host device to the container"),
+ )
+ createFlags.StringSliceVar(
+ &cf.DeviceCGroupRule,
+ "device-cgroup-rule", []string{},
+ "Add a rule to the cgroup allowed devices list",
+ )
+ createFlags.StringSliceVar(
+ &cf.DeviceReadBPs,
+ "device-read-bps", []string{},
+ "Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)",
+ )
+ createFlags.StringSliceVar(
+ &cf.DeviceReadIOPs,
+ "device-read-iops", []string{},
+ "Limit read rate (IO per second) from a device (e.g. --device-read-iops=/dev/sda:1000)",
+ )
+ createFlags.StringSliceVar(
+ &cf.DeviceWriteBPs,
+ "device-write-bps", []string{},
+ "Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb)",
+ )
+ createFlags.StringSliceVar(
+ &cf.DeviceWriteIOPs,
+ "device-write-iops", []string{},
+ "Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:1000)",
+ )
+ createFlags.StringVar(
+ &cf.Entrypoint,
+ "entrypoint", "",
+ "Overwrite the default ENTRYPOINT of the image",
+ )
+ createFlags.StringArrayVarP(
+ &cf.env,
+ "env", "e", getDefaultEnv(),
+ "Set environment variables in container",
+ )
+ createFlags.BoolVar(
+ &cf.EnvHost,
+ "env-host", false, "Use all current host environment variables in container",
+ )
+ createFlags.StringSliceVar(
+ &cf.EnvFile,
+ "env-file", []string{},
+ "Read in a file of environment variables",
+ )
+ createFlags.StringSliceVar(
+ &cf.Expose,
+ "expose", []string{},
+ "Expose a port or a range of ports",
+ )
+ createFlags.StringSliceVar(
+ &cf.GIDMap,
+ "gidmap", []string{},
+ "GID map to use for the user namespace",
+ )
+ createFlags.StringSliceVar(
+ &cf.GroupAdd,
+ "group-add", []string{},
+ "Add additional groups to join",
+ )
+ createFlags.Bool(
+ "help", false, "",
+ )
+ createFlags.StringVar(
+ &cf.HealthCmd,
+ "health-cmd", "",
+ "set a healthcheck command for the container ('none' disables the existing healthcheck)",
+ )
+ createFlags.StringVar(
+ &cf.HealthInterval,
+ "health-interval", cliconfig.DefaultHealthCheckInterval,
+ "set an interval for the healthchecks (a value of disable results in no automatic timer setup)",
+ )
+ createFlags.UintVar(
+ &cf.HealthRetries,
+ "health-retries", cliconfig.DefaultHealthCheckRetries,
+ "the number of retries allowed before a healthcheck is considered to be unhealthy",
+ )
+ createFlags.StringVar(
+ &cf.HealthStartPeriod,
+ "health-start-period", cliconfig.DefaultHealthCheckStartPeriod,
+ "the initialization time needed for a container to bootstrap",
+ )
+ createFlags.StringVar(
+ &cf.HealthTimeout,
+ "health-timeout", cliconfig.DefaultHealthCheckTimeout,
+ "the maximum time allowed to complete the healthcheck before an interval is considered failed",
+ )
+ createFlags.StringVarP(
+ &cf.Hostname,
+ "hostname", "h", "",
+ "Set container hostname",
+ )
+ createFlags.BoolVar(
+ &cf.HTTPProxy,
+ "http-proxy", true,
+ "Set proxy environment variables in the container based on the host proxy vars",
+ )
+ createFlags.StringVar(
+ &cf.ImageVolume,
+ "image-volume", cliconfig.DefaultImageVolume,
+ `Tells podman how to handle the builtin image volumes ("bind"|"tmpfs"|"ignore")`,
+ )
+ createFlags.BoolVar(
+ &cf.Init,
+ "init", false,
+ "Run an init binary inside the container that forwards signals and reaps processes",
+ )
+ createFlags.StringVar(
+ &cf.InitPath,
+ "init-path", getDefaultInitPath(),
+ // Do not use the Value field for setting the default value to determine user input (i.e., non-empty string)
+ fmt.Sprintf("Path to the container-init binary"),
+ )
+ createFlags.BoolVarP(
+ &cf.Interactive,
+ "interactive", "i", false,
+ "Keep STDIN open even if not attached",
+ )
+ createFlags.StringVar(
+ &cf.IPC,
+ "ipc", getDefaultIPCNS(),
+ "IPC namespace to use",
+ )
+ createFlags.StringVar(
+ &cf.KernelMemory,
+ "kernel-memory", "",
+ "Kernel memory limit "+sizeWithUnitFormat,
+ )
+ createFlags.StringArrayVarP(
+ &cf.Label,
+ "label", "l", []string{},
+ "Set metadata on container",
+ )
+ createFlags.StringSliceVar(
+ &cf.LabelFile,
+ "label-file", []string{},
+ "Read in a line delimited file of labels",
+ )
+ createFlags.StringVar(
+ &cf.LogDriver,
+ "log-driver", "",
+ "Logging driver for the container",
+ )
+ createFlags.StringSliceVar(
+ &cf.LogOptions,
+ "log-opt", []string{},
+ "Logging driver options",
+ )
+ createFlags.StringVarP(
+ &cf.Memory,
+ "memory", "m", "",
+ "Memory limit "+sizeWithUnitFormat,
+ )
+ createFlags.StringVar(
+ &cf.MemoryReservation,
+ "memory-reservation", "",
+ "Memory soft limit "+sizeWithUnitFormat,
+ )
+ createFlags.StringVar(
+ &cf.MemorySwap,
+ "memory-swap", "",
+ "Swap limit equal to memory plus swap: '-1' to enable unlimited swap",
+ )
+ createFlags.Int64Var(
+ &cf.MemorySwappiness,
+ "memory-swappiness", -1,
+ "Tune container memory swappiness (0 to 100, or -1 for system default)",
+ )
+ createFlags.StringVar(
+ &cf.Name,
+ "name", "",
+ "Assign a name to the container",
+ )
+ createFlags.BoolVar(
+ &cf.NoHealthCheck,
+ "no-healthcheck", false,
+ "Disable healthchecks on container",
+ )
+ createFlags.BoolVar(
+ &cf.OOMKillDisable,
+ "oom-kill-disable", false,
+ "Disable OOM Killer",
+ )
+ createFlags.IntVar(
+ &cf.OOMScoreAdj,
+ "oom-score-adj", 0,
+ "Tune the host's OOM preferences (-1000 to 1000)",
+ )
+ createFlags.StringVar(
+ &cf.OverrideArch,
+ "override-arch", "",
+ "use `ARCH` instead of the architecture of the machine for choosing images",
+ )
+ //markFlagHidden(createFlags, "override-arch")
+ createFlags.StringVar(
+ &cf.OverrideOS,
+ "override-os", "",
+ "use `OS` instead of the running OS for choosing images",
+ )
+ //markFlagHidden(createFlags, "override-os")
+ createFlags.StringVar(
+ &cf.PID,
+ "pid", getDefaultPidNS(),
+ "PID namespace to use",
+ )
+ createFlags.Int64Var(
+ &cf.PIDsLimit,
+ "pids-limit", getDefaultPidsLimit(),
+ getDefaultPidsDescription(),
+ )
+ createFlags.StringVar(
+ &cf.Pod,
+ "pod", "",
+ "Run container in an existing pod",
+ )
+ createFlags.BoolVar(
+ &cf.Privileged,
+ "privileged", false,
+ "Give extended privileges to container",
+ )
+ createFlags.BoolVarP(
+ &cf.PublishAll,
+ "publish-all", "P", false,
+ "Publish all exposed ports to random ports on the host interface",
+ )
+ createFlags.StringVar(
+ &cf.Pull,
+ "pull", "missing",
+ `Pull image before creating ("always"|"missing"|"never")`,
+ )
+ createFlags.BoolVarP(
+ &cf.Quiet,
+ "quiet", "q", false,
+ "Suppress output information when pulling images",
+ )
+ createFlags.BoolVar(
+ &cf.ReadOnly,
+ "read-only", false,
+ "Make containers root filesystem read-only",
+ )
+ createFlags.BoolVar(
+ &cf.ReadOnlyTmpFS,
+ "read-only-tmpfs", true,
+ "When running containers in read-only mode mount a read-write tmpfs on /run, /tmp and /var/tmp",
+ )
+ createFlags.StringVar(
+ &cf.Restart,
+ "restart", "",
+ `Restart policy to apply when a container exits ("always"|"no"|"on-failure")`,
+ )
+ createFlags.BoolVar(
+ &cf.Rm,
+ "rm", false,
+ "Remove container (and pod if created) after exit",
+ )
+ createFlags.BoolVar(
+ &cf.RootFS,
+ "rootfs", false,
+ "The first argument is not an image but the rootfs to the exploded container",
+ )
+ createFlags.StringArrayVar(
+ &cf.SecurityOpt,
+ "security-opt", getDefaultSecurityOptions(),
+ fmt.Sprintf("Security Options"),
+ )
+ createFlags.StringVar(
+ &cf.ShmSize,
+ "shm-size", getDefaultShmSize(),
+ "Size of /dev/shm "+sizeWithUnitFormat,
+ )
+ createFlags.StringVar(
+ &cf.StopSignal,
+ "stop-signal", "",
+ "Signal to stop a container. Default is SIGTERM",
+ )
+ createFlags.UintVar(
+ &cf.StopTimeout,
+ "stop-timeout", defaultContainerConfig.Engine.StopTimeout,
+ "Timeout (in seconds) to stop a container. Default is 10",
+ )
+ createFlags.StringSliceVar(
+ &cf.StoreageOpt,
+ "storage-opt", []string{},
+ "Storage driver options per container",
+ )
+ createFlags.StringVar(
+ &cf.SubUIDName,
+ "subgidname", "",
+ "Name of range listed in /etc/subgid for use in user namespace",
+ )
+ createFlags.StringVar(
+ &cf.SubGIDName,
+ "subuidname", "",
+ "Name of range listed in /etc/subuid for use in user namespace",
+ )
+
+ createFlags.StringSliceVar(
+ &cf.Sysctl,
+ "sysctl", getDefaultSysctls(),
+ "Sysctl options",
+ )
+ createFlags.StringVar(
+ &cf.SystemdD,
+ "systemd", "true",
+ `Run container in systemd mode ("true"|"false"|"always")`,
+ )
+ createFlags.StringArrayVar(
+ &cf.TmpFS,
+ "tmpfs", []string{},
+ "Mount a temporary filesystem (`tmpfs`) into a container",
+ )
+ createFlags.BoolVarP(
+ &cf.TTY,
+ "tty", "t", false,
+ "Allocate a pseudo-TTY for container",
+ )
+ createFlags.StringSliceVar(
+ &cf.UIDMap,
+ "uidmap", []string{},
+ "UID map to use for the user namespace",
+ )
+ createFlags.StringSliceVar(
+ &cf.Ulimit,
+ "ulimit", getDefaultUlimits(),
+ "Ulimit options",
+ )
+ createFlags.StringVarP(
+ &cf.User,
+ "user", "u", "",
+ "Username or UID (format: <name|uid>[:<group|gid>])",
+ )
+ createFlags.StringVar(
+ &cf.UserNS,
+ "userns", getDefaultUserNS(),
+ "User namespace to use",
+ )
+ createFlags.StringVar(
+ &cf.UTS,
+ "uts", getDefaultUTSNS(),
+ "UTS namespace to use",
+ )
+ createFlags.StringArrayVar(
+ &cf.Mount,
+ "mount", []string{},
+ "Attach a filesystem mount to the container",
+ )
+ createFlags.StringArrayVarP(
+ &cf.Volume,
+ "volume", "v", getDefaultVolumes(),
+ "Bind mount a volume into the container",
+ )
+ createFlags.StringSliceVar(
+ &cf.VolumesFrom,
+ "volumes-from", []string{},
+ "Mount volumes from the specified container(s)",
+ )
+ createFlags.StringVarP(
+ &cf.Workdir,
+ "workdir", "w", "",
+ "Working directory inside the container",
+ )
+ createFlags.StringVar(
+ &cf.SeccompPolicy,
+ "seccomp-policy", "default",
+ "Policy for selecting a seccomp profile (experimental)",
+ )
+ return &createFlags
+}
+
+func AliasFlags(f *pflag.FlagSet, name string) pflag.NormalizedName {
+ switch name {
+ case "healthcheck-command":
+ name = "health-cmd"
+ case "healthcheck-interval":
+ name = "health-interval"
+ case "healthcheck-retries":
+ name = "health-retries"
+ case "healthcheck-start-period":
+ name = "health-start-period"
+ case "healthcheck-timeout":
+ name = "health-timeout"
+ case "net":
+ name = "network"
+ }
+ return pflag.NormalizedName(name)
+}
diff --git a/cmd/podmanV2/common/create_opts.go b/cmd/podmanV2/common/create_opts.go
new file mode 100644
index 000000000..9d12e4b26
--- /dev/null
+++ b/cmd/podmanV2/common/create_opts.go
@@ -0,0 +1,103 @@
+package common
+
+import "github.com/containers/libpod/pkg/domain/entities"
+
+type ContainerCLIOpts struct {
+ Annotation []string
+ Attach []string
+ Authfile string
+ BlkIOWeight string
+ BlkIOWeightDevice []string
+ CapAdd []string
+ CapDrop []string
+ CGroupsNS string
+ CGroups string
+ CGroupParent string
+ CIDFile string
+ ConmonPIDFile string
+ CPUPeriod uint64
+ CPUQuota int64
+ CPURTPeriod uint64
+ CPURTRuntime int64
+ CPUShares uint64
+ CPUS float64
+ CPUSetCPUs string
+ CPUSetMems string
+ Detach bool
+ DetachKeys string
+ Device []string
+ DeviceCGroupRule []string
+ DeviceReadBPs []string
+ DeviceReadIOPs []string
+ DeviceWriteBPs []string
+ DeviceWriteIOPs []string
+ Entrypoint string
+ env []string
+ EnvHost bool
+ EnvFile []string
+ Expose []string
+ GIDMap []string
+ GroupAdd []string
+ HealthCmd string
+ HealthInterval string
+ HealthRetries uint
+ HealthStartPeriod string
+ HealthTimeout string
+ Hostname string
+ HTTPProxy bool
+ ImageVolume string
+ Init bool
+ InitPath string
+ Interactive bool
+ IPC string
+ KernelMemory string
+ Label []string
+ LabelFile []string
+ LogDriver string
+ LogOptions []string
+ Memory string
+ MemoryReservation string
+ MemorySwap string
+ MemorySwappiness int64
+ Name string
+ NoHealthCheck bool
+ OOMKillDisable bool
+ OOMScoreAdj int
+ OverrideArch string
+ OverrideOS string
+ PID string
+ PIDsLimit int64
+ Pod string
+ Privileged bool
+ PublishAll bool
+ Pull string
+ Quiet bool
+ ReadOnly bool
+ ReadOnlyTmpFS bool
+ Restart string
+ Rm bool
+ RootFS bool
+ SecurityOpt []string
+ ShmSize string
+ StopSignal string
+ StopTimeout uint
+ StoreageOpt []string
+ SubUIDName string
+ SubGIDName string
+ Sysctl []string
+ SystemdD string
+ TmpFS []string
+ TTY bool
+ UIDMap []string
+ Ulimit []string
+ User string
+ UserNS string
+ UTS string
+ Mount []string
+ Volume []string
+ VolumesFrom []string
+ Workdir string
+ SeccompPolicy string
+
+ Net *entities.NetOptions
+}
diff --git a/cmd/podmanV2/common/createparse.go b/cmd/podmanV2/common/createparse.go
new file mode 100644
index 000000000..89524a04b
--- /dev/null
+++ b/cmd/podmanV2/common/createparse.go
@@ -0,0 +1,51 @@
+package common
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/parse"
+ "github.com/containers/libpod/pkg/util"
+ "github.com/pkg/errors"
+)
+
+// validate determines if the flags and values given by the user are valid. things checked
+// by validate must not need any state information on the flag (i.e. changed)
+func (c *ContainerCLIOpts) validate() error {
+ var ()
+ if c.Rm && c.Restart != "" && c.Restart != "no" {
+ return errors.Errorf("the --rm option conflicts with --restart")
+ }
+
+ if _, err := util.ValidatePullType(c.Pull); err != nil {
+ return err
+ }
+ // Verify the additional hosts are in correct format
+ for _, host := range c.Net.AddHosts {
+ if _, err := parse.ValidateExtraHost(host); err != nil {
+ return err
+ }
+ }
+
+ if dnsSearches := c.Net.DNSSearch; len(dnsSearches) > 0 {
+ // Validate domains are good
+ for _, dom := range dnsSearches {
+ if dom == "." {
+ if len(dnsSearches) > 1 {
+ return errors.Errorf("cannot pass additional search domains when also specifying '.'")
+ }
+ continue
+ }
+ if _, err := parse.ValidateDomain(dom); err != nil {
+ return err
+ }
+ }
+ }
+ var imageVolType = map[string]string{
+ "bind": "",
+ "tmpfs": "",
+ "ignore": "",
+ }
+ if _, ok := imageVolType[c.ImageVolume]; !ok {
+ return errors.Errorf("invalid image-volume type %q. Pick one of bind, tmpfs, or ignore", c.ImageVolume)
+ }
+ return nil
+
+}
diff --git a/cmd/podmanV2/common/default.go b/cmd/podmanV2/common/default.go
new file mode 100644
index 000000000..b71fcb6f0
--- /dev/null
+++ b/cmd/podmanV2/common/default.go
@@ -0,0 +1,121 @@
+package common
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/containers/buildah/pkg/parse"
+ "github.com/containers/libpod/pkg/apparmor"
+ "github.com/containers/libpod/pkg/cgroups"
+ "github.com/containers/libpod/pkg/rootless"
+ "github.com/containers/libpod/pkg/sysinfo"
+ "github.com/opencontainers/selinux/go-selinux"
+)
+
+// TODO these options are directly embedded into many of the CLI cobra values, as such
+// this approach will not work in a remote client. so we will need to likely do something like a
+// supported and unsupported approach here and backload these options into the specgen
+// once we are "on" the host system.
+func getDefaultSecurityOptions() []string {
+ securityOpts := []string{}
+ if defaultContainerConfig.Containers.SeccompProfile != "" && defaultContainerConfig.Containers.SeccompProfile != parse.SeccompDefaultPath {
+ securityOpts = append(securityOpts, fmt.Sprintf("seccomp=%s", defaultContainerConfig.Containers.SeccompProfile))
+ }
+ if apparmor.IsEnabled() && defaultContainerConfig.Containers.ApparmorProfile != "" {
+ securityOpts = append(securityOpts, fmt.Sprintf("apparmor=%s", defaultContainerConfig.Containers.ApparmorProfile))
+ }
+ if selinux.GetEnabled() && !defaultContainerConfig.Containers.EnableLabeling {
+ securityOpts = append(securityOpts, fmt.Sprintf("label=%s", selinux.DisableSecOpt()[0]))
+ }
+ return securityOpts
+}
+
+// getDefaultSysctls
+func getDefaultSysctls() []string {
+ return defaultContainerConfig.Containers.DefaultSysctls
+}
+
+func getDefaultVolumes() []string {
+ return defaultContainerConfig.Containers.Volumes
+}
+
+func getDefaultDevices() []string {
+ return defaultContainerConfig.Containers.Devices
+}
+
+func getDefaultDNSServers() []string { //nolint
+ return defaultContainerConfig.Containers.DNSServers
+}
+
+func getDefaultDNSSearches() []string { //nolint
+ return defaultContainerConfig.Containers.DNSSearches
+}
+
+func getDefaultDNSOptions() []string { //nolint
+ return defaultContainerConfig.Containers.DNSOptions
+}
+
+func getDefaultEnv() []string {
+ return defaultContainerConfig.Containers.Env
+}
+
+func getDefaultInitPath() string {
+ return defaultContainerConfig.Containers.InitPath
+}
+
+func getDefaultIPCNS() string {
+ return defaultContainerConfig.Containers.IPCNS
+}
+
+func getDefaultPidNS() string {
+ return defaultContainerConfig.Containers.PidNS
+}
+
+func getDefaultNetNS() string { //nolint
+ if defaultContainerConfig.Containers.NetNS == "private" && rootless.IsRootless() {
+ return "slirp4netns"
+ }
+ return defaultContainerConfig.Containers.NetNS
+}
+
+func getDefaultCgroupNS() string {
+ return defaultContainerConfig.Containers.CgroupNS
+}
+
+func getDefaultUTSNS() string {
+ return defaultContainerConfig.Containers.UTSNS
+}
+
+func getDefaultShmSize() string {
+ return defaultContainerConfig.Containers.ShmSize
+}
+
+func getDefaultUlimits() []string {
+ return defaultContainerConfig.Containers.DefaultUlimits
+}
+
+func getDefaultUserNS() string {
+ userns := os.Getenv("PODMAN_USERNS")
+ if userns != "" {
+ return userns
+ }
+ return defaultContainerConfig.Containers.UserNS
+}
+
+func getDefaultPidsLimit() int64 {
+ if rootless.IsRootless() {
+ cgroup2, _ := cgroups.IsCgroup2UnifiedMode()
+ if cgroup2 {
+ return defaultContainerConfig.Containers.PidsLimit
+ }
+ }
+ return sysinfo.GetDefaultPidsLimit()
+}
+
+func getDefaultPidsDescription() string {
+ return "Tune container pids limit (set 0 for unlimited)"
+}
+
+func GetDefaultDetachKeys() string {
+ return defaultContainerConfig.Engine.DetachKeys
+}
diff --git a/cmd/podmanV2/common/ports.go b/cmd/podmanV2/common/ports.go
new file mode 100644
index 000000000..7e2b1e79d
--- /dev/null
+++ b/cmd/podmanV2/common/ports.go
@@ -0,0 +1,126 @@
+package common
+
+import (
+ "fmt"
+ "net"
+ "strconv"
+
+ "github.com/cri-o/ocicni/pkg/ocicni"
+ "github.com/docker/go-connections/nat"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+// ExposedPorts parses user and image ports and returns binding information
+func ExposedPorts(expose []string, publish []ocicni.PortMapping, publishAll bool, imageExposedPorts map[string]struct{}) ([]ocicni.PortMapping, error) {
+ containerPorts := make(map[string]string)
+
+ // TODO this needs to be added into a something that
+ // has access to an imageengine
+ // add expose ports from the image itself
+ //for expose := range imageExposedPorts {
+ // _, port := nat.SplitProtoPort(expose)
+ // containerPorts[port] = ""
+ //}
+
+ // add the expose ports from the user (--expose)
+ // can be single or a range
+ for _, expose := range expose {
+ //support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
+ _, port := nat.SplitProtoPort(expose)
+ //parse the start and end port and create a sequence of ports to expose
+ //if expose a port, the start and end port are the same
+ start, end, err := nat.ParsePortRange(port)
+ if err != nil {
+ return nil, fmt.Errorf("invalid range format for --expose: %s, error: %s", expose, err)
+ }
+ for i := start; i <= end; i++ {
+ containerPorts[strconv.Itoa(int(i))] = ""
+ }
+ }
+
+ // TODO/FIXME this is hell reencarnated
+ // parse user inputted port bindings
+ pbPorts, portBindings, err := nat.ParsePortSpecs([]string{})
+ if err != nil {
+ return nil, err
+ }
+
+ // delete exposed container ports if being used by -p
+ for i := range pbPorts {
+ delete(containerPorts, i.Port())
+ }
+
+ // iterate container ports and make port bindings from them
+ if publishAll {
+ for e := range containerPorts {
+ //support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
+ //proto, port := nat.SplitProtoPort(e)
+ p, err := nat.NewPort("tcp", e)
+ if err != nil {
+ return nil, err
+ }
+ rp, err := getRandomPort()
+ if err != nil {
+ return nil, err
+ }
+ logrus.Debug(fmt.Sprintf("Using random host port %d with container port %d", rp, p.Int()))
+ portBindings[p] = CreatePortBinding(rp, "")
+ }
+ }
+
+ // We need to see if any host ports are not populated and if so, we need to assign a
+ // random port to them.
+ for k, pb := range portBindings {
+ if pb[0].HostPort == "" {
+ hostPort, err := getRandomPort()
+ if err != nil {
+ return nil, err
+ }
+ logrus.Debug(fmt.Sprintf("Using random host port %d with container port %s", hostPort, k.Port()))
+ pb[0].HostPort = strconv.Itoa(hostPort)
+ }
+ }
+ var pms []ocicni.PortMapping
+ for k, v := range portBindings {
+ for _, pb := range v {
+ hp, err := strconv.Atoi(pb.HostPort)
+ if err != nil {
+ return nil, err
+ }
+ pms = append(pms, ocicni.PortMapping{
+ HostPort: int32(hp),
+ ContainerPort: int32(k.Int()),
+ //Protocol: "",
+ HostIP: pb.HostIP,
+ })
+ }
+ }
+ return pms, nil
+}
+
+func getRandomPort() (int, error) {
+ l, err := net.Listen("tcp", ":0")
+ if err != nil {
+ return 0, errors.Wrapf(err, "unable to get free port")
+ }
+ defer l.Close()
+ _, randomPort, err := net.SplitHostPort(l.Addr().String())
+ if err != nil {
+ return 0, errors.Wrapf(err, "unable to determine free port")
+ }
+ rp, err := strconv.Atoi(randomPort)
+ if err != nil {
+ return 0, errors.Wrapf(err, "unable to convert random port to int")
+ }
+ return rp, nil
+}
+
+//CreatePortBinding takes port (int) and IP (string) and creates an array of portbinding structs
+func CreatePortBinding(hostPort int, hostIP string) []nat.PortBinding {
+ pb := nat.PortBinding{
+ HostPort: strconv.Itoa(hostPort),
+ }
+ pb.HostIP = hostIP
+ return []nat.PortBinding{pb}
+}
diff --git a/cmd/podmanV2/common/specgen.go b/cmd/podmanV2/common/specgen.go
new file mode 100644
index 000000000..5245e206e
--- /dev/null
+++ b/cmd/podmanV2/common/specgen.go
@@ -0,0 +1,647 @@
+package common
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/containers/image/v5/manifest"
+ "github.com/containers/libpod/cmd/podmanV2/parse"
+ "github.com/containers/libpod/libpod"
+ ann "github.com/containers/libpod/pkg/annotations"
+ envLib "github.com/containers/libpod/pkg/env"
+ ns "github.com/containers/libpod/pkg/namespaces"
+ "github.com/containers/libpod/pkg/specgen"
+ systemdGen "github.com/containers/libpod/pkg/systemd/generate"
+ "github.com/containers/libpod/pkg/util"
+ "github.com/docker/go-units"
+ "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/pkg/errors"
+)
+
+func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string) error {
+ var (
+ err error
+ //namespaces map[string]string
+ )
+
+ // validate flags as needed
+ if err := c.validate(); err != nil {
+ return nil
+ }
+
+ inputCommand := args[1:]
+ if len(c.HealthCmd) > 0 {
+ s.HealthConfig, err = makeHealthCheckFromCli(c.HealthCmd, c.HealthInterval, c.HealthRetries, c.HealthTimeout, c.HealthStartPeriod)
+ if err != nil {
+ return err
+ }
+ }
+
+ s.IDMappings, err = util.ParseIDMapping(ns.UsernsMode(c.UserNS), c.UIDMap, c.GIDMap, c.SubUIDName, c.SubGIDName)
+ if err != nil {
+ return err
+ }
+ if m := c.Memory; len(m) > 0 {
+ ml, err := units.RAMInBytes(m)
+ if err != nil {
+ return errors.Wrapf(err, "invalid value for memory")
+ }
+ s.ResourceLimits.Memory.Limit = &ml
+ }
+ if m := c.MemoryReservation; len(m) > 0 {
+ mr, err := units.RAMInBytes(m)
+ if err != nil {
+ return errors.Wrapf(err, "invalid value for memory")
+ }
+ s.ResourceLimits.Memory.Reservation = &mr
+ }
+ if m := c.MemorySwap; len(m) > 0 {
+ var ms int64
+ if m == "-1" {
+ ms = int64(-1)
+ s.ResourceLimits.Memory.Swap = &ms
+ } else {
+ ms, err = units.RAMInBytes(m)
+ if err != nil {
+ return errors.Wrapf(err, "invalid value for memory")
+ }
+ }
+ s.ResourceLimits.Memory.Swap = &ms
+ }
+ if m := c.KernelMemory; len(m) > 0 {
+ mk, err := units.RAMInBytes(m)
+ if err != nil {
+ return errors.Wrapf(err, "invalid value for kernel-memory")
+ }
+ s.ResourceLimits.Memory.Kernel = &mk
+ }
+ if b := c.BlkIOWeight; len(b) > 0 {
+ u, err := strconv.ParseUint(b, 10, 16)
+ if err != nil {
+ return errors.Wrapf(err, "invalid value for blkio-weight")
+ }
+ nu := uint16(u)
+ s.ResourceLimits.BlockIO.Weight = &nu
+ }
+
+ s.Terminal = c.TTY
+ ep, err := ExposedPorts(c.Expose, c.Net.PublishPorts, c.PublishAll, nil)
+ if err != nil {
+ return err
+ }
+ s.PortMappings = ep
+ s.Pod = c.Pod
+
+ //s.CgroupNS = specgen.Namespace{
+ // NSMode: ,
+ // Value: "",
+ //}
+
+ //s.UserNS = specgen.Namespace{}
+
+ // Kernel Namespaces
+ // TODO Fix handling of namespace from pod
+ // Instead of integrating here, should be done in libpod
+ // However, that also involves setting up security opts
+ // when the pod's namespace is integrated
+ //namespaces = map[string]string{
+ // "cgroup": c.CGroupsNS,
+ // "pid": c.PID,
+ // //"net": c.Net.Network.Value, // TODO need help here
+ // "ipc": c.IPC,
+ // "user": c.User,
+ // "uts": c.UTS,
+ //}
+ //
+ //if len(c.PID) > 0 {
+ // split := strings.SplitN(c.PID, ":", 2)
+ // // need a way to do thsi
+ // specgen.Namespace{
+ // NSMode: split[0],
+ // }
+ // //Value: split1 if len allows
+ //}
+ // TODO this is going to have be done after things like pod creation are done because
+ // pod creation changes these values.
+ //pidMode := ns.PidMode(namespaces["pid"])
+ //usernsMode := ns.UsernsMode(namespaces["user"])
+ //utsMode := ns.UTSMode(namespaces["uts"])
+ //cgroupMode := ns.CgroupMode(namespaces["cgroup"])
+ //ipcMode := ns.IpcMode(namespaces["ipc"])
+ //// Make sure if network is set to container namespace, port binding is not also being asked for
+ //netMode := ns.NetworkMode(namespaces["net"])
+ //if netMode.IsContainer() {
+ // if len(portBindings) > 0 {
+ // return nil, errors.Errorf("cannot set port bindings on an existing container network namespace")
+ // }
+ //}
+
+ // TODO Remove when done with namespaces for realz
+ // Setting a default for IPC to get this working
+ s.IpcNS = specgen.Namespace{
+ NSMode: specgen.Private,
+ Value: "",
+ }
+
+ // TODO this is going to have to be done the libpod/server end of things
+ // USER
+ //user := c.String("user")
+ //if user == "" {
+ // switch {
+ // case usernsMode.IsKeepID():
+ // user = fmt.Sprintf("%d:%d", rootless.GetRootlessUID(), rootless.GetRootlessGID())
+ // case data == nil:
+ // user = "0"
+ // default:
+ // user = data.Config.User
+ // }
+ //}
+
+ // STOP SIGNAL
+ signalString := "TERM"
+ if sig := c.StopSignal; len(sig) > 0 {
+ signalString = sig
+ }
+ stopSignal, err := util.ParseSignal(signalString)
+ if err != nil {
+ return err
+ }
+ s.StopSignal = &stopSignal
+
+ // ENVIRONMENT VARIABLES
+ //
+ // Precedence order (higher index wins):
+ // 1) env-host, 2) image data, 3) env-file, 4) env
+ env := map[string]string{
+ "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 errors.Wrap(err, "error parsing host environment variables")
+ }
+
+ if c.EnvHost {
+ env = envLib.Join(env, osEnv)
+ }
+ // env-file overrides any previous variables
+ for _, f := range c.EnvFile {
+ fileEnv, err := envLib.ParseFile(f)
+ if err != nil {
+ return err
+ }
+ // File env is overridden by env.
+ env = envLib.Join(env, fileEnv)
+ }
+
+ // env overrides any previous variables
+ if cmdLineEnv := c.env; len(cmdLineEnv) > 0 {
+ parsedEnv, err := envLib.ParseSlice(cmdLineEnv)
+ if err != nil {
+ return err
+ }
+ env = envLib.Join(env, parsedEnv)
+ }
+ s.Env = env
+
+ // LABEL VARIABLES
+ labels, err := parse.GetAllLabels(c.LabelFile, c.Label)
+ if err != nil {
+ return errors.Wrapf(err, "unable to process labels")
+ }
+
+ if systemdUnit, exists := osEnv[systemdGen.EnvVariable]; exists {
+ labels[systemdGen.EnvVariable] = systemdUnit
+ }
+
+ s.Labels = labels
+
+ // ANNOTATIONS
+ annotations := make(map[string]string)
+
+ // First, add our default annotations
+ annotations[ann.TTY] = "false"
+ if c.TTY {
+ annotations[ann.TTY] = "true"
+ }
+
+ // Last, add user annotations
+ for _, annotation := range c.Annotation {
+ splitAnnotation := strings.SplitN(annotation, "=", 2)
+ if len(splitAnnotation) < 2 {
+ return errors.Errorf("Annotations must be formatted KEY=VALUE")
+ }
+ annotations[splitAnnotation[0]] = splitAnnotation[1]
+ }
+ s.Annotations = annotations
+
+ workDir := "/"
+ if wd := c.Workdir; len(wd) > 0 {
+ workDir = wd
+ }
+ s.WorkDir = workDir
+ entrypoint := []string{}
+ userCommand := []string{}
+ if ep := c.Entrypoint; len(ep) > 0 {
+ // Check if entrypoint specified is json
+ if err := json.Unmarshal([]byte(c.Entrypoint), &entrypoint); err != nil {
+ entrypoint = append(entrypoint, ep)
+ }
+ }
+
+ var command []string
+
+ // Build the command
+ // If we have an entry point, it goes first
+ if len(entrypoint) > 0 {
+ command = entrypoint
+ }
+ if len(inputCommand) > 0 {
+ // User command overrides data CMD
+ command = append(command, inputCommand...)
+ userCommand = append(userCommand, inputCommand...)
+ }
+
+ if len(inputCommand) > 0 {
+ s.Command = userCommand
+ } else {
+ s.Command = command
+ }
+
+ // SHM Size
+ shmSize, err := units.FromHumanSize(c.ShmSize)
+ if err != nil {
+ return errors.Wrapf(err, "unable to translate --shm-size")
+ }
+ s.ShmSize = &shmSize
+ s.HostAdd = c.Net.AddHosts
+ s.DNSServer = c.Net.DNSServers
+ s.DNSSearch = c.Net.DNSSearch
+ s.DNSOption = c.Net.DNSOptions
+
+ // deferred, must be added on libpod side
+ //var ImageVolumes map[string]struct{}
+ //if data != nil && c.String("image-volume") != "ignore" {
+ // ImageVolumes = data.Config.Volumes
+ //}
+
+ s.ImageVolumeMode = c.ImageVolume
+ systemd := c.SystemdD == "always"
+ if !systemd && command != nil {
+ x, err := strconv.ParseBool(c.SystemdD)
+ if err != nil {
+ return errors.Wrapf(err, "cannot parse bool %s", c.SystemdD)
+ }
+ if x && (command[0] == "/usr/sbin/init" || command[0] == "/sbin/init" || (filepath.Base(command[0]) == "systemd")) {
+ systemd = true
+ }
+ }
+ if systemd {
+ if s.StopSignal == nil {
+ stopSignal, err = util.ParseSignal("RTMIN+3")
+ if err != nil {
+ return errors.Wrapf(err, "error parsing systemd signal")
+ }
+ s.StopSignal = &stopSignal
+ }
+ }
+ swappiness := uint64(c.MemorySwappiness)
+ if s.ResourceLimits == nil {
+ s.ResourceLimits = &specs.LinuxResources{}
+ }
+ if s.ResourceLimits.Memory == nil {
+ s.ResourceLimits.Memory = &specs.LinuxMemory{}
+ }
+ s.ResourceLimits.Memory.Swappiness = &swappiness
+
+ if s.LogConfiguration == nil {
+ s.LogConfiguration = &specgen.LogConfig{}
+ }
+ s.LogConfiguration.Driver = libpod.KubernetesLogging
+ if ld := c.LogDriver; len(ld) > 0 {
+ s.LogConfiguration.Driver = ld
+ }
+ if s.ResourceLimits.Pids == nil {
+ s.ResourceLimits.Pids = &specs.LinuxPids{}
+ }
+ s.ResourceLimits.Pids.Limit = c.PIDsLimit
+ if c.CGroups == "disabled" && c.PIDsLimit > 0 {
+ s.ResourceLimits.Pids.Limit = -1
+ }
+ // TODO WTF
+ //cgroup := &cc.CgroupConfig{
+ // Cgroups: c.String("cgroups"),
+ // Cgroupns: c.String("cgroupns"),
+ // CgroupParent: c.String("cgroup-parent"),
+ // CgroupMode: cgroupMode,
+ //}
+ //
+ //userns := &cc.UserConfig{
+ // GroupAdd: c.StringSlice("group-add"),
+ // IDMappings: idmappings,
+ // UsernsMode: usernsMode,
+ // User: user,
+ //}
+ //
+ //uts := &cc.UtsConfig{
+ // UtsMode: utsMode,
+ // NoHosts: c.Bool("no-hosts"),
+ // HostAdd: c.StringSlice("add-host"),
+ // Hostname: c.String("hostname"),
+ //}
+
+ sysctl := map[string]string{}
+ if ctl := c.Sysctl; len(ctl) > 0 {
+ sysctl, err = util.ValidateSysctls(ctl)
+ if err != nil {
+ return err
+ }
+ }
+ s.Sysctl = sysctl
+
+ s.CapAdd = c.CapAdd
+ s.CapDrop = c.CapDrop
+ s.Privileged = c.Privileged
+ s.ReadOnlyFilesystem = c.ReadOnly
+
+ // TODO
+ // ouitside of specgen and oci though
+ // defaults to true, check spec/storage
+ //s.readon = c.ReadOnlyTmpFS
+ // TODO convert to map?
+ // check if key=value and convert
+ sysmap := make(map[string]string)
+ for _, ctl := range c.Sysctl {
+ splitCtl := strings.SplitN(ctl, "=", 2)
+ if len(splitCtl) < 2 {
+ return errors.Errorf("invalid sysctl value %q", ctl)
+ }
+ sysmap[splitCtl[0]] = splitCtl[1]
+ }
+ s.Sysctl = sysmap
+
+ for _, opt := range c.SecurityOpt {
+ if opt == "no-new-privileges" {
+ s.ContainerSecurityConfig.NoNewPrivileges = true
+ } else {
+ con := strings.SplitN(opt, "=", 2)
+ if len(con) != 2 {
+ return fmt.Errorf("invalid --security-opt 1: %q", opt)
+ }
+
+ switch con[0] {
+ case "label":
+ // TODO selinux opts and label opts are the same thing
+ s.ContainerSecurityConfig.SelinuxOpts = append(s.ContainerSecurityConfig.SelinuxOpts, con[1])
+ case "apparmor":
+ s.ContainerSecurityConfig.ApparmorProfile = con[1]
+ case "seccomp":
+ s.SeccompProfilePath = con[1]
+ default:
+ return fmt.Errorf("invalid --security-opt 2: %q", opt)
+ }
+ }
+ }
+
+ // TODO any idea why this was done
+ // storage.go from spec/
+ // grab it
+ //volumes := rtc.Containers.Volumes
+ // TODO conflict on populate?
+ //if v := c.Volume; len(v)> 0 {
+ // s.Volumes = append(volumes, c.StringSlice("volume")...)
+ //}
+ //s.volu
+
+ //s.Mounts = c.Mount
+ s.VolumesFrom = c.VolumesFrom
+
+ // TODO any idea why this was done
+ //devices := rtc.Containers.Devices
+ // TODO conflict on populate?
+ //
+ //if c.Changed("device") {
+ // devices = append(devices, c.StringSlice("device")...)
+ //}
+
+ // TODO things i cannot find in spec
+ // we dont think these are in the spec
+ // init - initbinary
+ // initpath
+ s.Stdin = c.Interactive
+ // quiet
+ //DeviceCgroupRules: c.StringSlice("device-cgroup-rule"),
+
+ if bps := c.DeviceReadBPs; len(bps) > 0 {
+ if s.ThrottleReadBpsDevice, err = parseThrottleBPSDevices(bps); err != nil {
+ return err
+ }
+ }
+
+ if bps := c.DeviceWriteBPs; len(bps) > 0 {
+ if s.ThrottleWriteBpsDevice, err = parseThrottleBPSDevices(bps); err != nil {
+ return err
+ }
+ }
+
+ if iops := c.DeviceReadIOPs; len(iops) > 0 {
+ if s.ThrottleReadIOPSDevice, err = parseThrottleIOPsDevices(iops); err != nil {
+ return err
+ }
+ }
+
+ if iops := c.DeviceWriteIOPs; len(iops) > 0 {
+ if s.ThrottleWriteIOPSDevice, err = parseThrottleIOPsDevices(iops); err != nil {
+ return err
+ }
+ }
+
+ s.ResourceLimits.Memory.DisableOOMKiller = &c.OOMKillDisable
+
+ // Rlimits/Ulimits
+ for _, u := range c.Ulimit {
+ if u == "host" {
+ s.Rlimits = nil
+ break
+ }
+ ul, err := units.ParseUlimit(u)
+ if err != nil {
+ return errors.Wrapf(err, "ulimit option %q requires name=SOFT:HARD, failed to be parsed", u)
+ }
+ rl := specs.POSIXRlimit{
+ Type: ul.Name,
+ Hard: uint64(ul.Hard),
+ Soft: uint64(ul.Soft),
+ }
+ s.Rlimits = append(s.Rlimits, rl)
+ }
+
+ //Tmpfs: c.StringArray("tmpfs"),
+
+ // TODO how to handle this?
+ //Syslog: c.Bool("syslog"),
+
+ logOpts := make(map[string]string)
+ for _, o := range c.LogOptions {
+ split := strings.SplitN(o, "=", 2)
+ if len(split) < 2 {
+ return errors.Errorf("invalid log option %q", o)
+ }
+ logOpts[split[0]] = split[1]
+ }
+ s.LogConfiguration.Options = logOpts
+ s.Name = c.Name
+
+ if err := parseWeightDevices(c.BlkIOWeightDevice, s); err != nil {
+ return err
+ }
+
+ if s.ResourceLimits.CPU == nil {
+ s.ResourceLimits.CPU = &specs.LinuxCPU{}
+ }
+ s.ResourceLimits.CPU.Shares = &c.CPUShares
+ s.ResourceLimits.CPU.Period = &c.CPUPeriod
+
+ // TODO research these
+ //s.ResourceLimits.CPU.Cpus = c.CPUS
+ //s.ResourceLimits.CPU.Cpus = c.CPUSetCPUs
+
+ //s.ResourceLimits.CPU. = c.CPUSetCPUs
+ s.ResourceLimits.CPU.Mems = c.CPUSetMems
+ s.ResourceLimits.CPU.Quota = &c.CPUQuota
+ s.ResourceLimits.CPU.RealtimePeriod = &c.CPURTPeriod
+ s.ResourceLimits.CPU.RealtimeRuntime = &c.CPURTRuntime
+ s.OOMScoreAdj = &c.OOMScoreAdj
+ s.RestartPolicy = c.Restart
+ s.Remove = c.Rm
+ s.StopTimeout = &c.StopTimeout
+
+ // TODO where should we do this?
+ //func verifyContainerResources(config *cc.CreateConfig, update bool) ([]string, error) {
+ return nil
+}
+
+func makeHealthCheckFromCli(inCmd, interval string, retries uint, timeout, startPeriod string) (*manifest.Schema2HealthConfig, error) {
+ // Every healthcheck requires a command
+ if len(inCmd) == 0 {
+ return nil, errors.New("Must define a healthcheck command for all healthchecks")
+ }
+
+ // first try to parse option value as JSON array of strings...
+ cmd := []string{}
+ err := json.Unmarshal([]byte(inCmd), &cmd)
+ if err != nil {
+ // ...otherwise pass it to "/bin/sh -c" inside the container
+ cmd = []string{"CMD-SHELL", inCmd}
+ }
+ hc := manifest.Schema2HealthConfig{
+ Test: cmd,
+ }
+
+ if interval == "disable" {
+ interval = "0"
+ }
+ intervalDuration, err := time.ParseDuration(interval)
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid healthcheck-interval %s ", interval)
+ }
+
+ hc.Interval = intervalDuration
+
+ if retries < 1 {
+ return nil, errors.New("healthcheck-retries must be greater than 0.")
+ }
+ hc.Retries = int(retries)
+ timeoutDuration, err := time.ParseDuration(timeout)
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid healthcheck-timeout %s", timeout)
+ }
+ if timeoutDuration < time.Duration(1) {
+ return nil, errors.New("healthcheck-timeout must be at least 1 second")
+ }
+ hc.Timeout = timeoutDuration
+
+ startPeriodDuration, err := time.ParseDuration(startPeriod)
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid healthcheck-start-period %s", startPeriod)
+ }
+ if startPeriodDuration < time.Duration(0) {
+ return nil, errors.New("healthcheck-start-period must be 0 seconds or greater")
+ }
+ hc.StartPeriod = startPeriodDuration
+
+ return &hc, nil
+}
+
+func parseWeightDevices(weightDevs []string, s *specgen.SpecGenerator) error {
+ for _, val := range weightDevs {
+ split := strings.SplitN(val, ":", 2)
+ if len(split) != 2 {
+ return fmt.Errorf("bad format: %s", val)
+ }
+ if !strings.HasPrefix(split[0], "/dev/") {
+ return fmt.Errorf("bad format for device path: %s", val)
+ }
+ weight, err := strconv.ParseUint(split[1], 10, 0)
+ if err != nil {
+ return fmt.Errorf("invalid weight for device: %s", val)
+ }
+ if weight > 0 && (weight < 10 || weight > 1000) {
+ return fmt.Errorf("invalid weight for device: %s", val)
+ }
+ w := uint16(weight)
+ s.WeightDevice[split[0]] = specs.LinuxWeightDevice{
+ Weight: &w,
+ LeafWeight: nil,
+ }
+ }
+ return nil
+}
+
+func parseThrottleBPSDevices(bpsDevices []string) (map[string]specs.LinuxThrottleDevice, error) {
+ td := make(map[string]specs.LinuxThrottleDevice)
+ for _, val := range bpsDevices {
+ split := strings.SplitN(val, ":", 2)
+ if len(split) != 2 {
+ return nil, fmt.Errorf("bad format: %s", val)
+ }
+ if !strings.HasPrefix(split[0], "/dev/") {
+ return nil, fmt.Errorf("bad format for device path: %s", val)
+ }
+ rate, err := units.RAMInBytes(split[1])
+ if err != nil {
+ return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val)
+ }
+ if rate < 0 {
+ return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val)
+ }
+ td[split[0]] = specs.LinuxThrottleDevice{Rate: uint64(rate)}
+ }
+ return td, nil
+}
+
+func parseThrottleIOPsDevices(iopsDevices []string) (map[string]specs.LinuxThrottleDevice, error) {
+ td := make(map[string]specs.LinuxThrottleDevice)
+ for _, val := range iopsDevices {
+ split := strings.SplitN(val, ":", 2)
+ if len(split) != 2 {
+ return nil, fmt.Errorf("bad format: %s", val)
+ }
+ if !strings.HasPrefix(split[0], "/dev/") {
+ return nil, fmt.Errorf("bad format for device path: %s", val)
+ }
+ rate, err := strconv.ParseUint(split[1], 10, 64)
+ if err != nil {
+ return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>. Number must be a positive integer", val)
+ }
+ td[split[0]] = specs.LinuxThrottleDevice{Rate: rate}
+ }
+ return td, nil
+}
diff --git a/cmd/podmanV2/containers/attach.go b/cmd/podmanV2/containers/attach.go
new file mode 100644
index 000000000..d62dcff86
--- /dev/null
+++ b/cmd/podmanV2/containers/attach.go
@@ -0,0 +1,60 @@
+package containers
+
+import (
+ "os"
+
+ "github.com/containers/libpod/cmd/podmanV2/common"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ attachDescription = "The podman attach command allows you to attach to a running container using the container's ID or name, either to view its ongoing output or to control it interactively."
+ attachCommand = &cobra.Command{
+ Use: "attach [flags] CONTAINER",
+ Short: "Attach to a running container",
+ Long: attachDescription,
+ RunE: attach,
+ Args: func(cmd *cobra.Command, args []string) error {
+ if len(args) > 1 || (len(args) == 0 && !cmd.Flag("latest").Changed) {
+ return errors.Errorf("attach requires the name or id of one running container or the latest flag")
+ }
+ return nil
+ },
+ PreRunE: preRunE,
+ Example: `podman attach ctrID
+ podman attach 1234
+ podman attach --no-stdin foobar`,
+ }
+)
+
+var (
+ attachOpts entities.AttachOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: attachCommand,
+ })
+ flags := attachCommand.Flags()
+ flags.StringVar(&attachOpts.DetachKeys, "detach-keys", common.GetDefaultDetachKeys(), "Select the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`")
+ flags.BoolVar(&attachOpts.NoStdin, "no-stdin", false, "Do not attach STDIN. The default is false")
+ flags.BoolVar(&attachOpts.SigProxy, "sig-proxy", true, "Proxy received signals to the process")
+ flags.BoolVarP(&attachOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ }
+}
+
+func attach(cmd *cobra.Command, args []string) error {
+ attachOpts.Stdin = os.Stdin
+ if attachOpts.NoStdin {
+ attachOpts.Stdin = nil
+ }
+ attachOpts.Stdout = os.Stdout
+ attachOpts.Stderr = os.Stderr
+ return registry.ContainerEngine().ContainerAttach(registry.GetContext(), args[0], attachOpts)
+}
diff --git a/cmd/podmanV2/containers/checkpoint.go b/cmd/podmanV2/containers/checkpoint.go
new file mode 100644
index 000000000..7c3e551bc
--- /dev/null
+++ b/cmd/podmanV2/containers/checkpoint.go
@@ -0,0 +1,79 @@
+package containers
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podmanV2/parse"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/cmd/podmanV2/utils"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/rootless"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ checkpointDescription = `
+ podman container checkpoint
+
+ Checkpoints one or more running containers. The container name or ID can be used.
+`
+ checkpointCommand = &cobra.Command{
+ Use: "checkpoint [flags] CONTAINER [CONTAINER...]",
+ Short: "Checkpoints one or more containers",
+ Long: checkpointDescription,
+ RunE: checkpoint,
+ Args: func(cmd *cobra.Command, args []string) error {
+ return parse.CheckAllLatestAndCIDFile(cmd, args, false, false)
+ },
+ Example: `podman container checkpoint --keep ctrID
+ podman container checkpoint --all
+ podman container checkpoint --leave-running --latest`,
+ }
+)
+
+var (
+ checkpointOptions entities.CheckpointOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: checkpointCommand,
+ Parent: containerCmd,
+ })
+ flags := checkpointCommand.Flags()
+ flags.BoolVarP(&checkpointOptions.Keep, "keep", "k", false, "Keep all temporary checkpoint files")
+ flags.BoolVarP(&checkpointOptions.LeaveRuninng, "leave-running", "R", false, "Leave the container running after writing checkpoint to disk")
+ flags.BoolVar(&checkpointOptions.TCPEstablished, "tcp-established", false, "Checkpoint a container with established TCP connections")
+ flags.BoolVarP(&checkpointOptions.All, "all", "a", false, "Checkpoint all running containers")
+ flags.BoolVarP(&checkpointOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ flags.StringVarP(&checkpointOptions.Export, "export", "e", "", "Export the checkpoint image to a tar.gz")
+ flags.BoolVar(&checkpointOptions.IgnoreRootFS, "ignore-rootfs", false, "Do not include root file-system changes when exporting")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ }
+}
+
+func checkpoint(cmd *cobra.Command, args []string) error {
+ var errs utils.OutputErrors
+ if rootless.IsRootless() {
+ return errors.New("checkpointing a container requires root")
+ }
+ if checkpointOptions.Export == "" && checkpointOptions.IgnoreRootFS {
+ return errors.Errorf("--ignore-rootfs can only be used with --export")
+ }
+ responses, err := registry.ContainerEngine().ContainerCheckpoint(context.Background(), args, checkpointOptions)
+ if err != nil {
+ return err
+ }
+ for _, r := range responses {
+ if r.Err == nil {
+ fmt.Println(r.Id)
+ } else {
+ errs = append(errs, r.Err)
+ }
+ }
+ return errs.PrintErrors()
+}
diff --git a/cmd/podmanV2/containers/create.go b/cmd/podmanV2/containers/create.go
new file mode 100644
index 000000000..fd5300966
--- /dev/null
+++ b/cmd/podmanV2/containers/create.go
@@ -0,0 +1,102 @@
+package containers
+
+import (
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podmanV2/common"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/specgen"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "github.com/spf13/cobra"
+)
+
+var (
+ createDescription = `Creates a new container from the given image or storage and prepares it for running the specified command.
+
+ The container ID is then printed to stdout. You can then start it at any time with the podman start <container_id> command. The container will be created with the initial state 'created'.`
+ createCommand = &cobra.Command{
+ Use: "create [flags] IMAGE [COMMAND [ARG...]]",
+ Short: "Create but do not start a container",
+ Long: createDescription,
+ RunE: create,
+ PersistentPreRunE: preRunE,
+ Args: cobra.MinimumNArgs(1),
+ Example: `podman create alpine ls
+ podman create --annotation HELLO=WORLD alpine ls
+ podman create -t -i --name myctr alpine ls`,
+ }
+)
+
+var (
+ cliVals common.ContainerCLIOpts
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: createCommand,
+ })
+ //common.GetCreateFlags(createCommand)
+ flags := createCommand.Flags()
+ flags.AddFlagSet(common.GetCreateFlags(&cliVals))
+ flags.AddFlagSet(common.GetNetFlags())
+ flags.SetNormalizeFunc(common.AliasFlags)
+}
+
+func create(cmd *cobra.Command, args []string) error {
+ var (
+ err error
+ rawImageInput string
+ )
+ cliVals.Net, err = common.NetFlagsToNetOptions(cmd)
+ if err != nil {
+ return err
+ }
+ if rfs := cliVals.RootFS; !rfs {
+ rawImageInput = args[0]
+ }
+
+ if err := createInit(cmd); err != nil {
+ return err
+ }
+ //TODO rootfs still
+ s := specgen.NewSpecGenerator(rawImageInput)
+ if err := common.FillOutSpecGen(s, &cliVals, args); err != nil {
+ return err
+ }
+
+ report, err := registry.ContainerEngine().ContainerCreate(registry.GetContext(), s)
+ if err != nil {
+ return err
+ }
+ fmt.Println(report.Id)
+ return nil
+}
+
+func createInit(c *cobra.Command) error {
+ if c.Flag("privileged").Changed && c.Flag("security-opt").Changed {
+ logrus.Warn("setting security options with --privileged has no effect")
+ }
+
+ if (c.Flag("dns").Changed || c.Flag("dns-opt").Changed || c.Flag("dns-search").Changed) && (cliVals.Net.Network.NSMode == specgen.NoNetwork || cliVals.Net.Network.IsContainer()) {
+ return errors.Errorf("conflicting options: dns and the network mode.")
+ }
+
+ if c.Flag("cpu-period").Changed && c.Flag("cpus").Changed {
+ return errors.Errorf("--cpu-period and --cpus cannot be set together")
+ }
+ if c.Flag("cpu-quota").Changed && c.Flag("cpus").Changed {
+ return errors.Errorf("--cpu-quota and --cpus cannot be set together")
+ }
+
+ if c.Flag("no-hosts").Changed && c.Flag("add-host").Changed {
+ return errors.Errorf("--no-hosts and --add-host cannot be set together")
+ }
+
+ // Docker-compatibility: the "-h" flag for run/create is reserved for
+ // the hostname (see https://github.com/containers/libpod/issues/1367).
+
+ return nil
+}
diff --git a/cmd/podmanV2/containers/exec.go b/cmd/podmanV2/containers/exec.go
new file mode 100644
index 000000000..4bff57dbb
--- /dev/null
+++ b/cmd/podmanV2/containers/exec.go
@@ -0,0 +1,93 @@
+package containers
+
+import (
+ "bufio"
+ "os"
+
+ "github.com/containers/libpod/cmd/podmanV2/common"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ envLib "github.com/containers/libpod/pkg/env"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ execDescription = `Execute the specified command inside a running container.
+`
+ execCommand = &cobra.Command{
+ Use: "exec [flags] CONTAINER [COMMAND [ARG...]]",
+ Short: "Run a process in a running container",
+ Long: execDescription,
+ PreRunE: preRunE,
+ RunE: exec,
+ Example: `podman exec -it ctrID ls
+ podman exec -it -w /tmp myCtr pwd
+ podman exec --user root ctrID ls`,
+ }
+)
+
+var (
+ envInput, envFile []string
+ execOpts entities.ExecOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: execCommand,
+ })
+ flags := execCommand.Flags()
+ flags.SetInterspersed(false)
+ flags.StringVar(&execOpts.DetachKeys, "detach-keys", common.GetDefaultDetachKeys(), "Select the key sequence for detaching a container. Format is a single character [a-Z] or ctrl-<value> where <value> is one of: a-z, @, ^, [, , or _")
+ flags.StringArrayVarP(&envInput, "env", "e", []string{}, "Set environment variables")
+ flags.StringSliceVar(&envFile, "env-file", []string{}, "Read in a file of environment variables")
+ flags.BoolVarP(&execOpts.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached")
+ flags.BoolVarP(&execOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ flags.BoolVar(&execOpts.Privileged, "privileged", false, "Give the process extended Linux capabilities inside the container. The default is false")
+ flags.BoolVarP(&execOpts.Tty, "tty", "t", false, "Allocate a pseudo-TTY. The default is false")
+ flags.StringVarP(&execOpts.User, "user", "u", "", "Sets the username or UID used and optionally the groupname or GID for the specified command")
+ flags.UintVar(&execOpts.PreserveFDs, "preserve-fds", 0, "Pass N additional file descriptors to the container")
+ flags.StringVarP(&execOpts.WorkDir, "workdir", "w", "", "Working directory inside the container")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ _ = flags.MarkHidden("preserve-fds")
+ }
+
+}
+func exec(cmd *cobra.Command, args []string) error {
+ var nameOrId string
+ execOpts.Cmd = args
+ if !execOpts.Latest {
+ execOpts.Cmd = args[1:]
+ nameOrId = args[0]
+ }
+ // Validate given environment variables
+ execOpts.Envs = make(map[string]string)
+ for _, f := range envFile {
+ fileEnv, err := envLib.ParseFile(f)
+ if err != nil {
+ return err
+ }
+ execOpts.Envs = envLib.Join(execOpts.Envs, fileEnv)
+ }
+
+ cliEnv, err := envLib.ParseSlice(envInput)
+ if err != nil {
+ return errors.Wrap(err, "error parsing environment variables")
+ }
+
+ execOpts.Envs = envLib.Join(execOpts.Envs, cliEnv)
+ execOpts.Streams.OutputStream = os.Stdout
+ execOpts.Streams.ErrorStream = os.Stderr
+ if execOpts.Interactive {
+ execOpts.Streams.InputStream = bufio.NewReader(os.Stdin)
+ execOpts.Streams.AttachInput = true
+ }
+ execOpts.Streams.AttachOutput = true
+ execOpts.Streams.AttachError = true
+
+ exitCode, err := registry.ContainerEngine().ContainerExec(registry.GetContext(), nameOrId, execOpts)
+ registry.SetExitCode(exitCode)
+ return err
+}
diff --git a/cmd/podmanV2/containers/export.go b/cmd/podmanV2/containers/export.go
new file mode 100644
index 000000000..b93b60878
--- /dev/null
+++ b/cmd/podmanV2/containers/export.go
@@ -0,0 +1,57 @@
+package containers
+
+import (
+ "context"
+ "os"
+
+ "github.com/containers/libpod/cmd/podmanV2/parse"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+ "golang.org/x/crypto/ssh/terminal"
+)
+
+var (
+ exportDescription = "Exports container's filesystem contents as a tar archive" +
+ " and saves it on the local machine."
+
+ exportCommand = &cobra.Command{
+ Use: "export [flags] CONTAINER",
+ Short: "Export container's filesystem contents as a tar archive",
+ Long: exportDescription,
+ PersistentPreRunE: preRunE,
+ RunE: export,
+ Args: cobra.ExactArgs(1),
+ Example: `podman export ctrID > myCtr.tar
+ podman export --output="myCtr.tar" ctrID`,
+ }
+)
+
+var (
+ exportOpts entities.ContainerExportOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: exportCommand,
+ })
+ exportCommand.SetHelpTemplate(registry.HelpTemplate())
+ exportCommand.SetUsageTemplate(registry.UsageTemplate())
+ flags := exportCommand.Flags()
+ flags.StringVarP(&exportOpts.Output, "output", "o", "", "Write to a specified file (default: stdout, which must be redirected)")
+}
+
+func export(cmd *cobra.Command, args []string) error {
+ if len(exportOpts.Output) == 0 {
+ file := os.Stdout
+ if terminal.IsTerminal(int(file.Fd())) {
+ return errors.Errorf("refusing to export to terminal. Use -o flag or redirect")
+ }
+ exportOpts.Output = "/dev/stdout"
+ } else if err := parse.ValidateFileName(exportOpts.Output); err != nil {
+ return err
+ }
+ return registry.ContainerEngine().ContainerExport(context.Background(), args[0], exportOpts)
+}
diff --git a/cmd/podmanV2/containers/restore.go b/cmd/podmanV2/containers/restore.go
new file mode 100644
index 000000000..6cab6ab50
--- /dev/null
+++ b/cmd/podmanV2/containers/restore.go
@@ -0,0 +1,104 @@
+package containers
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podmanV2/parse"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/cmd/podmanV2/utils"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/rootless"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ restoreDescription = `
+ podman container restore
+
+ Restores a container from a checkpoint. The container name or ID can be used.
+`
+ restoreCommand = &cobra.Command{
+ Use: "restore [flags] CONTAINER [CONTAINER...]",
+ Short: "Restores one or more containers from a checkpoint",
+ Long: restoreDescription,
+ RunE: restore,
+ Args: func(cmd *cobra.Command, args []string) error {
+ return parse.CheckAllLatestAndCIDFile(cmd, args, true, false)
+ },
+ Example: `podman container restore ctrID
+ podman container restore --latest
+ podman container restore --all`,
+ }
+)
+
+var (
+ restoreOptions entities.RestoreOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: restoreCommand,
+ Parent: containerCmd,
+ })
+ flags := restoreCommand.Flags()
+ flags.BoolVarP(&restoreOptions.All, "all", "a", false, "Restore all checkpointed containers")
+ flags.BoolVarP(&restoreOptions.Keep, "keep", "k", false, "Keep all temporary checkpoint files")
+ flags.BoolVarP(&restoreOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ flags.BoolVar(&restoreOptions.TCPEstablished, "tcp-established", false, "Restore a container with established TCP connections")
+ flags.StringVarP(&restoreOptions.Import, "import", "i", "", "Restore from exported checkpoint archive (tar.gz)")
+ flags.StringVarP(&restoreOptions.Name, "name", "n", "", "Specify new name for container restored from exported checkpoint (only works with --import)")
+ flags.BoolVar(&restoreOptions.IgnoreRootFS, "ignore-rootfs", false, "Do not apply root file-system changes when importing from exported checkpoint")
+ flags.BoolVar(&restoreOptions.IgnoreStaticIP, "ignore-static-ip", false, "Ignore IP address set via --static-ip")
+ flags.BoolVar(&restoreOptions.IgnoreStaticMAC, "ignore-static-mac", false, "Ignore MAC address set via --mac-address")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ }
+}
+
+func restore(cmd *cobra.Command, args []string) error {
+ var errs utils.OutputErrors
+ if rootless.IsRootless() {
+ return errors.New("restoring a container requires root")
+ }
+ if restoreOptions.Import == "" && restoreOptions.IgnoreRootFS {
+ return errors.Errorf("--ignore-rootfs can only be used with --import")
+ }
+ if restoreOptions.Import == "" && restoreOptions.Name != "" {
+ return errors.Errorf("--name can only be used with --import")
+ }
+ if restoreOptions.Name != "" && restoreOptions.TCPEstablished {
+ return errors.Errorf("--tcp-established cannot be used with --name")
+ }
+
+ argLen := len(args)
+ if restoreOptions.Import != "" {
+ if restoreOptions.All || restoreOptions.Latest {
+ return errors.Errorf("Cannot use --import with --all or --latest")
+ }
+ if argLen > 0 {
+ return errors.Errorf("Cannot use --import with positional arguments")
+ }
+ }
+ if (restoreOptions.All || restoreOptions.Latest) && argLen > 0 {
+ return errors.Errorf("no arguments are needed with --all or --latest")
+ }
+ if argLen < 1 && !restoreOptions.All && !restoreOptions.Latest && restoreOptions.Import == "" {
+ return errors.Errorf("you must provide at least one name or id")
+ }
+ responses, err := registry.ContainerEngine().ContainerRestore(context.Background(), args, restoreOptions)
+ if err != nil {
+ return err
+ }
+ for _, r := range responses {
+ if r.Err == nil {
+ fmt.Println(r.Id)
+ } else {
+ errs = append(errs, r.Err)
+ }
+ }
+ return errs.PrintErrors()
+
+}
diff --git a/cmd/podmanV2/images/import.go b/cmd/podmanV2/images/import.go
new file mode 100644
index 000000000..09a15585f
--- /dev/null
+++ b/cmd/podmanV2/images/import.go
@@ -0,0 +1,87 @@
+package images
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podmanV2/parse"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/hashicorp/go-multierror"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ importDescription = `Create a container image from the contents of the specified tarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz).
+
+ Note remote tar balls can be specified, via web address.
+ Optionally tag the image. You can specify the instructions using the --change option.`
+ importCommand = &cobra.Command{
+ Use: "import [flags] PATH [REFERENCE]",
+ Short: "Import a tarball to create a filesystem image",
+ Long: importDescription,
+ RunE: importCon,
+ PersistentPreRunE: preRunE,
+ Example: `podman import http://example.com/ctr.tar url-image
+ cat ctr.tar | podman -q import --message "importing the ctr.tar tarball" - image-imported
+ cat ctr.tar | podman import -`,
+ }
+)
+
+var (
+ importOpts entities.ImageImportOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: importCommand,
+ })
+
+ importCommand.SetHelpTemplate(registry.HelpTemplate())
+ importCommand.SetUsageTemplate(registry.UsageTemplate())
+ flags := importCommand.Flags()
+ flags.StringArrayVarP(&importOpts.Changes, "change", "c", []string{}, "Apply the following possible instructions to the created image (default []): CMD | ENTRYPOINT | ENV | EXPOSE | LABEL | STOPSIGNAL | USER | VOLUME | WORKDIR")
+ flags.StringVarP(&importOpts.Message, "message", "m", "", "Set commit message for imported image")
+ flags.BoolVarP(&importOpts.Quiet, "quiet", "q", false, "Suppress output")
+}
+
+func importCon(cmd *cobra.Command, args []string) error {
+ var (
+ source string
+ reference string
+ )
+ switch len(args) {
+ case 0:
+ return errors.Errorf("need to give the path to the tarball, or must specify a tarball of '-' for stdin")
+ case 1:
+ source = args[0]
+ case 2:
+ source = args[0]
+ // TODO when save is merged, we need to process reference
+ // like it is done in there or we end up with docker.io prepends
+ // instead of the localhost ones
+ reference = args[1]
+ default:
+ return errors.Errorf("too many arguments. Usage TARBALL [REFERENCE]")
+ }
+ errFileName := parse.ValidateFileName(source)
+ errURL := parse.ValidURL(source)
+ if errURL == nil {
+ importOpts.SourceIsURL = true
+ }
+ if errFileName != nil && errURL != nil {
+ return multierror.Append(errFileName, errURL)
+ }
+
+ importOpts.Source = source
+ importOpts.Reference = reference
+
+ response, err := registry.ImageEngine().Import(context.Background(), importOpts)
+ if err != nil {
+ return err
+ }
+ fmt.Println(response.Id)
+ return nil
+}
diff --git a/cmd/podmanV2/images/load.go b/cmd/podmanV2/images/load.go
new file mode 100644
index 000000000..f60dc4908
--- /dev/null
+++ b/cmd/podmanV2/images/load.go
@@ -0,0 +1,61 @@
+package images
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ loadDescription = "Loads an image from a locally stored archive (tar file) into container storage."
+ loadCommand = &cobra.Command{
+ Use: "load [flags] [NAME[:TAG]]",
+ Short: "Load an image from container archive",
+ Long: loadDescription,
+ RunE: load,
+ Args: cobra.MaximumNArgs(1),
+ PersistentPreRunE: preRunE,
+ }
+)
+
+var (
+ loadOpts entities.ImageLoadOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: loadCommand,
+ })
+
+ loadCommand.SetHelpTemplate(registry.HelpTemplate())
+ loadCommand.SetUsageTemplate(registry.UsageTemplate())
+ flags := loadCommand.Flags()
+ flags.StringVarP(&loadOpts.Input, "input", "i", "", "Read from specified archive file (default: stdin)")
+ flags.BoolVarP(&loadOpts.Quiet, "quiet", "q", false, "Suppress the output")
+ flags.StringVar(&loadOpts.SignaturePolicy, "signature-policy", "", "Pathname of signature policy file")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("signature-policy")
+ }
+
+}
+
+func load(cmd *cobra.Command, args []string) error {
+ if len(args) > 0 {
+ repo, err := image.NormalizedTag(args[0])
+ if err != nil {
+ return err
+ }
+ loadOpts.Name = repo.Name()
+ }
+ response, err := registry.ImageEngine().Load(context.Background(), loadOpts)
+ if err != nil {
+ return err
+ }
+ fmt.Println("Loaded image: " + response.Name)
+ return nil
+}
diff --git a/cmd/podmanV2/images/push.go b/cmd/podmanV2/images/push.go
new file mode 100644
index 000000000..82cc0c486
--- /dev/null
+++ b/cmd/podmanV2/images/push.go
@@ -0,0 +1,127 @@
+package images
+
+import (
+ buildahcli "github.com/containers/buildah/pkg/cli"
+ "github.com/containers/image/v5/types"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+ "github.com/spf13/pflag"
+)
+
+// pushOptionsWrapper wraps entities.ImagepushOptions and prevents leaking
+// CLI-only fields into the API types.
+type pushOptionsWrapper struct {
+ entities.ImagePushOptions
+ TLSVerifyCLI bool // CLI only
+}
+
+var (
+ pushOptions = pushOptionsWrapper{}
+ pushDescription = `Pushes a source image to a specified destination.
+
+ The Image "DESTINATION" uses a "transport":"details" format. See podman-push(1) section "DESTINATION" for the expected format.`
+
+ // Command: podman push
+ pushCmd = &cobra.Command{
+ Use: "push [flags] SOURCE DESTINATION",
+ Short: "Push an image to a specified destination",
+ Long: pushDescription,
+ PreRunE: preRunE,
+ RunE: imagePush,
+ Example: `podman push imageID docker://registry.example.com/repository:tag
+ podman push imageID oci-archive:/path/to/layout:image:tag`,
+ }
+
+ // Command: podman image push
+ // It's basically a clone of `pushCmd` with the exception of being a
+ // child of the images command.
+ imagePushCmd = &cobra.Command{
+ Use: pushCmd.Use,
+ Short: pushCmd.Short,
+ Long: pushCmd.Long,
+ PreRunE: pushCmd.PreRunE,
+ RunE: pushCmd.RunE,
+ Example: `podman image push imageID docker://registry.example.com/repository:tag
+ podman image push imageID oci-archive:/path/to/layout:image:tag`,
+ }
+)
+
+func init() {
+ // push
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: pushCmd,
+ })
+
+ pushCmd.SetHelpTemplate(registry.HelpTemplate())
+ pushCmd.SetUsageTemplate(registry.UsageTemplate())
+
+ flags := pushCmd.Flags()
+ pushFlags(flags)
+
+ // images push
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: imagePushCmd,
+ Parent: imageCmd,
+ })
+
+ imagePushCmd.SetHelpTemplate(registry.HelpTemplate())
+ imagePushCmd.SetUsageTemplate(registry.UsageTemplate())
+ pushFlags(imagePushCmd.Flags())
+}
+
+// pushFlags set the flags for the push command.
+func pushFlags(flags *pflag.FlagSet) {
+ flags.StringVar(&pushOptions.Authfile, "authfile", buildahcli.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
+ flags.StringVar(&pushOptions.CertDir, "cert-dir", "", "Path to a directory containing TLS certificates and keys")
+ flags.BoolVar(&pushOptions.Compress, "compress", false, "Compress tarball image layers when pushing to a directory using the 'dir' transport. (default is same compression type as source)")
+ flags.StringVar(&pushOptions.Credentials, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry")
+ flags.StringVar(&pushOptions.DigestFile, "digestfile", "", "Write the digest of the pushed image to the specified file")
+ flags.StringVarP(&pushOptions.Format, "format", "f", "", "Manifest type (oci, v2s1, or v2s2) to use when pushing an image using the 'dir' transport (default is manifest type of source)")
+ flags.BoolVarP(&pushOptions.Quiet, "quiet", "q", false, "Suppress output information when pushing images")
+ flags.BoolVar(&pushOptions.RemoveSignatures, "remove-signatures", false, "Discard any pre-existing signatures in the image")
+ flags.StringVar(&pushOptions.SignaturePolicy, "signature-policy", "", "Path to a signature-policy file")
+ flags.StringVar(&pushOptions.SignBy, "sign-by", "", "Add a signature at the destination using the specified key")
+ flags.BoolVar(&pushOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
+
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("authfile")
+ _ = flags.MarkHidden("cert-dir")
+ _ = flags.MarkHidden("compress")
+ _ = flags.MarkHidden("quiet")
+ _ = flags.MarkHidden("signature-policy")
+ _ = flags.MarkHidden("tls-verify")
+ }
+}
+
+// imagePush is implement the command for pushing images.
+func imagePush(cmd *cobra.Command, args []string) error {
+ var source, destination string
+ switch len(args) {
+ case 1:
+ source = args[0]
+ case 2:
+ source = args[0]
+ destination = args[1]
+ case 0:
+ fallthrough
+ default:
+ return errors.New("push requires at least one image name, or optionally a second to specify a different destination")
+ }
+
+ pushOptsAPI := pushOptions.ImagePushOptions
+ // TLS verification in c/image is controlled via a `types.OptionalBool`
+ // which allows for distinguishing among set-true, set-false, unspecified
+ // which is important to implement a sane way of dealing with defaults of
+ // boolean CLI flags.
+ if cmd.Flags().Changed("tls-verify") {
+ pushOptsAPI.TLSVerify = types.NewOptionalBool(pushOptions.TLSVerifyCLI)
+ }
+
+ // Let's do all the remaining Yoga in the API to prevent us from scattering
+ // logic across (too) many parts of the code.
+ return registry.ImageEngine().Push(registry.GetContext(), source, destination, pushOptsAPI)
+}
diff --git a/cmd/podmanV2/images/save.go b/cmd/podmanV2/images/save.go
new file mode 100644
index 000000000..ae39b7bce
--- /dev/null
+++ b/cmd/podmanV2/images/save.go
@@ -0,0 +1,87 @@
+package images
+
+import (
+ "context"
+ "os"
+ "strings"
+
+ "github.com/containers/libpod/libpod/define"
+
+ "github.com/containers/libpod/cmd/podmanV2/parse"
+ "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"
+ "golang.org/x/crypto/ssh/terminal"
+)
+
+var validFormats = []string{define.OCIManifestDir, define.OCIArchive, define.V2s2ManifestDir, define.V2s2Archive}
+
+var (
+ saveDescription = `Save an image to docker-archive or oci-archive on the local machine. Default is docker-archive.`
+
+ saveCommand = &cobra.Command{
+ Use: "save [flags] IMAGE",
+ Short: "Save image to an archive",
+ Long: saveDescription,
+ PersistentPreRunE: preRunE,
+ RunE: save,
+ Args: func(cmd *cobra.Command, args []string) error {
+ if len(args) == 0 {
+ return errors.Errorf("need at least 1 argument")
+ }
+ format, err := cmd.Flags().GetString("format")
+ if err != nil {
+ return err
+ }
+ if !util.StringInSlice(format, validFormats) {
+ return errors.Errorf("format value must be one of %s", strings.Join(validFormats, " "))
+ }
+ return nil
+ },
+ Example: `podman save --quiet -o myimage.tar imageID
+ podman save --format docker-dir -o ubuntu-dir ubuntu
+ podman save > alpine-all.tar alpine:latest`,
+ }
+)
+
+var (
+ saveOpts entities.ImageSaveOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: saveCommand,
+ })
+ flags := saveCommand.Flags()
+ flags.BoolVar(&saveOpts.Compress, "compress", false, "Compress tarball image layers when saving to a directory using the 'dir' transport. (default is same compression type as source)")
+ flags.StringVar(&saveOpts.Format, "format", define.V2s2Archive, "Save image to oci-archive, oci-dir (directory with oci manifest type), docker-archive, docker-dir (directory with v2s2 manifest type)")
+ flags.StringVarP(&saveOpts.Output, "output", "o", "", "Write to a specified file (default: stdout, which must be redirected)")
+ flags.BoolVarP(&saveOpts.Quiet, "quiet", "q", false, "Suppress the output")
+
+}
+
+func save(cmd *cobra.Command, args []string) error {
+ var (
+ tags []string
+ )
+ if cmd.Flag("compress").Changed && (saveOpts.Format != define.OCIManifestDir && saveOpts.Format != define.V2s2ManifestDir && saveOpts.Format == "") {
+ return errors.Errorf("--compress can only be set when --format is either 'oci-dir' or 'docker-dir'")
+ }
+ if len(saveOpts.Output) == 0 {
+ fi := os.Stdout
+ if terminal.IsTerminal(int(fi.Fd())) {
+ return errors.Errorf("refusing to save to terminal. Use -o flag or redirect")
+ }
+ saveOpts.Output = "/dev/stdout"
+ }
+ if err := parse.ValidateFileName(saveOpts.Output); err != nil {
+ return err
+ }
+ if len(args) > 1 {
+ tags = args[1:]
+ }
+ return registry.ImageEngine().Save(context.Background(), args[0], tags, saveOpts)
+}
diff --git a/cmd/podmanV2/main.go b/cmd/podmanV2/main.go
index e4daabb66..fe3cd9f16 100644
--- a/cmd/podmanV2/main.go
+++ b/cmd/podmanV2/main.go
@@ -12,9 +12,11 @@ import (
_ "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/system"
_ "github.com/containers/libpod/cmd/podmanV2/volumes"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/storage/pkg/reexec"
"github.com/sirupsen/logrus"
)
@@ -45,6 +47,11 @@ func init() {
}
func main() {
+ if reexec.Init() {
+ // We were invoked with a different argv[0] indicating that we
+ // had a specific job to do as a subprocess, and it's done.
+ return
+ }
for _, c := range registry.Commands {
if Contains(registry.EngineOptions.EngineMode, c.Mode) {
parent := rootCmd
diff --git a/cmd/podmanV2/parse/common.go b/cmd/podmanV2/parse/common.go
new file mode 100644
index 000000000..a5e9b4fc2
--- /dev/null
+++ b/cmd/podmanV2/parse/common.go
@@ -0,0 +1,50 @@
+package parse
+
+import (
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+// CheckAllLatestAndCIDFile checks that --all and --latest are used correctly.
+// If cidfile is set, also check for the --cidfile flag.
+func CheckAllLatestAndCIDFile(c *cobra.Command, args []string, ignoreArgLen bool, cidfile bool) error {
+ argLen := len(args)
+ if c.Flags().Lookup("all") == nil || c.Flags().Lookup("latest") == nil {
+ if !cidfile {
+ return errors.New("unable to lookup values for 'latest' or 'all'")
+ } else if c.Flags().Lookup("cidfile") == nil {
+ return errors.New("unable to lookup values for 'latest', 'all' or 'cidfile'")
+ }
+ }
+
+ specifiedAll, _ := c.Flags().GetBool("all")
+ specifiedLatest, _ := c.Flags().GetBool("latest")
+ specifiedCIDFile := false
+ if cid, _ := c.Flags().GetStringArray("cidfile"); len(cid) > 0 {
+ specifiedCIDFile = true
+ }
+
+ if specifiedCIDFile && (specifiedAll || specifiedLatest) {
+ return errors.Errorf("--all, --latest and --cidfile cannot be used together")
+ } else if specifiedAll && specifiedLatest {
+ return errors.Errorf("--all and --latest cannot be used together")
+ }
+
+ if ignoreArgLen {
+ return nil
+ }
+ if (argLen > 0) && (specifiedAll || specifiedLatest) {
+ return errors.Errorf("no arguments are needed with --all or --latest")
+ } else if cidfile && (argLen > 0) && (specifiedAll || specifiedLatest || specifiedCIDFile) {
+ return errors.Errorf("no arguments are needed with --all, --latest or --cidfile")
+ }
+
+ if specifiedCIDFile {
+ return nil
+ }
+
+ if argLen < 1 && !specifiedAll && !specifiedLatest && !specifiedCIDFile {
+ return errors.Errorf("you must provide at least one name or id")
+ }
+ return nil
+}
diff --git a/cmd/podmanV2/parse/parse.go b/cmd/podmanV2/parse/net.go
index 10d2146fa..03cda268c 100644
--- a/cmd/podmanV2/parse/parse.go
+++ b/cmd/podmanV2/parse/net.go
@@ -13,7 +13,6 @@ import (
"strings"
"github.com/pkg/errors"
- "github.com/spf13/cobra"
)
const (
@@ -187,47 +186,3 @@ func ValidURL(urlStr string) error {
}
return nil
}
-
-// checkAllLatestAndCIDFile checks that --all and --latest are used correctly.
-// If cidfile is set, also check for the --cidfile flag.
-func CheckAllLatestAndCIDFile(c *cobra.Command, args []string, ignoreArgLen bool, cidfile bool) error {
- argLen := len(args)
- if c.Flags().Lookup("all") == nil || c.Flags().Lookup("latest") == nil {
- if !cidfile {
- return errors.New("unable to lookup values for 'latest' or 'all'")
- } else if c.Flags().Lookup("cidfile") == nil {
- return errors.New("unable to lookup values for 'latest', 'all' or 'cidfile'")
- }
- }
-
- specifiedAll, _ := c.Flags().GetBool("all")
- specifiedLatest, _ := c.Flags().GetBool("latest")
- specifiedCIDFile := false
- if cid, _ := c.Flags().GetStringArray("cidfile"); len(cid) > 0 {
- specifiedCIDFile = true
- }
-
- if specifiedCIDFile && (specifiedAll || specifiedLatest) {
- return errors.Errorf("--all, --latest and --cidfile cannot be used together")
- } else if specifiedAll && specifiedLatest {
- return errors.Errorf("--all and --latest cannot be used together")
- }
-
- if ignoreArgLen {
- return nil
- }
- if (argLen > 0) && (specifiedAll || specifiedLatest) {
- return errors.Errorf("no arguments are needed with --all or --latest")
- } else if cidfile && (argLen > 0) && (specifiedAll || specifiedLatest || specifiedCIDFile) {
- return errors.Errorf("no arguments are needed with --all, --latest or --cidfile")
- }
-
- if specifiedCIDFile {
- return nil
- }
-
- if argLen < 1 && !specifiedAll && !specifiedLatest && !specifiedCIDFile {
- return errors.Errorf("you must provide at least one name or id")
- }
- return nil
-}
diff --git a/cmd/podmanV2/parse/parse_test.go b/cmd/podmanV2/parse/net_test.go
index a6ddc2be9..a6ddc2be9 100644
--- a/cmd/podmanV2/parse/parse_test.go
+++ b/cmd/podmanV2/parse/net_test.go
diff --git a/cmd/podmanV2/system/version.go b/cmd/podmanV2/system/version.go
new file mode 100644
index 000000000..e8002056b
--- /dev/null
+++ b/cmd/podmanV2/system/version.go
@@ -0,0 +1,119 @@
+package system
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "strings"
+ "text/tabwriter"
+ "time"
+
+ "github.com/containers/buildah/pkg/formats"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ versionCommand = &cobra.Command{
+ Use: "version",
+ Args: cobra.NoArgs,
+ Short: "Display the Podman Version Information",
+ RunE: version,
+ PersistentPreRunE: preRunE,
+ }
+ format string
+)
+
+type versionStruct struct {
+ Client define.Version
+ Server define.Version
+}
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: versionCommand,
+ })
+ flags := versionCommand.Flags()
+ flags.StringVarP(&format, "format", "f", "", "Change the output format to JSON or a Go template")
+}
+
+func version(cmd *cobra.Command, args []string) error {
+ var (
+ v versionStruct
+ err error
+ )
+ v.Client, err = define.GetVersion()
+ if err != nil {
+ return errors.Wrapf(err, "unable to determine version")
+ }
+ // TODO we need to discuss how to implement
+ // this more. current endpoints dont have a
+ // version endpoint. maybe we use info?
+ //if remote {
+ // v.Server, err = getRemoteVersion(c)
+ // if err != nil {
+ // return err
+ // }
+ //} else {
+ v.Server = v.Client
+ //}
+
+ versionOutputFormat := format
+ if versionOutputFormat != "" {
+ if strings.Join(strings.Fields(versionOutputFormat), "") == "{{json.}}" {
+ versionOutputFormat = formats.JSONString
+ }
+ var out formats.Writer
+ switch versionOutputFormat {
+ case formats.JSONString:
+ out = formats.JSONStruct{Output: v}
+ return out.Out()
+ default:
+ out = formats.StdoutTemplate{Output: v, Template: versionOutputFormat}
+ err := out.Out()
+ if err != nil {
+ // On Failure, assume user is using older version of podman version --format and check client
+ out = formats.StdoutTemplate{Output: v.Client, Template: versionOutputFormat}
+ if err1 := out.Out(); err1 != nil {
+ return err
+ }
+ }
+ }
+ return nil
+ }
+ w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
+ defer w.Flush()
+
+ if registry.IsRemote() {
+ if _, err := fmt.Fprintf(w, "Client:\n"); err != nil {
+ return err
+ }
+ formatVersion(w, v.Client)
+ if _, err := fmt.Fprintf(w, "\nServer:\n"); err != nil {
+ return err
+ }
+ formatVersion(w, v.Server)
+ } else {
+ formatVersion(w, v.Client)
+ }
+ return nil
+}
+
+func formatVersion(writer io.Writer, version define.Version) {
+ fmt.Fprintf(writer, "Version:\t%s\n", version.Version)
+ fmt.Fprintf(writer, "RemoteAPI Version:\t%d\n", version.RemoteAPIVersion)
+ fmt.Fprintf(writer, "Go Version:\t%s\n", version.GoVersion)
+ if version.GitCommit != "" {
+ fmt.Fprintf(writer, "Git Commit:\t%s\n", version.GitCommit)
+ }
+ // Prints out the build time in readable format
+ if version.Built != 0 {
+ fmt.Fprintf(writer, "Built:\t%s\n", time.Unix(version.Built, 0).Format(time.ANSIC))
+ }
+
+ fmt.Fprintf(writer, "OS/Arch:\t%s\n", version.OsArch)
+}
diff --git a/go.mod b/go.mod
index 8ab1eb8a6..96108b370 100644
--- a/go.mod
+++ b/go.mod
@@ -9,12 +9,12 @@ 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.5
- github.com/containers/common v0.6.1
+ github.com/containers/buildah v1.14.6-0.20200402210551-e9a6703edee2
+ github.com/containers/common v0.8.0
github.com/containers/conmon v2.0.14+incompatible
github.com/containers/image/v5 v5.3.1
github.com/containers/psgo v1.4.0
- github.com/containers/storage v1.16.6
+ github.com/containers/storage v1.18.1
github.com/coreos/go-systemd/v22 v22.0.0
github.com/cri-o/ocicni v0.1.1-0.20190920040751-deac903fd99b
github.com/cyphar/filepath-securejoin v0.2.2
diff --git a/go.sum b/go.sum
index 6efc9e968..5b150c690 100644
--- a/go.sum
+++ b/go.sum
@@ -64,8 +64,15 @@ github.com/containernetworking/plugins v0.8.5 h1:pCvEMrFf7yzJI8+/D/7jkvE96KD52b7
github.com/containernetworking/plugins v0.8.5/go.mod h1:UZ2539umj8djuRQmBxuazHeJbYrLV8BSBejkk+she6o=
github.com/containers/buildah v1.14.5 h1:0Q+UgkIG4gAgAEZCu+0Syu/fSKsM1EsrctwV8G299jo=
github.com/containers/buildah v1.14.5/go.mod h1:2rfICEnpTtrMhWF6FZLnAL1Bh7SNmjhiKrjuIo0ZuN8=
+github.com/containers/buildah v1.14.6-0.20200402210551-e9a6703edee2 h1:9WchHVTk/FuAHHMuClpAZqk8dxOsPi6i6Yw5ocLbZxk=
+github.com/containers/buildah v1.14.6-0.20200402210551-e9a6703edee2/go.mod h1:auylD7PH2uPpE+a/FmgZmP/uC30pIbR3cNYMPSNHxXg=
github.com/containers/common v0.6.1 h1:z9VeVXYeOnNV99uNLp7zoE5KO1n0hqz1mdm5a6AiIrA=
github.com/containers/common v0.6.1/go.mod h1:m62kenckrWi5rZx32kaLje2Og0hpf6NsaTBn6+b+Oys=
+github.com/containers/common v0.7.0/go.mod h1:UmhIdvSkhTR0hWR01AnuZGNufm80+A0s8isb05eTmz0=
+github.com/containers/common v0.8.0 h1:C+wjkcmR4gooeKCXZpyjsHSFARm5AZRegflGz0x0MMw=
+github.com/containers/common v0.8.0/go.mod h1:QJTx9+SvhHKP6e+p7Nxqc8oNnS5rSf0KVhxudIbDslU=
+github.com/containers/common v1.0.0 h1:sZB48LzGP4bP1CmrkQIFUzdUVBysqRv3kWVk4+qbaVA=
+github.com/containers/common v1.0.0/go.mod h1:m62kenckrWi5rZx32kaLje2Og0hpf6NsaTBn6+b+Oys=
github.com/containers/conmon v2.0.14+incompatible h1:knU1O1QxXy5YxtjMQVKEyCajROaehizK9FHaICl+P5Y=
github.com/containers/conmon v2.0.14+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
github.com/containers/image/v5 v5.2.1/go.mod h1:TfhmLwH+v1/HBVPIWH7diLs8XwcOkP3c7t7JFgqaUEc=
@@ -82,6 +89,9 @@ github.com/containers/storage v1.16.0/go.mod h1:nqN09JSi1/RSI1UAUwDYXPRiGSlq5FPb
github.com/containers/storage v1.16.5/go.mod h1:SdysZeLKJOvfHYysUWg9OZUC3gdZWi5b2b7NC18VpPE=
github.com/containers/storage v1.16.6 h1:G/thPW/LVRwJpQvve1V4DQXVZpxzSltC2fzc3yTEdi8=
github.com/containers/storage v1.16.6/go.mod h1:Fws4I+U+C4DmJxDbBs1z9SKk50DzN4LtA+g1b+FmkTY=
+github.com/containers/storage v1.18.0/go.mod h1:gbFeFybWhlVCk3buJ0sovNKs8MzWEBTrk8/sbJw8irQ=
+github.com/containers/storage v1.18.1 h1:W134oYa8ALd78yo6DKiDp6n7EWXrc+fCnYmJi6o49vo=
+github.com/containers/storage v1.18.1/go.mod h1:6NtCgnUeYsRlyZyrZ5qKkXYC560GRgvA7YrKRSAYSlo=
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-iptables v0.4.5 h1:DpHb9vJrZQEFMcVLFKAAGMUVX0XoRC0ptCthinRYm38=
@@ -345,6 +355,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.3 h1:8TiphsD2wboU7tygtGZ5ZBfCP9FH2ZtvEAli67V2PJ4=
github.com/openshift/imagebuilder v1.1.3/go.mod h1:9aJRczxCH0mvT6XQ+5STAQaPWz7OsWcU5/mRkt8IWeo=
+github.com/openshift/imagebuilder v1.1.4 h1:LUg8aTjyXMtlDx6IbtvaqofFGZ6aYqe+VIeATE735LM=
+github.com/openshift/imagebuilder v1.1.4/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/podmanv2-retry b/hack/podmanv2-retry
new file mode 100755
index 000000000..ea77486ff
--- /dev/null
+++ b/hack/podmanv2-retry
@@ -0,0 +1,37 @@
+#!/bin/bash
+#
+# podman-try - try running a command via PODMAN1; use PODMAN2 as fallback
+#
+# Intended for use with a podmanv2 client. If a command isn't yet
+# implemented, fall back to regular podman:
+#
+# Set PODMAN_V2 to the path to a podman v2 client
+# Set PODMAN_FALLBACK to the path to regular podman
+#
+# THIS IS IMPERFECT. In particular, it will not work if stdin is redirected
+# (e.g. 'podman ... < file' or 'something | podman'); nor for anything
+# that generates continuous output ('podman logs -f'); and probably more
+# situations.
+#
+
+die() {
+ echo "$(basename $0): $*" >&2
+ exit 1
+}
+
+test -n "$PODMAN_V2" || die "Please set \$PODMAN_V2 (path to podman v2)"
+test -n "$PODMAN_FALLBACK" || die "Please set \$PODMAN_FALLBACK (path to podman)"
+
+
+result=$(${PODMAN_V2} "$@" 2>&1)
+rc=$?
+
+if [ $rc == 125 ]; then
+ if [[ "$result" =~ unrecognized\ command|unknown\ flag|unknown\ shorthand ]]; then
+ result=$(${PODMAN_FALLBACK} "$@")
+ rc=$?
+ fi
+fi
+
+echo -n "$result"
+exit $rc
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 967180437..55c79fa74 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -3,7 +3,6 @@ package libpod
import (
"bufio"
"context"
- "io"
"io/ioutil"
"net"
"os"
@@ -96,7 +95,7 @@ func (c *Container) Start(ctx context.Context, recursive bool) (err error) {
// The channel will be closed automatically after the result of attach has been
// sent.
// If recursive is set, StartAndAttach will also start all containers this container depends on.
-func (c *Container) StartAndAttach(ctx context.Context, streams *AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, recursive bool) (attachResChan <-chan error, err error) {
+func (c *Container) StartAndAttach(ctx context.Context, streams *define.AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, recursive bool) (attachResChan <-chan error, err error) {
if !c.batched {
c.lock.Lock()
defer c.lock.Unlock()
@@ -213,29 +212,10 @@ func (c *Container) Kill(signal uint) error {
return c.save()
}
-// AttachStreams contains streams that will be attached to the container
-type AttachStreams struct {
- // OutputStream will be attached to container's STDOUT
- OutputStream io.WriteCloser
- // ErrorStream will be attached to container's STDERR
- ErrorStream io.WriteCloser
- // InputStream will be attached to container's STDIN
- InputStream *bufio.Reader
- // AttachOutput is whether to attach to STDOUT
- // If false, stdout will not be attached
- AttachOutput bool
- // AttachError is whether to attach to STDERR
- // If false, stdout will not be attached
- AttachError bool
- // AttachInput is whether to attach to STDIN
- // If false, stdout will not be attached
- AttachInput bool
-}
-
// Attach attaches to a container.
// This function returns when the attach finishes. It does not hold the lock for
// the duration of its runtime, only using it at the beginning to verify state.
-func (c *Container) Attach(streams *AttachStreams, keys string, resize <-chan remotecommand.TerminalSize) error {
+func (c *Container) Attach(streams *define.AttachStreams, keys string, resize <-chan remotecommand.TerminalSize) error {
if !c.batched {
c.lock.Lock()
if err := c.syncContainer(); err != nil {
diff --git a/libpod/container_exec.go b/libpod/container_exec.go
index 5469462f8..c1ce8b724 100644
--- a/libpod/container_exec.go
+++ b/libpod/container_exec.go
@@ -221,7 +221,7 @@ func (c *Container) ExecStart(sessionID string) error {
// ExecStartAndAttach starts and attaches to an exec session in a container.
// TODO: Should we include detach keys in the signature to allow override?
// TODO: How do we handle AttachStdin/AttachStdout/AttachStderr?
-func (c *Container) ExecStartAndAttach(sessionID string, streams *AttachStreams) error {
+func (c *Container) ExecStartAndAttach(sessionID string, streams *define.AttachStreams) error {
if !c.batched {
c.lock.Lock()
defer c.lock.Unlock()
@@ -544,7 +544,7 @@ func (c *Container) ExecResize(sessionID string, newSize remotecommand.TerminalS
// Exec emulates the old Libpod exec API, providing a single call to create,
// run, and remove an exec session. Returns exit code and error. Exit code is
// not guaranteed to be set sanely if error is not nil.
-func (c *Container) Exec(config *ExecConfig, streams *AttachStreams, resize <-chan remotecommand.TerminalSize) (int, error) {
+func (c *Container) Exec(config *ExecConfig, streams *define.AttachStreams, resize <-chan remotecommand.TerminalSize) (int, error) {
sessionID, err := c.ExecCreate(config)
if err != nil {
return -1, err
diff --git a/libpod/container_top_linux.go b/libpod/container_top_linux.go
index 2a35a2ae9..98a69966a 100644
--- a/libpod/container_top_linux.go
+++ b/libpod/container_top_linux.go
@@ -112,7 +112,7 @@ func (c *Container) execPS(args []string) ([]string, error) {
defer wErrPipe.Close()
defer rErrPipe.Close()
- streams := new(AttachStreams)
+ streams := new(define.AttachStreams)
streams.OutputStream = wPipe
streams.ErrorStream = wErrPipe
streams.AttachOutput = true
diff --git a/libpod/define/config.go b/libpod/define/config.go
index 5598f97a3..10e00062a 100644
--- a/libpod/define/config.go
+++ b/libpod/define/config.go
@@ -1,5 +1,10 @@
package define
+import (
+ "bufio"
+ "io"
+)
+
var (
// DefaultInfraImage to use for infra container
DefaultInfraImage = "k8s.gcr.io/pause:3.2"
@@ -26,3 +31,29 @@ type InfoData struct {
// VolumeDriverLocal is the "local" volume driver. It is managed by libpod
// itself.
const VolumeDriverLocal = "local"
+
+const (
+ OCIManifestDir = "oci-dir"
+ OCIArchive = "oci-archive"
+ V2s2ManifestDir = "docker-dir"
+ V2s2Archive = "docker-archive"
+)
+
+// AttachStreams contains streams that will be attached to the container
+type AttachStreams struct {
+ // OutputStream will be attached to container's STDOUT
+ OutputStream io.WriteCloser
+ // ErrorStream will be attached to container's STDERR
+ ErrorStream io.WriteCloser
+ // InputStream will be attached to container's STDIN
+ InputStream *bufio.Reader
+ // AttachOutput is whether to attach to STDOUT
+ // If false, stdout will not be attached
+ AttachOutput bool
+ // AttachError is whether to attach to STDERR
+ // If false, stdout will not be attached
+ AttachError bool
+ // AttachInput is whether to attach to STDIN
+ // If false, stdout will not be attached
+ AttachInput bool
+}
diff --git a/libpod/healthcheck.go b/libpod/healthcheck.go
index 08a613dfe..daddb6561 100644
--- a/libpod/healthcheck.go
+++ b/libpod/healthcheck.go
@@ -108,7 +108,7 @@ func (c *Container) runHealthCheck() (HealthCheckStatus, error) {
hcw := hcWriteCloser{
captureBuffer,
}
- streams := new(AttachStreams)
+ streams := new(define.AttachStreams)
streams.OutputStream = hcw
streams.ErrorStream = hcw
diff --git a/libpod/image/image.go b/libpod/image/image.go
index 5f914ed79..80cc6f15a 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -512,8 +512,8 @@ func getImageDigest(ctx context.Context, src types.ImageReference, sc *types.Sys
return "@" + imageDigest.Hex(), nil
}
-// normalizedTag returns the canonical version of tag for use in Image.Names()
-func normalizedTag(tag string) (reference.Named, error) {
+// NormalizedTag returns the canonical version of tag for use in Image.Names()
+func NormalizedTag(tag string) (reference.Named, error) {
decomposedTag, err := decompose(tag)
if err != nil {
return nil, err
@@ -541,7 +541,7 @@ func (i *Image) TagImage(tag string) error {
if err := i.reloadImage(); err != nil {
return err
}
- ref, err := normalizedTag(tag)
+ ref, err := NormalizedTag(tag)
if err != nil {
return err
}
diff --git a/libpod/image/image_test.go b/libpod/image/image_test.go
index 19f7eee1e..3cd368cdc 100644
--- a/libpod/image/image_test.go
+++ b/libpod/image/image_test.go
@@ -292,7 +292,7 @@ func TestNormalizedTag(t *testing.T) {
{"ns/busybox:latest", "localhost/ns/busybox:latest"}, // Unqualified with a dot-less namespace
{"docker.io/busybox:latest", "docker.io/library/busybox:latest"}, // docker.io without /library/
} {
- res, err := normalizedTag(c.input)
+ res, err := NormalizedTag(c.input)
if c.expected == "" {
assert.Error(t, err, c.input)
} else {
diff --git a/libpod/oci.go b/libpod/oci.go
index ef46cf5c3..e4fbcb62e 100644
--- a/libpod/oci.go
+++ b/libpod/oci.go
@@ -4,6 +4,8 @@ import (
"bufio"
"net"
+ "github.com/containers/libpod/libpod/define"
+
"k8s.io/client-go/tools/remotecommand"
)
@@ -141,7 +143,7 @@ type ExecOptions struct {
// the container was run as will be used.
User string
// Streams are the streams that will be attached to the container.
- Streams *AttachStreams
+ Streams *define.AttachStreams
// PreserveFDs is a number of additional file descriptors (in addition
// to 0, 1, 2) that will be passed to the executed process. The total FDs
// passed will be 3 + PreserveFDs.
diff --git a/libpod/oci_attach_linux.go b/libpod/oci_attach_linux.go
index 433993edb..ff158c2d1 100644
--- a/libpod/oci_attach_linux.go
+++ b/libpod/oci_attach_linux.go
@@ -31,7 +31,7 @@ const (
// Attach to the given container
// Does not check if state is appropriate
// started is only required if startContainer is true
-func (c *Container) attach(streams *AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, startContainer bool, started chan bool) error {
+func (c *Container) attach(streams *define.AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, startContainer bool, started chan bool) error {
if !streams.AttachOutput && !streams.AttachError && !streams.AttachInput {
return errors.Wrapf(define.ErrInvalidArg, "must provide at least one stream to attach to")
}
@@ -94,7 +94,7 @@ func (c *Container) attach(streams *AttachStreams, keys string, resize <-chan re
// 4. attachToExec sends on startFd, signalling it has attached to the socket and child is ready to go
// 5. child receives on startFd, runs the runtime exec command
// attachToExec is responsible for closing startFd and attachFd
-func (c *Container) attachToExec(streams *AttachStreams, keys *string, sessionID string, startFd, attachFd *os.File) error {
+func (c *Container) attachToExec(streams *define.AttachStreams, keys *string, sessionID string, startFd, attachFd *os.File) error {
if !streams.AttachOutput && !streams.AttachError && !streams.AttachInput {
return errors.Wrapf(define.ErrInvalidArg, "must provide at least one stream to attach to")
}
@@ -189,7 +189,7 @@ func buildSocketPath(socketPath string) string {
return socketPath
}
-func setupStdioChannels(streams *AttachStreams, conn *net.UnixConn, detachKeys []byte) (chan error, chan error) {
+func setupStdioChannels(streams *define.AttachStreams, conn *net.UnixConn, detachKeys []byte) (chan error, chan error) {
receiveStdoutError := make(chan error)
go func() {
receiveStdoutError <- redirectResponseToOutputStreams(streams.OutputStream, streams.ErrorStream, streams.AttachOutput, streams.AttachError, conn)
@@ -200,8 +200,10 @@ func setupStdioChannels(streams *AttachStreams, conn *net.UnixConn, detachKeys [
var err error
if streams.AttachInput {
_, err = utils.CopyDetachable(conn, streams.InputStream, detachKeys)
- if connErr := conn.CloseWrite(); connErr != nil {
- logrus.Errorf("unable to close conn: %q", connErr)
+ if err == nil {
+ if connErr := conn.CloseWrite(); connErr != nil {
+ logrus.Errorf("unable to close conn: %q", connErr)
+ }
}
}
stdinDone <- err
@@ -255,7 +257,7 @@ func redirectResponseToOutputStreams(outputStream, errorStream io.Writer, writeO
return err
}
-func readStdio(streams *AttachStreams, receiveStdoutError, stdinDone chan error) error {
+func readStdio(streams *define.AttachStreams, receiveStdoutError, stdinDone chan error) error {
var err error
select {
case err = <-receiveStdoutError:
diff --git a/libpod/oci_attach_unsupported.go b/libpod/oci_attach_unsupported.go
index 987d2c973..3b0216e5d 100644
--- a/libpod/oci_attach_unsupported.go
+++ b/libpod/oci_attach_unsupported.go
@@ -9,10 +9,10 @@ import (
"k8s.io/client-go/tools/remotecommand"
)
-func (c *Container) attach(streams *AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, startContainer bool, started chan bool) error {
+func (c *Container) attach(streams *define.AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, startContainer bool, started chan bool) error {
return define.ErrNotImplemented
}
-func (c *Container) attachToExec(streams *AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, sessionID string, startFd *os.File, attachFd *os.File) error {
+func (c *Container) attachToExec(streams *define.AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, sessionID string, startFd *os.File, attachFd *os.File) error {
return define.ErrNotImplemented
}
diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go
index ce888c690..2e96dbe57 100644
--- a/libpod/oci_conmon_linux.go
+++ b/libpod/oci_conmon_linux.go
@@ -353,6 +353,9 @@ func (r *ConmonOCIRuntime) StartContainer(ctr *Container) error {
if notify, ok := os.LookupEnv("NOTIFY_SOCKET"); ok {
env = append(env, fmt.Sprintf("NOTIFY_SOCKET=%s", notify))
}
+ if path, ok := os.LookupEnv("PATH"); ok {
+ env = append(env, fmt.Sprintf("PATH=%s", path))
+ }
if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, env, r.path, "start", ctr.ID()); err != nil {
return err
}
@@ -575,13 +578,36 @@ func (r *ConmonOCIRuntime) HTTPAttach(ctr *Container, httpConn net.Conn, httpBuf
}
}
+// isRetryable returns whether the error was caused by a blocked syscall or the
+// specified operation on a non blocking file descriptor wasn't ready for completion.
+func isRetryable(err error) bool {
+ if errno, isErrno := errors.Cause(err).(syscall.Errno); isErrno {
+ return errno == syscall.EINTR || errno == syscall.EAGAIN
+ }
+ return false
+}
+
+// openControlFile opens the terminal control file.
+func openControlFile(ctr *Container, parentDir string) (*os.File, error) {
+ controlPath := filepath.Join(parentDir, "ctl")
+ for i := 0; i < 600; i++ {
+ controlFile, err := os.OpenFile(controlPath, unix.O_WRONLY|unix.O_NONBLOCK, 0)
+ if err == nil {
+ return controlFile, err
+ }
+ if !isRetryable(err) {
+ return nil, errors.Wrapf(err, "could not open ctl file for terminal resize for container %s", ctr.ID())
+ }
+ time.Sleep(time.Second / 10)
+ }
+ return nil, errors.Errorf("timeout waiting for %q", controlPath)
+}
+
// AttachResize resizes the terminal used by the given container.
func (r *ConmonOCIRuntime) AttachResize(ctr *Container, newSize remotecommand.TerminalSize) error {
- // TODO: probably want a dedicated function to get ctl file path?
- controlPath := filepath.Join(ctr.bundlePath(), "ctl")
- controlFile, err := os.OpenFile(controlPath, unix.O_WRONLY, 0)
+ controlFile, err := openControlFile(ctr, ctr.bundlePath())
if err != nil {
- return errors.Wrapf(err, "could not open ctl file for terminal resize")
+ return err
}
defer controlFile.Close()
@@ -785,11 +811,9 @@ func (r *ConmonOCIRuntime) ExecContainer(c *Container, sessionID string, options
// ExecAttachResize resizes the TTY of the given exec session.
func (r *ConmonOCIRuntime) ExecAttachResize(ctr *Container, sessionID string, newSize remotecommand.TerminalSize) error {
- // TODO: probably want a dedicated function to get ctl file path?
- controlPath := filepath.Join(ctr.execBundlePath(sessionID), "ctl")
- controlFile, err := os.OpenFile(controlPath, unix.O_WRONLY, 0)
+ controlFile, err := openControlFile(ctr, ctr.execBundlePath(sessionID))
if err != nil {
- return errors.Wrapf(err, "could not open ctl file for terminal resize for container %s exec session %s", ctr.ID(), sessionID)
+ return err
}
defer controlFile.Close()
@@ -909,6 +933,13 @@ func (r *ConmonOCIRuntime) CheckpointContainer(ctr *Container, options Container
if options.TCPEstablished {
args = append(args, "--tcp-established")
}
+ runtimeDir, err := util.GetRuntimeDir()
+ if err != nil {
+ return err
+ }
+ if err = os.Setenv("XDG_RUNTIME_DIR", runtimeDir); err != nil {
+ return errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR")
+ }
args = append(args, ctr.ID())
return utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, nil, r.path, args...)
}
@@ -918,7 +949,7 @@ func (r *ConmonOCIRuntime) CheckpointContainer(ctr *Container, options Container
func (r *ConmonOCIRuntime) SupportsCheckpoint() bool {
// Check if the runtime implements checkpointing. Currently only
// runc's checkpoint/restore implementation is supported.
- cmd := exec.Command(r.path, "checkpoint", "-h")
+ cmd := exec.Command(r.path, "checkpoint", "--help")
if err := cmd.Start(); err != nil {
return false
}
diff --git a/libpod/options.go b/libpod/options.go
index dfbec364a..65a089131 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -1334,7 +1334,7 @@ func WithNamedVolumes(volumes []*ContainerNamedVolume) CtrCreateOption {
}
destinations[vol.Dest] = true
- mountOpts, err := util.ProcessOptions(vol.Options, false, nil)
+ mountOpts, err := util.ProcessOptions(vol.Options, false, "")
if err != nil {
return errors.Wrapf(err, "error processing options for named volume %q mounted at %q", vol.Name, vol.Dest)
}
diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go
index c395ffc7f..ecadbd2f9 100644
--- a/pkg/adapter/containers.go
+++ b/pkg/adapter/containers.go
@@ -26,6 +26,7 @@ import (
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/libpod/logs"
"github.com/containers/libpod/pkg/adapter/shortcuts"
+ "github.com/containers/libpod/pkg/checkpoint"
envLib "github.com/containers/libpod/pkg/env"
"github.com/containers/libpod/pkg/systemd/generate"
"github.com/containers/storage"
@@ -625,7 +626,7 @@ func (r *LocalRuntime) Restore(ctx context.Context, c *cliconfig.RestoreValues)
switch {
case c.Import != "":
- containers, err = crImportCheckpoint(ctx, r.Runtime, c.Import, c.Name)
+ containers, err = checkpoint.CRImportCheckpoint(ctx, r.Runtime, c.Import, c.Name)
case c.All:
containers, err = r.GetContainers(filterFuncs...)
default:
@@ -1003,7 +1004,7 @@ func (r *LocalRuntime) ExecContainer(ctx context.Context, cli *cliconfig.ExecVal
}
env = envLib.Join(env, cliEnv)
- streams := new(libpod.AttachStreams)
+ streams := new(define.AttachStreams)
streams.OutputStream = os.Stdout
streams.ErrorStream = os.Stderr
if cli.Interactive {
diff --git a/pkg/adapter/terminal_linux.go b/pkg/adapter/terminal_linux.go
index ef5a6f926..a56704be6 100644
--- a/pkg/adapter/terminal_linux.go
+++ b/pkg/adapter/terminal_linux.go
@@ -7,6 +7,7 @@ import (
"os"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh/terminal"
@@ -14,7 +15,7 @@ import (
)
// ExecAttachCtr execs and attaches to a container
-func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, tty, privileged bool, env map[string]string, cmd []string, user, workDir string, streams *libpod.AttachStreams, preserveFDs uint, detachKeys string) (int, error) {
+func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, tty, privileged bool, env map[string]string, cmd []string, user, workDir string, streams *define.AttachStreams, preserveFDs uint, detachKeys string) (int, error) {
resize := make(chan remotecommand.TerminalSize)
haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd()))
@@ -69,7 +70,7 @@ func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr,
defer cancel()
}
- streams := new(libpod.AttachStreams)
+ streams := new(define.AttachStreams)
streams.OutputStream = stdout
streams.ErrorStream = stderr
streams.InputStream = bufio.NewReader(stdin)
diff --git a/pkg/adapter/terminal_unsupported.go b/pkg/adapter/terminal_unsupported.go
index 3009f0a38..9067757a1 100644
--- a/pkg/adapter/terminal_unsupported.go
+++ b/pkg/adapter/terminal_unsupported.go
@@ -11,7 +11,7 @@ import (
)
// ExecAttachCtr execs and attaches to a container
-func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, tty, privileged bool, env map[string]string, cmd []string, user, workDir string, streams *libpod.AttachStreams, preserveFDs uint, detachKeys string) (int, error) {
+func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, tty, privileged bool, env map[string]string, cmd []string, user, workDir string, streams *define.AttachStreams, preserveFDs uint, detachKeys string) (int, error) {
return -1, define.ErrNotImplemented
}
diff --git a/pkg/api/handlers/compat/containers_export.go b/pkg/api/handlers/compat/containers_export.go
new file mode 100644
index 000000000..37b9fbf2b
--- /dev/null
+++ b/pkg/api/handlers/compat/containers_export.go
@@ -0,0 +1,42 @@
+package compat
+
+import (
+ "io/ioutil"
+ "net/http"
+ "os"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/pkg/errors"
+)
+
+func ExportContainer(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ name := utils.GetName(r)
+ con, err := runtime.LookupContainer(name)
+ if err != nil {
+ utils.ContainerNotFound(w, name, err)
+ return
+ }
+ tmpfile, err := ioutil.TempFile("", "api.tar")
+ if err != nil {
+ utils.Error(w, "unable to create tarball tempfile", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile"))
+ return
+ }
+ defer os.Remove(tmpfile.Name())
+ if err := tmpfile.Close(); err != nil {
+ utils.Error(w, "unable to close tempfile", http.StatusInternalServerError, errors.Wrap(err, "unable to close tempfile"))
+ return
+ }
+ if err := con.Export(tmpfile.Name()); err != nil {
+ utils.Error(w, "failed to save the image", http.StatusInternalServerError, errors.Wrap(err, "failed to save image"))
+ return
+ }
+ rdr, err := os.Open(tmpfile.Name())
+ if err != nil {
+ utils.Error(w, "failed to read temp tarball", http.StatusInternalServerError, errors.Wrap(err, "failed to read the exported tarfile"))
+ return
+ }
+ defer rdr.Close()
+ utils.WriteResponse(w, http.StatusOK, rdr)
+}
diff --git a/pkg/api/handlers/compat/images_push.go b/pkg/api/handlers/compat/images_push.go
new file mode 100644
index 000000000..2260d5557
--- /dev/null
+++ b/pkg/api/handlers/compat/images_push.go
@@ -0,0 +1,80 @@
+package compat
+
+import (
+ "context"
+ "net/http"
+ "os"
+ "strings"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/gorilla/schema"
+ "github.com/pkg/errors"
+)
+
+// PushImage is the handler for the compat http endpoint for pushing images.
+func PushImage(w http.ResponseWriter, r *http.Request) {
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+
+ query := struct {
+ Tag string `schema:"tag"`
+ }{
+ // This is where you can override the golang default value for one of fields
+ }
+
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+
+ // Note that Docker's docs state "Image name or ID" to be in the path
+ // parameter but it really must be a name as Docker does not allow for
+ // pushing an image by ID.
+ imageName := strings.TrimSuffix(utils.GetName(r), "/push") // GetName returns the entire path
+ if query.Tag != "" {
+ imageName += ":" + query.Tag
+ }
+ if _, err := utils.ParseStorageReference(imageName); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "image source %q is not a containers-storage-transport reference", imageName))
+ return
+ }
+
+ newImage, err := runtime.ImageRuntime().NewFromLocal(imageName)
+ if err != nil {
+ utils.ImageNotFound(w, imageName, errors.Wrapf(err, "Failed to find image %s", imageName))
+ return
+ }
+
+ // TODO: the X-Registry-Auth header is not checked yet here nor in any other
+ // endpoint. Pushing does NOT work with authentication at the moment.
+ dockerRegistryOptions := &image.DockerRegistryOptions{}
+ authfile := ""
+ if sys := runtime.SystemContext(); sys != nil {
+ dockerRegistryOptions.DockerCertPath = sys.DockerCertPath
+ authfile = sys.AuthFilePath
+ }
+
+ err = newImage.PushImageToHeuristicDestination(
+ context.Background(),
+ imageName,
+ "", // manifest type
+ authfile,
+ "", // digest file
+ "", // signature policy
+ os.Stderr,
+ false, // force compression
+ image.SigningOptions{},
+ dockerRegistryOptions,
+ nil, // additional tags
+ )
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Error pushing image %q", imageName))
+ return
+ }
+
+ utils.WriteResponse(w, http.StatusOK, "")
+
+}
diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go
index cdc34004f..fde72552b 100644
--- a/pkg/api/handlers/libpod/containers.go
+++ b/pkg/api/handlers/libpod/containers.go
@@ -1,16 +1,21 @@
package libpod
import (
+ "io/ioutil"
"net/http"
+ "os"
"path/filepath"
"sort"
"strconv"
"time"
+ "github.com/containers/libpod/pkg/api/handlers/compat"
+
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/containers/libpod/pkg/domain/entities"
"github.com/gorilla/schema"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -325,3 +330,129 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts shared.P
}
return ps, nil
}
+
+func Checkpoint(w http.ResponseWriter, r *http.Request) {
+ var targetFile string
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ Keep bool `schema:"keep"`
+ LeaveRunning bool `schema:"leaveRunning"`
+ TCPEstablished bool `schema:"tcpEstablished"`
+ Export bool `schema:"export"`
+ IgnoreRootFS bool `schema:"ignoreRootFS"`
+ }{
+ // override any golang type defaults
+ }
+
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+ name := utils.GetName(r)
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ ctr, err := runtime.LookupContainer(name)
+ if err != nil {
+ utils.ContainerNotFound(w, name, err)
+ return
+ }
+ if query.Export {
+ tmpFile, err := ioutil.TempFile("", "checkpoint")
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ defer os.Remove(tmpFile.Name())
+ if err := tmpFile.Close(); err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ targetFile = tmpFile.Name()
+ }
+ options := libpod.ContainerCheckpointOptions{
+ Keep: query.Keep,
+ KeepRunning: query.LeaveRunning,
+ TCPEstablished: query.TCPEstablished,
+ IgnoreRootfs: query.IgnoreRootFS,
+ }
+ if query.Export {
+ options.TargetFile = targetFile
+ }
+ err = ctr.Checkpoint(r.Context(), options)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ if query.Export {
+ f, err := os.Open(targetFile)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ defer f.Close()
+ utils.WriteResponse(w, http.StatusOK, f)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, entities.CheckpointReport{Id: ctr.ID()})
+}
+
+func Restore(w http.ResponseWriter, r *http.Request) {
+ var (
+ targetFile string
+ )
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ Keep bool `schema:"keep"`
+ TCPEstablished bool `schema:"tcpEstablished"`
+ Import bool `schema:"import"`
+ Name string `schema:"name"`
+ IgnoreRootFS bool `schema:"ignoreRootFS"`
+ IgnoreStaticIP bool `schema:"ignoreStaticIP"`
+ IgnoreStaticMAC bool `schema:"ignoreStaticMAC"`
+ }{
+ // override any golang type defaults
+ }
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+ name := utils.GetName(r)
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ ctr, err := runtime.LookupContainer(name)
+ if err != nil {
+ utils.ContainerNotFound(w, name, err)
+ return
+ }
+ if query.Import {
+ t, err := ioutil.TempFile("", "restore")
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ defer t.Close()
+ if err := compat.SaveFromBody(t, r); err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ targetFile = t.Name()
+ }
+
+ options := libpod.ContainerCheckpointOptions{
+ Keep: query.Keep,
+ TCPEstablished: query.TCPEstablished,
+ IgnoreRootfs: query.IgnoreRootFS,
+ IgnoreStaticIP: query.IgnoreStaticIP,
+ IgnoreStaticMAC: query.IgnoreStaticMAC,
+ }
+ if query.Import {
+ options.TargetFile = targetFile
+ options.Name = query.Name
+ }
+ err = ctr.Restore(r.Context(), options)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, entities.RestoreReport{Id: ctr.ID()})
+}
diff --git a/pkg/api/handlers/libpod/containers_create.go b/pkg/api/handlers/libpod/containers_create.go
index ebca41151..38a341a89 100644
--- a/pkg/api/handlers/libpod/containers_create.go
+++ b/pkg/api/handlers/libpod/containers_create.go
@@ -7,6 +7,7 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/api/handlers/utils"
"github.com/containers/libpod/pkg/specgen"
+ "github.com/containers/libpod/pkg/specgen/generate"
"github.com/pkg/errors"
)
@@ -19,7 +20,11 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
return
}
- ctr, err := sg.MakeContainer(runtime)
+ if err := generate.CompleteSpec(r.Context(), runtime, &sg); err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ ctr, err := generate.MakeContainer(runtime, &sg)
if err != nil {
utils.InternalServerError(w, err)
return
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index bc227d9a1..850de4598 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -14,15 +14,16 @@ import (
"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/image/v5/types"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/image"
image2 "github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/api/handlers/utils"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/util"
+ utils2 "github.com/containers/libpod/utils"
"github.com/gorilla/schema"
"github.com/pkg/errors"
)
@@ -162,13 +163,16 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
}
func ExportImage(w http.ResponseWriter, r *http.Request) {
+ var (
+ output string
+ )
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
Compress bool `schema:"compress"`
Format string `schema:"format"`
}{
- Format: "docker-archive",
+ Format: define.OCIArchive,
}
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
@@ -176,14 +180,27 @@ func ExportImage(w http.ResponseWriter, r *http.Request) {
errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
return
}
-
- tmpfile, err := ioutil.TempFile("", "api.tar")
- if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile"))
- return
- }
- if err := tmpfile.Close(); err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to close tempfile"))
+ switch query.Format {
+ case define.OCIArchive, define.V2s2Archive:
+ tmpfile, err := ioutil.TempFile("", "api.tar")
+ if err != nil {
+ utils.Error(w, "unable to create tmpfile", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile"))
+ return
+ }
+ output = tmpfile.Name()
+ if err := tmpfile.Close(); err != nil {
+ utils.Error(w, "unable to close tmpfile", http.StatusInternalServerError, errors.Wrap(err, "unable to close tempfile"))
+ return
+ }
+ case define.OCIManifestDir, define.V2s2ManifestDir:
+ tmpdir, err := ioutil.TempDir("", "save")
+ if err != nil {
+ utils.Error(w, "unable to create tmpdir", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempdir"))
+ return
+ }
+ output = tmpdir
+ default:
+ utils.Error(w, "unknown format", http.StatusInternalServerError, errors.Errorf("unknown format %q", query.Format))
return
}
name := utils.GetName(r)
@@ -193,17 +210,28 @@ func ExportImage(w http.ResponseWriter, r *http.Request) {
return
}
- if err := newImage.Save(r.Context(), name, query.Format, tmpfile.Name(), []string{}, false, query.Compress); err != nil {
+ if err := newImage.Save(r.Context(), name, query.Format, output, []string{}, false, query.Compress); err != nil {
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err)
return
}
- rdr, err := os.Open(tmpfile.Name())
+ defer os.RemoveAll(output)
+ // if dir format, we need to tar it
+ if query.Format == "oci-dir" || query.Format == "docker-dir" {
+ rdr, err := utils2.Tar(output)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ defer rdr.Close()
+ utils.WriteResponse(w, http.StatusOK, rdr)
+ return
+ }
+ rdr, err := os.Open(output)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to read the exported tarfile"))
return
}
defer rdr.Close()
- defer os.Remove(tmpfile.Name())
utils.WriteResponse(w, http.StatusOK, rdr)
}
@@ -254,7 +282,7 @@ func ImagesLoad(w http.ResponseWriter, r *http.Request) {
return
}
}
- utils.WriteResponse(w, http.StatusOK, handlers.LibpodImagesLoadReport{ID: loadedImage})
+ utils.WriteResponse(w, http.StatusOK, entities.ImageLoadReport{Name: loadedImage})
}
func ImagesImport(w http.ResponseWriter, r *http.Request) {
@@ -300,7 +328,7 @@ func ImagesImport(w http.ResponseWriter, r *http.Request) {
return
}
- utils.WriteResponse(w, http.StatusOK, handlers.LibpodImagesImportReport{ID: importedImage})
+ utils.WriteResponse(w, http.StatusOK, entities.ImageImportReport{Id: importedImage})
}
// ImagesPull is the v2 libpod endpoint for pulling images. Note that the
@@ -331,29 +359,16 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
utils.InternalServerError(w, errors.New("reference parameter cannot be empty"))
return
}
- // Enforce the docker transport. This is just a precaution as some callers
- // might be accustomed to using the "transport:reference" notation. Using
- // another than the "docker://" transport does not really make sense for a
- // remote case. For loading tarballs, the load and import endpoints should
- // be used.
- dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name())
- imageRef, err := alltransports.ParseImageName(query.Reference)
- if err == nil && imageRef.Transport().Name() != docker.Transport.Name() {
+
+ imageRef, err := utils.ParseDockerReference(query.Reference)
+ if err != nil {
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
- errors.Errorf("reference %q must be a docker reference", query.Reference))
+ errors.Wrapf(err, "image destination %q is not a docker-transport reference", query.Reference))
return
- } else if err != nil {
- origErr := err
- imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", dockerPrefix, query.Reference))
- if err != nil {
- utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
- errors.Wrapf(origErr, "reference %q must be a docker reference", query.Reference))
- return
- }
}
// Trim the docker-transport prefix.
- rawImage := strings.TrimPrefix(query.Reference, dockerPrefix)
+ rawImage := strings.TrimPrefix(query.Reference, fmt.Sprintf("%s://", docker.Transport.Name()))
// all-tags doesn't work with a tagged reference, so let's check early
namedRef, err := reference.Parse(rawImage)
@@ -385,7 +400,7 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
OSChoice: query.OverrideOS,
ArchitectureChoice: query.OverrideArch,
}
- if query.TLSVerify {
+ if _, found := r.URL.Query()["tlsVerify"]; found {
dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
}
@@ -408,13 +423,19 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
}
}
+ authfile := ""
+ if sys := runtime.SystemContext(); sys != nil {
+ dockerRegistryOptions.DockerCertPath = sys.DockerCertPath
+ authfile = sys.AuthFilePath
+ }
+
// Finally pull the images
for _, img := range imagesToPull {
newImage, err := runtime.ImageRuntime().New(
context.Background(),
img,
"",
- "",
+ authfile,
os.Stderr,
&dockerRegistryOptions,
image.SigningOptions{},
@@ -430,6 +451,94 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusOK, res)
}
+// PushImage is the handler for the compat http endpoint for pushing images.
+func PushImage(w http.ResponseWriter, r *http.Request) {
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+
+ query := struct {
+ Credentials string `schema:"credentials"`
+ Destination string `schema:"destination"`
+ TLSVerify bool `schema:"tlsVerify"`
+ }{
+ // This is where you can override the golang default value for one of fields
+ }
+
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+
+ source := strings.TrimSuffix(utils.GetName(r), "/push") // GetName returns the entire path
+ if _, err := utils.ParseStorageReference(source); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "image source %q is not a containers-storage-transport reference", source))
+ return
+ }
+
+ destination := query.Destination
+ if destination == "" {
+ destination = source
+ }
+
+ if _, err := utils.ParseDockerReference(destination); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "image destination %q is not a docker-transport reference", destination))
+ return
+ }
+
+ newImage, err := runtime.ImageRuntime().NewFromLocal(source)
+ if err != nil {
+ utils.ImageNotFound(w, source, errors.Wrapf(err, "Failed to find image %s", source))
+ return
+ }
+
+ var registryCreds *types.DockerAuthConfig
+ if len(query.Credentials) != 0 {
+ creds, err := util.ParseRegistryCreds(query.Credentials)
+ if err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "error parsing credentials %q", query.Credentials))
+ return
+ }
+ registryCreds = creds
+ }
+
+ // TODO: the X-Registry-Auth header is not checked yet here nor in any other
+ // endpoint. Pushing does NOT work with authentication at the moment.
+ dockerRegistryOptions := &image.DockerRegistryOptions{
+ DockerRegistryCreds: registryCreds,
+ }
+ authfile := ""
+ if sys := runtime.SystemContext(); sys != nil {
+ dockerRegistryOptions.DockerCertPath = sys.DockerCertPath
+ authfile = sys.AuthFilePath
+ }
+ if _, found := r.URL.Query()["tlsVerify"]; found {
+ dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
+ }
+
+ err = newImage.PushImageToHeuristicDestination(
+ context.Background(),
+ destination,
+ "", // manifest type
+ authfile,
+ "", // digest file
+ "", // signature policy
+ os.Stderr,
+ false, // force compression
+ image.SigningOptions{},
+ dockerRegistryOptions,
+ nil, // additional tags
+ )
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Error pushing image %q", destination))
+ return
+ }
+
+ utils.WriteResponse(w, http.StatusOK, "")
+}
+
func CommitContainer(w http.ResponseWriter, r *http.Request) {
var (
destImage string
diff --git a/pkg/api/handlers/swagger.go b/pkg/api/handlers/swagger.go
index 52763a050..33a9fdd58 100644
--- a/pkg/api/handlers/swagger.go
+++ b/pkg/api/handlers/swagger.go
@@ -31,14 +31,14 @@ type swagImageInspect struct {
// swagger:response DocsLibpodImagesLoadResponse
type swagLibpodImagesLoadResponse struct {
// in:body
- Body []LibpodImagesLoadReport
+ Body entities.ImageLoadReport
}
// Import response
// swagger:response DocsLibpodImagesImportResponse
type swagLibpodImagesImportResponse struct {
// in:body
- Body LibpodImagesImportReport
+ Body entities.ImageImportReport
}
// Pull response
diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go
index 89a571e67..496512f2e 100644
--- a/pkg/api/handlers/types.go
+++ b/pkg/api/handlers/types.go
@@ -38,10 +38,6 @@ type LibpodImagesLoadReport struct {
ID string `json:"id"`
}
-type LibpodImagesImportReport struct {
- ID string `json:"id"`
-}
-
type LibpodImagesPullReport struct {
ID string `json:"id"`
}
diff --git a/pkg/api/handlers/utils/handler.go b/pkg/api/handlers/utils/handler.go
index 32b8c5b0a..b5bd488fb 100644
--- a/pkg/api/handlers/utils/handler.go
+++ b/pkg/api/handlers/utils/handler.go
@@ -46,6 +46,13 @@ func WriteResponse(w http.ResponseWriter, code int, value interface{}) {
if _, err := io.Copy(w, v); err != nil {
logrus.Errorf("unable to copy to response: %q", err)
}
+ case io.Reader:
+ w.Header().Set("Content-Type", "application/x-tar")
+ w.WriteHeader(code)
+
+ if _, err := io.Copy(w, v); err != nil {
+ logrus.Errorf("unable to copy to response: %q", err)
+ }
default:
WriteJSON(w, code, value)
}
diff --git a/pkg/api/handlers/utils/images.go b/pkg/api/handlers/utils/images.go
index 696d5f745..1c67de9db 100644
--- a/pkg/api/handlers/utils/images.go
+++ b/pkg/api/handlers/utils/images.go
@@ -4,11 +4,52 @@ import (
"fmt"
"net/http"
+ "github.com/containers/image/v5/docker"
+ "github.com/containers/image/v5/storage"
+ "github.com/containers/image/v5/transports/alltransports"
+ "github.com/containers/image/v5/types"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image"
"github.com/gorilla/schema"
+ "github.com/pkg/errors"
)
+// ParseDockerReference parses the specified image name to a
+// `types.ImageReference` and enforces it to refer to a docker-transport
+// reference.
+func ParseDockerReference(name string) (types.ImageReference, error) {
+ dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name())
+ imageRef, err := alltransports.ParseImageName(name)
+ if err == nil && imageRef.Transport().Name() != docker.Transport.Name() {
+ return nil, errors.Errorf("reference %q must be a docker reference", name)
+ } else if err != nil {
+ origErr := err
+ imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", dockerPrefix, name))
+ if err != nil {
+ return nil, errors.Wrapf(origErr, "reference %q must be a docker reference", name)
+ }
+ }
+ return imageRef, nil
+}
+
+// ParseStorageReference parses the specified image name to a
+// `types.ImageReference` and enforces it to refer to a
+// containers-storage-transport reference.
+func ParseStorageReference(name string) (types.ImageReference, error) {
+ storagePrefix := fmt.Sprintf("%s:", storage.Transport.Name())
+ imageRef, err := alltransports.ParseImageName(name)
+ if err == nil && imageRef.Transport().Name() != docker.Transport.Name() {
+ return nil, errors.Errorf("reference %q must be a storage reference", name)
+ } else if err != nil {
+ origErr := err
+ imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", storagePrefix, name))
+ if err != nil {
+ return nil, errors.Wrapf(origErr, "reference %q must be a storage reference", name)
+ }
+ }
+ return imageRef, nil
+}
+
// GetImages is a common function used to get images for libpod and other compatibility
// mechanisms
func GetImages(w http.ResponseWriter, r *http.Request) ([]*image.Image, error) {
diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go
index 08834ff01..f126112d0 100644
--- a/pkg/api/server/register_containers.go
+++ b/pkg/api/server/register_containers.go
@@ -587,6 +587,29 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
r.HandleFunc(VersionedPath("/containers/{name}/resize"), s.APIHandler(compat.ResizeContainer)).Methods(http.MethodPost)
// Added non version path to URI to support docker non versioned paths
r.HandleFunc("/containers/{name}/resize", s.APIHandler(compat.ResizeContainer)).Methods(http.MethodPost)
+ // swagger:operation GET /containers/{name}/export compat exportContainer
+ // ---
+ // tags:
+ // - containers (compat)
+ // summary: Export a container
+ // description: Export the contents of a container as a tarball.
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: the name or ID of the container
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // description: tarball is returned in body
+ // 404:
+ // $ref: "#/responses/NoSuchContainer"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/containers/{name}/export"), s.APIHandler(compat.ExportContainer)).Methods(http.MethodGet)
+ r.HandleFunc("/containers/{name}/export", s.APIHandler(compat.ExportContainer)).Methods(http.MethodGet)
/*
libpod endpoints
@@ -1237,5 +1260,122 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// 500:
// $ref: "#/responses/InternalError"
r.HandleFunc(VersionedPath("/libpod/containers/{name}/resize"), s.APIHandler(compat.ResizeContainer)).Methods(http.MethodPost)
+ // swagger:operation GET /libpod/containers/{name}/export libpod libpodExportContainer
+ // ---
+ // tags:
+ // - containers
+ // summary: Export a container
+ // description: Export the contents of a container as a tarball.
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: the name or ID of the container
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // description: tarball is returned in body
+ // 404:
+ // $ref: "#/responses/NoSuchContainer"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/libpod/containers/{name}/export"), s.APIHandler(compat.ExportContainer)).Methods(http.MethodGet)
+ // swagger:operation GET /libpod/containers/{name}/checkout libpod libpodCheckpointContainer
+ // ---
+ // tags:
+ // - containers
+ // summary: Checkpoint a container
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: the name or ID of the container
+ // - in: query
+ // name: keep
+ // type: boolean
+ // description: keep all temporary checkpoint files
+ // - in: query
+ // name: leaveRunning
+ // type: boolean
+ // description: leave the container running after writing checkpoint to disk
+ // - in: query
+ // name: tcpEstablished
+ // type: boolean
+ // description: checkpoint a container with established TCP connections
+ // - in: query
+ // name: export
+ // type: boolean
+ // description: export the checkpoint image to a tar.gz
+ // - in: query
+ // name: ignoreRootFS
+ // type: boolean
+ // description: do not include root file-system changes when exporting
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // description: tarball is returned in body if exported
+ // 404:
+ // $ref: "#/responses/NoSuchContainer"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/libpod/containers/{name}/checkpoint"), s.APIHandler(libpod.Checkpoint)).Methods(http.MethodPost)
+ // swagger:operation GET /libpod/containers/{name} restore libpod libpodRestoreContainer
+ // ---
+ // tags:
+ // - containers
+ // summary: Restore a container
+ // description: Restore a container from a checkpoint.
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: the name or id of the container
+ // - in: query
+ // name: name
+ // type: string
+ // description: the name of the container when restored from a tar. can only be used with import
+ // - in: query
+ // name: keep
+ // type: boolean
+ // description: keep all temporary checkpoint files
+ // - in: query
+ // name: leaveRunning
+ // type: boolean
+ // description: leave the container running after writing checkpoint to disk
+ // - in: query
+ // name: tcpEstablished
+ // type: boolean
+ // description: checkpoint a container with established TCP connections
+ // - in: query
+ // name: import
+ // type: boolean
+ // description: import the restore from a checkpoint tar.gz
+ // - in: query
+ // name: ignoreRootFS
+ // type: boolean
+ // description: do not include root file-system changes when exporting
+ // - in: query
+ // name: ignoreStaticIP
+ // type: boolean
+ // description: ignore IP address if set statically
+ // - in: query
+ // name: ignoreStaticMAC
+ // type: boolean
+ // description: ignore MAC address if set statically
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // description: tarball is returned in body if exported
+ // 404:
+ // $ref: "#/responses/NoSuchContainer"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/libpod/containers/{name}/restore"), s.APIHandler(libpod.Restore)).Methods(http.MethodPost)
return nil
}
diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go
index 74b245a77..d45423096 100644
--- a/pkg/api/server/register_images.go
+++ b/pkg/api/server/register_images.go
@@ -211,6 +211,41 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
r.Handle(VersionedPath("/images/{name:.*}"), s.APIHandler(compat.RemoveImage)).Methods(http.MethodDelete)
// Added non version path to URI to support docker non versioned paths
r.Handle("/images/{name:.*}", s.APIHandler(compat.RemoveImage)).Methods(http.MethodDelete)
+ // swagger:operation POST /images/{name:.*}/push compat pushImage
+ // ---
+ // tags:
+ // - images (compat)
+ // summary: Push Image
+ // description: Push an image to a container registry
+ // parameters:
+ // - in: path
+ // name: name:.*
+ // type: string
+ // required: true
+ // description: Name of image to push.
+ // - in: query
+ // name: tag
+ // type: string
+ // description: The tag to associate with the image on the registry.
+ // - in: header
+ // name: X-Registry-Auth
+ // type: string
+ // description: A base64-encoded auth configuration.
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // description: no error
+ // schema:
+ // type: string
+ // format: binary
+ // 404:
+ // $ref: '#/responses/NoSuchImage'
+ // 500:
+ // $ref: '#/responses/InternalError'
+ r.Handle(VersionedPath("/images/{name:.*}/push"), s.APIHandler(compat.PushImage)).Methods(http.MethodPost)
+ // Added non version path to URI to support docker non versioned paths
+ r.Handle("/images/{name:.*}/push", s.APIHandler(compat.PushImage)).Methods(http.MethodPost)
// swagger:operation GET /images/{name:.*}/get compat exportImage
// ---
// tags:
@@ -583,6 +618,43 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
libpod endpoints
*/
+ // swagger:operation POST /libpod/images/{name:.*}/push libpod libpodPushImage
+ // ---
+ // tags:
+ // - images (libpod)
+ // summary: Push Image
+ // description: Push an image to a container registry
+ // parameters:
+ // - in: path
+ // name: name:.*
+ // type: string
+ // required: true
+ // description: Name of image to push.
+ // - in: query
+ // name: tag
+ // type: string
+ // description: The tag to associate with the image on the registry.
+ // - in: query
+ // name: credentials
+ // description: username:password for the registry.
+ // type: string
+ // - in: header
+ // name: X-Registry-Auth
+ // type: string
+ // description: A base64-encoded auth configuration.
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // description: no error
+ // schema:
+ // type: string
+ // format: binary
+ // 404:
+ // $ref: '#/responses/NoSuchImage'
+ // 500:
+ // $ref: '#/responses/InternalError'
+ r.Handle(VersionedPath("/libpod/images/{name:.*}/push"), s.APIHandler(libpod.PushImage)).Methods(http.MethodPost)
// swagger:operation GET /libpod/images/{name:.*}/exists libpod libpodImageExists
// ---
// tags:
@@ -883,7 +955,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// tags:
// - images
// summary: Export an image
- // description: Export an image as a tarball
+ // description: Export an image
// parameters:
// - in: path
// name: name:.*
diff --git a/pkg/bindings/containers/checkpoint.go b/pkg/bindings/containers/checkpoint.go
new file mode 100644
index 000000000..84924587b
--- /dev/null
+++ b/pkg/bindings/containers/checkpoint.go
@@ -0,0 +1,79 @@
+package containers
+
+import (
+ "context"
+ "net/http"
+ "net/url"
+ "strconv"
+
+ "github.com/containers/libpod/pkg/bindings"
+ "github.com/containers/libpod/pkg/domain/entities"
+)
+
+// Checkpoint checkpoints the given container (identified by nameOrId). All additional
+// options are options and allow for more fine grained control of the checkpoint process.
+func Checkpoint(ctx context.Context, nameOrId string, keep, leaveRunning, tcpEstablished, ignoreRootFS *bool, export *string) (*entities.CheckpointReport, error) {
+ var report entities.CheckpointReport
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+ params := url.Values{}
+ if keep != nil {
+ params.Set("keep", strconv.FormatBool(*keep))
+ }
+ if leaveRunning != nil {
+ params.Set("leaveRunning", strconv.FormatBool(*leaveRunning))
+ }
+ if tcpEstablished != nil {
+ params.Set("TCPestablished", strconv.FormatBool(*tcpEstablished))
+ }
+ if ignoreRootFS != nil {
+ params.Set("ignoreRootFS", strconv.FormatBool(*ignoreRootFS))
+ }
+ if export != nil {
+ params.Set("export", *export)
+ }
+ response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/checkpoint", params, nameOrId)
+ if err != nil {
+ return nil, err
+ }
+ return &report, response.Process(&report)
+}
+
+// Restore restores a checkpointed container to running. The container is identified by the nameOrId option. All
+// additional options are optional and allow finer control of the restore processs.
+func Restore(ctx context.Context, nameOrId string, keep, tcpEstablished, ignoreRootFS, ignoreStaticIP, ignoreStaticMAC *bool, name, importArchive *string) (*entities.RestoreReport, error) {
+ var report entities.RestoreReport
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+ params := url.Values{}
+ if keep != nil {
+ params.Set("keep", strconv.FormatBool(*keep))
+ }
+ if tcpEstablished != nil {
+ params.Set("TCPestablished", strconv.FormatBool(*tcpEstablished))
+ }
+ if ignoreRootFS != nil {
+ params.Set("ignoreRootFS", strconv.FormatBool(*ignoreRootFS))
+ }
+ if ignoreStaticIP != nil {
+ params.Set("ignoreStaticIP", strconv.FormatBool(*ignoreStaticIP))
+ }
+ if ignoreStaticMAC != nil {
+ params.Set("ignoreStaticMAC", strconv.FormatBool(*ignoreStaticMAC))
+ }
+ if name != nil {
+ params.Set("name", *name)
+ }
+ if importArchive != nil {
+ params.Set("import", *importArchive)
+ }
+ response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/restore", params, nameOrId)
+ if err != nil {
+ return nil, err
+ }
+ return &report, response.Process(&report)
+}
diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go
index bad1294f4..49a2dfd58 100644
--- a/pkg/bindings/containers/containers.go
+++ b/pkg/bindings/containers/containers.go
@@ -2,6 +2,7 @@ package containers
import (
"context"
+ "io"
"net/http"
"net/url"
"strconv"
@@ -296,3 +297,22 @@ func Stop(ctx context.Context, nameOrID string, timeout *uint) error {
}
return response.Process(nil)
}
+
+// Export creates a tarball of the given name or ID of a container. It
+// requires an io.Writer be provided to write the tarball.
+func Export(ctx context.Context, nameOrID string, w io.Writer) error {
+ params := url.Values{}
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return err
+ }
+ response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/export", params, nameOrID)
+ if err != nil {
+ return err
+ }
+ if response.StatusCode/100 == 2 {
+ _, err = io.Copy(w, response.Body)
+ return err
+ }
+ return response.Process(nil)
+}
diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go
index ddc67bebc..1b3df609b 100644
--- a/pkg/bindings/images/images.go
+++ b/pkg/bindings/images/images.go
@@ -3,6 +3,7 @@ package images
import (
"context"
"errors"
+ "fmt"
"io"
"net/http"
"net/url"
@@ -91,11 +92,11 @@ func History(ctx context.Context, nameOrID string) ([]*handlers.HistoryResponse,
return history, response.Process(&history)
}
-func Load(ctx context.Context, r io.Reader, name *string) (string, error) {
- var id handlers.IDResponse
+func Load(ctx context.Context, r io.Reader, name *string) (*entities.ImageLoadReport, error) {
+ var report entities.ImageLoadReport
conn, err := bindings.GetClient(ctx)
if err != nil {
- return "", err
+ return nil, err
}
params := url.Values{}
if name != nil {
@@ -103,9 +104,9 @@ func Load(ctx context.Context, r io.Reader, name *string) (string, error) {
}
response, err := conn.DoRequest(r, http.MethodPost, "/images/load", params)
if err != nil {
- return "", err
+ return nil, err
}
- return id.ID, response.Process(&id)
+ return &report, response.Process(&report)
}
// Remove deletes an image from local storage. The optional force parameter will forcibly remove
@@ -145,11 +146,12 @@ func Export(ctx context.Context, nameOrID string, w io.Writer, format *string, c
if err != nil {
return err
}
- if err := response.Process(nil); err != nil {
+
+ if response.StatusCode/100 == 2 || response.StatusCode/100 == 3 {
+ _, err = io.Copy(w, response.Body)
return err
}
- _, err = io.Copy(w, response.Body)
- return err
+ return nil
}
// Prune removes unused images from local storage. The optional filters can be used to further
@@ -217,14 +219,14 @@ func Build(nameOrId string) {}
// Imports adds the given image to the local image store. This can be done by file and the given reader
// or via the url parameter. Additional metadata can be associated with the image by using the changes and
// message parameters. The image can also be tagged given a reference. One of url OR r must be provided.
-func Import(ctx context.Context, changes []string, message, reference, u *string, r io.Reader) (string, error) {
- var id handlers.IDResponse
+func Import(ctx context.Context, changes []string, message, reference, u *string, r io.Reader) (*entities.ImageImportReport, error) {
+ var report entities.ImageImportReport
if r != nil && u != nil {
- return "", errors.New("url and r parameters cannot be used together")
+ return nil, errors.New("url and r parameters cannot be used together")
}
conn, err := bindings.GetClient(ctx)
if err != nil {
- return "", err
+ return nil, err
}
params := url.Values{}
for _, change := range changes {
@@ -241,9 +243,9 @@ func Import(ctx context.Context, changes []string, message, reference, u *string
}
response, err := conn.DoRequest(r, http.MethodPost, "/images/import", params)
if err != nil {
- return "", err
+ return nil, err
}
- return id.ID, response.Process(&id)
+ return &report, response.Process(&report)
}
// Pull is the binding for libpod's v2 endpoints for pulling images. Note that
@@ -283,3 +285,26 @@ func Pull(ctx context.Context, rawImage string, options entities.ImagePullOption
return pulledImages, nil
}
+
+// Push is the binding for libpod's v2 endpoints for push images. Note that
+// `source` must be a refering to an image in the remote's container storage.
+// The destination must be a reference to a registry (i.e., of docker transport
+// or be normalized to one). Other transports are rejected as they do not make
+// sense in a remote context.
+func Push(ctx context.Context, source string, destination string, options entities.ImagePushOptions) error {
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return err
+ }
+ params := url.Values{}
+ params.Set("credentials", options.Credentials)
+ params.Set("destination", destination)
+ if options.TLSVerify != types.OptionalBoolUndefined {
+ val := bool(options.TLSVerify == types.OptionalBoolTrue)
+ params.Set("tlsVerify", strconv.FormatBool(val))
+ }
+
+ path := fmt.Sprintf("/images/%s/push", source)
+ _, err = conn.DoRequest(nil, http.MethodPost, path, params)
+ return err
+}
diff --git a/pkg/bindings/test/images_test.go b/pkg/bindings/test/images_test.go
index dc01a793b..992720196 100644
--- a/pkg/bindings/test/images_test.go
+++ b/pkg/bindings/test/images_test.go
@@ -219,7 +219,7 @@ var _ = Describe("Podman images", func() {
Expect(err).To(BeNil())
names, err := images.Load(bt.conn, f, nil)
Expect(err).To(BeNil())
- Expect(names).To(Equal(alpine.name))
+ Expect(names.Name).To(Equal(alpine.name))
exists, err = images.Exists(bt.conn, alpine.name)
Expect(err).To(BeNil())
Expect(exists).To(BeTrue())
@@ -235,7 +235,7 @@ var _ = Describe("Podman images", func() {
newName := "quay.io/newname:fizzle"
names, err = images.Load(bt.conn, f, &newName)
Expect(err).To(BeNil())
- Expect(names).To(Equal(alpine.name))
+ Expect(names.Name).To(Equal(alpine.name))
exists, err = images.Exists(bt.conn, newName)
Expect(err).To(BeNil())
Expect(exists).To(BeTrue())
diff --git a/pkg/adapter/checkpoint_restore.go b/pkg/checkpoint/checkpoint_restore.go
index a5b74013b..78f592d32 100644
--- a/pkg/adapter/checkpoint_restore.go
+++ b/pkg/checkpoint/checkpoint_restore.go
@@ -1,6 +1,4 @@
-// +build !remoteclient
-
-package adapter
+package checkpoint
import (
"context"
@@ -42,9 +40,9 @@ func crImportFromJSON(filePath string, v interface{}) error {
return nil
}
-// crImportCheckpoint it the function which imports the information
+// CRImportCheckpoint it the function which imports the information
// from checkpoint tarball and re-creates the container from that information
-func crImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input string, name string) ([]*libpod.Container, error) {
+func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input string, name string) ([]*libpod.Container, error) {
// First get the container definition from the
// tarball to a temporary directory
archiveFile, err := os.Open(input)
diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go
index b7f1cd812..cf907eb1b 100644
--- a/pkg/domain/entities/containers.go
+++ b/pkg/domain/entities/containers.go
@@ -2,6 +2,7 @@ package entities
import (
"io"
+ "os"
"time"
"github.com/containers/libpod/libpod/define"
@@ -117,3 +118,71 @@ type CommitOptions struct {
type CommitReport struct {
Id string
}
+
+type ContainerExportOptions struct {
+ Output string
+}
+
+type CheckpointOptions struct {
+ All bool
+ Export string
+ IgnoreRootFS bool
+ Keep bool
+ Latest bool
+ LeaveRuninng bool
+ TCPEstablished bool
+}
+
+type CheckpointReport struct {
+ Err error
+ Id string
+}
+
+type RestoreOptions struct {
+ All bool
+ IgnoreRootFS bool
+ IgnoreStaticIP bool
+ IgnoreStaticMAC bool
+ Import string
+ Keep bool
+ Latest bool
+ Name string
+ TCPEstablished bool
+}
+
+type RestoreReport struct {
+ Err error
+ Id string
+}
+
+type ContainerCreateReport struct {
+ Id string
+}
+
+// AttachOptions describes the cli and other values
+// needed to perform an attach
+type AttachOptions struct {
+ DetachKeys string
+ Latest bool
+ NoStdin bool
+ SigProxy bool
+ Stdin *os.File
+ Stdout *os.File
+ Stderr *os.File
+}
+
+// ExecOptions describes the cli values to exec into
+// a container
+type ExecOptions struct {
+ Cmd []string
+ DetachKeys string
+ Envs map[string]string
+ Interactive bool
+ Latest bool
+ PreserveFDs uint
+ Privileged bool
+ Streams define.AttachStreams
+ Tty bool
+ User string
+ WorkDir string
+}
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
index ce0c161e9..9bf3d51de 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -4,12 +4,19 @@ import (
"context"
"github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/specgen"
)
type ContainerEngine interface {
+ ContainerAttach(ctx context.Context, nameOrId string, options AttachOptions) error
ContainerCommit(ctx context.Context, nameOrId string, options CommitOptions) (*CommitReport, error)
+ ContainerCheckpoint(ctx context.Context, namesOrIds []string, options CheckpointOptions) ([]*CheckpointReport, error)
+ ContainerRestore(ctx context.Context, namesOrIds []string, options RestoreOptions) ([]*RestoreReport, error)
+ ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*ContainerCreateReport, error)
+ ContainerExec(ctx context.Context, nameOrId string, options ExecOptions) (int, error)
ContainerExists(ctx context.Context, nameOrId string) (*BoolReport, error)
ContainerInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]*ContainerInspectReport, error)
+ ContainerExport(ctx context.Context, nameOrId string, options ContainerExportOptions) error
ContainerKill(ctx context.Context, namesOrIds []string, options KillOptions) ([]*KillReport, error)
ContainerPause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error)
ContainerRestart(ctx context.Context, namesOrIds []string, options RestartOptions) ([]*RestartReport, error)
diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go
index 2ca48e795..a28bfc548 100644
--- a/pkg/domain/entities/engine_image.go
+++ b/pkg/domain/entities/engine_image.go
@@ -14,4 +14,8 @@ type ImageEngine interface {
Pull(ctx context.Context, rawImage string, opts ImagePullOptions) (*ImagePullReport, error)
Tag(ctx context.Context, nameOrId string, tags []string, options ImageTagOptions) error
Untag(ctx context.Context, nameOrId string, tags []string, options ImageUntagOptions) error
+ Load(ctx context.Context, opts ImageLoadOptions) (*ImageLoadReport, error)
+ Import(ctx context.Context, opts ImageImportOptions) (*ImageImportReport, error)
+ Push(ctx context.Context, source string, destination string, opts ImagePushOptions) error
+ Save(ctx context.Context, nameOrId string, tags []string, options ImageSaveOptions) error
}
diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go
index 20682b05b..bc8a34c13 100644
--- a/pkg/domain/entities/images.go
+++ b/pkg/domain/entities/images.go
@@ -144,6 +144,43 @@ type ImagePullReport struct {
Images []string
}
+// ImagePushOptions are the arguments for pushing images.
+type ImagePushOptions struct {
+ // Authfile is the path to the authentication file. Ignored for remote
+ // calls.
+ Authfile string
+ // CertDir is the path to certificate directories. Ignored for remote
+ // calls.
+ CertDir string
+ // Compress tarball image layers when pushing to a directory using the 'dir'
+ // transport. Default is same compression type as source. Ignored for remote
+ // calls.
+ Compress bool
+ // Credentials for authenticating against the registry in the format
+ // USERNAME:PASSWORD.
+ Credentials string
+ // DigestFile, after copying the image, write the digest of the resulting
+ // image to the file. Ignored for remote calls.
+ DigestFile string
+ // Format is the Manifest type (oci, v2s1, or v2s2) to use when pushing an
+ // image using the 'dir' transport. Default is manifest type of source.
+ // Ignored for remote calls.
+ Format string
+ // Quiet can be specified to suppress pull progress when pulling. Ignored
+ // for remote calls.
+ Quiet bool
+ // RemoveSignatures, discard any pre-existing signatures in the image.
+ // Ignored for remote calls.
+ RemoveSignatures bool
+ // SignaturePolicy to use when pulling. Ignored for remote calls.
+ SignaturePolicy string
+ // SignBy adds a signature at the destination using the specified key.
+ // Ignored for remote calls.
+ SignBy string
+ // TLSVerify to enable/disable HTTPS and certificate verification.
+ TLSVerify types.OptionalBool
+}
+
type ImageListOptions struct {
All bool `json:"all" schema:"all"`
Filter []string `json:"Filter,omitempty"`
@@ -172,3 +209,35 @@ type ImageInspectReport struct {
Images []*ImageData
Errors map[string]error
}
+
+type ImageLoadOptions struct {
+ Name string
+ Tag string
+ Input string
+ Quiet bool
+ SignaturePolicy string
+}
+
+type ImageLoadReport struct {
+ Name string
+}
+
+type ImageImportOptions struct {
+ Changes []string
+ Message string
+ Quiet bool
+ Reference string
+ Source string
+ SourceIsURL bool
+}
+
+type ImageImportReport struct {
+ Id string
+}
+
+type ImageSaveOptions struct {
+ Compress bool
+ Format string
+ Output string
+ Quiet bool
+}
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index 172c7d1a3..b93e7665a 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -5,6 +5,7 @@ package abi
import (
"context"
"io/ioutil"
+ "strconv"
"strings"
"github.com/containers/buildah"
@@ -12,13 +13,45 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/image"
- "github.com/containers/libpod/pkg/adapter/shortcuts"
+ "github.com/containers/libpod/pkg/checkpoint"
"github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/domain/infra/abi/terminal"
"github.com/containers/libpod/pkg/signal"
+ "github.com/containers/libpod/pkg/specgen"
+ "github.com/containers/libpod/pkg/specgen/generate"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
+// getContainersByContext gets pods whether all, latest, or a slice of names/ids
+// is specified.
+func getContainersByContext(all, latest bool, names []string, runtime *libpod.Runtime) (ctrs []*libpod.Container, err error) {
+ var ctr *libpod.Container
+ ctrs = []*libpod.Container{}
+
+ switch {
+ case all:
+ ctrs, err = runtime.GetAllContainers()
+ case latest:
+ ctr, err = runtime.GetLatestContainer()
+ ctrs = append(ctrs, ctr)
+ default:
+ for _, n := range names {
+ ctr, e := runtime.LookupContainer(n)
+ if e != nil {
+ // Log all errors here, so callers don't need to.
+ logrus.Debugf("Error looking up container %q: %v", n, e)
+ if err == nil {
+ err = e
+ }
+ } else {
+ ctrs = append(ctrs, ctr)
+ }
+ }
+ }
+ return
+}
+
// TODO: Should return *entities.ContainerExistsReport, error
func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrId string) (*entities.BoolReport, error) {
_, err := ic.Libpod.LookupContainer(nameOrId)
@@ -32,7 +65,7 @@ func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []strin
var (
responses []entities.WaitReport
)
- ctrs, err := shortcuts.GetContainersByContext(false, options.Latest, namesOrIds, ic.Libpod)
+ ctrs, err := getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod)
if err != nil {
return nil, err
}
@@ -58,7 +91,7 @@ func (ic *ContainerEngine) ContainerPause(ctx context.Context, namesOrIds []stri
if options.All {
ctrs, err = ic.Libpod.GetAllContainers()
} else {
- ctrs, err = shortcuts.GetContainersByContext(false, false, namesOrIds, ic.Libpod)
+ ctrs, err = getContainersByContext(false, false, namesOrIds, ic.Libpod)
}
if err != nil {
return nil, err
@@ -79,7 +112,7 @@ func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []st
if options.All {
ctrs, err = ic.Libpod.GetAllContainers()
} else {
- ctrs, err = shortcuts.GetContainersByContext(false, false, namesOrIds, ic.Libpod)
+ ctrs, err = getContainersByContext(false, false, namesOrIds, ic.Libpod)
}
if err != nil {
return nil, err
@@ -103,7 +136,7 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin
id := strings.Split(string(content), "\n")[0]
names = append(names, id)
}
- ctrs, err := shortcuts.GetContainersByContext(options.All, options.Latest, names, ic.Libpod)
+ ctrs, err := getContainersByContext(options.All, options.Latest, names, ic.Libpod)
if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) {
return nil, err
}
@@ -139,7 +172,7 @@ func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []strin
if err != nil {
return nil, err
}
- ctrs, err := shortcuts.GetContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
+ ctrs, err := getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
if err != nil {
return nil, err
}
@@ -155,7 +188,7 @@ func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []st
var (
reports []*entities.RestartReport
)
- ctrs, err := shortcuts.GetContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
+ ctrs, err := getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
if err != nil {
return nil, err
}
@@ -197,7 +230,7 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string,
names = append(names, id)
}
- ctrs, err := shortcuts.GetContainersByContext(options.All, options.Latest, names, ic.Libpod)
+ ctrs, err := getContainersByContext(options.All, options.Latest, names, ic.Libpod)
if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) {
// Failed to get containers. If force is specified, get the containers ID
// and evict them
@@ -245,7 +278,7 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string,
func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]*entities.ContainerInspectReport, error) {
var reports []*entities.ContainerInspectReport
- ctrs, err := shortcuts.GetContainersByContext(false, options.Latest, namesOrIds, ic.Libpod)
+ ctrs, err := getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod)
if err != nil {
return nil, err
}
@@ -325,3 +358,154 @@ func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrId string,
}
return &entities.CommitReport{Id: newImage.ID()}, nil
}
+
+func (ic *ContainerEngine) ContainerExport(ctx context.Context, nameOrId string, options entities.ContainerExportOptions) error {
+ ctr, err := ic.Libpod.LookupContainer(nameOrId)
+ if err != nil {
+ return err
+ }
+ return ctr.Export(options.Output)
+}
+
+func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds []string, options entities.CheckpointOptions) ([]*entities.CheckpointReport, error) {
+ var (
+ err error
+ cons []*libpod.Container
+ reports []*entities.CheckpointReport
+ )
+ checkOpts := libpod.ContainerCheckpointOptions{
+ Keep: options.Keep,
+ TCPEstablished: options.TCPEstablished,
+ TargetFile: options.Export,
+ IgnoreRootfs: options.IgnoreRootFS,
+ }
+
+ if options.All {
+ running := func(c *libpod.Container) bool {
+ state, _ := c.State()
+ return state == define.ContainerStateRunning
+ }
+ cons, err = ic.Libpod.GetContainers(running)
+ } else {
+ cons, err = getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod)
+ }
+ if err != nil {
+ return nil, err
+ }
+ for _, con := range cons {
+ err = con.Checkpoint(ctx, checkOpts)
+ reports = append(reports, &entities.CheckpointReport{
+ Err: err,
+ Id: con.ID(),
+ })
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []string, options entities.RestoreOptions) ([]*entities.RestoreReport, error) {
+ var (
+ cons []*libpod.Container
+ err error
+ filterFuncs []libpod.ContainerFilter
+ reports []*entities.RestoreReport
+ )
+
+ restoreOptions := libpod.ContainerCheckpointOptions{
+ Keep: options.Keep,
+ TCPEstablished: options.TCPEstablished,
+ TargetFile: options.Import,
+ Name: options.Name,
+ IgnoreRootfs: options.IgnoreRootFS,
+ IgnoreStaticIP: options.IgnoreStaticIP,
+ IgnoreStaticMAC: options.IgnoreStaticMAC,
+ }
+
+ filterFuncs = append(filterFuncs, func(c *libpod.Container) bool {
+ state, _ := c.State()
+ return state == define.ContainerStateExited
+ })
+
+ switch {
+ case options.Import != "":
+ cons, err = checkpoint.CRImportCheckpoint(ctx, ic.Libpod, options.Import, options.Name)
+ case options.All:
+ cons, err = ic.Libpod.GetContainers(filterFuncs...)
+ default:
+ cons, err = getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod)
+ }
+ if err != nil {
+ return nil, err
+ }
+ for _, con := range cons {
+ err := con.Restore(ctx, restoreOptions)
+ reports = append(reports, &entities.RestoreReport{
+ Err: err,
+ Id: con.ID(),
+ })
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*entities.ContainerCreateReport, error) {
+ if err := generate.CompleteSpec(ctx, ic.Libpod, s); err != nil {
+ return nil, err
+ }
+ ctr, err := generate.MakeContainer(ic.Libpod, s)
+ if err != nil {
+ return nil, err
+ }
+ return &entities.ContainerCreateReport{Id: ctr.ID()}, nil
+}
+
+func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrId string, options entities.AttachOptions) error {
+ ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrId}, ic.Libpod)
+ if err != nil {
+ return err
+ }
+ ctr := ctrs[0]
+ conState, err := ctr.State()
+ if err != nil {
+ return errors.Wrapf(err, "unable to determine state of %s", ctr.ID())
+ }
+ if conState != define.ContainerStateRunning {
+ return errors.Errorf("you can only attach to running containers")
+ }
+
+ // If the container is in a pod, also set to recursively start dependencies
+ if err := terminal.StartAttachCtr(ctx, ctr, options.Stdin, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, false, ctr.PodID() != ""); err != nil && errors.Cause(err) != define.ErrDetach {
+ return errors.Wrapf(err, "error attaching to container %s", ctr.ID())
+ }
+ return nil
+}
+
+func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, options entities.ExecOptions) (int, error) {
+ ec := define.ExecErrorCodeGeneric
+ if options.PreserveFDs > 0 {
+ entries, err := ioutil.ReadDir("/proc/self/fd")
+ if err != nil {
+ return ec, errors.Wrapf(err, "unable to read /proc/self/fd")
+ }
+
+ m := make(map[int]bool)
+ for _, e := range entries {
+ i, err := strconv.Atoi(e.Name())
+ if err != nil {
+ return ec, errors.Wrapf(err, "cannot parse %s in /proc/self/fd", e.Name())
+ }
+ m[i] = true
+ }
+
+ for i := 3; i < 3+int(options.PreserveFDs); i++ {
+ if _, found := m[i]; !found {
+ return ec, errors.New("invalid --preserve-fds=N specified. Not enough FDs available")
+ }
+ }
+ }
+ ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrId}, ic.Libpod)
+ if err != nil {
+ return ec, err
+ }
+ ctr := ctrs[0]
+ ec, err = terminal.ExecAttachCtr(ctx, ctr, options.Tty, options.Privileged, options.Envs, options.Cmd, options.User, options.WorkDir, &options.Streams, options.PreserveFDs, options.DetachKeys)
+ return define.TranslateExecErrorToExitCode(ec, err), err
+}
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index 5a7acb2f7..9d706a112 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -12,6 +12,7 @@ import (
"github.com/containers/image/v5/docker"
dockerarchive "github.com/containers/image/v5/docker/archive"
"github.com/containers/image/v5/docker/reference"
+ "github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/transports/alltransports"
"github.com/containers/image/v5/types"
"github.com/containers/libpod/libpod/image"
@@ -20,6 +21,7 @@ import (
domainUtils "github.com/containers/libpod/pkg/domain/utils"
"github.com/containers/libpod/pkg/util"
"github.com/containers/storage"
+ imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -260,6 +262,64 @@ func (ir *ImageEngine) Inspect(ctx context.Context, names []string, opts entitie
return &report, nil
}
+func (ir *ImageEngine) Push(ctx context.Context, source string, destination string, options entities.ImagePushOptions) error {
+ var writer io.Writer
+ if !options.Quiet {
+ writer = os.Stderr
+ }
+
+ var manifestType string
+ switch options.Format {
+ case "":
+ // Default
+ case "oci":
+ manifestType = imgspecv1.MediaTypeImageManifest
+ case "v2s1":
+ manifestType = manifest.DockerV2Schema1SignedMediaType
+ case "v2s2", "docker":
+ manifestType = manifest.DockerV2Schema2MediaType
+ default:
+ return fmt.Errorf("unknown format %q. Choose on of the supported formats: 'oci', 'v2s1', or 'v2s2'", options.Format)
+ }
+
+ var registryCreds *types.DockerAuthConfig
+ if options.Credentials != "" {
+ creds, err := util.ParseRegistryCreds(options.Credentials)
+ if err != nil {
+ return err
+ }
+ registryCreds = creds
+ }
+ dockerRegistryOptions := image.DockerRegistryOptions{
+ DockerRegistryCreds: registryCreds,
+ DockerCertPath: options.CertDir,
+ DockerInsecureSkipTLSVerify: options.TLSVerify,
+ }
+
+ signOptions := image.SigningOptions{
+ RemoveSignatures: options.RemoveSignatures,
+ SignBy: options.SignBy,
+ }
+
+ newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(source)
+ if err != nil {
+ return err
+ }
+
+ return newImage.PushImageToHeuristicDestination(
+ ctx,
+ destination,
+ manifestType,
+ options.Authfile,
+ options.DigestFile,
+ options.SignaturePolicy,
+ writer,
+ options.Compress,
+ signOptions,
+ &dockerRegistryOptions,
+ nil)
+}
+
// 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 {
@@ -303,6 +363,7 @@ func (ir *ImageEngine) Tag(ctx context.Context, nameOrId string, tags []string,
}
return nil
}
+
func (ir *ImageEngine) Untag(ctx context.Context, nameOrId string, tags []string, options entities.ImageUntagOptions) error {
newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId)
if err != nil {
@@ -315,3 +376,40 @@ func (ir *ImageEngine) Untag(ctx context.Context, nameOrId string, tags []string
}
return nil
}
+
+func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions) (*entities.ImageLoadReport, error) {
+ var (
+ writer io.Writer
+ )
+ if !opts.Quiet {
+ writer = os.Stderr
+ }
+ name, err := ir.Libpod.LoadImage(ctx, opts.Name, opts.Input, writer, opts.SignaturePolicy)
+ if err != nil {
+ return nil, err
+ }
+ newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(name)
+ if err != nil {
+ return nil, errors.Wrap(err, "image loaded but no additional tags were created")
+ }
+ if err := newImage.TagImage(opts.Name); err != nil {
+ return nil, errors.Wrapf(err, "error adding %q to image %q", opts.Name, newImage.InputName)
+ }
+ return &entities.ImageLoadReport{Name: name}, nil
+}
+
+func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOptions) (*entities.ImageImportReport, error) {
+ id, err := ir.Libpod.Import(ctx, opts.Source, opts.Reference, opts.Changes, opts.Message, opts.Quiet)
+ if err != nil {
+ return nil, err
+ }
+ return &entities.ImageImportReport{Id: id}, nil
+}
+
+func (ir *ImageEngine) Save(ctx context.Context, nameOrId string, tags []string, options entities.ImageSaveOptions) error {
+ newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId)
+ if err != nil {
+ return err
+ }
+ return newImage.Save(ctx, nameOrId, options.Format, options.Output, tags, options.Quiet, options.Compress)
+}
diff --git a/pkg/domain/infra/abi/terminal/sigproxy_linux.go b/pkg/domain/infra/abi/terminal/sigproxy_linux.go
new file mode 100644
index 000000000..d7f5853d8
--- /dev/null
+++ b/pkg/domain/infra/abi/terminal/sigproxy_linux.go
@@ -0,0 +1,47 @@
+// +build ABISupport
+
+package terminal
+
+import (
+ "os"
+ "syscall"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/signal"
+ "github.com/sirupsen/logrus"
+)
+
+// ProxySignals ...
+func ProxySignals(ctr *libpod.Container) {
+ sigBuffer := make(chan os.Signal, 128)
+ signal.CatchAll(sigBuffer)
+
+ logrus.Debugf("Enabling signal proxying")
+
+ go func() {
+ for s := range sigBuffer {
+ // Ignore SIGCHLD and SIGPIPE - these are mostly likely
+ // intended for the podman command itself.
+ // SIGURG was added because of golang 1.14 and its preemptive changes
+ // causing more signals to "show up".
+ // https://github.com/containers/libpod/issues/5483
+ if s == syscall.SIGCHLD || s == syscall.SIGPIPE || s == syscall.SIGURG {
+ continue
+ }
+
+ if err := ctr.Kill(uint(s.(syscall.Signal))); err != nil {
+ // If the container dies, and we find out here,
+ // we need to forward that one signal to
+ // ourselves so that it is not lost, and then
+ // we terminate the proxy and let the defaults
+ // play out.
+ logrus.Errorf("Error forwarding signal %d to container %s: %v", s, ctr.ID(), err)
+ signal.StopCatch(sigBuffer)
+ if err := syscall.Kill(syscall.Getpid(), s.(syscall.Signal)); err != nil {
+ logrus.Errorf("failed to kill pid %d", syscall.Getpid())
+ }
+ return
+ }
+ }
+ }()
+}
diff --git a/pkg/domain/infra/abi/terminal/terminal.go b/pkg/domain/infra/abi/terminal/terminal.go
new file mode 100644
index 000000000..f187bdd6b
--- /dev/null
+++ b/pkg/domain/infra/abi/terminal/terminal.go
@@ -0,0 +1,103 @@
+// +build ABISupport
+
+package terminal
+
+import (
+ "context"
+ "os"
+ "os/signal"
+
+ lsignal "github.com/containers/libpod/pkg/signal"
+ "github.com/docker/docker/pkg/term"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "k8s.io/client-go/tools/remotecommand"
+)
+
+// RawTtyFormatter ...
+type RawTtyFormatter struct {
+}
+
+// getResize returns a TerminalSize command matching stdin's current
+// size on success, and nil on errors.
+func getResize() *remotecommand.TerminalSize {
+ winsize, err := term.GetWinsize(os.Stdin.Fd())
+ if err != nil {
+ logrus.Warnf("Could not get terminal size %v", err)
+ return nil
+ }
+ return &remotecommand.TerminalSize{
+ Width: winsize.Width,
+ Height: winsize.Height,
+ }
+}
+
+// Helper for prepareAttach - set up a goroutine to generate terminal resize events
+func resizeTty(ctx context.Context, resize chan remotecommand.TerminalSize) {
+ sigchan := make(chan os.Signal, 1)
+ signal.Notify(sigchan, lsignal.SIGWINCH)
+ go func() {
+ defer close(resize)
+ // Update the terminal size immediately without waiting
+ // for a SIGWINCH to get the correct initial size.
+ resizeEvent := getResize()
+ for {
+ if resizeEvent == nil {
+ select {
+ case <-ctx.Done():
+ return
+ case <-sigchan:
+ resizeEvent = getResize()
+ }
+ } else {
+ select {
+ case <-ctx.Done():
+ return
+ case <-sigchan:
+ resizeEvent = getResize()
+ case resize <- *resizeEvent:
+ resizeEvent = nil
+ }
+ }
+ }
+ }()
+}
+
+func restoreTerminal(state *term.State) error {
+ logrus.SetFormatter(&logrus.TextFormatter{})
+ return term.RestoreTerminal(os.Stdin.Fd(), state)
+}
+
+// Format ...
+func (f *RawTtyFormatter) Format(entry *logrus.Entry) ([]byte, error) {
+ textFormatter := logrus.TextFormatter{}
+ bytes, err := textFormatter.Format(entry)
+
+ if err == nil {
+ bytes = append(bytes, '\r')
+ }
+
+ return bytes, err
+}
+
+func handleTerminalAttach(ctx context.Context, resize chan remotecommand.TerminalSize) (context.CancelFunc, *term.State, error) {
+ logrus.Debugf("Handling terminal attach")
+
+ subCtx, cancel := context.WithCancel(ctx)
+
+ resizeTty(subCtx, resize)
+
+ oldTermState, err := term.SaveState(os.Stdin.Fd())
+ if err != nil {
+ // allow caller to not have to do any cleaning up if we error here
+ cancel()
+ return nil, nil, errors.Wrapf(err, "unable to save terminal state")
+ }
+
+ logrus.SetFormatter(&RawTtyFormatter{})
+ if _, err := term.SetRawTerminal(os.Stdin.Fd()); err != nil {
+ return cancel, nil, err
+ }
+
+ return cancel, oldTermState, nil
+}
diff --git a/pkg/domain/infra/abi/terminal/terminal_linux.go b/pkg/domain/infra/abi/terminal/terminal_linux.go
new file mode 100644
index 000000000..664205df1
--- /dev/null
+++ b/pkg/domain/infra/abi/terminal/terminal_linux.go
@@ -0,0 +1,123 @@
+// +build ABISupport
+
+package terminal
+
+import (
+ "bufio"
+ "context"
+ "fmt"
+ "os"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "golang.org/x/crypto/ssh/terminal"
+ "k8s.io/client-go/tools/remotecommand"
+)
+
+// ExecAttachCtr execs and attaches to a container
+func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, tty, privileged bool, env map[string]string, cmd []string, user, workDir string, streams *define.AttachStreams, preserveFDs uint, detachKeys string) (int, error) {
+ resize := make(chan remotecommand.TerminalSize)
+ haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd()))
+
+ // Check if we are attached to a terminal. If we are, generate resize
+ // events, and set the terminal to raw mode
+ if haveTerminal && tty {
+ cancel, oldTermState, err := handleTerminalAttach(ctx, resize)
+ if err != nil {
+ return -1, err
+ }
+ defer cancel()
+ defer func() {
+ if err := restoreTerminal(oldTermState); err != nil {
+ logrus.Errorf("unable to restore terminal: %q", err)
+ }
+ }()
+ }
+
+ execConfig := new(libpod.ExecConfig)
+ execConfig.Command = cmd
+ execConfig.Terminal = tty
+ execConfig.Privileged = privileged
+ execConfig.Environment = env
+ execConfig.User = user
+ execConfig.WorkDir = workDir
+ execConfig.DetachKeys = &detachKeys
+ execConfig.PreserveFDs = preserveFDs
+
+ return ctr.Exec(execConfig, streams, resize)
+}
+
+// StartAttachCtr starts and (if required) attaches to a container
+// if you change the signature of this function from os.File to io.Writer, it will trigger a downstream
+// error. we may need to just lint disable this one.
+func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr, stdin *os.File, detachKeys string, sigProxy bool, startContainer bool, recursive bool) error { //nolint-interfacer
+ resize := make(chan remotecommand.TerminalSize)
+
+ haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd()))
+
+ // Check if we are attached to a terminal. If we are, generate resize
+ // events, and set the terminal to raw mode
+ if haveTerminal && ctr.Spec().Process.Terminal {
+ cancel, oldTermState, err := handleTerminalAttach(ctx, resize)
+ if err != nil {
+ return err
+ }
+ defer func() {
+ if err := restoreTerminal(oldTermState); err != nil {
+ logrus.Errorf("unable to restore terminal: %q", err)
+ }
+ }()
+ defer cancel()
+ }
+
+ streams := new(define.AttachStreams)
+ streams.OutputStream = stdout
+ streams.ErrorStream = stderr
+ streams.InputStream = bufio.NewReader(stdin)
+ streams.AttachOutput = true
+ streams.AttachError = true
+ streams.AttachInput = true
+
+ if stdout == nil {
+ logrus.Debugf("Not attaching to stdout")
+ streams.AttachOutput = false
+ }
+ if stderr == nil {
+ logrus.Debugf("Not attaching to stderr")
+ streams.AttachError = false
+ }
+ if stdin == nil {
+ logrus.Debugf("Not attaching to stdin")
+ streams.AttachInput = false
+ }
+
+ if !startContainer {
+ if sigProxy {
+ ProxySignals(ctr)
+ }
+
+ return ctr.Attach(streams, detachKeys, resize)
+ }
+
+ attachChan, err := ctr.StartAndAttach(ctx, streams, detachKeys, resize, recursive)
+ if err != nil {
+ return err
+ }
+
+ if sigProxy {
+ ProxySignals(ctr)
+ }
+
+ if stdout == nil && stderr == nil {
+ fmt.Printf("%s\n", ctr.ID())
+ }
+
+ err = <-attachChan
+ if err != nil {
+ return errors.Wrapf(err, "error attaching to container %s", ctr.ID())
+ }
+
+ return nil
+}
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index c1bade4ba..1e71cba2c 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -2,11 +2,15 @@ package tunnel
import (
"context"
+ "io"
+ "os"
"github.com/containers/image/v5/docker/reference"
-
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/api/handlers/libpod"
"github.com/containers/libpod/pkg/bindings/containers"
"github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/specgen"
"github.com/pkg/errors"
)
@@ -210,3 +214,102 @@ func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrId string,
}
return &entities.CommitReport{Id: response.ID}, nil
}
+
+func (ic *ContainerEngine) ContainerExport(ctx context.Context, nameOrId string, options entities.ContainerExportOptions) error {
+ var (
+ err error
+ w io.Writer
+ )
+ if len(options.Output) > 0 {
+ w, err = os.Create(options.Output)
+ if err != nil {
+ return err
+ }
+ }
+ return containers.Export(ic.ClientCxt, nameOrId, w)
+}
+
+func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds []string, options entities.CheckpointOptions) ([]*entities.CheckpointReport, error) {
+ var (
+ reports []*entities.CheckpointReport
+ err error
+ ctrs []libpod.ListContainer
+ )
+
+ if options.All {
+ allCtrs, err := getContainersByContext(ic.ClientCxt, true, []string{})
+ if err != nil {
+ return nil, err
+ }
+ // narrow the list to running only
+ for _, c := range allCtrs {
+ if c.State == define.ContainerStateRunning.String() {
+ ctrs = append(ctrs, c)
+ }
+ }
+
+ } else {
+ ctrs, err = getContainersByContext(ic.ClientCxt, false, namesOrIds)
+ if err != nil {
+ return nil, err
+ }
+ }
+ for _, c := range ctrs {
+ report, err := containers.Checkpoint(ic.ClientCxt, c.ID, &options.Keep, &options.LeaveRuninng, &options.TCPEstablished, &options.IgnoreRootFS, &options.Export)
+ if err != nil {
+ reports = append(reports, &entities.CheckpointReport{Id: c.ID, Err: err})
+ }
+ reports = append(reports, report)
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []string, options entities.RestoreOptions) ([]*entities.RestoreReport, error) {
+ var (
+ reports []*entities.RestoreReport
+ err error
+ ctrs []libpod.ListContainer
+ )
+ if options.All {
+ allCtrs, err := getContainersByContext(ic.ClientCxt, true, []string{})
+ if err != nil {
+ return nil, err
+ }
+ // narrow the list to exited only
+ for _, c := range allCtrs {
+ if c.State == define.ContainerStateExited.String() {
+ ctrs = append(ctrs, c)
+ }
+ }
+
+ } else {
+ ctrs, err = getContainersByContext(ic.ClientCxt, false, namesOrIds)
+ if err != nil {
+ return nil, err
+ }
+ }
+ for _, c := range ctrs {
+ report, err := containers.Restore(ic.ClientCxt, c.ID, &options.Keep, &options.TCPEstablished, &options.IgnoreRootFS, &options.IgnoreStaticIP, &options.IgnoreStaticMAC, &options.Name, &options.Import)
+ if err != nil {
+ reports = append(reports, &entities.RestoreReport{Id: c.ID, Err: err})
+ }
+ reports = append(reports, report)
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*entities.ContainerCreateReport, error) {
+ response, err := containers.CreateWithSpec(ic.ClientCxt, s)
+ if err != nil {
+ return nil, err
+ }
+ return &entities.ContainerCreateReport{Id: response.ID}, nil
+}
+
+func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrId string, options entities.AttachOptions) error {
+ return errors.New("not implemented")
+}
+
+func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, options entities.ExecOptions) (int, error) {
+ return 125, errors.New("not implemented")
+}
diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go
index 6a8b9be37..516914a68 100644
--- a/pkg/domain/infra/tunnel/images.go
+++ b/pkg/domain/infra/tunnel/images.go
@@ -2,11 +2,14 @@ package tunnel
import (
"context"
+ "io/ioutil"
+ "os"
"github.com/containers/image/v5/docker/reference"
images "github.com/containers/libpod/pkg/bindings/images"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/domain/utils"
+ utils2 "github.com/containers/libpod/utils"
"github.com/pkg/errors"
)
@@ -157,3 +160,84 @@ func (ir *ImageEngine) Inspect(_ context.Context, names []string, opts entities.
}
return &report, nil
}
+
+func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions) (*entities.ImageLoadReport, error) {
+ f, err := os.Open(opts.Input)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ return images.Load(ir.ClientCxt, f, &opts.Name)
+}
+
+func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOptions) (*entities.ImageImportReport, error) {
+ var (
+ err error
+ sourceURL *string
+ f *os.File
+ )
+ if opts.SourceIsURL {
+ sourceURL = &opts.Source
+ } else {
+ f, err = os.Open(opts.Source)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return images.Import(ir.ClientCxt, opts.Changes, &opts.Message, &opts.Reference, sourceURL, f)
+}
+
+func (ir *ImageEngine) Push(ctx context.Context, source string, destination string, options entities.ImagePushOptions) error {
+ return images.Push(ir.ClientCxt, source, destination, options)
+}
+
+func (ir *ImageEngine) Save(ctx context.Context, nameOrId string, tags []string, options entities.ImageSaveOptions) error {
+ var (
+ f *os.File
+ err error
+ )
+
+ switch options.Format {
+ case "oci-dir", "docker-dir":
+ f, err = ioutil.TempFile("", "podman_save")
+ if err == nil {
+ defer func() { _ = os.Remove(f.Name()) }()
+ }
+ default:
+ f, err = os.Create(options.Output)
+ }
+ if err != nil {
+ return err
+ }
+
+ exErr := images.Export(ir.ClientCxt, nameOrId, f, &options.Format, &options.Compress)
+ if err := f.Close(); err != nil {
+ return err
+ }
+ if exErr != nil {
+ return exErr
+ }
+
+ if options.Format != "oci-dir" && options.Format != "docker-dir" {
+ return nil
+ }
+
+ f, err = os.Open(f.Name())
+ if err != nil {
+ return err
+ }
+ info, err := os.Stat(options.Output)
+ switch {
+ case err == nil:
+ if info.Mode().IsRegular() {
+ return errors.Errorf("%q already exists as a regular file", options.Output)
+ }
+ case os.IsNotExist(err):
+ if err := os.Mkdir(options.Output, 0755); err != nil {
+ return err
+ }
+ default:
+ return err
+ }
+ return utils2.UntarToFileSystem(options.Output, f, nil)
+}
diff --git a/pkg/domain/infra/tunnel/system.go b/pkg/domain/infra/tunnel/system.go
new file mode 100644
index 000000000..5bafef1fe
--- /dev/null
+++ b/pkg/domain/infra/tunnel/system.go
@@ -0,0 +1 @@
+package tunnel
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index d4fd5976f..5de07fc28 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -316,7 +316,17 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
// Make sure to always set the default variables unless overridden in the
// config.
- config.Env = env.Join(env.DefaultEnvVariables, config.Env)
+ var defaultEnv map[string]string
+ if runtimeConfig == nil {
+ defaultEnv = env.DefaultEnvVariables
+ } else {
+ defaultEnv, err = env.ParseSlice(runtimeConfig.Containers.Env)
+ if err != nil {
+ return nil, errors.Wrap(err, "Env fields in containers.conf failed ot parse")
+ }
+ defaultEnv = env.Join(env.DefaultEnvVariables, defaultEnv)
+ }
+ config.Env = env.Join(defaultEnv, config.Env)
for name, val := range config.Env {
g.AddProcessEnv(name, val)
}
@@ -371,11 +381,9 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
// BIND MOUNTS
configSpec.Mounts = SupercedeUserMounts(userMounts, configSpec.Mounts)
// Process mounts to ensure correct options
- finalMounts, err := InitFSMounts(configSpec.Mounts)
- if err != nil {
+ if err := InitFSMounts(configSpec.Mounts); err != nil {
return nil, err
}
- configSpec.Mounts = finalMounts
// BLOCK IO
blkio, err := config.CreateBlockIO()
diff --git a/pkg/spec/storage.go b/pkg/spec/storage.go
index b0687b4c2..68a84d638 100644
--- a/pkg/spec/storage.go
+++ b/pkg/spec/storage.go
@@ -10,7 +10,6 @@ import (
"github.com/containers/buildah/pkg/parse"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/util"
- pmount "github.com/containers/storage/pkg/mount"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -855,75 +854,22 @@ func SupercedeUserMounts(mounts []spec.Mount, configMount []spec.Mount) []spec.M
}
// Ensure mount options on all mounts are correct
-func InitFSMounts(inputMounts []spec.Mount) ([]spec.Mount, error) {
- // We need to look up mounts so we can figure out the proper mount flags
- // to apply.
- systemMounts, err := pmount.GetMounts()
- if err != nil {
- return nil, errors.Wrapf(err, "error retrieving system mounts to look up mount options")
- }
-
- // TODO: We probably don't need to re-build the mounts array
- var mounts []spec.Mount
- for _, m := range inputMounts {
- if m.Type == TypeBind {
- baseMnt, err := findMount(m.Destination, systemMounts)
+func InitFSMounts(mounts []spec.Mount) error {
+ for i, m := range mounts {
+ switch {
+ case m.Type == TypeBind:
+ opts, err := util.ProcessOptions(m.Options, false, m.Source)
if err != nil {
- return nil, errors.Wrapf(err, "error looking up mountpoint for mount %s", m.Destination)
- }
- var noexec, nosuid, nodev bool
- for _, baseOpt := range strings.Split(baseMnt.Opts, ",") {
- switch baseOpt {
- case "noexec":
- noexec = true
- case "nosuid":
- nosuid = true
- case "nodev":
- nodev = true
- }
+ return err
}
-
- defaultMountOpts := new(util.DefaultMountOptions)
- defaultMountOpts.Noexec = noexec
- defaultMountOpts.Nosuid = nosuid
- defaultMountOpts.Nodev = nodev
-
- opts, err := util.ProcessOptions(m.Options, false, defaultMountOpts)
+ mounts[i].Options = opts
+ case m.Type == TypeTmpfs && filepath.Clean(m.Destination) != "/dev":
+ opts, err := util.ProcessOptions(m.Options, true, "")
if err != nil {
- return nil, err
+ return err
}
- m.Options = opts
- }
- if m.Type == TypeTmpfs && filepath.Clean(m.Destination) != "/dev" {
- opts, err := util.ProcessOptions(m.Options, true, nil)
- if err != nil {
- return nil, err
- }
- m.Options = opts
- }
-
- mounts = append(mounts, m)
- }
- return mounts, nil
-}
-
-// TODO: We could make this a bit faster by building a tree of the mountpoints
-// and traversing it to identify the correct mount.
-func findMount(target string, mounts []*pmount.Info) (*pmount.Info, error) {
- var err error
- target, err = filepath.Abs(target)
- if err != nil {
- return nil, errors.Wrapf(err, "cannot resolve %s", target)
- }
- var bestSoFar *pmount.Info
- for _, i := range mounts {
- if bestSoFar != nil && len(bestSoFar.Mountpoint) > len(i.Mountpoint) {
- // Won't be better than what we have already found
- continue
- }
- if strings.HasPrefix(target, i.Mountpoint) {
- bestSoFar = i
+ mounts[i].Options = opts
}
}
- return bestSoFar, nil
+ return nil
}
diff --git a/pkg/specgen/config_linux.go b/pkg/specgen/config_linux.go
new file mode 100644
index 000000000..82a371492
--- /dev/null
+++ b/pkg/specgen/config_linux.go
@@ -0,0 +1,93 @@
+package specgen
+
+//func createBlockIO() (*spec.LinuxBlockIO, error) {
+// var ret *spec.LinuxBlockIO
+// bio := &spec.LinuxBlockIO{}
+// if c.Resources.BlkioWeight > 0 {
+// ret = bio
+// bio.Weight = &c.Resources.BlkioWeight
+// }
+// if len(c.Resources.BlkioWeightDevice) > 0 {
+// var lwds []spec.LinuxWeightDevice
+// ret = bio
+// for _, i := range c.Resources.BlkioWeightDevice {
+// wd, err := ValidateweightDevice(i)
+// if err != nil {
+// return ret, errors.Wrapf(err, "invalid values for blkio-weight-device")
+// }
+// wdStat, err := GetStatFromPath(wd.Path)
+// if err != nil {
+// return ret, errors.Wrapf(err, "error getting stat from path %q", wd.Path)
+// }
+// lwd := spec.LinuxWeightDevice{
+// Weight: &wd.Weight,
+// }
+// lwd.Major = int64(unix.Major(wdStat.Rdev))
+// lwd.Minor = int64(unix.Minor(wdStat.Rdev))
+// lwds = append(lwds, lwd)
+// }
+// bio.WeightDevice = lwds
+// }
+// if len(c.Resources.DeviceReadBps) > 0 {
+// ret = bio
+// readBps, err := makeThrottleArray(c.Resources.DeviceReadBps, bps)
+// if err != nil {
+// return ret, err
+// }
+// bio.ThrottleReadBpsDevice = readBps
+// }
+// if len(c.Resources.DeviceWriteBps) > 0 {
+// ret = bio
+// writeBpds, err := makeThrottleArray(c.Resources.DeviceWriteBps, bps)
+// if err != nil {
+// return ret, err
+// }
+// bio.ThrottleWriteBpsDevice = writeBpds
+// }
+// if len(c.Resources.DeviceReadIOps) > 0 {
+// ret = bio
+// readIOps, err := makeThrottleArray(c.Resources.DeviceReadIOps, iops)
+// if err != nil {
+// return ret, err
+// }
+// bio.ThrottleReadIOPSDevice = readIOps
+// }
+// if len(c.Resources.DeviceWriteIOps) > 0 {
+// ret = bio
+// writeIOps, err := makeThrottleArray(c.Resources.DeviceWriteIOps, iops)
+// if err != nil {
+// return ret, err
+// }
+// bio.ThrottleWriteIOPSDevice = writeIOps
+// }
+// return ret, nil
+//}
+
+//func makeThrottleArray(throttleInput []string, rateType int) ([]spec.LinuxThrottleDevice, error) {
+// var (
+// ltds []spec.LinuxThrottleDevice
+// t *throttleDevice
+// err error
+// )
+// for _, i := range throttleInput {
+// if rateType == bps {
+// t, err = validateBpsDevice(i)
+// } else {
+// t, err = validateIOpsDevice(i)
+// }
+// if err != nil {
+// return []spec.LinuxThrottleDevice{}, err
+// }
+// ltdStat, err := GetStatFromPath(t.path)
+// if err != nil {
+// return ltds, errors.Wrapf(err, "error getting stat from path %q", t.path)
+// }
+// ltd := spec.LinuxThrottleDevice{
+// Rate: t.rate,
+// }
+// ltd.Major = int64(unix.Major(ltdStat.Rdev))
+// ltd.Minor = int64(unix.Minor(ltdStat.Rdev))
+// ltds = append(ltds, ltd)
+// }
+// return ltds, nil
+//}
diff --git a/pkg/specgen/config_linux_cgo.go b/pkg/specgen/config_linux_cgo.go
index 6f547a40d..ef6c6e951 100644
--- a/pkg/specgen/config_linux_cgo.go
+++ b/pkg/specgen/config_linux_cgo.go
@@ -17,7 +17,6 @@ import (
func (s *SpecGenerator) getSeccompConfig(configSpec *spec.Spec, img *image.Image) (*spec.LinuxSeccomp, error) {
var seccompConfig *spec.LinuxSeccomp
var err error
-
scp, err := seccomp.LookupPolicy(s.SeccompPolicy)
if err != nil {
return nil, err
diff --git a/pkg/specgen/container_validate.go b/pkg/specgen/container_validate.go
index b27659f5f..aad14ddcb 100644
--- a/pkg/specgen/container_validate.go
+++ b/pkg/specgen/container_validate.go
@@ -14,7 +14,7 @@ var (
// SystemDValues describes the only values that SystemD can be
SystemDValues = []string{"true", "false", "always"}
// ImageVolumeModeValues describes the only values that ImageVolumeMode can be
- ImageVolumeModeValues = []string{"ignore", "tmpfs", "anonymous"}
+ ImageVolumeModeValues = []string{"ignore", "tmpfs", "bind"}
)
func exclusiveOptions(opt1, opt2 string) error {
@@ -23,7 +23,7 @@ func exclusiveOptions(opt1, opt2 string) error {
// Validate verifies that the given SpecGenerator is valid and satisfies required
// input for creating a container.
-func (s *SpecGenerator) validate() error {
+func (s *SpecGenerator) Validate() error {
//
// ContainerBasicConfig
diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go
new file mode 100644
index 000000000..78c77fec1
--- /dev/null
+++ b/pkg/specgen/generate/container.go
@@ -0,0 +1,168 @@
+package generate
+
+import (
+ "context"
+
+ "github.com/containers/libpod/libpod"
+ ann "github.com/containers/libpod/pkg/annotations"
+ envLib "github.com/containers/libpod/pkg/env"
+ "github.com/containers/libpod/pkg/signal"
+ "github.com/containers/libpod/pkg/specgen"
+ "github.com/pkg/errors"
+ "golang.org/x/sys/unix"
+)
+
+func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerator) error {
+
+ newImage, err := r.ImageRuntime().NewFromLocal(s.Image)
+ if err != nil {
+ return err
+ }
+
+ // Image stop signal
+ if s.StopSignal == nil && newImage.Config != nil {
+ sig, err := signal.ParseSignalNameOrNumber(newImage.Config.StopSignal)
+ if err != nil {
+ return err
+ }
+ s.StopSignal = &sig
+ }
+ // Image envs from the image if they don't exist
+ // already
+ if newImage.Config != nil && len(newImage.Config.Env) > 0 {
+ envs, err := envLib.ParseSlice(newImage.Config.Env)
+ if err != nil {
+ return err
+ }
+ for k, v := range envs {
+ if _, exists := s.Env[k]; !exists {
+ s.Env[v] = k
+ }
+ }
+ }
+
+ // labels from the image that dont exist already
+ if config := newImage.Config; config != nil {
+ for k, v := range config.Labels {
+ if _, exists := s.Labels[k]; !exists {
+ s.Labels[k] = v
+ }
+ }
+ }
+
+ // annotations
+ // in the event this container is in a pod, and the pod has an infra container
+ // we will want to configure it as a type "container" instead defaulting to
+ // the behavior of a "sandbox" container
+ // In Kata containers:
+ // - "sandbox" is the annotation that denotes the container should use its own
+ // VM, which is the default behavior
+ // - "container" denotes the container should join the VM of the SandboxID
+ // (the infra container)
+ s.Annotations = make(map[string]string)
+ if len(s.Pod) > 0 {
+ s.Annotations[ann.SandboxID] = s.Pod
+ s.Annotations[ann.ContainerType] = ann.ContainerTypeContainer
+ }
+ //
+ // Next, add annotations from the image
+ annotations, err := newImage.Annotations(ctx)
+ if err != nil {
+ return err
+ }
+ for k, v := range annotations {
+ annotations[k] = v
+ }
+
+ // entrypoint
+ if config := newImage.Config; config != nil {
+ if len(s.Entrypoint) < 1 && len(config.Entrypoint) > 0 {
+ s.Entrypoint = config.Entrypoint
+ }
+ if len(s.Command) < 1 && len(config.Cmd) > 0 {
+ s.Command = config.Cmd
+ }
+ if len(s.Command) < 1 && len(s.Entrypoint) < 1 {
+ return errors.Errorf("No command provided or as CMD or ENTRYPOINT in this image")
+ }
+ // workdir
+ if len(s.WorkDir) < 1 && len(config.WorkingDir) > 1 {
+ s.WorkDir = config.WorkingDir
+ }
+ }
+
+ if len(s.SeccompProfilePath) < 1 {
+ p, err := libpod.DefaultSeccompPath()
+ if err != nil {
+ return err
+ }
+ s.SeccompProfilePath = p
+ }
+
+ if user := s.User; len(user) == 0 {
+ switch {
+ // TODO This should be enabled when namespaces actually work
+ //case usernsMode.IsKeepID():
+ // user = fmt.Sprintf("%d:%d", rootless.GetRootlessUID(), rootless.GetRootlessGID())
+ case newImage.Config == nil || (newImage.Config != nil && len(newImage.Config.User) == 0):
+ s.User = "0"
+ default:
+ s.User = newImage.Config.User
+ }
+ }
+ if err := finishThrottleDevices(s); err != nil {
+ return err
+ }
+ return nil
+}
+
+// finishThrottleDevices takes the temporary representation of the throttle
+// devices in the specgen and looks up the major and major minors. it then
+// sets the throttle devices proper in the specgen
+func finishThrottleDevices(s *specgen.SpecGenerator) error {
+ if bps := s.ThrottleReadBpsDevice; len(bps) > 0 {
+ for k, v := range bps {
+ statT := unix.Stat_t{}
+ if err := unix.Stat(k, &statT); err != nil {
+ return err
+ }
+ v.Major = (int64(unix.Major(statT.Rdev)))
+ v.Minor = (int64(unix.Minor(statT.Rdev)))
+ s.ResourceLimits.BlockIO.ThrottleReadBpsDevice = append(s.ResourceLimits.BlockIO.ThrottleReadBpsDevice, v)
+ }
+ }
+ if bps := s.ThrottleWriteBpsDevice; len(bps) > 0 {
+ for k, v := range bps {
+ statT := unix.Stat_t{}
+ if err := unix.Stat(k, &statT); err != nil {
+ return err
+ }
+ v.Major = (int64(unix.Major(statT.Rdev)))
+ v.Minor = (int64(unix.Minor(statT.Rdev)))
+ s.ResourceLimits.BlockIO.ThrottleWriteBpsDevice = append(s.ResourceLimits.BlockIO.ThrottleWriteBpsDevice, v)
+ }
+ }
+ if iops := s.ThrottleReadIOPSDevice; len(iops) > 0 {
+ for k, v := range iops {
+ statT := unix.Stat_t{}
+ if err := unix.Stat(k, &statT); err != nil {
+ return err
+ }
+ v.Major = (int64(unix.Major(statT.Rdev)))
+ v.Minor = (int64(unix.Minor(statT.Rdev)))
+ s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice, v)
+ }
+ }
+ if iops := s.ThrottleWriteBpsDevice; len(iops) > 0 {
+ for k, v := range iops {
+ statT := unix.Stat_t{}
+ if err := unix.Stat(k, &statT); err != nil {
+ return err
+ }
+ v.Major = (int64(unix.Major(statT.Rdev)))
+ v.Minor = (int64(unix.Minor(statT.Rdev)))
+ s.ResourceLimits.BlockIO.ThrottleWriteIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleWriteIOPSDevice, v)
+ }
+ }
+ return nil
+}
diff --git a/pkg/specgen/container_create.go b/pkg/specgen/generate/container_create.go
index b4039bb91..aad59a861 100644
--- a/pkg/specgen/container_create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -1,4 +1,4 @@
-package specgen
+package generate
import (
"context"
@@ -7,14 +7,15 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/specgen"
"github.com/containers/storage"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// MakeContainer creates a container based on the SpecGenerator
-func (s *SpecGenerator) MakeContainer(rt *libpod.Runtime) (*libpod.Container, error) {
- if err := s.validate(); err != nil {
+func MakeContainer(rt *libpod.Runtime, s *specgen.SpecGenerator) (*libpod.Container, error) {
+ if err := s.Validate(); err != nil {
return nil, errors.Wrap(err, "invalid config provided")
}
rtc, err := rt.GetConfig()
@@ -22,7 +23,7 @@ func (s *SpecGenerator) MakeContainer(rt *libpod.Runtime) (*libpod.Container, er
return nil, err
}
- options, err := s.createContainerOptions(rt)
+ options, err := createContainerOptions(rt, s)
if err != nil {
return nil, err
}
@@ -31,7 +32,7 @@ func (s *SpecGenerator) MakeContainer(rt *libpod.Runtime) (*libpod.Container, er
if err != nil {
return nil, err
}
- options = append(options, s.createExitCommandOption(rt.StorageConfig(), rtc, podmanPath))
+ options = append(options, createExitCommandOption(s, rt.StorageConfig(), rtc, podmanPath))
newImage, err := rt.ImageRuntime().NewFromLocal(s.Image)
if err != nil {
return nil, err
@@ -39,14 +40,14 @@ func (s *SpecGenerator) MakeContainer(rt *libpod.Runtime) (*libpod.Container, er
options = append(options, libpod.WithRootFSFromImage(newImage.ID(), s.Image, s.RawImageName))
- runtimeSpec, err := s.toOCISpec(rt, newImage)
+ runtimeSpec, err := s.ToOCISpec(rt, newImage)
if err != nil {
return nil, err
}
return rt.NewContainer(context.Background(), runtimeSpec, options...)
}
-func (s *SpecGenerator) createContainerOptions(rt *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
+func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator) ([]libpod.CtrCreateOption, error) {
var options []libpod.CtrCreateOption
var err error
@@ -114,7 +115,7 @@ func (s *SpecGenerator) createContainerOptions(rt *libpod.Runtime) ([]libpod.Ctr
options = append(options, libpod.WithPrivileged(s.Privileged))
// Get namespace related options
- namespaceOptions, err := s.generateNamespaceContainerOpts(rt)
+ namespaceOptions, err := s.GenerateNamespaceContainerOpts(rt)
if err != nil {
return nil, err
}
@@ -149,7 +150,7 @@ func (s *SpecGenerator) createContainerOptions(rt *libpod.Runtime) ([]libpod.Ctr
return options, nil
}
-func (s *SpecGenerator) createExitCommandOption(storageConfig storage.StoreOptions, config *config.Config, podmanPath string) libpod.CtrCreateOption {
+func createExitCommandOption(s *specgen.SpecGenerator, storageConfig storage.StoreOptions, config *config.Config, podmanPath string) libpod.CtrCreateOption {
// We need a cleanup process for containers in the current model.
// But we can't assume that the caller is Podman - it could be another
// user of the API.
diff --git a/pkg/specgen/namespaces.go b/pkg/specgen/namespaces.go
index fa2dee77d..2a7bb3495 100644
--- a/pkg/specgen/namespaces.go
+++ b/pkg/specgen/namespaces.go
@@ -16,6 +16,9 @@ import (
type NamespaceMode string
const (
+ // Default indicates the spec generator should determine
+ // a sane default
+ Default NamespaceMode = "default"
// Host means the the namespace is derived from
// the host
Host NamespaceMode = "host"
@@ -83,7 +86,7 @@ func validateNetNS(n *Namespace) error {
return nil
}
-// validate perform simple validation on the namespace to make sure it is not
+// Validate perform simple validation on the namespace to make sure it is not
// invalid from the get-go
func (n *Namespace) validate() error {
if n == nil {
@@ -103,7 +106,7 @@ func (n *Namespace) validate() error {
return nil
}
-func (s *SpecGenerator) generateNamespaceContainerOpts(rt *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
+func (s *SpecGenerator) GenerateNamespaceContainerOpts(rt *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
var portBindings []ocicni.PortMapping
options := make([]libpod.CtrCreateOption, 0)
diff --git a/pkg/specgen/oci.go b/pkg/specgen/oci.go
index 2523f21b3..0756782b4 100644
--- a/pkg/specgen/oci.go
+++ b/pkg/specgen/oci.go
@@ -11,7 +11,7 @@ import (
"github.com/opencontainers/runtime-tools/generate"
)
-func (s *SpecGenerator) toOCISpec(rt *libpod.Runtime, newImage *image.Image) (*spec.Spec, error) {
+func (s *SpecGenerator) ToOCISpec(rt *libpod.Runtime, newImage *image.Image) (*spec.Spec, error) {
var (
inUserNS bool
)
@@ -215,11 +215,9 @@ func (s *SpecGenerator) toOCISpec(rt *libpod.Runtime, newImage *image.Image) (*s
// BIND MOUNTS
configSpec.Mounts = createconfig.SupercedeUserMounts(s.Mounts, configSpec.Mounts)
// Process mounts to ensure correct options
- finalMounts, err := createconfig.InitFSMounts(configSpec.Mounts)
- if err != nil {
+ if err := createconfig.InitFSMounts(configSpec.Mounts); err != nil {
return nil, err
}
- configSpec.Mounts = finalMounts
// Add annotations
if configSpec.Annotations == nil {
diff --git a/pkg/specgen/security.go b/pkg/specgen/security.go
new file mode 100644
index 000000000..158e4a7b3
--- /dev/null
+++ b/pkg/specgen/security.go
@@ -0,0 +1,165 @@
+package specgen
+
+// ToCreateOptions convert the SecurityConfig to a slice of container create
+// options.
+/*
+func (c *SecurityConfig) ToCreateOptions() ([]libpod.CtrCreateOption, error) {
+ options := make([]libpod.CtrCreateOption, 0)
+ options = append(options, libpod.WithSecLabels(c.LabelOpts))
+ options = append(options, libpod.WithPrivileged(c.Privileged))
+ return options, nil
+}
+*/
+
+// SetLabelOpts sets the label options of the SecurityConfig according to the
+// input.
+/*
+func (c *SecurityConfig) SetLabelOpts(runtime *libpod.Runtime, pidConfig *PidConfig, ipcConfig *IpcConfig) error {
+ if c.Privileged {
+ c.LabelOpts = label.DisableSecOpt()
+ return nil
+ }
+
+ var labelOpts []string
+ if pidConfig.PidMode.IsHost() {
+ labelOpts = append(labelOpts, label.DisableSecOpt()...)
+ } else if pidConfig.PidMode.IsContainer() {
+ ctr, err := runtime.LookupContainer(pidConfig.PidMode.Container())
+ if err != nil {
+ return errors.Wrapf(err, "container %q not found", pidConfig.PidMode.Container())
+ }
+ secopts, err := label.DupSecOpt(ctr.ProcessLabel())
+ if err != nil {
+ return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel())
+ }
+ labelOpts = append(labelOpts, secopts...)
+ }
+
+ if ipcConfig.IpcMode.IsHost() {
+ labelOpts = append(labelOpts, label.DisableSecOpt()...)
+ } else if ipcConfig.IpcMode.IsContainer() {
+ ctr, err := runtime.LookupContainer(ipcConfig.IpcMode.Container())
+ if err != nil {
+ return errors.Wrapf(err, "container %q not found", ipcConfig.IpcMode.Container())
+ }
+ secopts, err := label.DupSecOpt(ctr.ProcessLabel())
+ if err != nil {
+ return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel())
+ }
+ labelOpts = append(labelOpts, secopts...)
+ }
+
+ c.LabelOpts = append(c.LabelOpts, labelOpts...)
+ return nil
+}
+*/
+
+// SetSecurityOpts the the security options (labels, apparmor, seccomp, etc.).
+func SetSecurityOpts(securityOpts []string) error {
+ return nil
+}
+
+// ConfigureGenerator configures the generator according to the input.
+/*
+func (c *SecurityConfig) ConfigureGenerator(g *generate.Generator, user *UserConfig) error {
+ // HANDLE CAPABILITIES
+ // NOTE: Must happen before SECCOMP
+ if c.Privileged {
+ g.SetupPrivileged(true)
+ }
+
+ useNotRoot := func(user string) bool {
+ if user == "" || user == "root" || user == "0" {
+ return false
+ }
+ return true
+ }
+
+ configSpec := g.Config
+ var err error
+ var defaultCaplist []string
+ bounding := configSpec.Process.Capabilities.Bounding
+ if useNotRoot(user.User) {
+ configSpec.Process.Capabilities.Bounding = defaultCaplist
+ }
+ defaultCaplist, err = capabilities.MergeCapabilities(configSpec.Process.Capabilities.Bounding, c.CapAdd, c.CapDrop)
+ if err != nil {
+ return err
+ }
+
+ privCapRequired := []string{}
+
+ if !c.Privileged && len(c.CapRequired) > 0 {
+ // Pass CapRequired in CapAdd field to normalize capabilities names
+ capRequired, err := capabilities.MergeCapabilities(nil, c.CapRequired, nil)
+ if err != nil {
+ logrus.Errorf("capabilities requested by user or image are not valid: %q", strings.Join(c.CapRequired, ","))
+ } else {
+ // Verify all capRequiered are in the defaultCapList
+ for _, cap := range capRequired {
+ if !util.StringInSlice(cap, defaultCaplist) {
+ privCapRequired = append(privCapRequired, cap)
+ }
+ }
+ }
+ if len(privCapRequired) == 0 {
+ defaultCaplist = capRequired
+ } else {
+ logrus.Errorf("capabilities requested by user or image are not allowed by default: %q", strings.Join(privCapRequired, ","))
+ }
+ }
+ configSpec.Process.Capabilities.Bounding = defaultCaplist
+ configSpec.Process.Capabilities.Permitted = defaultCaplist
+ configSpec.Process.Capabilities.Inheritable = defaultCaplist
+ configSpec.Process.Capabilities.Effective = defaultCaplist
+ configSpec.Process.Capabilities.Ambient = defaultCaplist
+ if useNotRoot(user.User) {
+ defaultCaplist, err = capabilities.MergeCapabilities(bounding, c.CapAdd, c.CapDrop)
+ if err != nil {
+ return err
+ }
+ }
+ configSpec.Process.Capabilities.Bounding = defaultCaplist
+
+ // HANDLE SECCOMP
+ if c.SeccompProfilePath != "unconfined" {
+ seccompConfig, err := getSeccompConfig(c, configSpec)
+ if err != nil {
+ return err
+ }
+ configSpec.Linux.Seccomp = seccompConfig
+ }
+
+ // Clear default Seccomp profile from Generator for privileged containers
+ if c.SeccompProfilePath == "unconfined" || c.Privileged {
+ configSpec.Linux.Seccomp = nil
+ }
+
+ for _, opt := range c.SecurityOpts {
+ // Split on both : and =
+ splitOpt := strings.Split(opt, "=")
+ if len(splitOpt) == 1 {
+ splitOpt = strings.Split(opt, ":")
+ }
+ if len(splitOpt) < 2 {
+ continue
+ }
+ switch splitOpt[0] {
+ case "label":
+ configSpec.Annotations[libpod.InspectAnnotationLabel] = splitOpt[1]
+ case "seccomp":
+ configSpec.Annotations[libpod.InspectAnnotationSeccomp] = splitOpt[1]
+ case "apparmor":
+ configSpec.Annotations[libpod.InspectAnnotationApparmor] = splitOpt[1]
+ }
+ }
+
+ g.SetRootReadonly(c.ReadOnlyRootfs)
+ for sysctlKey, sysctlVal := range c.Sysctl {
+ g.AddLinuxSysctl(sysctlKey, sysctlVal)
+ }
+
+ return nil
+}
+
+*/
diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go
index 89c76c273..2e6dd9c1d 100644
--- a/pkg/specgen/specgen.go
+++ b/pkg/specgen/specgen.go
@@ -4,8 +4,9 @@ import (
"net"
"syscall"
- "github.com/containers/image/v5/manifest"
"github.com/containers/libpod/libpod"
+
+ "github.com/containers/image/v5/manifest"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/storage"
"github.com/cri-o/ocicni/pkg/ocicni"
@@ -371,6 +372,16 @@ type ContainerResourceConfig struct {
// processes to kill for the container's process.
// Optional.
OOMScoreAdj *int `json:"oom_score_adj,omitempty"`
+ // Weight per cgroup per device, can override BlkioWeight
+ WeightDevice map[string]spec.LinuxWeightDevice `json:"weightDevice,omitempty"`
+ // IO read rate limit per cgroup per device, bytes per second
+ ThrottleReadBpsDevice map[string]spec.LinuxThrottleDevice `json:"throttleReadBpsDevice,omitempty"`
+ // IO write rate limit per cgroup per device, bytes per second
+ ThrottleWriteBpsDevice map[string]spec.LinuxThrottleDevice `json:"throttleWriteBpsDevice,omitempty"`
+ // IO read rate limit per cgroup per device, IO per second
+ ThrottleReadIOPSDevice map[string]spec.LinuxThrottleDevice `json:"throttleReadIOPSDevice,omitempty"`
+ // IO write rate limit per cgroup per device, IO per second
+ ThrottleWriteIOPSDevice map[string]spec.LinuxThrottleDevice `json:"throttleWriteIOPSDevice,omitempty"`
}
// ContainerHealthCheckConfig describes a container healthcheck with attributes
diff --git a/pkg/specgen/storage.go b/pkg/specgen/storage.go
new file mode 100644
index 000000000..1b903f608
--- /dev/null
+++ b/pkg/specgen/storage.go
@@ -0,0 +1,885 @@
+package specgen
+
+//nolint
+
+import (
+ "fmt"
+ "path"
+ "path/filepath"
+ "strings"
+
+ "github.com/containers/libpod/libpod"
+
+ "github.com/containers/buildah/pkg/parse"
+ "github.com/containers/libpod/pkg/util"
+ spec "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+const (
+ // TypeBind is the type for mounting host dir
+ TypeBind = "bind"
+ // TypeVolume is the type for named volumes
+ TypeVolume = "volume"
+ // TypeTmpfs is the type for mounting tmpfs
+ TypeTmpfs = "tmpfs"
+)
+
+var (
+ errDuplicateDest = errors.Errorf("duplicate mount destination") //nolint
+ optionArgError = errors.Errorf("must provide an argument for option") //nolint
+ noDestError = errors.Errorf("must set volume destination") //nolint
+)
+
+// Parse all volume-related options in the create config into a set of mounts
+// and named volumes to add to the container.
+// Handles --volumes-from, --volumes, --tmpfs, --init, and --init-path flags.
+// TODO: Named volume options - should we default to rprivate? It bakes into a
+// bind mount under the hood...
+// TODO: handle options parsing/processing via containers/storage/pkg/mount
+func (s *SpecGenerator) parseVolumes(mounts, volMounts, tmpMounts []string) error { //nolint
+
+ // TODO this needs to come from the image and erquires a runtime
+
+ // Add image volumes.
+ //baseMounts, baseVolumes, err := config.getImageVolumes()
+ //if err != nil {
+ // return nil, nil, err
+ //}
+
+ // Add --volumes-from.
+ // Overrides image volumes unconditionally.
+ //vFromMounts, vFromVolumes, err := config.getVolumesFrom(runtime)
+ //if err != nil {
+ // return nil, nil, err
+ //}
+ //for dest, mount := range vFromMounts {
+ // baseMounts[dest] = mount
+ //}
+ //for dest, volume := range vFromVolumes {
+ // baseVolumes[dest] = volume
+ //}
+
+ // Next mounts from the --mounts flag.
+ // Do not override yet.
+ //unifiedMounts, _, err := getMounts(mounts)
+ //if err != nil {
+ // return err
+ //}
+ //
+ //// Next --volumes flag.
+ //// Do not override yet.
+ //volumeMounts, _ , err := getVolumeMounts(volMounts)
+ //if err != nil {
+ // return err
+ //}
+ //
+ //// Next --tmpfs flag.
+ //// Do not override yet.
+ //tmpfsMounts, err := getTmpfsMounts(tmpMounts)
+ //if err != nil {
+ // return err
+ //}
+
+ //// Unify mounts from --mount, --volume, --tmpfs.
+ //// Also add mounts + volumes directly from createconfig.
+ //// Start with --volume.
+ //for dest, mount := range volumeMounts {
+ // if _, ok := unifiedMounts[dest]; ok {
+ // return nil, nil, errors.Wrapf(errDuplicateDest, dest)
+ // }
+ // unifiedMounts[dest] = mount
+ //}
+ //for dest, volume := range volumeVolumes {
+ // if _, ok := unifiedVolumes[dest]; ok {
+ // return nil, nil, errors.Wrapf(errDuplicateDest, dest)
+ // }
+ // unifiedVolumes[dest] = volume
+ //}
+ //// Now --tmpfs
+ //for dest, tmpfs := range tmpfsMounts {
+ // if _, ok := unifiedMounts[dest]; ok {
+ // return nil, nil, errors.Wrapf(errDuplicateDest, dest)
+ // }
+ // unifiedMounts[dest] = tmpfs
+ //}
+ //// Now spec mounts and volumes
+ //for _, mount := range config.Mounts {
+ // dest := mount.Destination
+ // if _, ok := unifiedMounts[dest]; ok {
+ // return nil, nil, errors.Wrapf(errDuplicateDest, dest)
+ // }
+ // unifiedMounts[dest] = mount
+ //}
+ //for _, volume := range config.NamedVolumes {
+ // dest := volume.Dest
+ // if _, ok := unifiedVolumes[dest]; ok {
+ // return nil, nil, errors.Wrapf(errDuplicateDest, dest)
+ // }
+ // unifiedVolumes[dest] = volume
+ //}
+ //
+ //// If requested, add container init binary
+ //if config.Init {
+ // initPath := config.InitPath
+ // if initPath == "" {
+ // rtc, err := runtime.GetConfig()
+ // if err != nil {
+ // return nil, nil, err
+ // }
+ // initPath = rtc.Engine.InitPath
+ // }
+ // initMount, err := config.addContainerInitBinary(initPath)
+ // if err != nil {
+ // return nil, nil, err
+ // }
+ // if _, ok := unifiedMounts[initMount.Destination]; ok {
+ // return nil, nil, errors.Wrapf(errDuplicateDest, "conflict with mount added by --init to %q", initMount.Destination)
+ // }
+ // unifiedMounts[initMount.Destination] = initMount
+ //}
+ //
+ //// Before superseding, we need to find volume mounts which conflict with
+ //// named volumes, and vice versa.
+ //// We'll delete the conflicts here as we supersede.
+ //for dest := range unifiedMounts {
+ // if _, ok := baseVolumes[dest]; ok {
+ // delete(baseVolumes, dest)
+ // }
+ //}
+ //for dest := range unifiedVolumes {
+ // if _, ok := baseMounts[dest]; ok {
+ // delete(baseMounts, dest)
+ // }
+ //}
+ //
+ //// Supersede volumes-from/image volumes with unified volumes from above.
+ //// This is an unconditional replacement.
+ //for dest, mount := range unifiedMounts {
+ // baseMounts[dest] = mount
+ //}
+ //for dest, volume := range unifiedVolumes {
+ // baseVolumes[dest] = volume
+ //}
+ //
+ //// If requested, add tmpfs filesystems for read-only containers.
+ //if config.Security.ReadOnlyRootfs && config.Security.ReadOnlyTmpfs {
+ // readonlyTmpfs := []string{"/tmp", "/var/tmp", "/run"}
+ // options := []string{"rw", "rprivate", "nosuid", "nodev", "tmpcopyup"}
+ // for _, dest := range readonlyTmpfs {
+ // if _, ok := baseMounts[dest]; ok {
+ // continue
+ // }
+ // if _, ok := baseVolumes[dest]; ok {
+ // continue
+ // }
+ // localOpts := options
+ // if dest == "/run" {
+ // localOpts = append(localOpts, "noexec", "size=65536k")
+ // } else {
+ // localOpts = append(localOpts, "exec")
+ // }
+ // baseMounts[dest] = spec.Mount{
+ // Destination: dest,
+ // Type: "tmpfs",
+ // Source: "tmpfs",
+ // Options: localOpts,
+ // }
+ // }
+ //}
+ //
+ //// Check for conflicts between named volumes and mounts
+ //for dest := range baseMounts {
+ // if _, ok := baseVolumes[dest]; ok {
+ // return nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
+ // }
+ //}
+ //for dest := range baseVolumes {
+ // if _, ok := baseMounts[dest]; ok {
+ // return nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
+ // }
+ //}
+ //
+ //// Final step: maps to arrays
+ //finalMounts := make([]spec.Mount, 0, len(baseMounts))
+ //for _, mount := range baseMounts {
+ // if mount.Type == TypeBind {
+ // absSrc, err := filepath.Abs(mount.Source)
+ // if err != nil {
+ // return nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source)
+ // }
+ // mount.Source = absSrc
+ // }
+ // finalMounts = append(finalMounts, mount)
+ //}
+ //finalVolumes := make([]*define.ContainerNamedVolume, 0, len(baseVolumes))
+ //for _, volume := range baseVolumes {
+ // finalVolumes = append(finalVolumes, volume)
+ //}
+
+ //return finalMounts, finalVolumes, nil
+ return nil
+}
+
+// Parse volumes from - a set of containers whose volumes we will mount in.
+// Grab the containers, retrieve any user-created spec mounts and all named
+// volumes, and return a list of them.
+// Conflicts are resolved simply - the last container specified wins.
+// Container names may be suffixed by mount options after a colon.
+// TODO: We should clean these paths if possible
+// TODO deferred baude
+func getVolumesFrom() (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) { //nolint
+ // Both of these are maps of mount destination to mount type.
+ // We ensure that each destination is only mounted to once in this way.
+ //finalMounts := make(map[string]spec.Mount)
+ //finalNamedVolumes := make(map[string]*define.ContainerNamedVolume)
+ //
+ //for _, vol := range config.VolumesFrom {
+ // var (
+ // options = []string{}
+ // err error
+ // splitVol = strings.SplitN(vol, ":", 2)
+ // )
+ // if len(splitVol) == 2 {
+ // splitOpts := strings.Split(splitVol[1], ",")
+ // for _, checkOpt := range splitOpts {
+ // switch checkOpt {
+ // case "z", "ro", "rw":
+ // // Do nothing, these are valid options
+ // default:
+ // return nil, nil, errors.Errorf("invalid options %q, can only specify 'ro', 'rw', and 'z'", splitVol[1])
+ // }
+ // }
+ //
+ // if options, err = parse.ValidateVolumeOpts(splitOpts); err != nil {
+ // return nil, nil, err
+ // }
+ // }
+ // ctr, err := runtime.LookupContainer(splitVol[0])
+ // if err != nil {
+ // return nil, nil, errors.Wrapf(err, "error looking up container %q for volumes-from", splitVol[0])
+ // }
+ //
+ // logrus.Debugf("Adding volumes from container %s", ctr.ID())
+ //
+ // // Look up the container's user volumes. This gets us the
+ // // destinations of all mounts the user added to the container.
+ // userVolumesArr := ctr.UserVolumes()
+ //
+ // // We're going to need to access them a lot, so convert to a map
+ // // to reduce looping.
+ // // We'll also use the map to indicate if we missed any volumes along the way.
+ // userVolumes := make(map[string]bool)
+ // for _, dest := range userVolumesArr {
+ // userVolumes[dest] = false
+ // }
+ //
+ // // Now we get the container's spec and loop through its volumes
+ // // and append them in if we can find them.
+ // spec := ctr.Spec()
+ // if spec == nil {
+ // return nil, nil, errors.Errorf("error retrieving container %s spec for volumes-from", ctr.ID())
+ // }
+ // for _, mnt := range spec.Mounts {
+ // if mnt.Type != TypeBind {
+ // continue
+ // }
+ // if _, exists := userVolumes[mnt.Destination]; exists {
+ // userVolumes[mnt.Destination] = true
+ //
+ // if len(options) != 0 {
+ // mnt.Options = options
+ // }
+ //
+ // if _, ok := finalMounts[mnt.Destination]; ok {
+ // logrus.Debugf("Overriding mount to %s with new mount from container %s", mnt.Destination, ctr.ID())
+ // }
+ // finalMounts[mnt.Destination] = mnt
+ // }
+ // }
+ //
+ // // We're done with the spec mounts. Add named volumes.
+ // // Add these unconditionally - none of them are automatically
+ // // part of the container, as some spec mounts are.
+ // namedVolumes := ctr.NamedVolumes()
+ // for _, namedVol := range namedVolumes {
+ // if _, exists := userVolumes[namedVol.Dest]; exists {
+ // userVolumes[namedVol.Dest] = true
+ // }
+ //
+ // if len(options) != 0 {
+ // namedVol.Options = options
+ // }
+ //
+ // if _, ok := finalMounts[namedVol.Dest]; ok {
+ // logrus.Debugf("Overriding named volume mount to %s with new named volume from container %s", namedVol.Dest, ctr.ID())
+ // }
+ // finalNamedVolumes[namedVol.Dest] = namedVol
+ // }
+ //
+ // // Check if we missed any volumes
+ // for volDest, found := range userVolumes {
+ // if !found {
+ // logrus.Warnf("Unable to match volume %s from container %s for volumes-from", volDest, ctr.ID())
+ // }
+ // }
+ //}
+ //
+ //return finalMounts, finalNamedVolumes, nil
+ return nil, nil, nil
+}
+
+// getMounts takes user-provided input from the --mount flag and creates OCI
+// spec mounts and Libpod named volumes.
+// podman run --mount type=bind,src=/etc/resolv.conf,target=/etc/resolv.conf ...
+// podman run --mount type=tmpfs,target=/dev/shm ...
+// podman run --mount type=volume,source=test-volume, ...
+func getMounts(mounts []string) (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) { //nolint
+ finalMounts := make(map[string]spec.Mount)
+ finalNamedVolumes := make(map[string]*libpod.ContainerNamedVolume)
+
+ errInvalidSyntax := errors.Errorf("incorrect mount format: should be --mount type=<bind|tmpfs|volume>,[src=<host-dir|volume-name>,]target=<ctr-dir>[,options]")
+
+ // TODO(vrothberg): the manual parsing can be replaced with a regular expression
+ // to allow a more robust parsing of the mount format and to give
+ // precise errors regarding supported format versus supported options.
+ for _, mount := range mounts {
+ arr := strings.SplitN(mount, ",", 2)
+ if len(arr) < 2 {
+ return nil, nil, errors.Wrapf(errInvalidSyntax, "%q", mount)
+ }
+ kv := strings.Split(arr[0], "=")
+ // TODO: type is not explicitly required in Docker.
+ // If not specified, it defaults to "volume".
+ if len(kv) != 2 || kv[0] != "type" {
+ return nil, nil, errors.Wrapf(errInvalidSyntax, "%q", mount)
+ }
+
+ tokens := strings.Split(arr[1], ",")
+ switch kv[1] {
+ case TypeBind:
+ mount, err := getBindMount(tokens)
+ if err != nil {
+ return nil, nil, err
+ }
+ if _, ok := finalMounts[mount.Destination]; ok {
+ return nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination)
+ }
+ finalMounts[mount.Destination] = mount
+ case TypeTmpfs:
+ mount, err := getTmpfsMount(tokens)
+ if err != nil {
+ return nil, nil, err
+ }
+ if _, ok := finalMounts[mount.Destination]; ok {
+ return nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination)
+ }
+ finalMounts[mount.Destination] = mount
+ case "volume":
+ volume, err := getNamedVolume(tokens)
+ if err != nil {
+ return nil, nil, err
+ }
+ if _, ok := finalNamedVolumes[volume.Dest]; ok {
+ return nil, nil, errors.Wrapf(errDuplicateDest, volume.Dest)
+ }
+ finalNamedVolumes[volume.Dest] = volume
+ default:
+ return nil, nil, errors.Errorf("invalid filesystem type %q", kv[1])
+ }
+ }
+
+ return finalMounts, finalNamedVolumes, nil
+}
+
+// Parse a single bind mount entry from the --mount flag.
+func getBindMount(args []string) (spec.Mount, error) { //nolint
+ newMount := spec.Mount{
+ Type: TypeBind,
+ }
+
+ var setSource, setDest, setRORW, setSuid, setDev, setExec, setRelabel bool
+
+ for _, val := range args {
+ kv := strings.Split(val, "=")
+ switch kv[0] {
+ case "bind-nonrecursive":
+ newMount.Options = append(newMount.Options, "bind")
+ case "ro", "rw":
+ if setRORW {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'ro' or 'rw' options more than once")
+ }
+ setRORW = true
+ // Can be formatted as one of:
+ // ro
+ // ro=[true|false]
+ // rw
+ // rw=[true|false]
+ switch len(kv) {
+ case 1:
+ newMount.Options = append(newMount.Options, kv[0])
+ case 2:
+ switch strings.ToLower(kv[1]) {
+ case "true":
+ newMount.Options = append(newMount.Options, kv[0])
+ case "false":
+ // Set the opposite only for rw
+ // ro's opposite is the default
+ if kv[0] == "rw" {
+ newMount.Options = append(newMount.Options, "ro")
+ }
+ default:
+ return newMount, errors.Wrapf(optionArgError, "%s must be set to true or false, instead received %q", kv[0], kv[1])
+ }
+ default:
+ return newMount, errors.Wrapf(optionArgError, "badly formatted option %q", val)
+ }
+ case "nosuid", "suid":
+ if setSuid {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once")
+ }
+ setSuid = true
+ newMount.Options = append(newMount.Options, kv[0])
+ case "nodev", "dev":
+ if setDev {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once")
+ }
+ setDev = true
+ newMount.Options = append(newMount.Options, kv[0])
+ case "noexec", "exec":
+ if setExec {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once")
+ }
+ setExec = true
+ newMount.Options = append(newMount.Options, kv[0])
+ case "shared", "rshared", "private", "rprivate", "slave", "rslave", "Z", "z":
+ newMount.Options = append(newMount.Options, kv[0])
+ case "bind-propagation":
+ if len(kv) == 1 {
+ return newMount, errors.Wrapf(optionArgError, kv[0])
+ }
+ newMount.Options = append(newMount.Options, kv[1])
+ case "src", "source":
+ if len(kv) == 1 {
+ return newMount, errors.Wrapf(optionArgError, kv[0])
+ }
+ if err := parse.ValidateVolumeHostDir(kv[1]); err != nil {
+ return newMount, err
+ }
+ newMount.Source = kv[1]
+ setSource = true
+ case "target", "dst", "destination":
+ if len(kv) == 1 {
+ return newMount, errors.Wrapf(optionArgError, kv[0])
+ }
+ if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
+ return newMount, err
+ }
+ newMount.Destination = filepath.Clean(kv[1])
+ setDest = true
+ case "relabel":
+ if setRelabel {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'relabel' option more than once")
+ }
+ setRelabel = true
+ if len(kv) != 2 {
+ return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0])
+ }
+ switch kv[1] {
+ case "private":
+ newMount.Options = append(newMount.Options, "z")
+ case "shared":
+ newMount.Options = append(newMount.Options, "Z")
+ default:
+ return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0])
+ }
+ default:
+ return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0])
+ }
+ }
+
+ if !setDest {
+ return newMount, noDestError
+ }
+
+ if !setSource {
+ newMount.Source = newMount.Destination
+ }
+
+ options, err := parse.ValidateVolumeOpts(newMount.Options)
+ if err != nil {
+ return newMount, err
+ }
+ newMount.Options = options
+ return newMount, nil
+}
+
+// Parse a single tmpfs mount entry from the --mount flag
+func getTmpfsMount(args []string) (spec.Mount, error) { //nolint
+ newMount := spec.Mount{
+ Type: TypeTmpfs,
+ Source: TypeTmpfs,
+ }
+
+ var setDest, setRORW, setSuid, setDev, setExec, setTmpcopyup bool
+
+ for _, val := range args {
+ kv := strings.Split(val, "=")
+ switch kv[0] {
+ case "tmpcopyup", "notmpcopyup":
+ if setTmpcopyup {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'tmpcopyup' and 'notmpcopyup' options more than once")
+ }
+ setTmpcopyup = true
+ newMount.Options = append(newMount.Options, kv[0])
+ case "ro", "rw":
+ if setRORW {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'ro' and 'rw' options more than once")
+ }
+ setRORW = true
+ newMount.Options = append(newMount.Options, kv[0])
+ case "nosuid", "suid":
+ if setSuid {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once")
+ }
+ setSuid = true
+ newMount.Options = append(newMount.Options, kv[0])
+ case "nodev", "dev":
+ if setDev {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once")
+ }
+ setDev = true
+ newMount.Options = append(newMount.Options, kv[0])
+ case "noexec", "exec":
+ if setExec {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once")
+ }
+ setExec = true
+ newMount.Options = append(newMount.Options, kv[0])
+ case "tmpfs-mode":
+ if len(kv) == 1 {
+ return newMount, errors.Wrapf(optionArgError, kv[0])
+ }
+ newMount.Options = append(newMount.Options, fmt.Sprintf("mode=%s", kv[1]))
+ case "tmpfs-size":
+ if len(kv) == 1 {
+ return newMount, errors.Wrapf(optionArgError, kv[0])
+ }
+ newMount.Options = append(newMount.Options, fmt.Sprintf("size=%s", kv[1]))
+ case "src", "source":
+ return newMount, errors.Errorf("source is not supported with tmpfs mounts")
+ case "target", "dst", "destination":
+ if len(kv) == 1 {
+ return newMount, errors.Wrapf(optionArgError, kv[0])
+ }
+ if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
+ return newMount, err
+ }
+ newMount.Destination = filepath.Clean(kv[1])
+ setDest = true
+ default:
+ return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0])
+ }
+ }
+
+ if !setDest {
+ return newMount, noDestError
+ }
+
+ return newMount, nil
+}
+
+// Parse a single volume mount entry from the --mount flag.
+// Note that the volume-label option for named volumes is currently NOT supported.
+// TODO: add support for --volume-label
+func getNamedVolume(args []string) (*libpod.ContainerNamedVolume, error) { //nolint
+ newVolume := new(libpod.ContainerNamedVolume)
+
+ var setSource, setDest, setRORW, setSuid, setDev, setExec bool
+
+ for _, val := range args {
+ kv := strings.Split(val, "=")
+ switch kv[0] {
+ case "ro", "rw":
+ if setRORW {
+ return nil, errors.Wrapf(optionArgError, "cannot pass 'ro' and 'rw' options more than once")
+ }
+ setRORW = true
+ newVolume.Options = append(newVolume.Options, kv[0])
+ case "nosuid", "suid":
+ if setSuid {
+ return nil, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once")
+ }
+ setSuid = true
+ newVolume.Options = append(newVolume.Options, kv[0])
+ case "nodev", "dev":
+ if setDev {
+ return nil, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once")
+ }
+ setDev = true
+ newVolume.Options = append(newVolume.Options, kv[0])
+ case "noexec", "exec":
+ if setExec {
+ return nil, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once")
+ }
+ setExec = true
+ newVolume.Options = append(newVolume.Options, kv[0])
+ case "volume-label":
+ return nil, errors.Errorf("the --volume-label option is not presently implemented")
+ case "src", "source":
+ if len(kv) == 1 {
+ return nil, errors.Wrapf(optionArgError, kv[0])
+ }
+ newVolume.Name = kv[1]
+ setSource = true
+ case "target", "dst", "destination":
+ if len(kv) == 1 {
+ return nil, errors.Wrapf(optionArgError, kv[0])
+ }
+ if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
+ return nil, err
+ }
+ newVolume.Dest = filepath.Clean(kv[1])
+ setDest = true
+ default:
+ return nil, errors.Wrapf(util.ErrBadMntOption, kv[0])
+ }
+ }
+
+ if !setSource {
+ return nil, errors.Errorf("must set source volume")
+ }
+ if !setDest {
+ return nil, noDestError
+ }
+
+ return newVolume, nil
+}
+
+func getVolumeMounts(vols []string) (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) { //nolint
+ mounts := make(map[string]spec.Mount)
+ volumes := make(map[string]*libpod.ContainerNamedVolume)
+
+ volumeFormatErr := errors.Errorf("incorrect volume format, should be [host-dir:]ctr-dir[:option]")
+
+ for _, vol := range vols {
+ var (
+ options []string
+ src string
+ dest string
+ err error
+ )
+
+ splitVol := strings.Split(vol, ":")
+ if len(splitVol) > 3 {
+ return nil, nil, errors.Wrapf(volumeFormatErr, vol)
+ }
+
+ src = splitVol[0]
+ if len(splitVol) == 1 {
+ // This is an anonymous named volume. Only thing given
+ // is destination.
+ // Name/source will be blank, and populated by libpod.
+ src = ""
+ dest = splitVol[0]
+ } else if len(splitVol) > 1 {
+ dest = splitVol[1]
+ }
+ if len(splitVol) > 2 {
+ if options, err = parse.ValidateVolumeOpts(strings.Split(splitVol[2], ",")); err != nil {
+ return nil, nil, err
+ }
+ }
+
+ // Do not check source dir for anonymous volumes
+ if len(splitVol) > 1 {
+ if err := parse.ValidateVolumeHostDir(src); err != nil {
+ return nil, nil, err
+ }
+ }
+ if err := parse.ValidateVolumeCtrDir(dest); err != nil {
+ return nil, nil, err
+ }
+
+ cleanDest := filepath.Clean(dest)
+
+ if strings.HasPrefix(src, "/") || strings.HasPrefix(src, ".") {
+ // This is not a named volume
+ newMount := spec.Mount{
+ Destination: cleanDest,
+ Type: string(TypeBind),
+ Source: src,
+ Options: options,
+ }
+ if _, ok := mounts[newMount.Destination]; ok {
+ return nil, nil, errors.Wrapf(errDuplicateDest, newMount.Destination)
+ }
+ mounts[newMount.Destination] = newMount
+ } else {
+ // This is a named volume
+ newNamedVol := new(libpod.ContainerNamedVolume)
+ newNamedVol.Name = src
+ newNamedVol.Dest = cleanDest
+ newNamedVol.Options = options
+
+ if _, ok := volumes[newNamedVol.Dest]; ok {
+ return nil, nil, errors.Wrapf(errDuplicateDest, newNamedVol.Dest)
+ }
+ volumes[newNamedVol.Dest] = newNamedVol
+ }
+
+ logrus.Debugf("User mount %s:%s options %v", src, dest, options)
+ }
+
+ return mounts, volumes, nil
+}
+
+// Get mounts for container's image volumes
+// TODO deferred baude
+func getImageVolumes() (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) { //nolint
+ //mounts := make(map[string]spec.Mount)
+ //volumes := make(map[string]*define.ContainerNamedVolume)
+ //
+ //if config.ImageVolumeType == "ignore" {
+ // return mounts, volumes, nil
+ //}
+ //
+ //for vol := range config.BuiltinImgVolumes {
+ // cleanDest := filepath.Clean(vol)
+ // logrus.Debugf("Adding image volume at %s", cleanDest)
+ // if config.ImageVolumeType == "tmpfs" {
+ // // Tmpfs image volumes are handled as mounts
+ // mount := spec.Mount{
+ // Destination: cleanDest,
+ // Source: TypeTmpfs,
+ // Type: TypeTmpfs,
+ // Options: []string{"rprivate", "rw", "nodev", "exec"},
+ // }
+ // mounts[cleanDest] = mount
+ // } else {
+ // // Anonymous volumes have no name.
+ // namedVolume := new(define.ContainerNamedVolume)
+ // namedVolume.Options = []string{"rprivate", "rw", "nodev", "exec"}
+ // namedVolume.Dest = cleanDest
+ // volumes[cleanDest] = namedVolume
+ // }
+ //}
+ //
+ //return mounts, volumes, nil
+ return nil, nil, nil
+}
+
+// GetTmpfsMounts creates spec.Mount structs for user-requested tmpfs mounts
+func getTmpfsMounts(mounts []string) (map[string]spec.Mount, error) { //nolint
+ m := make(map[string]spec.Mount)
+ for _, i := range mounts {
+ // Default options if nothing passed
+ var options []string
+ spliti := strings.Split(i, ":")
+ destPath := spliti[0]
+ if err := parse.ValidateVolumeCtrDir(spliti[0]); err != nil {
+ return nil, err
+ }
+ if len(spliti) > 1 {
+ options = strings.Split(spliti[1], ",")
+ }
+
+ if _, ok := m[destPath]; ok {
+ return nil, errors.Wrapf(errDuplicateDest, destPath)
+ }
+
+ mount := spec.Mount{
+ Destination: filepath.Clean(destPath),
+ Type: string(TypeTmpfs),
+ Options: options,
+ Source: string(TypeTmpfs),
+ }
+ m[destPath] = mount
+ }
+ return m, nil
+}
+
+// AddContainerInitBinary adds the init binary specified by path iff the
+// container will run in a private PID namespace that is not shared with the
+// host or another pre-existing container, where an init-like process is
+// already running.
+//
+// Note that AddContainerInitBinary prepends "/dev/init" "--" to the command
+// to execute the bind-mounted binary as PID 1.
+// TODO this needs to be worked on to work in new env
+func addContainerInitBinary(path string) (spec.Mount, error) { //nolint
+ mount := spec.Mount{
+ Destination: "/dev/init",
+ Type: TypeBind,
+ Source: path,
+ Options: []string{TypeBind, "ro"},
+ }
+
+ //if path == "" {
+ // return mount, fmt.Errorf("please specify a path to the container-init binary")
+ //}
+ //if !config.Pid.PidMode.IsPrivate() {
+ // return mount, fmt.Errorf("cannot add init binary as PID 1 (PID namespace isn't private)")
+ //}
+ //if config.Systemd {
+ // return mount, fmt.Errorf("cannot use container-init binary with systemd")
+ //}
+ //if _, err := os.Stat(path); os.IsNotExist(err) {
+ // return mount, errors.Wrap(err, "container-init binary not found on the host")
+ //}
+ //config.Command = append([]string{"/dev/init", "--"}, config.Command...)
+ return mount, nil
+}
+
+// Supersede existing mounts in the spec with new, user-specified mounts.
+// TODO: Should we unmount subtree mounts? E.g., if /tmp/ is mounted by
+// one mount, and we already have /tmp/a and /tmp/b, should we remove
+// the /tmp/a and /tmp/b mounts in favor of the more general /tmp?
+func SupercedeUserMounts(mounts []spec.Mount, configMount []spec.Mount) []spec.Mount {
+ if len(mounts) > 0 {
+ // If we have overlappings mounts, remove them from the spec in favor of
+ // the user-added volume mounts
+ destinations := make(map[string]bool)
+ for _, mount := range mounts {
+ destinations[path.Clean(mount.Destination)] = true
+ }
+ // Copy all mounts from spec to defaultMounts, except for
+ // - mounts overridden by a user supplied mount;
+ // - all mounts under /dev if a user supplied /dev is present;
+ mountDev := destinations["/dev"]
+ for _, mount := range configMount {
+ if _, ok := destinations[path.Clean(mount.Destination)]; !ok {
+ if mountDev && strings.HasPrefix(mount.Destination, "/dev/") {
+ // filter out everything under /dev if /dev is user-mounted
+ continue
+ }
+
+ logrus.Debugf("Adding mount %s", mount.Destination)
+ mounts = append(mounts, mount)
+ }
+ }
+ return mounts
+ }
+ return configMount
+}
+
+func InitFSMounts(mounts []spec.Mount) error {
+ for i, m := range mounts {
+ switch {
+ case m.Type == TypeBind:
+ opts, err := util.ProcessOptions(m.Options, false, m.Source)
+ if err != nil {
+ return err
+ }
+ mounts[i].Options = opts
+ case m.Type == TypeTmpfs && filepath.Clean(m.Destination) != "/dev":
+ opts, err := util.ProcessOptions(m.Options, true, "")
+ if err != nil {
+ return err
+ }
+ mounts[i].Options = opts
+ }
+ }
+ return nil
+}
diff --git a/pkg/util/mountOpts.go b/pkg/util/mountOpts.go
index d21800bc3..329a7c913 100644
--- a/pkg/util/mountOpts.go
+++ b/pkg/util/mountOpts.go
@@ -13,19 +13,17 @@ var (
ErrDupeMntOption = errors.Errorf("duplicate mount option passed")
)
-// DefaultMountOptions sets default mount options for ProcessOptions.
-type DefaultMountOptions struct {
- Noexec bool
- Nosuid bool
- Nodev bool
+type defaultMountOptions struct {
+ noexec bool
+ nosuid bool
+ nodev bool
}
// ProcessOptions parses the options for a bind or tmpfs mount and ensures that
// they are sensible and follow convention. The isTmpfs variable controls
// whether extra, tmpfs-specific options will be allowed.
-// The defaults variable controls default mount options that will be set. If it
-// is not included, they will be set unconditionally.
-func ProcessOptions(options []string, isTmpfs bool, defaults *DefaultMountOptions) ([]string, error) {
+// The sourcePath variable, if not empty, contains a bind mount source.
+func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string, error) {
var (
foundWrite, foundSize, foundProp, foundMode, foundExec, foundSuid, foundDev, foundCopyUp, foundBind, foundZ bool
)
@@ -122,13 +120,17 @@ func ProcessOptions(options []string, isTmpfs bool, defaults *DefaultMountOption
if !foundProp {
newOptions = append(newOptions, "rprivate")
}
- if !foundExec && (defaults == nil || defaults.Noexec) {
+ defaults, err := getDefaultMountOptions(sourcePath)
+ if err != nil {
+ return nil, err
+ }
+ if !foundExec && defaults.noexec {
newOptions = append(newOptions, "noexec")
}
- if !foundSuid && (defaults == nil || defaults.Nosuid) {
+ if !foundSuid && defaults.nosuid {
newOptions = append(newOptions, "nosuid")
}
- if !foundDev && (defaults == nil || defaults.Nodev) {
+ if !foundDev && defaults.nodev {
newOptions = append(newOptions, "nodev")
}
if isTmpfs && !foundCopyUp {
diff --git a/pkg/util/mountOpts_linux.go b/pkg/util/mountOpts_linux.go
new file mode 100644
index 000000000..3eac4dd25
--- /dev/null
+++ b/pkg/util/mountOpts_linux.go
@@ -0,0 +1,23 @@
+package util
+
+import (
+ "os"
+
+ "golang.org/x/sys/unix"
+)
+
+func getDefaultMountOptions(path string) (defaultMountOptions, error) {
+ opts := defaultMountOptions{true, true, true}
+ if path == "" {
+ return opts, nil
+ }
+ var statfs unix.Statfs_t
+ if e := unix.Statfs(path, &statfs); e != nil {
+ return opts, &os.PathError{Op: "statfs", Path: path, Err: e}
+ }
+ opts.nodev = (statfs.Flags&unix.MS_NODEV == unix.MS_NODEV)
+ opts.noexec = (statfs.Flags&unix.MS_NOEXEC == unix.MS_NOEXEC)
+ opts.nosuid = (statfs.Flags&unix.MS_NOSUID == unix.MS_NOSUID)
+
+ return opts, nil
+}
diff --git a/pkg/util/mountOpts_other.go b/pkg/util/mountOpts_other.go
new file mode 100644
index 000000000..6a34942e5
--- /dev/null
+++ b/pkg/util/mountOpts_other.go
@@ -0,0 +1,7 @@
+// +build !linux
+
+package util
+
+func getDefaultMountOptions(path string) (opts defaultMountOptions, err error) {
+ return
+}
diff --git a/pkg/varlinkapi/attach.go b/pkg/varlinkapi/attach.go
index 94f4d653e..34f351669 100644
--- a/pkg/varlinkapi/attach.go
+++ b/pkg/varlinkapi/attach.go
@@ -16,7 +16,7 @@ import (
"k8s.io/client-go/tools/remotecommand"
)
-func setupStreams(call iopodman.VarlinkCall) (*bufio.Reader, *bufio.Writer, *io.PipeReader, *io.PipeWriter, *libpod.AttachStreams) {
+func setupStreams(call iopodman.VarlinkCall) (*bufio.Reader, *bufio.Writer, *io.PipeReader, *io.PipeWriter, *define.AttachStreams) {
// These are the varlink sockets
reader := call.Call.Reader
@@ -30,7 +30,7 @@ func setupStreams(call iopodman.VarlinkCall) (*bufio.Reader, *bufio.Writer, *io.
// TODO if runc ever starts passing stderr, we can too
// stderrWriter := NewVirtWriteCloser(writer, ToStderr)
- streams := libpod.AttachStreams{
+ streams := define.AttachStreams{
OutputStream: stdoutWriter,
InputStream: bufio.NewReader(pr),
// Runc eats the error stream
@@ -117,7 +117,7 @@ func (i *LibpodAPI) Attach(call iopodman.VarlinkCall, name string, detachKeys st
return call.Writer.Flush()
}
-func attach(ctr *libpod.Container, streams *libpod.AttachStreams, detachKeys string, resize chan remotecommand.TerminalSize, errChan chan error) error {
+func attach(ctr *libpod.Container, streams *define.AttachStreams, detachKeys string, resize chan remotecommand.TerminalSize, errChan chan error) error {
go func() {
if err := ctr.Attach(streams, detachKeys, resize); err != nil {
errChan <- err
@@ -127,7 +127,7 @@ func attach(ctr *libpod.Container, streams *libpod.AttachStreams, detachKeys str
return attachError
}
-func startAndAttach(ctr *libpod.Container, streams *libpod.AttachStreams, detachKeys string, resize chan remotecommand.TerminalSize, errChan chan error) error {
+func startAndAttach(ctr *libpod.Container, streams *define.AttachStreams, detachKeys string, resize chan remotecommand.TerminalSize, errChan chan error) error {
var finalErr error
attachChan, err := ctr.StartAndAttach(getContext(), streams, detachKeys, resize, false)
if err != nil {
diff --git a/test/e2e/build/basicalpine/Containerfile.path b/test/e2e/build/basicalpine/Containerfile.path
new file mode 100644
index 000000000..d2b03a6b8
--- /dev/null
+++ b/test/e2e/build/basicalpine/Containerfile.path
@@ -0,0 +1,2 @@
+FROM alpine
+ENV PATH=/tmp:/bin:/usr/bin:/usr/sbin
diff --git a/test/e2e/build_test.go b/test/e2e/build_test.go
index 8b03e9386..9e41fd231 100644
--- a/test/e2e/build_test.go
+++ b/test/e2e/build_test.go
@@ -175,4 +175,24 @@ var _ = Describe("Podman build", func() {
data := inspect.InspectImageJSON()
Expect(data[0].ID).To(Equal(string(id)))
})
+
+ It("podman Test PATH in built image", func() {
+ path := "/tmp:/bin:/usr/bin:/usr/sbin"
+ session := podmanTest.PodmanNoCache([]string{
+ "build", "-f", "build/basicalpine/Containerfile.path", "-t", "test-path",
+ })
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"run", "test-path", "printenv", "PATH"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ stdoutLines := session.OutputToStringArray()
+ Expect(stdoutLines[0]).Should(Equal(path))
+
+ session = podmanTest.PodmanNoCache([]string{"rmi", "-a", "-f"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ })
+
})
diff --git a/test/e2e/checkpoint_test.go b/test/e2e/checkpoint_test.go
index 237223283..e6a3d2f7a 100644
--- a/test/e2e/checkpoint_test.go
+++ b/test/e2e/checkpoint_test.go
@@ -37,7 +37,7 @@ var _ = Describe("Podman checkpoint", func() {
podmanTest.SeedImages()
// Check if the runtime implements checkpointing. Currently only
// runc's checkpoint/restore implementation is supported.
- cmd := exec.Command(podmanTest.OCIRuntime, "checkpoint", "-h")
+ cmd := exec.Command(podmanTest.OCIRuntime, "checkpoint", "--help")
if err := cmd.Start(); err != nil {
Skip("OCI runtime does not support checkpoint/restore")
}
diff --git a/test/e2e/exec_test.go b/test/e2e/exec_test.go
index ab806f683..5d0d6e689 100644
--- a/test/e2e/exec_test.go
+++ b/test/e2e/exec_test.go
@@ -122,6 +122,18 @@ var _ = Describe("Podman exec", func() {
Expect(session.ExitCode()).To(Equal(100))
})
+ It("podman exec terminal doesn't hang", func() {
+ setup := podmanTest.Podman([]string{"run", "-dti", fedoraMinimal, "sleep", "+Inf"})
+ setup.WaitWithDefaultTimeout()
+ Expect(setup.ExitCode()).To(Equal(0))
+
+ for i := 0; i < 5; i++ {
+ session := podmanTest.Podman([]string{"exec", "-lti", "true"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ }
+ })
+
It("podman exec pseudo-terminal sanity check", func() {
setup := podmanTest.Podman([]string{"run", "--detach", "--name", "test1", fedoraMinimal, "sleep", "+Inf"})
setup.WaitWithDefaultTimeout()
diff --git a/test/e2e/run_volume_test.go b/test/e2e/run_volume_test.go
index 667f03627..1f892d9f8 100644
--- a/test/e2e/run_volume_test.go
+++ b/test/e2e/run_volume_test.go
@@ -15,9 +15,9 @@ import (
"github.com/onsi/gomega/gexec"
)
-var VolumeTrailingSlashDockerfile = `
-FROM alpine:latest
-VOLUME /test/`
+// in-container mount point: using a path that is definitely not present
+// on the host system might help to uncover some issues.
+const dest = "/unique/path"
var _ = Describe("Podman run with volumes", func() {
var (
@@ -45,46 +45,44 @@ var _ = Describe("Podman run with volumes", func() {
It("podman run with volume flag", func() {
mountPath := filepath.Join(podmanTest.TempDir, "secrets")
os.Mkdir(mountPath, 0755)
- session := podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:/run/test", mountPath), ALPINE, "grep", "/run/test", "/proc/self/mountinfo"})
+ vol := mountPath + ":" + dest
+
+ session := podmanTest.Podman([]string{"run", "--rm", "-v", vol, ALPINE, "grep", dest, "/proc/self/mountinfo"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- found, matches := session.GrepString("/run/test")
+ found, matches := session.GrepString(dest)
Expect(found).Should(BeTrue())
Expect(matches[0]).To(ContainSubstring("rw"))
- mountPath = filepath.Join(podmanTest.TempDir, "secrets")
- os.Mkdir(mountPath, 0755)
- session = podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:/run/test:ro", mountPath), ALPINE, "grep", "/run/test", "/proc/self/mountinfo"})
+ session = podmanTest.Podman([]string{"run", "--rm", "-v", vol + ":ro", ALPINE, "grep", dest, "/proc/self/mountinfo"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- found, matches = session.GrepString("/run/test")
+ found, matches = session.GrepString(dest)
Expect(found).Should(BeTrue())
Expect(matches[0]).To(ContainSubstring("ro"))
- mountPath = filepath.Join(podmanTest.TempDir, "secrets")
- os.Mkdir(mountPath, 0755)
- session = podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:/run/test:shared", mountPath), ALPINE, "grep", "/run/test", "/proc/self/mountinfo"})
+ session = podmanTest.Podman([]string{"run", "--rm", "-v", vol + ":shared", ALPINE, "grep", dest, "/proc/self/mountinfo"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- found, matches = session.GrepString("/run/test")
+ found, matches = session.GrepString(dest)
Expect(found).Should(BeTrue())
Expect(matches[0]).To(ContainSubstring("rw"))
Expect(matches[0]).To(ContainSubstring("shared"))
// Cached is ignored
- session = podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:/run/test:cached", mountPath), ALPINE, "grep", "/run/test", "/proc/self/mountinfo"})
+ session = podmanTest.Podman([]string{"run", "--rm", "-v", vol + ":cached", ALPINE, "grep", dest, "/proc/self/mountinfo"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- found, matches = session.GrepString("/run/test")
+ found, matches = session.GrepString(dest)
Expect(found).Should(BeTrue())
Expect(matches[0]).To(ContainSubstring("rw"))
Expect(matches[0]).To(Not(ContainSubstring("cached")))
// Delegated is ignored
- session = podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:/run/test:delegated", mountPath), ALPINE, "grep", "/run/test", "/proc/self/mountinfo"})
+ session = podmanTest.Podman([]string{"run", "--rm", "-v", vol + ":delegated", ALPINE, "grep", dest, "/proc/self/mountinfo"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- found, matches = session.GrepString("/run/test")
+ found, matches = session.GrepString(dest)
Expect(found).Should(BeTrue())
Expect(matches[0]).To(ContainSubstring("rw"))
Expect(matches[0]).To(Not(ContainSubstring("delegated")))
@@ -96,30 +94,30 @@ var _ = Describe("Podman run with volumes", func() {
}
mountPath := filepath.Join(podmanTest.TempDir, "secrets")
os.Mkdir(mountPath, 0755)
- session := podmanTest.Podman([]string{"run", "--rm", "--mount", fmt.Sprintf("type=bind,src=%s,target=/run/test", mountPath), ALPINE, "grep", "/run/test", "/proc/self/mountinfo"})
+ mount := "type=bind,src=" + mountPath + ",target=" + dest
+
+ session := podmanTest.Podman([]string{"run", "--rm", "--mount", mount, ALPINE, "grep", dest, "/proc/self/mountinfo"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- Expect(session.OutputToString()).To(ContainSubstring("/run/test rw"))
+ Expect(session.OutputToString()).To(ContainSubstring(dest + " rw"))
- session = podmanTest.Podman([]string{"run", "--rm", "--mount", fmt.Sprintf("type=bind,src=%s,target=/run/test,ro", mountPath), ALPINE, "grep", "/run/test", "/proc/self/mountinfo"})
+ session = podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",ro", ALPINE, "grep", dest, "/proc/self/mountinfo"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- Expect(session.OutputToString()).To(ContainSubstring("/run/test ro"))
+ Expect(session.OutputToString()).To(ContainSubstring(dest + " ro"))
- session = podmanTest.Podman([]string{"run", "--rm", "--mount", fmt.Sprintf("type=bind,src=%s,target=/run/test,shared", mountPath), ALPINE, "grep", "/run/test", "/proc/self/mountinfo"})
+ session = podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",shared", ALPINE, "grep", dest, "/proc/self/mountinfo"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- found, matches := session.GrepString("/run/test")
+ found, matches := session.GrepString(dest)
Expect(found).Should(BeTrue())
Expect(matches[0]).To(ContainSubstring("rw"))
Expect(matches[0]).To(ContainSubstring("shared"))
- mountPath = filepath.Join(podmanTest.TempDir, "scratchpad")
- os.Mkdir(mountPath, 0755)
- session = podmanTest.Podman([]string{"run", "--rm", "--mount", "type=tmpfs,target=/run/test", ALPINE, "grep", "/run/test", "/proc/self/mountinfo"})
+ session = podmanTest.Podman([]string{"run", "--rm", "--mount", "type=tmpfs,target=" + dest, ALPINE, "grep", dest, "/proc/self/mountinfo"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- Expect(session.OutputToString()).To(ContainSubstring("/run/test rw,nosuid,nodev,noexec,relatime - tmpfs"))
+ Expect(session.OutputToString()).To(ContainSubstring(dest + " rw,nosuid,nodev,noexec,relatime - tmpfs"))
session = podmanTest.Podman([]string{"run", "--rm", "--mount", "type=tmpfs,target=/etc/ssl,tmpcopyup", ALPINE, "ls", "/etc/ssl"})
session.WaitWithDefaultTimeout()
@@ -147,7 +145,7 @@ var _ = Describe("Podman run with volumes", func() {
It("podman run with conflicting volumes errors", func() {
mountPath := filepath.Join(podmanTest.TmpDir, "secrets")
os.Mkdir(mountPath, 0755)
- session := podmanTest.Podman([]string{"run", "-v", fmt.Sprintf("%s:/run/test", mountPath), "-v", "/tmp:/run/test", ALPINE, "ls"})
+ session := podmanTest.Podman([]string{"run", "-v", mountPath + ":" + dest, "-v", "/tmp" + ":" + dest, ALPINE, "ls"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(125))
})
@@ -169,17 +167,19 @@ var _ = Describe("Podman run with volumes", func() {
It("podman run with mount flag and boolean options", func() {
mountPath := filepath.Join(podmanTest.TempDir, "secrets")
os.Mkdir(mountPath, 0755)
- session := podmanTest.Podman([]string{"run", "--rm", "--mount", fmt.Sprintf("type=bind,src=%s,target=/run/test,ro=false", mountPath), ALPINE, "grep", "/run/test", "/proc/self/mountinfo"})
+ mount := "type=bind,src=" + mountPath + ",target=" + dest
+
+ session := podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",ro=false", ALPINE, "grep", dest, "/proc/self/mountinfo"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- Expect(session.OutputToString()).To(ContainSubstring("/run/test rw"))
+ Expect(session.OutputToString()).To(ContainSubstring(dest + " rw"))
- session = podmanTest.Podman([]string{"run", "--rm", "--mount", fmt.Sprintf("type=bind,src=%s,target=/run/test,ro=true", mountPath), ALPINE, "grep", "/run/test", "/proc/self/mountinfo"})
+ session = podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",ro=true", ALPINE, "grep", dest, "/proc/self/mountinfo"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- Expect(session.OutputToString()).To(ContainSubstring("/run/test ro"))
+ Expect(session.OutputToString()).To(ContainSubstring(dest + " ro"))
- session = podmanTest.Podman([]string{"run", "--rm", "--mount", fmt.Sprintf("type=bind,src=%s,target=/run/test,ro=true,rw=false", mountPath), ALPINE, "grep", "/run/test", "/proc/self/mountinfo"})
+ session = podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",ro=true,rw=false", ALPINE, "grep", dest, "/proc/self/mountinfo"})
session.WaitWithDefaultTimeout()
Expect(session).To(ExitWithError())
})
@@ -195,19 +195,20 @@ var _ = Describe("Podman run with volumes", func() {
It("podman run with volumes and suid/dev/exec options", func() {
mountPath := filepath.Join(podmanTest.TempDir, "secrets")
os.Mkdir(mountPath, 0755)
- session := podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:/run/test:suid,dev,exec", mountPath), ALPINE, "grep", "/run/test", "/proc/self/mountinfo"})
+
+ session := podmanTest.Podman([]string{"run", "--rm", "-v", mountPath + ":" + dest + ":suid,dev,exec", ALPINE, "grep", dest, "/proc/self/mountinfo"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- found, matches := session.GrepString("/run/test")
+ found, matches := session.GrepString(dest)
Expect(found).Should(BeTrue())
Expect(matches[0]).To(Not(ContainSubstring("noexec")))
Expect(matches[0]).To(Not(ContainSubstring("nodev")))
Expect(matches[0]).To(Not(ContainSubstring("nosuid")))
- session = podmanTest.Podman([]string{"run", "--rm", "--tmpfs", "/run/test:suid,dev,exec", ALPINE, "grep", "/run/test", "/proc/self/mountinfo"})
+ session = podmanTest.Podman([]string{"run", "--rm", "--tmpfs", dest + ":suid,dev,exec", ALPINE, "grep", dest, "/proc/self/mountinfo"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- found, matches = session.GrepString("/run/test")
+ found, matches = session.GrepString(dest)
Expect(found).Should(BeTrue())
Expect(matches[0]).To(Not(ContainSubstring("noexec")))
Expect(matches[0]).To(Not(ContainSubstring("nodev")))
@@ -298,11 +299,11 @@ var _ = Describe("Podman run with volumes", func() {
})
It("podman read-only tmpfs conflict with volume", func() {
- session := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", "--read-only", "-v", "tmp_volume:/run", ALPINE, "touch", "/run/a"})
+ session := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", "--read-only", "-v", "tmp_volume:" + dest, ALPINE, "touch", dest + "/a"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- session2 := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", "--read-only", "--tmpfs", "/run", ALPINE, "touch", "/run/a"})
+ session2 := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", "--read-only", "--tmpfs", dest, ALPINE, "touch", dest + "/a"})
session2.WaitWithDefaultTimeout()
Expect(session2.ExitCode()).To(Equal(0))
})
@@ -428,7 +429,10 @@ var _ = Describe("Podman run with volumes", func() {
It("Podman mount over image volume with trailing /", func() {
image := "podman-volume-test:trailing"
- podmanTest.BuildImage(VolumeTrailingSlashDockerfile, image, "false")
+ dockerfile := `
+FROM alpine:latest
+VOLUME /test/`
+ podmanTest.BuildImage(dockerfile, image, "false")
ctrName := "testCtr"
create := podmanTest.Podman([]string{"create", "-v", "/tmp:/test", "--name", ctrName, image, "ls"})
diff --git a/utils/utils.go b/utils/utils.go
index 3c8c0a9b0..cf58ca3fb 100644
--- a/utils/utils.go
+++ b/utils/utils.go
@@ -65,7 +65,6 @@ func CopyDetachable(dst io.Writer, src io.Reader, keys []byte) (written int64, e
break
}
if i == len(keys)-1 {
- // src.Close()
return 0, ErrDetach
}
nr, er = src.Read(buf)
diff --git a/vendor/github.com/containers/buildah/.gitignore b/vendor/github.com/containers/buildah/.gitignore
index c70cab336..a362b3800 100644
--- a/vendor/github.com/containers/buildah/.gitignore
+++ b/vendor/github.com/containers/buildah/.gitignore
@@ -4,3 +4,4 @@ docs/buildah*.1
/build/
tests/tools/build
Dockerfile*
+*.swp
diff --git a/vendor/github.com/containers/buildah/CHANGELOG.md b/vendor/github.com/containers/buildah/CHANGELOG.md
index a888a1569..b456cffa8 100644
--- a/vendor/github.com/containers/buildah/CHANGELOG.md
+++ b/vendor/github.com/containers/buildah/CHANGELOG.md
@@ -2,6 +2,20 @@
# Changelog
+## v1.14.6 (2020-04-02)
+ bud.bats - cleanup, refactoring
+ vendor in latest containers/storage 1.18.0 and containers/common v0.7.0
+ Bump github.com/spf13/cobra from 0.0.6 to 0.0.7
+ Bump github.com/containers/storage from 1.16.5 to 1.17.0
+ Bump github.com/containers/image/v5 from 5.2.1 to 5.3.1
+ Fix Amazon install step
+ Bump back to v1.15.0-dev
+ Fix bud-build-arg-cache test
+ Make image history work correctly with new args handling
+ Don't add args to the RUN environment from the Builder
+ Update github.com/openshift/imagebuilder to v1.1.4
+ Add .swp files to .gitignore
+
## v1.14.5 (2020-03-26)
revert #2246 FIPS mode change
Bump back to v1.15.0-dev
diff --git a/vendor/github.com/containers/buildah/buildah.go b/vendor/github.com/containers/buildah/buildah.go
index de0350d3d..2ece11acd 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.14.5"
+ Version = "1.15.0-dev"
// 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 91affb33a..c740a4da3 100644
--- a/vendor/github.com/containers/buildah/changelog.txt
+++ b/vendor/github.com/containers/buildah/changelog.txt
@@ -1,3 +1,17 @@
+- Changelog for v1.14.6 (2020-04-02)
+ * bud.bats - cleanup, refactoring
+ * vendor in latest containers/storage 1.18.0 and containers/common v0.7.0
+ * Bump github.com/spf13/cobra from 0.0.6 to 0.0.7
+ * Bump github.com/containers/storage from 1.16.5 to 1.17.0
+ * Bump github.com/containers/image/v5 from 5.2.1 to 5.3.1
+ * Fix Amazon install step
+ * Bump back to v1.15.0-dev
+ * Fix bud-build-arg-cache test
+ * Make image history work correctly with new args handling
+ * Don't add args to the RUN environment from the Builder
+ * Update github.com/openshift/imagebuilder to v1.1.4
+ * Add .swp files to .gitignore
+
- Changelog for v1.14.5 (2020-03-26)
* revert #2246 FIPS mode change
* Bump back to v1.15.0-dev
diff --git a/vendor/github.com/containers/buildah/chroot/run.go b/vendor/github.com/containers/buildah/chroot/run.go
index 8dfa8aba0..d65c36470 100644
--- a/vendor/github.com/containers/buildah/chroot/run.go
+++ b/vendor/github.com/containers/buildah/chroot/run.go
@@ -20,10 +20,10 @@ import (
"github.com/containers/buildah/bind"
"github.com/containers/buildah/util"
- "github.com/containers/common/pkg/unshare"
"github.com/containers/storage/pkg/ioutils"
"github.com/containers/storage/pkg/mount"
"github.com/containers/storage/pkg/reexec"
+ "github.com/containers/storage/pkg/unshare"
"github.com/opencontainers/runc/libcontainer/apparmor"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
diff --git a/vendor/github.com/containers/buildah/common.go b/vendor/github.com/containers/buildah/common.go
index 22bbb1efd..8fb3ebdb7 100644
--- a/vendor/github.com/containers/buildah/common.go
+++ b/vendor/github.com/containers/buildah/common.go
@@ -10,12 +10,12 @@ import (
"syscall"
"time"
- "github.com/containers/common/pkg/unshare"
cp "github.com/containers/image/v5/copy"
"github.com/containers/image/v5/docker"
"github.com/containers/image/v5/signature"
"github.com/containers/image/v5/types"
"github.com/containers/storage"
+ "github.com/containers/storage/pkg/unshare"
"github.com/docker/distribution/registry/api/errcode"
errcodev2 "github.com/docker/distribution/registry/api/v2"
multierror "github.com/hashicorp/go-multierror"
diff --git a/vendor/github.com/containers/buildah/go.mod b/vendor/github.com/containers/buildah/go.mod
index aec65c5da..a04a72f12 100644
--- a/vendor/github.com/containers/buildah/go.mod
+++ b/vendor/github.com/containers/buildah/go.mod
@@ -4,9 +4,9 @@ go 1.12
require (
github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784
- github.com/containers/common v0.6.1
- github.com/containers/image/v5 v5.2.1
- github.com/containers/storage v1.16.5
+ github.com/containers/common v0.7.0
+ github.com/containers/image/v5 v5.3.1
+ github.com/containers/storage v1.18.0
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,15 +27,15 @@ 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.3
+ github.com/openshift/imagebuilder v1.1.4
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
- github.com/sirupsen/logrus v1.4.2
- github.com/spf13/cobra v0.0.6
+ github.com/sirupsen/logrus v1.5.0
+ github.com/spf13/cobra v0.0.7
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.5.1
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975
- golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2
+ golang.org/x/sys v0.0.0-20200217220822-9197077df867
)
diff --git a/vendor/github.com/containers/buildah/go.sum b/vendor/github.com/containers/buildah/go.sum
index 6c4a7724a..9e235bb3d 100644
--- a/vendor/github.com/containers/buildah/go.sum
+++ b/vendor/github.com/containers/buildah/go.sum
@@ -5,7 +5,6 @@ github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/DataDog/zstd v1.4.0/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
github.com/Microsoft/hcsshim v0.8.7-0.20191101173118-65519b62243c/go.mod h1:7xhjOwRV2+0HXGmM0jxaEu+ZiXJFoVZOTfL/dmqbrD8=
@@ -47,20 +46,17 @@ github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDG
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784 h1:rqUVLD8I859xRgUx/WMC3v7QAFqbLKZbs+0kqYboRJc=
github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
-github.com/containers/common v0.5.0 h1:ZAef7h3oO46PcbTyfooZf8XLHrYad+GkhSu3EhH6P24=
-github.com/containers/common v0.5.0/go.mod h1:m62kenckrWi5rZx32kaLje2Og0hpf6NsaTBn6+b+Oys=
-github.com/containers/common v0.6.1 h1:z9VeVXYeOnNV99uNLp7zoE5KO1n0hqz1mdm5a6AiIrA=
-github.com/containers/common v0.6.1/go.mod h1:m62kenckrWi5rZx32kaLje2Og0hpf6NsaTBn6+b+Oys=
-github.com/containers/image/v5 v5.2.1 h1:rQR6QSUneWBoW1bTFpP9EJJTevQFv27YsKYQVJIzg+s=
-github.com/containers/image/v5 v5.2.1/go.mod h1:TfhmLwH+v1/HBVPIWH7diLs8XwcOkP3c7t7JFgqaUEc=
+github.com/containers/common v0.7.0 h1:wlcHuOa8CcsreCMd0BlvKUubIVzkLy8EMLtJ0JO+Y6I=
+github.com/containers/common v0.7.0/go.mod h1:UmhIdvSkhTR0hWR01AnuZGNufm80+A0s8isb05eTmz0=
+github.com/containers/image/v5 v5.3.1 h1:AL0pR0d1ho3kLUAuBr+wnFlXuD3ChzKVljk0M8JBJHQ=
+github.com/containers/image/v5 v5.3.1/go.mod h1:JnCfhbTIL9IxPPZm1JoQwiE0S9KET46M4OZySJsLylk=
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE=
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY=
github.com/containers/ocicrypt v0.0.0-20190930154801-b87a4a69c741 h1:8tQkOcednLJtUcZgK7sPglscXtxvMOnFOa6wd09VWLM=
github.com/containers/ocicrypt v0.0.0-20190930154801-b87a4a69c741/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc=
-github.com/containers/storage v1.15.8/go.mod h1:zhvjIIl/fR6wt/lgqQAC+xanHQ+8gUQ0GBVeXYN81qI=
-github.com/containers/storage v1.16.0/go.mod h1:nqN09JSi1/RSI1UAUwDYXPRiGSlq5FPbNkN/xb0TfG0=
-github.com/containers/storage v1.16.5 h1:eHeWEhUEWX3VMIG1Vn1rEjfRoLHUQev3cwtA5zd89wk=
-github.com/containers/storage v1.16.5/go.mod h1:SdysZeLKJOvfHYysUWg9OZUC3gdZWi5b2b7NC18VpPE=
+github.com/containers/storage v1.16.6/go.mod h1:Fws4I+U+C4DmJxDbBs1z9SKk50DzN4LtA+g1b+FmkTY=
+github.com/containers/storage v1.18.0 h1:l0vqAJwhMvfg2VM8Kwcc92bMyBrsQIul+Rs88pd7c+A=
+github.com/containers/storage v1.18.0/go.mod h1:gbFeFybWhlVCk3buJ0sovNKs8MzWEBTrk8/sbJw8irQ=
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-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
@@ -79,7 +75,6 @@ github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8
github.com/docker/distribution v0.0.0-20170817175659-5f6282db7d65/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
-github.com/docker/docker v0.0.0-20171019062838-86f080cff091/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v0.0.0-20180522102801-da99009bbb11/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v1.4.2-0.20191101170500-ac7306503d23 h1:oqgGT9O61YAYvI41EBsLePOr+LE6roB0xY4gpkZuFSE=
github.com/docker/docker v1.4.2-0.20191101170500-ac7306503d23/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
@@ -185,14 +180,10 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/klauspost/compress v1.9.8/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
-github.com/klauspost/compress v1.10.0/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/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
-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/klauspost/pgzip v1.2.3 h1:Ce2to9wvs/cuJ2b86/CKQoTYr9VHfpanYosZ0UBJqdw=
+github.com/klauspost/pgzip v1.2.3/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
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=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -206,8 +197,6 @@ github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
-github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/mattn/go-shellwords v1.0.9/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/mattn/go-shellwords v1.0.10 h1:Y7Xqm8piKOO3v10Thp7Z36h4FYFjt5xB//6XvOrs2Gw=
github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
@@ -255,16 +244,12 @@ github.com/opencontainers/runtime-spec v0.1.2-0.20190618234442-a950415649c7/go.m
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
github.com/opencontainers/runtime-tools v0.9.0 h1:FYgwVsKRI/H9hU32MJ/4MLOzXWodKK5zsQavY8NPMkU=
github.com/opencontainers/runtime-tools v0.9.0/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
-github.com/opencontainers/selinux v1.3.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g=
-github.com/opencontainers/selinux v1.3.2/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g=
github.com/opencontainers/selinux v1.4.0 h1:cpiX/2wWIju/6My60T6/z9CxNG7c8xTQyEmA9fChpUo=
github.com/opencontainers/selinux v1.4.0/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g=
github.com/openshift/api v0.0.0-20200106203948-7ab22a2c8316 h1:enQG2QUGwug4fR1yM6hL0Fjzx6Km/exZY6RbSPwMu3o=
github.com/openshift/api v0.0.0-20200106203948-7ab22a2c8316/go.mod h1:dv+J0b/HWai0QnMVb37/H0v36klkLBi2TNpPeWDxX10=
-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/openshift/imagebuilder v1.1.3 h1:8TiphsD2wboU7tygtGZ5ZBfCP9FH2ZtvEAli67V2PJ4=
-github.com/openshift/imagebuilder v1.1.3/go.mod h1:9aJRczxCH0mvT6XQ+5STAQaPWz7OsWcU5/mRkt8IWeo=
+github.com/openshift/imagebuilder v1.1.4 h1:LUg8aTjyXMtlDx6IbtvaqofFGZ6aYqe+VIeATE735LM=
+github.com/openshift/imagebuilder v1.1.4/go.mod h1:9aJRczxCH0mvT6XQ+5STAQaPWz7OsWcU5/mRkt8IWeo=
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=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
@@ -308,14 +293,15 @@ github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvW
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
-github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/sirupsen/logrus v1.5.0 h1:1N5EYkVAPEywqZRJd7cwnRtCb6xJx7NH3T3WUTF980Q=
+github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs=
-github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
+github.com/spf13/cobra v0.0.7 h1:FfTH+vuMXOas8jmfb5/M7dzEYx7LpcLb7a0LPe34uOU=
+github.com/spf13/cobra v0.0.7/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
@@ -338,14 +324,14 @@ github.com/tchap/go-patricia v2.3.0+incompatible h1:GkY4dP3cEfEASBPPkWd+AmjYxhmD
github.com/tchap/go-patricia v2.3.0+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
-github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8=
-github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
+github.com/ulikunitz/xz v0.5.7 h1:YvTNdFzX6+W5m9msiYg/zpkSURPPtOlzbqYjrFn7Yt4=
+github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vbatts/tar-split v0.11.1 h1:0Odu65rhcZ3JZaPHxl7tCI3V/C/Q9Zf82UFravl02dE=
github.com/vbatts/tar-split v0.11.1/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g=
-github.com/vbauerster/mpb/v4 v4.11.2 h1:ynkUoKzi65DZ1UsQPx7sgi/KN6G9f7br+Us2nKm35AM=
-github.com/vbauerster/mpb/v4 v4.11.2/go.mod h1:jIuIRCltGJUnm6DCyPVkwjlLUk4nHTH+m4eD14CdFF0=
+github.com/vbauerster/mpb/v4 v4.12.2 h1:TsBs1nWRYF0m8cUH13pxNhOUqY6yKcOr2PeSYxp2L3I=
+github.com/vbauerster/mpb/v4 v4.12.2/go.mod h1:LVRGvMch8T4HQO3eg2pFPsACH9kO/O6fT/7vhGje3QE=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190809123943-df4f5c81cb3b h1:6cLsL+2FW6dRAdl5iMtHgRogVCff0QpRi9653YmdcJA=
github.com/xeipuuv/gojsonpointer v0.0.0-20190809123943-df4f5c81cb3b/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@@ -357,7 +343,8 @@ github.com/xeipuuv/gojsonschema v0.0.0-20190816131739-be0936907f66/go.mod h1:anY
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
-go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
+go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@@ -367,7 +354,7 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -418,11 +405,12 @@ golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2 h1:/J2nHFg1MTqaRLFO7M+J78ASNsJoz3r0cvHBPQ77fsE=
golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200217220822-9197077df867 h1:JoRuNIf+rpHl+VhScRQQvzbHed86tKkqwPMV34T8myw=
+golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
diff --git a/vendor/github.com/containers/buildah/imagebuildah/executor.go b/vendor/github.com/containers/buildah/imagebuildah/executor.go
index 846900656..a0debc460 100644
--- a/vendor/github.com/containers/buildah/imagebuildah/executor.go
+++ b/vendor/github.com/containers/buildah/imagebuildah/executor.go
@@ -93,7 +93,6 @@ type Executor struct {
blobDirectory string
excludes []string
unusedArgs map[string]struct{}
- buildArgs map[string]string
capabilities []string
devices []configs.Device
signBy string
@@ -179,7 +178,6 @@ func NewExecutor(store storage.Store, options BuildOptions, mainNode *parser.Nod
rootfsMap: make(map[string]bool),
blobDirectory: options.BlobDirectory,
unusedArgs: make(map[string]struct{}),
- buildArgs: copyStringStringMap(options.Args),
capabilities: capabilities,
devices: devices,
signBy: options.SignBy,
@@ -232,25 +230,26 @@ func NewExecutor(store storage.Store, options BuildOptions, mainNode *parser.Nod
// startStage creates a new stage executor that will be referenced whenever a
// COPY or ADD statement uses a --from=NAME flag.
-func (b *Executor) startStage(name string, index, stages int, from, output string) *StageExecutor {
+func (b *Executor) startStage(stage *imagebuilder.Stage, stages int, from, output string) *StageExecutor {
if b.stages == nil {
b.stages = make(map[string]*StageExecutor)
}
- stage := &StageExecutor{
+ stageExec := &StageExecutor{
executor: b,
- index: index,
+ index: stage.Position,
stages: stages,
- name: name,
+ name: stage.Name,
volumeCache: make(map[string]string),
volumeCacheInfo: make(map[string]os.FileInfo),
output: output,
+ stage: stage,
}
- b.stages[name] = stage
- b.stages[from] = stage
- if idx := strconv.Itoa(index); idx != name {
- b.stages[idx] = stage
+ b.stages[stage.Name] = stageExec
+ b.stages[from] = stageExec
+ if idx := strconv.Itoa(stage.Position); idx != stage.Name {
+ b.stages[idx] = stageExec
}
- return stage
+ return stageExec
}
// resolveNameToImageRef creates a types.ImageReference for the output name in local storage
@@ -291,81 +290,6 @@ func (b *Executor) getImageHistory(ctx context.Context, imageID string) ([]v1.Hi
return oci.History, nil
}
-// getCreatedBy returns the command the image at node will be created by. If
-// the passed-in CompositeDigester is not nil, it is assumed to have the digest
-// information for the content if the node is ADD or COPY.
-func (b *Executor) getCreatedBy(node *parser.Node, addedContentDigest string) string {
- if node == nil {
- return "/bin/sh"
- }
- switch strings.ToUpper(node.Value) {
- case "RUN":
- buildArgs := b.getBuildArgs()
- if buildArgs != "" {
- return "|" + strconv.Itoa(len(strings.Split(buildArgs, " "))) + " " + buildArgs + " /bin/sh -c " + node.Original[4:]
- }
- return "/bin/sh -c " + node.Original[4:]
- case "ADD", "COPY":
- destination := node
- for destination.Next != nil {
- destination = destination.Next
- }
- return "/bin/sh -c #(nop) " + strings.ToUpper(node.Value) + " " + addedContentDigest + " in " + destination.Value + " "
- default:
- return "/bin/sh -c #(nop) " + node.Original
- }
-}
-
-// historyMatches returns true if a candidate history matches the history of our
-// base image (if we have one), plus the current instruction.
-// Used to verify whether a cache of the intermediate image exists and whether
-// to run the build again.
-func (b *Executor) historyMatches(baseHistory []v1.History, child *parser.Node, history []v1.History, addedContentDigest string) bool {
- if len(baseHistory) >= len(history) {
- return false
- }
- if len(history)-len(baseHistory) != 1 {
- return false
- }
- for i := range baseHistory {
- if baseHistory[i].CreatedBy != history[i].CreatedBy {
- return false
- }
- if baseHistory[i].Comment != history[i].Comment {
- return false
- }
- if baseHistory[i].Author != history[i].Author {
- return false
- }
- if baseHistory[i].EmptyLayer != history[i].EmptyLayer {
- return false
- }
- if baseHistory[i].Created != nil && history[i].Created == nil {
- return false
- }
- if baseHistory[i].Created == nil && history[i].Created != nil {
- return false
- }
- if baseHistory[i].Created != nil && history[i].Created != nil && *baseHistory[i].Created != *history[i].Created {
- return false
- }
- }
- return history[len(baseHistory)].CreatedBy == b.getCreatedBy(child, addedContentDigest)
-}
-
-// getBuildArgs returns a string of the build-args specified during the build process
-// it excludes any build-args that were not used in the build process
-func (b *Executor) getBuildArgs() string {
- var buildArgs []string
- for k, v := range b.buildArgs {
- if _, ok := b.unusedArgs[k]; !ok {
- buildArgs = append(buildArgs, k+"="+v)
- }
- }
- sort.Strings(buildArgs)
- return strings.Join(buildArgs, " ")
-}
-
// Build takes care of the details of running Prepare/Execute/Commit/Delete
// over each of the one or more parsed Dockerfiles and stages.
func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (imageID string, ref reference.Canonical, err error) {
@@ -494,7 +418,7 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image
output = b.output
}
- stageExecutor := b.startStage(stage.Name, stage.Position, len(stages), base, output)
+ stageExecutor := b.startStage(&stage, len(stages), base, output)
// If this a single-layer build, or if it's a multi-layered
// build and b.forceRmIntermediateCtrs is set, make sure we
@@ -505,7 +429,7 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image
}
// Build this stage.
- if imageID, ref, err = stageExecutor.Execute(ctx, stage, base); err != nil {
+ if imageID, ref, err = stageExecutor.Execute(ctx, base); err != nil {
lastErr = err
}
if lastErr != nil {
diff --git a/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go b/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go
index 8e49395a4..5ab70e54c 100644
--- a/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go
+++ b/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go
@@ -6,6 +6,7 @@ import (
"io"
"os"
"path/filepath"
+ "sort"
"strconv"
"strings"
"time"
@@ -56,6 +57,7 @@ type StageExecutor struct {
copyFrom string // Used to keep track of the --from flag from COPY and ADD
output string
containerIDs []string
+ stage *imagebuilder.Stage
}
// Preserve informs the stage executor that from this point on, it needs to
@@ -579,7 +581,8 @@ func (s *StageExecutor) UnrecognizedInstruction(step *imagebuilder.Step) error {
// prepare creates a working container based on the specified image, or if one
// isn't specified, the first argument passed to the first FROM instruction we
// can find in the stage's parsed tree.
-func (s *StageExecutor) prepare(ctx context.Context, stage imagebuilder.Stage, from string, initializeIBConfig, rebase bool) (builder *buildah.Builder, err error) {
+func (s *StageExecutor) prepare(ctx context.Context, from string, initializeIBConfig, rebase bool) (builder *buildah.Builder, err error) {
+ stage := s.stage
ib := stage.Builder
node := stage.Node
@@ -732,11 +735,11 @@ func (*StageExecutor) stepRequiresLayer(step *imagebuilder.Step) bool {
// storage. If it isn't found, it pulls down a copy. Then, if we don't have a
// working container root filesystem based on the image, it creates one. Then
// it returns that root filesystem's location.
-func (s *StageExecutor) getImageRootfs(ctx context.Context, stage imagebuilder.Stage, image string) (mountPoint string, err error) {
+func (s *StageExecutor) getImageRootfs(ctx context.Context, image string) (mountPoint string, err error) {
if builder, ok := s.executor.containerMap[image]; ok {
return builder.MountPoint, nil
}
- builder, err := s.prepare(ctx, stage, image, false, false)
+ builder, err := s.prepare(ctx, image, false, false)
if err != nil {
return "", err
}
@@ -745,7 +748,8 @@ func (s *StageExecutor) getImageRootfs(ctx context.Context, stage imagebuilder.S
}
// Execute runs each of the steps in the stage's parsed tree, in turn.
-func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage, base string) (imgID string, ref reference.Canonical, err error) {
+func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, ref reference.Canonical, err error) {
+ stage := s.stage
ib := stage.Builder
checkForLayers := s.executor.layers && s.executor.useCache
moreStages := s.index < s.stages-1
@@ -765,7 +769,7 @@ func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage, b
// Create the (first) working container for this stage. Reinitializing
// the imagebuilder configuration may alter the list of steps we have,
// so take a snapshot of them *after* that.
- if _, err := s.prepare(ctx, stage, base, true, true); err != nil {
+ if _, err := s.prepare(ctx, base, true, true); err != nil {
return "", nil, err
}
children := stage.Node.Children
@@ -809,14 +813,14 @@ func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage, b
// squash the contents of the base image. Whichever is
// the case, we need to commit() to create a new image.
logCommit(s.output, -1)
- if imgID, ref, err = s.commit(ctx, ib, s.executor.getCreatedBy(nil, ""), false, s.output); err != nil {
+ if imgID, ref, err = s.commit(ctx, s.getCreatedBy(nil, ""), false, s.output); err != nil {
return "", nil, errors.Wrapf(err, "error committing base container")
}
} else if len(s.executor.labels) > 0 || len(s.executor.annotations) > 0 {
// The image would be modified by the labels passed
// via the command line, so we need to commit.
logCommit(s.output, -1)
- if imgID, ref, err = s.commit(ctx, ib, s.executor.getCreatedBy(stage.Node, ""), true, s.output); err != nil {
+ if imgID, ref, err = s.commit(ctx, s.getCreatedBy(stage.Node, ""), true, s.output); err != nil {
return "", nil, err
}
} else {
@@ -866,7 +870,7 @@ func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage, b
}
otherStage, ok := s.executor.stages[arr[1]]
if !ok {
- if mountPoint, err = s.getImageRootfs(ctx, stage, arr[1]); err != nil {
+ if mountPoint, err = s.getImageRootfs(ctx, arr[1]); err != nil {
return "", nil, errors.Errorf("%s --from=%s: no stage or image found with that name", command, arr[1])
}
} else {
@@ -905,7 +909,7 @@ func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage, b
// instruction in the history that we'll write
// for the image when we eventually commit it.
now := time.Now()
- s.builder.AddPrependedEmptyLayer(&now, s.executor.getCreatedBy(node, addedContentDigest), "", "")
+ s.builder.AddPrependedEmptyLayer(&now, s.getCreatedBy(node, addedContentDigest), "", "")
continue
} else {
// This is the last instruction for this stage,
@@ -914,7 +918,7 @@ func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage, b
// if it's used as the basis for a later stage.
if lastStage || imageIsUsedLater {
logCommit(s.output, i)
- imgID, ref, err = s.commit(ctx, ib, s.executor.getCreatedBy(node, addedContentDigest), false, s.output)
+ imgID, ref, err = s.commit(ctx, s.getCreatedBy(node, addedContentDigest), false, s.output)
if err != nil {
return "", nil, errors.Wrapf(err, "error committing container for step %+v", *step)
}
@@ -1008,7 +1012,7 @@ func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage, b
}
// Create a new image, maybe with a new layer.
logCommit(s.output, i)
- imgID, ref, err = s.commit(ctx, ib, s.executor.getCreatedBy(node, addedContentDigest), !s.stepRequiresLayer(step), commitName)
+ imgID, ref, err = s.commit(ctx, s.getCreatedBy(node, addedContentDigest), !s.stepRequiresLayer(step), commitName)
if err != nil {
return "", nil, errors.Wrapf(err, "error committing container for step %+v", *step)
}
@@ -1034,7 +1038,7 @@ func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage, b
// creating a new working container with the
// just-committed or updated cached image as its new
// base image.
- if _, err := s.prepare(ctx, stage, imgID, false, true); err != nil {
+ if _, err := s.prepare(ctx, imgID, false, true); err != nil {
return "", nil, errors.Wrap(err, "error preparing container for next step")
}
}
@@ -1042,6 +1046,76 @@ func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage, b
return imgID, ref, nil
}
+// historyMatches returns true if a candidate history matches the history of our
+// base image (if we have one), plus the current instruction.
+// Used to verify whether a cache of the intermediate image exists and whether
+// to run the build again.
+func (s *StageExecutor) historyMatches(baseHistory []v1.History, child *parser.Node, history []v1.History, addedContentDigest string) bool {
+ if len(baseHistory) >= len(history) {
+ return false
+ }
+ if len(history)-len(baseHistory) != 1 {
+ return false
+ }
+ for i := range baseHistory {
+ if baseHistory[i].CreatedBy != history[i].CreatedBy {
+ return false
+ }
+ if baseHistory[i].Comment != history[i].Comment {
+ return false
+ }
+ if baseHistory[i].Author != history[i].Author {
+ return false
+ }
+ if baseHistory[i].EmptyLayer != history[i].EmptyLayer {
+ return false
+ }
+ if baseHistory[i].Created != nil && history[i].Created == nil {
+ return false
+ }
+ if baseHistory[i].Created == nil && history[i].Created != nil {
+ return false
+ }
+ if baseHistory[i].Created != nil && history[i].Created != nil && *baseHistory[i].Created != *history[i].Created {
+ return false
+ }
+ }
+ return history[len(baseHistory)].CreatedBy == s.getCreatedBy(child, addedContentDigest)
+}
+
+// getCreatedBy returns the command the image at node will be created by. If
+// the passed-in CompositeDigester is not nil, it is assumed to have the digest
+// information for the content if the node is ADD or COPY.
+func (s *StageExecutor) getCreatedBy(node *parser.Node, addedContentDigest string) string {
+ if node == nil {
+ return "/bin/sh"
+ }
+ switch strings.ToUpper(node.Value) {
+ case "RUN":
+ buildArgs := s.getBuildArgs()
+ if buildArgs != "" {
+ return "|" + strconv.Itoa(len(strings.Split(buildArgs, " "))) + " " + buildArgs + " /bin/sh -c " + node.Original[4:]
+ }
+ return "/bin/sh -c " + node.Original[4:]
+ case "ADD", "COPY":
+ destination := node
+ for destination.Next != nil {
+ destination = destination.Next
+ }
+ return "/bin/sh -c #(nop) " + strings.ToUpper(node.Value) + " " + addedContentDigest + " in " + destination.Value + " "
+ default:
+ return "/bin/sh -c #(nop) " + node.Original
+ }
+}
+
+// getBuildArgs returns a string of the build-args specified during the build process
+// it excludes any build-args that were not used in the build process
+func (s *StageExecutor) getBuildArgs() string {
+ buildArgs := s.stage.Builder.Arguments()
+ sort.Strings(buildArgs)
+ return strings.Join(buildArgs, " ")
+}
+
// tagExistingImage adds names to an image already in the store
func (s *StageExecutor) tagExistingImage(ctx context.Context, cacheID, output string) (string, reference.Canonical, error) {
// If we don't need to attach a name to the image, just return the cache ID.
@@ -1128,7 +1202,7 @@ func (s *StageExecutor) intermediateImageExists(ctx context.Context, currNode *p
return "", errors.Wrapf(err, "error getting history of %q", image.ID)
}
// children + currNode is the point of the Dockerfile we are currently at.
- if s.executor.historyMatches(baseHistory, currNode, history, addedContentDigest) {
+ if s.historyMatches(baseHistory, currNode, history, addedContentDigest) {
return image.ID, nil
}
}
@@ -1138,7 +1212,8 @@ func (s *StageExecutor) intermediateImageExists(ctx context.Context, currNode *p
// commit writes the container's contents to an image, using a passed-in tag as
// the name if there is one, generating a unique ID-based one otherwise.
-func (s *StageExecutor) commit(ctx context.Context, ib *imagebuilder.Builder, createdBy string, emptyLayer bool, output string) (string, reference.Canonical, error) {
+func (s *StageExecutor) commit(ctx context.Context, createdBy string, emptyLayer bool, output string) (string, reference.Canonical, error) {
+ ib := s.stage.Builder
var imageRef types.ImageReference
if output != "" {
imageRef2, err := s.executor.resolveNameToImageRef(output)
diff --git a/vendor/github.com/containers/buildah/imagebuildah/util.go b/vendor/github.com/containers/buildah/imagebuildah/util.go
index 5f14c9883..29cdf44d0 100644
--- a/vendor/github.com/containers/buildah/imagebuildah/util.go
+++ b/vendor/github.com/containers/buildah/imagebuildah/util.go
@@ -166,11 +166,3 @@ func convertMounts(mounts []Mount) []specs.Mount {
}
return specmounts
}
-
-func copyStringStringMap(m map[string]string) map[string]string {
- n := map[string]string{}
- for k, v := range m {
- n[k] = v
- }
- return n
-}
diff --git a/vendor/github.com/containers/buildah/info.go b/vendor/github.com/containers/buildah/info.go
index a40af08ed..4332a0adc 100644
--- a/vendor/github.com/containers/buildah/info.go
+++ b/vendor/github.com/containers/buildah/info.go
@@ -12,9 +12,9 @@ import (
"time"
"github.com/containers/buildah/util"
- "github.com/containers/common/pkg/unshare"
"github.com/containers/storage"
"github.com/containers/storage/pkg/system"
+ "github.com/containers/storage/pkg/unshare"
"github.com/sirupsen/logrus"
)
diff --git a/vendor/github.com/containers/buildah/install.md b/vendor/github.com/containers/buildah/install.md
index 78d70765b..8dd429bc8 100644
--- a/vendor/github.com/containers/buildah/install.md
+++ b/vendor/github.com/containers/buildah/install.md
@@ -12,7 +12,7 @@ provides updated packages for CentOS 7 which can be used unmodified on Amazon Li
```bash
cd /etc/yum.repos.d/
sudo wget https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable/CentOS_7/devel:kubic:libcontainers:stable.repo
-sudo yum -y yum-plugin-copr
+sudo yum -y install yum-plugin-copr
sudo yum -y copr enable lsm5/container-selinux
sudo yum -y install buildah
```
diff --git a/vendor/github.com/containers/buildah/pkg/overlay/overlay.go b/vendor/github.com/containers/buildah/pkg/overlay/overlay.go
index 61b65e425..c3d9302bb 100644
--- a/vendor/github.com/containers/buildah/pkg/overlay/overlay.go
+++ b/vendor/github.com/containers/buildah/pkg/overlay/overlay.go
@@ -8,9 +8,9 @@ import (
"path/filepath"
"strings"
- "github.com/containers/common/pkg/unshare"
"github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/system"
+ "github.com/containers/storage/pkg/unshare"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
diff --git a/vendor/github.com/containers/buildah/pkg/parse/parse_unix.go b/vendor/github.com/containers/buildah/pkg/parse/parse_unix.go
index 33dc565d9..0a6d44195 100644
--- a/vendor/github.com/containers/buildah/pkg/parse/parse_unix.go
+++ b/vendor/github.com/containers/buildah/pkg/parse/parse_unix.go
@@ -6,7 +6,7 @@ import (
"os"
"path/filepath"
- "github.com/containers/common/pkg/unshare"
+ "github.com/containers/storage/pkg/unshare"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/devices"
"github.com/pkg/errors"
diff --git a/vendor/github.com/containers/buildah/run_linux.go b/vendor/github.com/containers/buildah/run_linux.go
index 6e4d31d78..ca112d50c 100644
--- a/vendor/github.com/containers/buildah/run_linux.go
+++ b/vendor/github.com/containers/buildah/run_linux.go
@@ -28,11 +28,11 @@ import (
"github.com/containers/buildah/util"
"github.com/containers/common/pkg/capabilities"
"github.com/containers/common/pkg/config"
- "github.com/containers/common/pkg/unshare"
"github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/ioutils"
"github.com/containers/storage/pkg/reexec"
"github.com/containers/storage/pkg/stringid"
+ "github.com/containers/storage/pkg/unshare"
"github.com/docker/go-units"
"github.com/docker/libnetwork/resolvconf"
"github.com/docker/libnetwork/types"
@@ -1972,10 +1972,6 @@ func (b *Builder) configureEnvironment(g *generate.Generator, options RunOptions
g.AddProcessEnv(env[0], env[1])
}
}
-
- for src, dest := range b.Args {
- g.AddProcessEnv(src, dest)
- }
}
func setupRootlessSpecChanges(spec *specs.Spec, bundleDir string, shmSize string) error {
diff --git a/vendor/github.com/containers/common/pkg/apparmor/apparmor.go b/vendor/github.com/containers/common/pkg/apparmor/apparmor.go
new file mode 100644
index 000000000..8b4207efc
--- /dev/null
+++ b/vendor/github.com/containers/common/pkg/apparmor/apparmor.go
@@ -0,0 +1,21 @@
+package apparmor
+
+import (
+ "errors"
+)
+
+const (
+ // ProfilePrefix is used for version-independent presence checks.
+ ProfilePrefix = "apparmor_profile"
+
+ // Profile default name
+ Profile = "container-default"
+)
+
+var (
+
+ // ErrApparmorUnsupported indicates that AppArmor support is not supported.
+ ErrApparmorUnsupported = errors.New("AppArmor is not supported")
+ // ErrApparmorRootless indicates that AppArmor support is not supported in rootless mode.
+ ErrApparmorRootless = errors.New("AppArmor is not supported in rootless mode")
+)
diff --git a/vendor/github.com/containers/common/pkg/apparmor/apparmor_linux.go b/vendor/github.com/containers/common/pkg/apparmor/apparmor_linux.go
new file mode 100644
index 000000000..f0fab4597
--- /dev/null
+++ b/vendor/github.com/containers/common/pkg/apparmor/apparmor_linux.go
@@ -0,0 +1,289 @@
+// +build linux,apparmor
+
+package apparmor
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "os"
+ "os/exec"
+ "path"
+ "strconv"
+ "strings"
+ "text/template"
+
+ "github.com/containers/storage/pkg/unshare"
+ runcaa "github.com/opencontainers/runc/libcontainer/apparmor"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+// profileDirectory is the file store for apparmor profiles and macros.
+var profileDirectory = "/etc/apparmor.d"
+
+// IsEnabled returns true if AppArmor is enabled on the host.
+func IsEnabled() bool {
+ if unshare.IsRootless() {
+ return false
+ }
+ return runcaa.IsEnabled()
+}
+
+// profileData holds information about the given profile for generation.
+type profileData struct {
+ // Name is profile name.
+ Name string
+ // Imports defines the apparmor functions to import, before defining the profile.
+ Imports []string
+ // InnerImports defines the apparmor functions to import in the profile.
+ InnerImports []string
+ // Version is the {major, minor, patch} version of apparmor_parser as a single number.
+ Version int
+}
+
+// generateDefault creates an apparmor profile from ProfileData.
+func (p *profileData) generateDefault(out io.Writer) error {
+ compiled, err := template.New("apparmor_profile").Parse(defaultProfileTemplate)
+ if err != nil {
+ return err
+ }
+
+ if macroExists("tunables/global") {
+ p.Imports = append(p.Imports, "#include <tunables/global>")
+ } else {
+ p.Imports = append(p.Imports, "@{PROC}=/proc/")
+ }
+
+ if macroExists("abstractions/base") {
+ p.InnerImports = append(p.InnerImports, "#include <abstractions/base>")
+ }
+
+ ver, err := getAAParserVersion()
+ if err != nil {
+ return err
+ }
+ p.Version = ver
+
+ return compiled.Execute(out, p)
+}
+
+// macrosExists checks if the passed macro exists.
+func macroExists(m string) bool {
+ _, err := os.Stat(path.Join(profileDirectory, m))
+ return err == nil
+}
+
+// InstallDefault generates a default profile and loads it into the kernel
+// using 'apparmor_parser'.
+func InstallDefault(name string) error {
+ if unshare.IsRootless() {
+ return ErrApparmorRootless
+ }
+
+ p := profileData{
+ Name: name,
+ }
+
+ cmd := exec.Command("apparmor_parser", "-Kr")
+ pipe, err := cmd.StdinPipe()
+ if err != nil {
+ return err
+ }
+ if err := cmd.Start(); err != nil {
+ if pipeErr := pipe.Close(); pipeErr != nil {
+ logrus.Errorf("unable to close apparmor pipe: %q", pipeErr)
+ }
+ return err
+ }
+ if err := p.generateDefault(pipe); err != nil {
+ if pipeErr := pipe.Close(); pipeErr != nil {
+ logrus.Errorf("unable to close apparmor pipe: %q", pipeErr)
+ }
+ if cmdErr := cmd.Wait(); cmdErr != nil {
+ logrus.Errorf("unable to wait for apparmor command: %q", cmdErr)
+ }
+ return err
+ }
+
+ if pipeErr := pipe.Close(); pipeErr != nil {
+ logrus.Errorf("unable to close apparmor pipe: %q", pipeErr)
+ }
+ return cmd.Wait()
+}
+
+// DefaultContent returns the default profile content as byte slice. The
+// profile is named as the provided `name`. The function errors if the profile
+// generation fails.
+func DefaultContent(name string) ([]byte, error) {
+ p := profileData{Name: name}
+ var bytes bytes.Buffer
+ if err := p.generateDefault(&bytes); err != nil {
+ return nil, err
+ }
+ return bytes.Bytes(), nil
+}
+
+// IsLoaded checks if a profile with the given name has been loaded into the
+// kernel.
+func IsLoaded(name string) (bool, error) {
+ if name != "" && unshare.IsRootless() {
+ return false, errors.Wrapf(ErrApparmorRootless, "cannot load AppArmor profile %q", name)
+ }
+
+ file, err := os.Open("/sys/kernel/security/apparmor/profiles")
+ if err != nil {
+ if os.IsNotExist(err) {
+ return false, nil
+ }
+ return false, err
+ }
+ defer file.Close()
+
+ r := bufio.NewReader(file)
+ for {
+ p, err := r.ReadString('\n')
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ return false, err
+ }
+ if strings.HasPrefix(p, name+" ") {
+ return true, nil
+ }
+ }
+
+ return false, nil
+}
+
+// execAAParser runs `apparmor_parser` with the passed arguments.
+func execAAParser(dir string, args ...string) (string, error) {
+ c := exec.Command("apparmor_parser", args...)
+ c.Dir = dir
+
+ output, err := c.CombinedOutput()
+ if err != nil {
+ return "", fmt.Errorf("running `%s %s` failed with output: %s\nerror: %v", c.Path, strings.Join(c.Args, " "), output, err)
+ }
+
+ return string(output), nil
+}
+
+// getAAParserVersion returns the major and minor version of apparmor_parser.
+func getAAParserVersion() (int, error) {
+ output, err := execAAParser("", "--version")
+ if err != nil {
+ return -1, err
+ }
+ return parseAAParserVersion(output)
+}
+
+// parseAAParserVersion parses the given `apparmor_parser --version` output and
+// returns the major and minor version number as an integer.
+func parseAAParserVersion(output string) (int, error) {
+ // output is in the form of the following:
+ // AppArmor parser version 2.9.1
+ // Copyright (C) 1999-2008 Novell Inc.
+ // Copyright 2009-2012 Canonical Ltd.
+ lines := strings.SplitN(output, "\n", 2)
+ words := strings.Split(lines[0], " ")
+ version := words[len(words)-1]
+
+ // split by major minor version
+ v := strings.Split(version, ".")
+ if len(v) == 0 || len(v) > 3 {
+ return -1, fmt.Errorf("parsing version failed for output: `%s`", output)
+ }
+
+ // Default the versions to 0.
+ var majorVersion, minorVersion, patchLevel int
+
+ majorVersion, err := strconv.Atoi(v[0])
+ if err != nil {
+ return -1, err
+ }
+
+ if len(v) > 1 {
+ minorVersion, err = strconv.Atoi(v[1])
+ if err != nil {
+ return -1, err
+ }
+ }
+ if len(v) > 2 {
+ patchLevel, err = strconv.Atoi(v[2])
+ if err != nil {
+ return -1, err
+ }
+ }
+
+ // major*10^5 + minor*10^3 + patch*10^0
+ numericVersion := majorVersion*1e5 + minorVersion*1e3 + patchLevel
+ return numericVersion, nil
+
+}
+
+// CheckProfileAndLoadDefault checks if the specified profile is loaded and
+// loads the DefaultLibpodProfile if the specified on is prefixed by
+// DefaultLipodProfilePrefix. This allows to always load and apply the latest
+// default AppArmor profile. Note that AppArmor requires root. If it's a
+// default profile, return DefaultLipodProfilePrefix, otherwise the specified
+// one.
+func CheckProfileAndLoadDefault(name string) (string, error) {
+ if name == "unconfined" {
+ return name, nil
+ }
+
+ // AppArmor is not supported in rootless mode as it requires root
+ // privileges. Return an error in case a specific profile is specified.
+ if unshare.IsRootless() {
+ if name != "" {
+ return "", errors.Wrapf(ErrApparmorRootless, "cannot load AppArmor profile %q", name)
+ } else {
+ logrus.Debug("skipping loading default AppArmor profile (rootless mode)")
+ return "", nil
+ }
+ }
+
+ // Check if AppArmor is disabled and error out if a profile is to be set.
+ if !runcaa.IsEnabled() {
+ if name == "" {
+ return "", nil
+ } else {
+ return "", fmt.Errorf("profile %q specified but AppArmor is disabled on the host", name)
+ }
+ }
+
+ // If the specified name is not empty or is not a default libpod one,
+ // ignore it and return the name.
+ if name != "" && !strings.HasPrefix(name, ProfilePrefix) {
+ isLoaded, err := IsLoaded(name)
+ if err != nil {
+ return "", err
+ }
+ if !isLoaded {
+ return "", fmt.Errorf("AppArmor profile %q specified but not loaded", name)
+ }
+ return name, nil
+ }
+
+ name = Profile
+ // To avoid expensive redundant loads on each invocation, check
+ // if it's loaded before installing it.
+ isLoaded, err := IsLoaded(name)
+ if err != nil {
+ return "", err
+ }
+ if !isLoaded {
+ err = InstallDefault(name)
+ if err != nil {
+ return "", err
+ }
+ logrus.Infof("successfully loaded AppAmor profile %q", name)
+ } else {
+ logrus.Infof("AppAmor profile %q is already loaded", name)
+ }
+
+ return name, nil
+}
diff --git a/vendor/github.com/containers/common/pkg/apparmor/apparmor_linux_template.go b/vendor/github.com/containers/common/pkg/apparmor/apparmor_linux_template.go
new file mode 100644
index 000000000..021e32571
--- /dev/null
+++ b/vendor/github.com/containers/common/pkg/apparmor/apparmor_linux_template.go
@@ -0,0 +1,49 @@
+// +build linux,apparmor
+
+package apparmor
+
+const defaultProfileTemplate = `
+{{range $value := .Imports}}
+{{$value}}
+{{end}}
+
+profile {{.Name}} flags=(attach_disconnected,mediate_deleted) {
+{{range $value := .InnerImports}}
+ {{$value}}
+{{end}}
+
+ network,
+ capability,
+ file,
+ umount,
+
+{{if ge .Version 208096}}
+ # Allow signals from privileged profiles and from within the same profile
+ signal (receive) peer=unconfined,
+ signal (send,receive) peer={{.Name}},
+{{end}}
+
+ deny @{PROC}/* w, # deny write for all files directly in /proc (not in a subdir)
+ # deny write to files not in /proc/<number>/** or /proc/sys/**
+ deny @{PROC}/{[^1-9],[^1-9][^0-9],[^1-9s][^0-9y][^0-9s],[^1-9][^0-9][^0-9][^0-9]*}/** w,
+ deny @{PROC}/sys/[^k]** w, # deny /proc/sys except /proc/sys/k* (effectively /proc/sys/kernel)
+ deny @{PROC}/sys/kernel/{?,??,[^s][^h][^m]**} w, # deny everything except shm* in /proc/sys/kernel/
+ deny @{PROC}/sysrq-trigger rwklx,
+ deny @{PROC}/kcore rwklx,
+
+ deny mount,
+
+ deny /sys/[^f]*/** wklx,
+ deny /sys/f[^s]*/** wklx,
+ deny /sys/fs/[^c]*/** wklx,
+ deny /sys/fs/c[^g]*/** wklx,
+ deny /sys/fs/cg[^r]*/** wklx,
+ deny /sys/firmware/** rwklx,
+ deny /sys/kernel/security/** rwklx,
+
+{{if ge .Version 208095}}
+ # suppress ptrace denials when using using 'ps' inside a container
+ ptrace (trace,read) peer={{.Name}},
+{{end}}
+}
+`
diff --git a/vendor/github.com/containers/common/pkg/apparmor/apparmor_unsupported.go b/vendor/github.com/containers/common/pkg/apparmor/apparmor_unsupported.go
new file mode 100644
index 000000000..13469f1b6
--- /dev/null
+++ b/vendor/github.com/containers/common/pkg/apparmor/apparmor_unsupported.go
@@ -0,0 +1,31 @@
+// +build !linux !apparmor
+
+package apparmor
+
+// IsEnabled dummy.
+func IsEnabled() bool {
+ return false
+}
+
+// InstallDefault dummy.
+func InstallDefault(name string) error {
+ return ErrApparmorUnsupported
+}
+
+// IsLoaded dummy.
+func IsLoaded(name string) (bool, error) {
+ return false, ErrApparmorUnsupported
+}
+
+// CheckProfileAndLoadDefault dummy.
+func CheckProfileAndLoadDefault(name string) (string, error) {
+ if name == "" {
+ return "", nil
+ }
+ return "", ErrApparmorUnsupported
+}
+
+// DefaultContent dummy.
+func DefaultContent(name string) ([]byte, error) {
+ return nil, nil
+}
diff --git a/vendor/github.com/containers/common/pkg/capabilities/capabilities.go b/vendor/github.com/containers/common/pkg/capabilities/capabilities.go
index a57c048bc..51e711631 100644
--- a/vendor/github.com/containers/common/pkg/capabilities/capabilities.go
+++ b/vendor/github.com/containers/common/pkg/capabilities/capabilities.go
@@ -99,6 +99,10 @@ func MergeCapabilities(base, adds, drops []string) ([]string, error) {
return base, nil
}
+ base, err := normalizeCapabilities(base)
+ if err != nil {
+ return nil, err
+ }
capDrop, err := normalizeCapabilities(drops)
if err != nil {
return nil, err
diff --git a/vendor/github.com/containers/common/pkg/cgroupv2/cgroups_linux.go b/vendor/github.com/containers/common/pkg/cgroupv2/cgroups_linux.go
new file mode 100644
index 000000000..b11eafebb
--- /dev/null
+++ b/vendor/github.com/containers/common/pkg/cgroupv2/cgroups_linux.go
@@ -0,0 +1,27 @@
+package cgroupv2
+
+import (
+ "sync"
+ "syscall"
+
+ "golang.org/x/sys/unix"
+)
+
+var (
+ isCgroupV2Once sync.Once
+ isCgroupV2 bool
+ isCgroupV2Err error
+)
+
+// Enabled returns whether we are running in cgroup 2 cgroup2 mode.
+func Enabled() (bool, error) {
+ isCgroupV2Once.Do(func() {
+ var st syscall.Statfs_t
+ if err := syscall.Statfs("/sys/fs/cgroup", &st); err != nil {
+ isCgroupV2, isCgroupV2Err = false, err
+ } else {
+ isCgroupV2, isCgroupV2Err = st.Type == unix.CGROUP2_SUPER_MAGIC, nil
+ }
+ })
+ return isCgroupV2, isCgroupV2Err
+}
diff --git a/vendor/github.com/containers/common/pkg/cgroupv2/cgroups_unsupported.go b/vendor/github.com/containers/common/pkg/cgroupv2/cgroups_unsupported.go
new file mode 100644
index 000000000..cda68b405
--- /dev/null
+++ b/vendor/github.com/containers/common/pkg/cgroupv2/cgroups_unsupported.go
@@ -0,0 +1,8 @@
+// +build !linux
+
+package cgroupv2
+
+// Enabled returns whether we are running in cgroup 2 cgroup2 mode.
+func Enabled() (bool, error) {
+ return false, nil
+}
diff --git a/vendor/github.com/containers/common/pkg/config/config.go b/vendor/github.com/containers/common/pkg/config/config.go
index db70e53a4..b65db2722 100644
--- a/vendor/github.com/containers/common/pkg/config/config.go
+++ b/vendor/github.com/containers/common/pkg/config/config.go
@@ -11,7 +11,7 @@ import (
"github.com/BurntSushi/toml"
"github.com/containers/common/pkg/capabilities"
- "github.com/containers/common/pkg/unshare"
+ "github.com/containers/storage/pkg/unshare"
units "github.com/docker/go-units"
selinux "github.com/opencontainers/selinux/go-selinux"
"github.com/pkg/errors"
@@ -173,7 +173,7 @@ type ContainersConfig struct {
// EngineConfig contains configuration options used to set up a engine runtime
type EngineConfig struct {
// CgroupCheck indicates the configuration has been rewritten after an
- // upgrade to Fedora 31 to change the default OCI runtime for cgroupsv2.
+ // upgrade to Fedora 31 to change the default OCI runtime for cgroupv2v2.
CgroupCheck bool `toml:"cgroup_check,omitempty"`
// CGroupManager is the CGroup Manager to use Valid values are "cgroupfs"
@@ -269,7 +269,7 @@ type EngineConfig struct {
// RuntimeSupportsNoCgroups is a list of OCI runtimes that support
// running containers without CGroups.
- RuntimeSupportsNoCgroups []string `toml:"runtime_supports_nocgroups"`
+ RuntimeSupportsNoCgroups []string `toml:"runtime_supports_nocgroupv2"`
// SetOptions contains a subset of config options. It's used to indicate if
// a given option has either been set by the user or by the parsed
@@ -373,7 +373,7 @@ type NetworkConfig struct {
// running as root or rootless, we then merge the system configuration followed
// by merging the default config (hard-coded default in memory).
// Note that the OCI runtime is hard-set to `crun` if we're running on a system
-// with cgroupsv2. Other OCI runtimes are not yet supporting cgroupsv2. This
+// with cgroupv2v2. Other OCI runtimes are not yet supporting cgroupv2v2. This
// might change in the future.
func NewConfig(userConfigPath string) (*Config, error) {
@@ -494,7 +494,7 @@ func (c *Config) CheckCgroupsAndAdjustConfig() {
}
if !hasSession {
- logrus.Warningf("The cgroups manager is set to systemd but there is no systemd user session available")
+ logrus.Warningf("The cgroupv2 manager is set to systemd but there is no systemd user session available")
logrus.Warningf("For using systemd, you may need to login using an user session")
logrus.Warningf("Alternatively, you can enable lingering with: `loginctl enable-linger %d` (possibly as root)", unshare.GetRootlessUID())
logrus.Warningf("Falling back to --cgroup-manager=cgroupfs")
@@ -806,9 +806,35 @@ func IsValidDeviceMode(mode string) bool {
return true
}
+// resolveHomeDir converts a path referencing the home directory via "~"
+// to an absolute path
+func resolveHomeDir(path string) (string, error) {
+ // check if the path references the home dir to avoid work
+ // don't use strings.HasPrefix(path, "~") as this doesn't match "~" alone
+ // use strings.HasPrefix(...) to not match "something/~/something"
+ if !(path == "~" || strings.HasPrefix(path, "~/")) {
+ // path does not reference home dir -> Nothing to do
+ return path, nil
+ }
+
+ // only get HomeDir when necessary
+ home, err := unshare.HomeDir()
+ if err != nil {
+ return "", err
+ }
+
+ // replace the first "~" (start of path) with the HomeDir to resolve "~"
+ return strings.Replace(path, "~", home, 1), nil
+}
+
// isDirectory tests whether the given path exists and is a directory. It
// follows symlinks.
func isDirectory(path string) error {
+ path, err := resolveHomeDir(path)
+ if err != nil {
+ return err
+ }
+
info, err := os.Stat(path)
if err != nil {
return err
diff --git a/vendor/github.com/containers/common/pkg/config/containers.conf b/vendor/github.com/containers/common/pkg/config/containers.conf
index b01db5f88..067be429e 100644
--- a/vendor/github.com/containers/common/pkg/config/containers.conf
+++ b/vendor/github.com/containers/common/pkg/config/containers.conf
@@ -288,7 +288,7 @@
# associated with the pod. This container does nothing other then sleep,
# reserving the pods resources for the lifetime of the pod.
#
-# infra_image = "k8s.gcr.io/pause:3.1"
+# infra_image = "k8s.gcr.io/pause:3.2"
# Specify the locking mechanism to use; valid values are "shm" and "file".
# Change the default only if you are sure of what you are doing, in general
@@ -345,9 +345,9 @@
# List of the OCI runtimes that support --format=json. When json is supported
# engine will use it for reporting nicer errors.
#
-# runtime_supports_json = ["crun", "runc"]
+# runtime_supports_json = ["crun", "runc", "kata"]
-# Paths to look for a valid OCI runtime (runc, runv, etc)
+# Paths to look for a valid OCI runtime (runc, runv, kata, etc)
[engine.runtimes]
# runc = [
# "/usr/bin/runc",
@@ -369,6 +369,15 @@
# "/run/current-system/sw/bin/crun",
# ]
+# kata = [
+# "/usr/bin/kata-runtime",
+# "/usr/sbin/kata-runtime",
+# "/usr/local/bin/kata-runtime",
+# "/usr/local/sbin/kata-runtime",
+# "/sbin/kata-runtime",
+# "/bin/kata-runtime",
+# ]
+
# Number of seconds to wait for container to exit before sending kill signal.
#stop_timeout = 10
diff --git a/vendor/github.com/containers/common/pkg/config/default.go b/vendor/github.com/containers/common/pkg/config/default.go
index 04c3f9773..78bfd8a28 100644
--- a/vendor/github.com/containers/common/pkg/config/default.go
+++ b/vendor/github.com/containers/common/pkg/config/default.go
@@ -2,14 +2,19 @@ package config
import (
"bytes"
+ "fmt"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
- "github.com/containers/common/pkg/unshare"
+ "github.com/containers/common/pkg/apparmor"
+ "github.com/containers/common/pkg/cgroupv2"
+ "github.com/containers/common/pkg/sysinfo"
"github.com/containers/storage"
+ "github.com/containers/storage/pkg/unshare"
+ "github.com/opencontainers/selinux/go-selinux"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -40,7 +45,7 @@ var (
// DefaultInitPath is the default path to the container-init binary
DefaultInitPath = "/usr/libexec/podman/catatonit"
// DefaultInfraImage to use for infra container
- DefaultInfraImage = "k8s.gcr.io/pause:3.1"
+ DefaultInfraImage = "k8s.gcr.io/pause:3.2"
// DefaultInfraCommand to be run in an infra container
DefaultInfraCommand = "/pause"
// DefaultRootlessSHMLockPath is the default path for rootless SHM locks
@@ -87,7 +92,7 @@ const (
// CgroupfsCgroupsManager represents cgroupfs native cgroup manager
CgroupfsCgroupsManager = "cgroupfs"
// DefaultApparmorProfile specifies the default apparmor profile for the container.
- DefaultApparmorProfile = "container-default"
+ DefaultApparmorProfile = apparmor.Profile
// SystemdCgroupsManager represents systemd native cgroup manager
SystemdCgroupsManager = "systemd"
// DefaultLogDriver is the default type of log files
@@ -207,11 +212,11 @@ func defaultConfigFromMemory() (*EngineConfig, error) {
c.StateType = BoltDBStateStore
c.OCIRuntime = "runc"
- // If we're running on cgroups v2, default to using crun.
- if onCgroupsv2, _ := isCgroup2UnifiedMode(); onCgroupsv2 {
+ // If we're running on cgroupv2 v2, default to using crun.
+ if cgroup2, _ := cgroupv2.Enabled(); cgroup2 {
c.OCIRuntime = "crun"
}
- c.CgroupManager = SystemdCgroupsManager
+ c.CgroupManager = defaultCgroupManager()
c.StopTimeout = uint(10)
c.OCIRuntimes = map[string][]string{
@@ -234,6 +239,14 @@ func defaultConfigFromMemory() (*EngineConfig, error) {
"/bin/crun",
"/run/current-system/sw/bin/crun",
},
+ "kata": {
+ "/usr/bin/kata-runtime",
+ "/usr/sbin/kata-runtime",
+ "/usr/local/bin/kata-runtime",
+ "/usr/local/sbin/kata-runtime",
+ "/sbin/kata-runtime",
+ "/bin/kata-runtime",
+ },
}
c.ConmonEnvVars = []string{
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
@@ -261,7 +274,7 @@ func defaultConfigFromMemory() (*EngineConfig, error) {
c.InfraImage = DefaultInfraImage
c.EnablePortReservation = true
c.NumLocks = 2048
- c.EventsLogger = "journald"
+ c.EventsLogger = defaultEventsLogger()
c.DetachKeys = DefaultDetachKeys
c.SDNotify = false
// TODO - ideally we should expose a `type LockType string` along with
@@ -344,3 +357,112 @@ func probeConmon(conmonBinary string) error {
return nil
}
+
+// NetNS returns the default network namespace
+func (c *Config) NetNS() string {
+ if c.Containers.NetNS == "private" && unshare.IsRootless() {
+ return "slirp4netns"
+ }
+ return c.Containers.NetNS
+}
+
+// SecurityOptions returns the default security options
+func (c *Config) SecurityOptions() []string {
+ securityOpts := []string{}
+ if c.Containers.SeccompProfile != "" && c.Containers.SeccompProfile != SeccompDefaultPath {
+ securityOpts = append(securityOpts, fmt.Sprintf("seccomp=%s", c.Containers.SeccompProfile))
+ }
+ if apparmor.IsEnabled() && c.Containers.ApparmorProfile != "" {
+ securityOpts = append(securityOpts, fmt.Sprintf("apparmor=%s", c.Containers.ApparmorProfile))
+ }
+ if selinux.GetEnabled() && !c.Containers.EnableLabeling {
+ securityOpts = append(securityOpts, fmt.Sprintf("label=%s", selinux.DisableSecOpt()[0]))
+ }
+ return securityOpts
+}
+
+// Sysctls returns the default sysctls
+func (c *Config) Sysctls() []string {
+ return c.Containers.DefaultSysctls
+}
+
+// Volumes returns the default additional volumes for containersvolumes
+func (c *Config) Volumes() []string {
+ return c.Containers.Volumes
+}
+
+// Devices returns the default additional devices for containers
+func (c *Config) Devices() []string {
+ return c.Containers.Devices
+}
+
+// DNSServers returns the default DNS servers to add to resolv.conf in containers
+func (c *Config) DNSServers() []string {
+ return c.Containers.DNSServers
+}
+
+// DNSSerches returns the default DNS searches to add to resolv.conf in containers
+func (c *Config) DNSSearches() []string {
+ return c.Containers.DNSSearches
+}
+
+// DNSOptions returns the default DNS options to add to resolv.conf in containers
+func (c *Config) DNSOptions() []string {
+ return c.Containers.DNSOptions
+}
+
+// Env returns the default additional environment variables to add to containers
+func (c *Config) Env() []string {
+ return c.Containers.Env
+}
+
+// InitPath returns the default init path to add to containers
+func (c *Config) InitPath() string {
+ return c.Containers.InitPath
+}
+
+// IPCNS returns the default IPC Namespace configuration to run containers with
+func (c *Config) IPCNS() string {
+ return c.Containers.IPCNS
+}
+
+// PIDNS returns the default PID Namespace configuration to run containers with
+func (c *Config) PidNS() string {
+ return c.Containers.PidNS
+}
+
+// CgroupNS returns the default Cgroup Namespace configuration to run containers with
+func (c *Config) CgroupNS() string {
+ return c.Containers.CgroupNS
+}
+
+// UTSNS returns the default UTS Namespace configuration to run containers with
+func (c *Config) UTSNS() string {
+ return c.Containers.UTSNS
+}
+
+// ShmSize returns the default size for temporary file systems to use in containers
+func (c *Config) ShmSize() string {
+ return c.Containers.ShmSize
+}
+
+// Ulimits returns the default ulimits to use in containers
+func (c *Config) Ulimits() []string {
+ return c.Containers.DefaultUlimits
+}
+
+// PidsLimit returns the default maximum number of pids to use in containers
+func (c *Config) PidsLimit() int64 {
+ if unshare.IsRootless() {
+ cgroup2, _ := cgroupv2.Enabled()
+ if cgroup2 {
+ return c.Containers.PidsLimit
+ }
+ }
+ return sysinfo.GetDefaultPidsLimit()
+}
+
+// DetachKeys returns the default detach keys to detach from a container
+func (c *Config) DetachKeys() string {
+ return c.Engine.DetachKeys
+}
diff --git a/vendor/github.com/containers/common/pkg/config/default_linux.go b/vendor/github.com/containers/common/pkg/config/default_linux.go
index 91b73d344..e49413d7a 100644
--- a/vendor/github.com/containers/common/pkg/config/default_linux.go
+++ b/vendor/github.com/containers/common/pkg/config/default_linux.go
@@ -5,24 +5,10 @@ import (
"io/ioutil"
"strconv"
"strings"
- "syscall"
"golang.org/x/sys/unix"
)
-// isCgroup2UnifiedMode returns whether we are running in cgroup2 mode.
-func isCgroup2UnifiedMode() (isUnified bool, isUnifiedErr error) {
- cgroupRoot := "/sys/fs/cgroup"
-
- var st syscall.Statfs_t
- if err := syscall.Statfs(cgroupRoot, &st); err != nil {
- isUnified, isUnifiedErr = false, err
- } else {
- isUnified, isUnifiedErr = int64(st.Type) == int64(unix.CGROUP2_SUPER_MAGIC), nil
- }
- return
-}
-
const (
oldMaxSize = uint64(1048576)
)
diff --git a/vendor/github.com/containers/common/pkg/config/libpodConfig.go b/vendor/github.com/containers/common/pkg/config/libpodConfig.go
index 333f43815..cdb38a514 100644
--- a/vendor/github.com/containers/common/pkg/config/libpodConfig.go
+++ b/vendor/github.com/containers/common/pkg/config/libpodConfig.go
@@ -9,7 +9,8 @@ import (
"path/filepath"
"github.com/BurntSushi/toml"
- "github.com/containers/common/pkg/unshare"
+ "github.com/containers/common/pkg/cgroupv2"
+ "github.com/containers/storage/pkg/unshare"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -69,7 +70,7 @@ type ConfigFromLibpod struct {
// RuntimeSupportsNoCgroups is a list of OCI runtimes that support
// running containers without CGroups.
- RuntimeSupportsNoCgroups []string `toml:"runtime_supports_nocgroups,omitempty"`
+ RuntimeSupportsNoCgroups []string `toml:"runtime_supports_nocgroupv2,omitempty"`
// RuntimePath is the path to OCI runtime binary for launching containers.
// The first path pointing to a valid file will be used This is used only
@@ -175,7 +176,7 @@ type ConfigFromLibpod struct {
SDNotify bool `toml:",omitempty"`
// CgroupCheck indicates the configuration has been rewritten after an
- // upgrade to Fedora 31 to change the default OCI runtime for cgroupsv2.
+ // upgrade to Fedora 31 to change the default OCI runtime for cgroupv2v2.
CgroupCheck bool `toml:"cgroup_check,omitempty"`
}
@@ -183,7 +184,7 @@ type ConfigFromLibpod struct {
// Depending if we're running as root or rootless, we then merge the system configuration followed
// by merging the default config (hard-coded default in memory).
// Note that the OCI runtime is hard-set to `crun` if we're running on a system
-// with cgroupsv2. Other OCI runtimes are not yet supporting cgroupsv2. This
+// with cgroupv2v2. Other OCI runtimes are not yet supporting cgroupv2v2. This
// might change in the future.
func newLibpodConfig(c *Config) error {
// Start with the default config and interatively merge
@@ -205,13 +206,13 @@ func newLibpodConfig(c *Config) error {
// Since runc does not currently support cgroupV2
// Change to default crun on first running of libpod.conf
- // TODO Once runc has support for cgroups, this function should be removed.
+ // TODO Once runc has support for cgroupv2, this function should be removed.
if !config.CgroupCheck && unshare.IsRootless() {
- cgroupsV2, err := isCgroup2UnifiedMode()
+ cgroup2, err := cgroupv2.Enabled()
if err != nil {
return err
}
- if cgroupsV2 {
+ if cgroup2 {
path, err := exec.LookPath("crun")
if err != nil {
// Can't find crun path so do nothing
diff --git a/vendor/github.com/containers/common/pkg/config/nosystemd.go b/vendor/github.com/containers/common/pkg/config/nosystemd.go
new file mode 100644
index 000000000..5b82b1389
--- /dev/null
+++ b/vendor/github.com/containers/common/pkg/config/nosystemd.go
@@ -0,0 +1,11 @@
+// +build !systemd
+
+package config
+
+func defaultCgroupManager() string {
+ return "cgroupfs"
+}
+
+func defaultEventsLogger() string {
+ return "file"
+}
diff --git a/vendor/github.com/containers/common/pkg/config/systemd.go b/vendor/github.com/containers/common/pkg/config/systemd.go
new file mode 100644
index 000000000..e02f52192
--- /dev/null
+++ b/vendor/github.com/containers/common/pkg/config/systemd.go
@@ -0,0 +1,10 @@
+// +build systemd
+
+package config
+
+func defaultCgroupManager() string {
+ return SystemdCgroupsManager
+}
+func defaultEventsLogger() string {
+ return "journald"
+}
diff --git a/vendor/github.com/containers/common/pkg/config/util_supported.go b/vendor/github.com/containers/common/pkg/config/util_supported.go
index 101952f1d..2cdf54549 100644
--- a/vendor/github.com/containers/common/pkg/config/util_supported.go
+++ b/vendor/github.com/containers/common/pkg/config/util_supported.go
@@ -9,7 +9,7 @@ import (
"sync"
"syscall"
- "github.com/containers/common/pkg/unshare"
+ "github.com/containers/storage/pkg/unshare"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
diff --git a/vendor/github.com/containers/common/pkg/sysinfo/README.md b/vendor/github.com/containers/common/pkg/sysinfo/README.md
new file mode 100644
index 000000000..c1530cef0
--- /dev/null
+++ b/vendor/github.com/containers/common/pkg/sysinfo/README.md
@@ -0,0 +1 @@
+SysInfo stores information about which features a kernel supports.
diff --git a/vendor/github.com/containers/common/pkg/sysinfo/numcpu.go b/vendor/github.com/containers/common/pkg/sysinfo/numcpu.go
new file mode 100644
index 000000000..aeb1a3a80
--- /dev/null
+++ b/vendor/github.com/containers/common/pkg/sysinfo/numcpu.go
@@ -0,0 +1,12 @@
+// +build !linux,!windows
+
+package sysinfo
+
+import (
+ "runtime"
+)
+
+// NumCPU returns the number of CPUs
+func NumCPU() int {
+ return runtime.NumCPU()
+}
diff --git a/vendor/github.com/containers/common/pkg/sysinfo/numcpu_linux.go b/vendor/github.com/containers/common/pkg/sysinfo/numcpu_linux.go
new file mode 100644
index 000000000..f1d2d9db3
--- /dev/null
+++ b/vendor/github.com/containers/common/pkg/sysinfo/numcpu_linux.go
@@ -0,0 +1,44 @@
+// +build linux
+
+package sysinfo
+
+import (
+ "runtime"
+ "unsafe"
+
+ "golang.org/x/sys/unix"
+)
+
+// numCPU queries the system for the count of threads available
+// for use to this process.
+//
+// Issues two syscalls.
+// Returns 0 on errors. Use |runtime.NumCPU| in that case.
+func numCPU() int {
+ // Gets the affinity mask for a process: The very one invoking this function.
+ pid, _, _ := unix.RawSyscall(unix.SYS_GETPID, 0, 0, 0)
+
+ var mask [1024 / 64]uintptr
+ _, _, err := unix.RawSyscall(unix.SYS_SCHED_GETAFFINITY, pid, uintptr(len(mask)*8), uintptr(unsafe.Pointer(&mask[0])))
+ if err != 0 {
+ return 0
+ }
+
+ // For every available thread a bit is set in the mask.
+ ncpu := 0
+ for _, e := range mask {
+ if e == 0 {
+ continue
+ }
+ ncpu += int(popcnt(uint64(e)))
+ }
+ return ncpu
+}
+
+// NumCPU returns the number of CPUs which are currently online
+func NumCPU() int {
+ if ncpu := numCPU(); ncpu > 0 {
+ return ncpu
+ }
+ return runtime.NumCPU()
+}
diff --git a/vendor/github.com/containers/common/pkg/sysinfo/numcpu_windows.go b/vendor/github.com/containers/common/pkg/sysinfo/numcpu_windows.go
new file mode 100644
index 000000000..1d89dd550
--- /dev/null
+++ b/vendor/github.com/containers/common/pkg/sysinfo/numcpu_windows.go
@@ -0,0 +1,37 @@
+// +build windows
+
+package sysinfo
+
+import (
+ "runtime"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+)
+
+var (
+ kernel32 = windows.NewLazySystemDLL("kernel32.dll")
+ getCurrentProcess = kernel32.NewProc("GetCurrentProcess")
+ getProcessAffinityMask = kernel32.NewProc("GetProcessAffinityMask")
+)
+
+func numCPU() int {
+ // Gets the affinity mask for a process
+ var mask, sysmask uintptr
+ currentProcess, _, _ := getCurrentProcess.Call()
+ ret, _, _ := getProcessAffinityMask.Call(currentProcess, uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask)))
+ if ret == 0 {
+ return 0
+ }
+ // For every available thread a bit is set in the mask.
+ ncpu := int(popcnt(uint64(mask)))
+ return ncpu
+}
+
+// NumCPU returns the number of CPUs which are currently online
+func NumCPU() int {
+ if ncpu := numCPU(); ncpu > 0 {
+ return ncpu
+ }
+ return runtime.NumCPU()
+}
diff --git a/vendor/github.com/containers/common/pkg/sysinfo/sysinfo.go b/vendor/github.com/containers/common/pkg/sysinfo/sysinfo.go
new file mode 100644
index 000000000..686f66ce5
--- /dev/null
+++ b/vendor/github.com/containers/common/pkg/sysinfo/sysinfo.go
@@ -0,0 +1,153 @@
+package sysinfo
+
+import "github.com/docker/docker/pkg/parsers"
+
+// SysInfo stores information about which features a kernel supports.
+// TODO Windows: Factor out platform specific capabilities.
+type SysInfo struct {
+ // Whether the kernel supports AppArmor or not
+ AppArmor bool
+ // Whether the kernel supports Seccomp or not
+ Seccomp bool
+
+ cgroupMemInfo
+ cgroupCPUInfo
+ cgroupBlkioInfo
+ cgroupCpusetInfo
+ cgroupPids
+
+ // Whether IPv4 forwarding is supported or not, if this was disabled, networking will not work
+ IPv4ForwardingDisabled bool
+
+ // Whether bridge-nf-call-iptables is supported or not
+ BridgeNFCallIPTablesDisabled bool
+
+ // Whether bridge-nf-call-ip6tables is supported or not
+ BridgeNFCallIP6TablesDisabled bool
+
+ // Whether the cgroup has the mountpoint of "devices" or not
+ CgroupDevicesEnabled bool
+}
+
+type cgroupMemInfo struct {
+ // Whether memory limit is supported or not
+ MemoryLimit bool
+
+ // Whether swap limit is supported or not
+ SwapLimit bool
+
+ // Whether soft limit is supported or not
+ MemoryReservation bool
+
+ // Whether OOM killer disable is supported or not
+ OomKillDisable bool
+
+ // Whether memory swappiness is supported or not
+ MemorySwappiness bool
+
+ // Whether kernel memory limit is supported or not
+ KernelMemory bool
+}
+
+type cgroupCPUInfo struct {
+ // Whether CPU shares is supported or not
+ CPUShares bool
+
+ // Whether CPU CFS(Completely Fair Scheduler) period is supported or not
+ CPUCfsPeriod bool
+
+ // Whether CPU CFS(Completely Fair Scheduler) quota is supported or not
+ CPUCfsQuota bool
+
+ // Whether CPU real-time period is supported or not
+ CPURealtimePeriod bool
+
+ // Whether CPU real-time runtime is supported or not
+ CPURealtimeRuntime bool
+}
+
+type cgroupBlkioInfo struct {
+ // Whether Block IO weight is supported or not
+ BlkioWeight bool
+
+ // Whether Block IO weight_device is supported or not
+ BlkioWeightDevice bool
+
+ // Whether Block IO read limit in bytes per second is supported or not
+ BlkioReadBpsDevice bool
+
+ // Whether Block IO write limit in bytes per second is supported or not
+ BlkioWriteBpsDevice bool
+
+ // Whether Block IO read limit in IO per second is supported or not
+ BlkioReadIOpsDevice bool
+
+ // Whether Block IO write limit in IO per second is supported or not
+ BlkioWriteIOpsDevice bool
+}
+
+type cgroupCpusetInfo struct {
+ // Whether Cpuset is supported or not
+ Cpuset bool
+
+ // Available Cpuset's cpus
+ Cpus string
+
+ // Available Cpuset's memory nodes
+ Mems string
+}
+
+type cgroupPids struct {
+ // Whether Pids Limit is supported or not
+ PidsLimit bool
+}
+
+// IsCpusetCpusAvailable returns `true` if the provided string set is contained
+// in cgroup's cpuset.cpus set, `false` otherwise.
+// If error is not nil a parsing error occurred.
+func (c cgroupCpusetInfo) IsCpusetCpusAvailable(provided string) (bool, error) {
+ return isCpusetListAvailable(provided, c.Cpus)
+}
+
+// IsCpusetMemsAvailable returns `true` if the provided string set is contained
+// in cgroup's cpuset.mems set, `false` otherwise.
+// If error is not nil a parsing error occurred.
+func (c cgroupCpusetInfo) IsCpusetMemsAvailable(provided string) (bool, error) {
+ return isCpusetListAvailable(provided, c.Mems)
+}
+
+func isCpusetListAvailable(provided, available string) (bool, error) {
+ parsedProvided, err := parsers.ParseUintList(provided)
+ if err != nil {
+ return false, err
+ }
+ parsedAvailable, err := parsers.ParseUintList(available)
+ if err != nil {
+ return false, err
+ }
+ for k := range parsedProvided {
+ if !parsedAvailable[k] {
+ return false, nil
+ }
+ }
+ return true, nil
+}
+
+// Returns bit count of 1, used by NumCPU
+func popcnt(x uint64) (n byte) {
+ x -= (x >> 1) & 0x5555555555555555
+ x = (x>>2)&0x3333333333333333 + x&0x3333333333333333
+ x += x >> 4
+ x &= 0x0f0f0f0f0f0f0f0f
+ x *= 0x0101010101010101
+ return byte(x >> 56)
+}
+
+// GetDefaultPidsLimit returns the default pids limit to run containers with
+func GetDefaultPidsLimit() int64 {
+ sysInfo := New(true)
+ if !sysInfo.PidsLimit {
+ return 0
+ }
+ return 4096
+}
diff --git a/vendor/github.com/containers/common/pkg/sysinfo/sysinfo_linux.go b/vendor/github.com/containers/common/pkg/sysinfo/sysinfo_linux.go
new file mode 100644
index 000000000..269ea686a
--- /dev/null
+++ b/vendor/github.com/containers/common/pkg/sysinfo/sysinfo_linux.go
@@ -0,0 +1,261 @@
+package sysinfo
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path"
+ "strings"
+
+ "github.com/containers/common/pkg/cgroupv2"
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+ "github.com/sirupsen/logrus"
+ "golang.org/x/sys/unix"
+)
+
+func findCgroupMountpoints() (map[string]string, error) {
+ cgMounts, err := cgroups.GetCgroupMounts(false)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse cgroup information: %v", err)
+ }
+ mps := make(map[string]string)
+ for _, m := range cgMounts {
+ for _, ss := range m.Subsystems {
+ mps[ss] = m.Mountpoint
+ }
+ }
+ return mps, nil
+}
+
+// New returns a new SysInfo, using the filesystem to detect which features
+// the kernel supports. If `quiet` is `false` warnings are printed in logs
+// whenever an error occurs or misconfigurations are present.
+func New(quiet bool) *SysInfo {
+ sysInfo := &SysInfo{}
+ cgMounts, err := findCgroupMountpoints()
+ if err != nil {
+ logrus.Warnf("Failed to parse cgroup information: %v", err)
+ } else {
+ sysInfo.cgroupMemInfo = checkCgroupMem(cgMounts, quiet)
+ sysInfo.cgroupCPUInfo = checkCgroupCPU(cgMounts, quiet)
+ sysInfo.cgroupBlkioInfo = checkCgroupBlkioInfo(cgMounts, quiet)
+ sysInfo.cgroupCpusetInfo = checkCgroupCpusetInfo(cgMounts, quiet)
+ sysInfo.cgroupPids = checkCgroupPids(quiet)
+ }
+
+ _, ok := cgMounts["devices"]
+ sysInfo.CgroupDevicesEnabled = ok
+
+ sysInfo.IPv4ForwardingDisabled = !readProcBool("/proc/sys/net/ipv4/ip_forward")
+ sysInfo.BridgeNFCallIPTablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-iptables")
+ sysInfo.BridgeNFCallIP6TablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-ip6tables")
+
+ // Check if AppArmor is supported.
+ if _, err := os.Stat("/sys/kernel/security/apparmor"); !os.IsNotExist(err) {
+ sysInfo.AppArmor = true
+ }
+
+ // Check if Seccomp is supported, via CONFIG_SECCOMP.
+ if err := unix.Prctl(unix.PR_GET_SECCOMP, 0, 0, 0, 0); err != unix.EINVAL {
+ // Make sure the kernel has CONFIG_SECCOMP_FILTER.
+ if err := unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0); err != unix.EINVAL {
+ sysInfo.Seccomp = true
+ }
+ }
+
+ return sysInfo
+}
+
+// checkCgroupMem reads the memory information from the memory cgroup mount point.
+func checkCgroupMem(cgMounts map[string]string, quiet bool) cgroupMemInfo {
+ mountPoint, ok := cgMounts["memory"]
+ if !ok {
+ if !quiet {
+ logrus.Warn("Your kernel does not support cgroup memory limit")
+ }
+ return cgroupMemInfo{}
+ }
+
+ swapLimit := cgroupEnabled(mountPoint, "memory.memsw.limit_in_bytes")
+ if !quiet && !swapLimit {
+ logrus.Warn("Your kernel does not support swap memory limit")
+ }
+ memoryReservation := cgroupEnabled(mountPoint, "memory.soft_limit_in_bytes")
+ if !quiet && !memoryReservation {
+ logrus.Warn("Your kernel does not support memory reservation")
+ }
+ oomKillDisable := cgroupEnabled(mountPoint, "memory.oom_control")
+ if !quiet && !oomKillDisable {
+ logrus.Warn("Your kernel does not support oom control")
+ }
+ memorySwappiness := cgroupEnabled(mountPoint, "memory.swappiness")
+ if !quiet && !memorySwappiness {
+ logrus.Warn("Your kernel does not support memory swappiness")
+ }
+ kernelMemory := cgroupEnabled(mountPoint, "memory.kmem.limit_in_bytes")
+ if !quiet && !kernelMemory {
+ logrus.Warn("Your kernel does not support kernel memory limit")
+ }
+
+ return cgroupMemInfo{
+ MemoryLimit: true,
+ SwapLimit: swapLimit,
+ MemoryReservation: memoryReservation,
+ OomKillDisable: oomKillDisable,
+ MemorySwappiness: memorySwappiness,
+ KernelMemory: kernelMemory,
+ }
+}
+
+// checkCgroupCPU reads the cpu information from the cpu cgroup mount point.
+func checkCgroupCPU(cgMounts map[string]string, quiet bool) cgroupCPUInfo {
+ mountPoint, ok := cgMounts["cpu"]
+ if !ok {
+ if !quiet {
+ logrus.Warn("Unable to find cpu cgroup in mounts")
+ }
+ return cgroupCPUInfo{}
+ }
+
+ cpuShares := cgroupEnabled(mountPoint, "cpu.shares")
+ if !quiet && !cpuShares {
+ logrus.Warn("Your kernel does not support cgroup cpu shares")
+ }
+
+ cpuCfsPeriod := cgroupEnabled(mountPoint, "cpu.cfs_period_us")
+ if !quiet && !cpuCfsPeriod {
+ logrus.Warn("Your kernel does not support cgroup cfs period")
+ }
+
+ cpuCfsQuota := cgroupEnabled(mountPoint, "cpu.cfs_quota_us")
+ if !quiet && !cpuCfsQuota {
+ logrus.Warn("Your kernel does not support cgroup cfs quotas")
+ }
+
+ cpuRealtimePeriod := cgroupEnabled(mountPoint, "cpu.rt_period_us")
+ if !quiet && !cpuRealtimePeriod {
+ logrus.Warn("Your kernel does not support cgroup rt period")
+ }
+
+ cpuRealtimeRuntime := cgroupEnabled(mountPoint, "cpu.rt_runtime_us")
+ if !quiet && !cpuRealtimeRuntime {
+ logrus.Warn("Your kernel does not support cgroup rt runtime")
+ }
+
+ return cgroupCPUInfo{
+ CPUShares: cpuShares,
+ CPUCfsPeriod: cpuCfsPeriod,
+ CPUCfsQuota: cpuCfsQuota,
+ CPURealtimePeriod: cpuRealtimePeriod,
+ CPURealtimeRuntime: cpuRealtimeRuntime,
+ }
+}
+
+// checkCgroupBlkioInfo reads the blkio information from the blkio cgroup mount point.
+func checkCgroupBlkioInfo(cgMounts map[string]string, quiet bool) cgroupBlkioInfo {
+ mountPoint, ok := cgMounts["blkio"]
+ if !ok {
+ if !quiet {
+ logrus.Warn("Unable to find blkio cgroup in mounts")
+ }
+ return cgroupBlkioInfo{}
+ }
+
+ weight := cgroupEnabled(mountPoint, "blkio.weight")
+ if !quiet && !weight {
+ logrus.Warn("Your kernel does not support cgroup blkio weight")
+ }
+
+ weightDevice := cgroupEnabled(mountPoint, "blkio.weight_device")
+ if !quiet && !weightDevice {
+ logrus.Warn("Your kernel does not support cgroup blkio weight_device")
+ }
+
+ readBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_bps_device")
+ if !quiet && !readBpsDevice {
+ logrus.Warn("Your kernel does not support cgroup blkio throttle.read_bps_device")
+ }
+
+ writeBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_bps_device")
+ if !quiet && !writeBpsDevice {
+ logrus.Warn("Your kernel does not support cgroup blkio throttle.write_bps_device")
+ }
+ readIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_iops_device")
+ if !quiet && !readIOpsDevice {
+ logrus.Warn("Your kernel does not support cgroup blkio throttle.read_iops_device")
+ }
+
+ writeIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_iops_device")
+ if !quiet && !writeIOpsDevice {
+ logrus.Warn("Your kernel does not support cgroup blkio throttle.write_iops_device")
+ }
+ return cgroupBlkioInfo{
+ BlkioWeight: weight,
+ BlkioWeightDevice: weightDevice,
+ BlkioReadBpsDevice: readBpsDevice,
+ BlkioWriteBpsDevice: writeBpsDevice,
+ BlkioReadIOpsDevice: readIOpsDevice,
+ BlkioWriteIOpsDevice: writeIOpsDevice,
+ }
+}
+
+// checkCgroupCpusetInfo reads the cpuset information from the cpuset cgroup mount point.
+func checkCgroupCpusetInfo(cgMounts map[string]string, quiet bool) cgroupCpusetInfo {
+ mountPoint, ok := cgMounts["cpuset"]
+ if !ok {
+ if !quiet {
+ logrus.Warn("Unable to find cpuset cgroup in mounts")
+ }
+ return cgroupCpusetInfo{}
+ }
+
+ cpus, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.cpus"))
+ if err != nil {
+ return cgroupCpusetInfo{}
+ }
+
+ mems, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.mems"))
+ if err != nil {
+ return cgroupCpusetInfo{}
+ }
+
+ return cgroupCpusetInfo{
+ Cpuset: true,
+ Cpus: strings.TrimSpace(string(cpus)),
+ Mems: strings.TrimSpace(string(mems)),
+ }
+}
+
+// checkCgroupPids reads the pids information from the pids cgroup mount point.
+func checkCgroupPids(quiet bool) cgroupPids {
+ cgroup2, err := cgroupv2.Enabled()
+ if err != nil {
+ logrus.Errorf("Failed to check cgroups version: %v", err)
+ }
+ if !cgroup2 {
+ _, err := cgroups.FindCgroupMountpoint("", "pids")
+ if err != nil {
+ if !quiet {
+ logrus.Warn(err)
+ }
+ return cgroupPids{}
+ }
+ }
+
+ return cgroupPids{
+ PidsLimit: true,
+ }
+}
+
+func cgroupEnabled(mountPoint, name string) bool {
+ _, err := os.Stat(path.Join(mountPoint, name))
+ return err == nil
+}
+
+func readProcBool(path string) bool {
+ val, err := ioutil.ReadFile(path)
+ if err != nil {
+ return false
+ }
+ return strings.TrimSpace(string(val)) == "1"
+}
diff --git a/vendor/github.com/containers/common/pkg/sysinfo/sysinfo_solaris.go b/vendor/github.com/containers/common/pkg/sysinfo/sysinfo_solaris.go
new file mode 100644
index 000000000..7463cdd8f
--- /dev/null
+++ b/vendor/github.com/containers/common/pkg/sysinfo/sysinfo_solaris.go
@@ -0,0 +1,122 @@
+// +build solaris,cgo
+
+package sysinfo
+
+import (
+ "bytes"
+ "os/exec"
+ "strconv"
+ "strings"
+)
+
+/*
+#cgo LDFLAGS: -llgrp
+#cgo CFLAGS: -Wall -Werror
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/lgrp_user.h>
+int getLgrpCount() {
+ lgrp_cookie_t lgrpcookie = LGRP_COOKIE_NONE;
+ uint_t nlgrps;
+
+ if ((lgrpcookie = lgrp_init(LGRP_VIEW_OS)) == LGRP_COOKIE_NONE) {
+ return -1;
+ }
+ nlgrps = lgrp_nlgrps(lgrpcookie);
+ return nlgrps;
+}
+*/
+import "C"
+
+// IsCPUSharesAvailable returns whether CPUShares setting is supported.
+// We need FSS to be set as default scheduling class to support CPU Shares
+func IsCPUSharesAvailable() bool {
+ cmd := exec.Command("/usr/sbin/dispadmin", "-d")
+ outBuf := new(bytes.Buffer)
+ errBuf := new(bytes.Buffer)
+ cmd.Stderr = errBuf
+ cmd.Stdout = outBuf
+
+ if err := cmd.Run(); err != nil {
+ return false
+ }
+ return (strings.Contains(outBuf.String(), "FSS"))
+}
+
+// New returns a new SysInfo, using the filesystem to detect which features
+// the kernel supports.
+//NOTE Solaris: If we change the below capabilities be sure
+// to update verifyPlatformContainerSettings() in daemon_solaris.go
+func New(quiet bool) *SysInfo {
+ sysInfo := &SysInfo{}
+ sysInfo.cgroupMemInfo = setCgroupMem(quiet)
+ sysInfo.cgroupCPUInfo = setCgroupCPU(quiet)
+ sysInfo.cgroupBlkioInfo = setCgroupBlkioInfo(quiet)
+ sysInfo.cgroupCpusetInfo = setCgroupCPUsetInfo(quiet)
+
+ sysInfo.IPv4ForwardingDisabled = false
+
+ sysInfo.AppArmor = false
+
+ return sysInfo
+}
+
+// setCgroupMem reads the memory information for Solaris.
+func setCgroupMem(quiet bool) cgroupMemInfo {
+
+ return cgroupMemInfo{
+ MemoryLimit: true,
+ SwapLimit: true,
+ MemoryReservation: false,
+ OomKillDisable: false,
+ MemorySwappiness: false,
+ KernelMemory: false,
+ }
+}
+
+// setCgroupCPU reads the cpu information for Solaris.
+func setCgroupCPU(quiet bool) cgroupCPUInfo {
+
+ return cgroupCPUInfo{
+ CPUShares: true,
+ CPUCfsPeriod: false,
+ CPUCfsQuota: true,
+ CPURealtimePeriod: false,
+ CPURealtimeRuntime: false,
+ }
+}
+
+// blkio switches are not supported in Solaris.
+func setCgroupBlkioInfo(quiet bool) cgroupBlkioInfo {
+
+ return cgroupBlkioInfo{
+ BlkioWeight: false,
+ BlkioWeightDevice: false,
+ }
+}
+
+// setCgroupCPUsetInfo reads the cpuset information for Solaris.
+func setCgroupCPUsetInfo(quiet bool) cgroupCpusetInfo {
+
+ return cgroupCpusetInfo{
+ Cpuset: true,
+ Cpus: getCPUCount(),
+ Mems: getLgrpCount(),
+ }
+}
+
+func getCPUCount() string {
+ ncpus := C.sysconf(C._SC_NPROCESSORS_ONLN)
+ if ncpus <= 0 {
+ return ""
+ }
+ return strconv.FormatInt(int64(ncpus), 16)
+}
+
+func getLgrpCount() string {
+ nlgrps := C.getLgrpCount()
+ if nlgrps <= 0 {
+ return ""
+ }
+ return strconv.FormatInt(int64(nlgrps), 16)
+}
diff --git a/vendor/github.com/containers/common/pkg/sysinfo/sysinfo_unix.go b/vendor/github.com/containers/common/pkg/sysinfo/sysinfo_unix.go
new file mode 100644
index 000000000..45f3ef1c6
--- /dev/null
+++ b/vendor/github.com/containers/common/pkg/sysinfo/sysinfo_unix.go
@@ -0,0 +1,9 @@
+// +build !linux,!solaris,!windows
+
+package sysinfo
+
+// New returns an empty SysInfo for non linux nor solaris for now.
+func New(quiet bool) *SysInfo {
+ sysInfo := &SysInfo{}
+ return sysInfo
+}
diff --git a/vendor/github.com/containers/common/pkg/sysinfo/sysinfo_windows.go b/vendor/github.com/containers/common/pkg/sysinfo/sysinfo_windows.go
new file mode 100644
index 000000000..4e6255bc5
--- /dev/null
+++ b/vendor/github.com/containers/common/pkg/sysinfo/sysinfo_windows.go
@@ -0,0 +1,9 @@
+// +build windows
+
+package sysinfo
+
+// New returns an empty SysInfo for windows for now.
+func New(quiet bool) *SysInfo {
+ sysInfo := &SysInfo{}
+ return sysInfo
+}
diff --git a/vendor/github.com/containers/storage/VERSION b/vendor/github.com/containers/storage/VERSION
index de646d2fc..ec6d649be 100644
--- a/vendor/github.com/containers/storage/VERSION
+++ b/vendor/github.com/containers/storage/VERSION
@@ -1 +1 @@
-1.16.6
+1.18.1
diff --git a/vendor/github.com/containers/storage/go.mod b/vendor/github.com/containers/storage/go.mod
index 40cd856a9..b2426c9f9 100644
--- a/vendor/github.com/containers/storage/go.mod
+++ b/vendor/github.com/containers/storage/go.mod
@@ -5,12 +5,14 @@ require (
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5
github.com/Microsoft/hcsshim v0.8.7
github.com/docker/go-units v0.4.0
+ github.com/hashicorp/go-multierror v1.0.0
github.com/klauspost/compress v1.10.3
github.com/klauspost/pgzip v1.2.3
github.com/mattn/go-shellwords v1.0.10
github.com/mistifyio/go-zfs v2.1.1+incompatible
github.com/opencontainers/go-digest v1.0.0-rc1
github.com/opencontainers/runc v1.0.0-rc9
+ github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700
github.com/opencontainers/selinux v1.4.0
github.com/pkg/errors v0.9.1
github.com/pquerna/ffjson v0.0.0-20181028064349-e517b90714f7
@@ -20,7 +22,7 @@ require (
github.com/tchap/go-patricia v2.3.0+incompatible
github.com/vbatts/tar-split v0.11.1
golang.org/x/net v0.0.0-20190628185345-da137c7871d7
- golang.org/x/sys v0.0.0-20191115151921-52ab43148777
+ golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2
gotest.tools v2.2.0+incompatible
)
diff --git a/vendor/github.com/containers/storage/go.sum b/vendor/github.com/containers/storage/go.sum
index 2c53f6ba6..990cfef57 100644
--- a/vendor/github.com/containers/storage/go.sum
+++ b/vendor/github.com/containers/storage/go.sum
@@ -1,6 +1,7 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/DataDog/zstd v1.4.0/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
github.com/Microsoft/hcsshim v0.8.7 h1:ptnOoufxGSzauVTsdE+wMYnCWA301PdoN4xg5oRdZpg=
@@ -16,12 +17,19 @@ github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
+github.com/containers/common v0.5.0 h1:ZAef7h3oO46PcbTyfooZf8XLHrYad+GkhSu3EhH6P24=
+github.com/containers/common v0.5.0/go.mod h1:m62kenckrWi5rZx32kaLje2Og0hpf6NsaTBn6+b+Oys=
+github.com/containers/common v0.6.1 h1:z9VeVXYeOnNV99uNLp7zoE5KO1n0hqz1mdm5a6AiIrA=
+github.com/containers/common v0.6.1/go.mod h1:m62kenckrWi5rZx32kaLje2Og0hpf6NsaTBn6+b+Oys=
+github.com/containers/storage v1.16.0/go.mod h1:nqN09JSi1/RSI1UAUwDYXPRiGSlq5FPbNkN/xb0TfG0=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/docker/docker v0.0.0-20171019062838-86f080cff091/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
@@ -32,15 +40,22 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
+github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+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/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=
@@ -53,14 +68,23 @@ github.com/mattn/go-shellwords v1.0.10 h1:Y7Xqm8piKOO3v10Thp7Z36h4FYFjt5xB//6XvO
github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/mistifyio/go-zfs v2.1.1+incompatible h1:gAMO1HM9xBRONLHHYnu5iFsOJUiJdNZo6oqSENd4eW8=
github.com/mistifyio/go-zfs v2.1.1+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
+github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v1.0.0-rc9 h1:/k06BMULKF5hidyoZymkoDCzdJzltZpz/UU4LguQVtc=
github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
+github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700 h1:eNUVfm/RFLIi1G7flU5/ZRTHvd4kcVuzfRnL6OFlzCI=
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/runtime-spec v0.1.2-0.20190618234442-a950415649c7 h1:Dliu5QO+4JYWu/yMshaMU7G3JN2POGpwjJN7gjy10Go=
+github.com/opencontainers/runtime-spec v0.1.2-0.20190618234442-a950415649c7/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
+github.com/opencontainers/selinux v1.3.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g=
+github.com/opencontainers/selinux v1.3.2/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g=
github.com/opencontainers/selinux v1.4.0 h1:cpiX/2wWIju/6My60T6/z9CxNG7c8xTQyEmA9fChpUo=
github.com/opencontainers/selinux v1.4.0/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
@@ -77,10 +101,12 @@ github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.5.0 h1:1N5EYkVAPEywqZRJd7cwnRtCb6xJx7NH3T3WUTF980Q=
github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
@@ -103,6 +129,7 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -115,6 +142,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -123,6 +151,9 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3 h1:7TYNF4UdlohbFwpNH04CoPMp1
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777 h1:wejkGHRTr38uaKRqECZlsCsJ1/TGxIyFbH32x5zUdu4=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2 h1:/J2nHFg1MTqaRLFO7M+J78ASNsJoz3r0cvHBPQ77fsE=
+golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -130,6 +161,7 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
@@ -138,8 +170,12 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/vendor/github.com/containers/storage/layers.go b/vendor/github.com/containers/storage/layers.go
index dccfc169d..17227266e 100644
--- a/vendor/github.com/containers/storage/layers.go
+++ b/vendor/github.com/containers/storage/layers.go
@@ -281,6 +281,8 @@ func copyLayer(l *Layer) *Layer {
Flags: copyStringInterfaceMap(l.Flags),
UIDMap: copyIDMap(l.UIDMap),
GIDMap: copyIDMap(l.GIDMap),
+ UIDs: copyUint32Slice(l.UIDs),
+ GIDs: copyUint32Slice(l.GIDs),
}
}
diff --git a/vendor/github.com/containers/storage/pkg/config/config.go b/vendor/github.com/containers/storage/pkg/config/config.go
index 9e1131823..1ac6c04e3 100644
--- a/vendor/github.com/containers/storage/pkg/config/config.go
+++ b/vendor/github.com/containers/storage/pkg/config/config.go
@@ -134,6 +134,18 @@ type OptionsConfig struct {
// should be used to set up default GID mappings.
RemapGroup string `toml:"remap-group"`
+ // RootAutoUsernsUser is the name of one or more entries in /etc/subuid and
+ // /etc/subgid which should be used to set up automatically a userns.
+ RootAutoUsernsUser string `toml:"root-auto-userns-user"`
+
+ // AutoUsernsMinSize is the minimum size for a user namespace that is
+ // created automatically.
+ AutoUsernsMinSize uint32 `toml:"auto-userns-min-size"`
+
+ // AutoUsernsMaxSize is the maximum size for a user namespace that is
+ // created automatically.
+ AutoUsernsMaxSize uint32 `toml:"auto-userns-max-size"`
+
// Aufs container options to be handed to aufs drivers
Aufs struct{ AufsOptionsConfig } `toml:"aufs"`
diff --git a/vendor/github.com/containers/common/pkg/unshare/getenv_linux_cgo.go b/vendor/github.com/containers/storage/pkg/unshare/getenv_linux_cgo.go
index 4f441c32c..4f441c32c 100644
--- a/vendor/github.com/containers/common/pkg/unshare/getenv_linux_cgo.go
+++ b/vendor/github.com/containers/storage/pkg/unshare/getenv_linux_cgo.go
diff --git a/vendor/github.com/containers/common/pkg/unshare/getenv_linux_nocgo.go b/vendor/github.com/containers/storage/pkg/unshare/getenv_linux_nocgo.go
index a5005403a..a5005403a 100644
--- a/vendor/github.com/containers/common/pkg/unshare/getenv_linux_nocgo.go
+++ b/vendor/github.com/containers/storage/pkg/unshare/getenv_linux_nocgo.go
diff --git a/vendor/github.com/containers/common/pkg/unshare/unshare.c b/vendor/github.com/containers/storage/pkg/unshare/unshare.c
index fd0d48d43..8969191fa 100644
--- a/vendor/github.com/containers/common/pkg/unshare/unshare.c
+++ b/vendor/github.com/containers/storage/pkg/unshare/unshare.c
@@ -1,3 +1,5 @@
+#ifndef UNSHARE_NO_CODE_AT_ALL
+
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/ioctl.h>
@@ -285,3 +287,5 @@ void _containers_unshare(void)
}
return;
}
+
+#endif // !UNSHARE_NO_CODE_AT_ALL
diff --git a/vendor/github.com/containers/common/pkg/unshare/unshare.go b/vendor/github.com/containers/storage/pkg/unshare/unshare.go
index 1eff82e8e..1eff82e8e 100644
--- a/vendor/github.com/containers/common/pkg/unshare/unshare.go
+++ b/vendor/github.com/containers/storage/pkg/unshare/unshare.go
diff --git a/vendor/github.com/containers/common/pkg/unshare/unshare_cgo.go b/vendor/github.com/containers/storage/pkg/unshare/unshare_cgo.go
index b3f8099f6..b3f8099f6 100644
--- a/vendor/github.com/containers/common/pkg/unshare/unshare_cgo.go
+++ b/vendor/github.com/containers/storage/pkg/unshare/unshare_cgo.go
diff --git a/vendor/github.com/containers/common/pkg/unshare/unshare_gccgo.go b/vendor/github.com/containers/storage/pkg/unshare/unshare_gccgo.go
index 2f95da7d8..2f95da7d8 100644
--- a/vendor/github.com/containers/common/pkg/unshare/unshare_gccgo.go
+++ b/vendor/github.com/containers/storage/pkg/unshare/unshare_gccgo.go
diff --git a/vendor/github.com/containers/common/pkg/unshare/unshare_linux.go b/vendor/github.com/containers/storage/pkg/unshare/unshare_linux.go
index ef33ab8eb..96b857543 100644
--- a/vendor/github.com/containers/common/pkg/unshare/unshare_linux.go
+++ b/vendor/github.com/containers/storage/pkg/unshare/unshare_linux.go
@@ -31,9 +31,9 @@ type Cmd struct {
*exec.Cmd
UnshareFlags int
UseNewuidmap bool
- UidMappings []specs.LinuxIDMapping
+ UidMappings []specs.LinuxIDMapping // nolint: golint
UseNewgidmap bool
- GidMappings []specs.LinuxIDMapping
+ GidMappings []specs.LinuxIDMapping // nolint: golint
GidMappingsEnableSetgroups bool
Setsid bool
Setpgrp bool
@@ -367,7 +367,7 @@ type Runnable interface {
Run() error
}
-func bailOnError(err error, format string, a ...interface{}) {
+func bailOnError(err error, format string, a ...interface{}) { // nolint: golint,goprintffuncname
if err != nil {
if format != "" {
logrus.Errorf("%s: %v", fmt.Sprintf(format, a...), err)
diff --git a/vendor/github.com/containers/common/pkg/unshare/unshare_unsupported.go b/vendor/github.com/containers/storage/pkg/unshare/unshare_unsupported.go
index bf4d567b8..bf4d567b8 100644
--- a/vendor/github.com/containers/common/pkg/unshare/unshare_unsupported.go
+++ b/vendor/github.com/containers/storage/pkg/unshare/unshare_unsupported.go
diff --git a/vendor/github.com/containers/storage/pkg/unshare/unshare_unsupported_cgo.go b/vendor/github.com/containers/storage/pkg/unshare/unshare_unsupported_cgo.go
new file mode 100644
index 000000000..d5f2d22a8
--- /dev/null
+++ b/vendor/github.com/containers/storage/pkg/unshare/unshare_unsupported_cgo.go
@@ -0,0 +1,10 @@
+// +build !linux,cgo
+
+package unshare
+
+// Go refuses to compile a subpackage with CGO_ENABLED=1 if there is a *.c file but no 'import "C"'.
+// OTOH if we did have an 'import "C"', the Linux-only code would fail to compile.
+// So, satisfy the Go compiler by using import "C" but #ifdef-ing out all of the code.
+
+// #cgo CPPFLAGS: -DUNSHARE_NO_CODE_AT_ALL
+import "C"
diff --git a/vendor/github.com/containers/storage/storage.conf b/vendor/github.com/containers/storage/storage.conf
index 895b479de..c7f9b2cf8 100644
--- a/vendor/github.com/containers/storage/storage.conf
+++ b/vendor/github.com/containers/storage/storage.conf
@@ -43,8 +43,22 @@ additionalimagestores = [
# lowest host-level IDs first, to the lowest not-yet-mapped in-container ID,
# until all of the entries have been used for maps.
#
-# remap-user = "storage"
-# remap-group = "storage"
+# remap-user = "containers"
+# remap-group = "containers"
+
+# Root-auto-userns-user is a user name which can be used to look up one or more UID/GID
+# ranges in the /etc/subuid and /etc/subgid file. These ranges will be partioned
+# to containers configured to create automatically a user namespace. Containers
+# configured to automatically create a user namespace can still overlap with containers
+# having an explicit mapping set.
+# This setting is ignored when running as rootless.
+# root-auto-userns-user = "storage"
+#
+# Auto-userns-min-size is the minimum size for a user namespace created automatically.
+# auto-userns-min-size=1024
+#
+# Auto-userns-max-size is the minimum size for a user namespace created automatically.
+# auto-userns-max-size=65536
[storage.options.overlay]
# ignore_chown_errors can be set to allow a non privileged user running with
diff --git a/vendor/github.com/containers/storage/store.go b/vendor/github.com/containers/storage/store.go
index 9bf32cbaa..697f30b5c 100644
--- a/vendor/github.com/containers/storage/store.go
+++ b/vendor/github.com/containers/storage/store.go
@@ -26,6 +26,7 @@ import (
"github.com/containers/storage/pkg/parsers"
"github.com/containers/storage/pkg/stringid"
"github.com/containers/storage/pkg/stringutils"
+ "github.com/hashicorp/go-multierror"
digest "github.com/opencontainers/go-digest"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
@@ -152,6 +153,13 @@ type StoreOptions struct {
// for use inside of a user namespace where UID mapping is being used.
UIDMap []idtools.IDMap `json:"uidmap,omitempty"`
GIDMap []idtools.IDMap `json:"gidmap,omitempty"`
+ // RootAutoNsUser is the user used to pick a subrange when automatically setting
+ // a user namespace for the root user.
+ RootAutoNsUser string `json:"root_auto_ns_user,omitempty"`
+ // AutoNsMinSize is the minimum size for an automatic user namespace.
+ AutoNsMinSize uint32 `json:"auto_userns_min_size,omitempty"`
+ // AutoNsMaxSize is the maximum size for an automatic user namespace.
+ AutoNsMaxSize uint32 `json:"auto_userns_max_size,omitempty"`
}
// Store wraps up the various types of file-based stores that we use into a
@@ -469,6 +477,27 @@ type Store interface {
GetDigestLock(digest.Digest) (Locker, error)
}
+// AutoUserNsOptions defines how to automatically create a user namespace.
+type AutoUserNsOptions struct {
+ // Size defines the size for the user namespace. If it is set to a
+ // value bigger than 0, the user namespace will have exactly this size.
+ // If it is not set, some heuristics will be used to find its size.
+ Size uint32
+ // InitialSize defines the minimum size for the user namespace.
+ // The created user namespace will have at least this size.
+ InitialSize uint32
+ // PasswdFile to use if the container uses a volume.
+ PasswdFile string
+ // GroupFile to use if the container uses a volume.
+ GroupFile string
+ // AdditionalUIDMappings specified additional UID mappings to include in
+ // the generated user namespace.
+ AdditionalUIDMappings []idtools.IDMap
+ // AdditionalGIDMappings specified additional GID mappings to include in
+ // the generated user namespace.
+ AdditionalGIDMappings []idtools.IDMap
+}
+
// IDMappingOptions are used for specifying how ID mapping should be set up for
// a layer or container.
type IDMappingOptions struct {
@@ -485,6 +514,8 @@ type IDMappingOptions struct {
HostGIDMapping bool
UIDMap []idtools.IDMap
GIDMap []idtools.IDMap
+ AutoUserNs bool
+ AutoUserNsOpts AutoUserNsOptions
}
// LayerOptions is used for passing options to a Store's CreateLayer() and PutLayer() methods.
@@ -525,11 +556,17 @@ type store struct {
lastLoaded time.Time
runRoot string
graphLock Locker
+ usernsLock Locker
graphRoot string
graphDriverName string
graphOptions []string
uidMap []idtools.IDMap
gidMap []idtools.IDMap
+ autoUsernsUser string
+ autoUIDMap []idtools.IDMap // Set by getAvailableMappings()
+ autoGIDMap []idtools.IDMap // Set by getAvailableMappings()
+ autoNsMinSize uint32
+ autoNsMaxSize uint32
graphDriver drivers.Driver
layerStore LayerStore
roLayerStores []ROLayerStore
@@ -608,6 +645,20 @@ func GetStore(options StoreOptions) (Store, error) {
if err != nil {
return nil, err
}
+
+ usernsLock, err := GetLockfile(filepath.Join(options.GraphRoot, "userns.lock"))
+ if err != nil {
+ return nil, err
+ }
+
+ autoNsMinSize := options.AutoNsMinSize
+ autoNsMaxSize := options.AutoNsMaxSize
+ if autoNsMinSize == 0 {
+ autoNsMinSize = AutoUserNsMinSize
+ }
+ if autoNsMaxSize == 0 {
+ autoNsMaxSize = AutoUserNsMaxSize
+ }
s := &store{
runRoot: options.RunRoot,
graphLock: graphLock,
@@ -616,6 +667,12 @@ func GetStore(options StoreOptions) (Store, error) {
graphOptions: options.GraphDriverOptions,
uidMap: copyIDMap(options.UIDMap),
gidMap: copyIDMap(options.GIDMap),
+ autoUsernsUser: options.RootAutoNsUser,
+ autoNsMinSize: autoNsMinSize,
+ autoNsMaxSize: autoNsMaxSize,
+ autoUIDMap: nil,
+ autoGIDMap: nil,
+ usernsLock: usernsLock,
}
if err := s.load(); err != nil {
return nil, err
@@ -626,6 +683,18 @@ func GetStore(options StoreOptions) (Store, error) {
return s, nil
}
+func copyUint32Slice(slice []uint32) []uint32 {
+ m := []uint32{}
+ if slice != nil {
+ m = make([]uint32, len(slice))
+ copy(m, slice)
+ }
+ if len(m) > 0 {
+ return m[:]
+ }
+ return nil
+}
+
func copyIDMap(idmap []idtools.IDMap) []idtools.IDMap {
m := []idtools.IDMap{}
if idmap != nil {
@@ -1151,21 +1220,32 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat
var imageTopLayer *Layer
imageID := ""
- uidMap := options.UIDMap
- gidMap := options.GIDMap
- idMappingsOptions := options.IDMappingOptions
+ if options.AutoUserNs || options.UIDMap != nil || options.GIDMap != nil {
+ // Prevent multiple instances to retrieve the same range when AutoUserNs
+ // are used.
+ // It doesn't prevent containers that specify an explicit mapping to overlap
+ // with AutoUserNs.
+ s.usernsLock.Lock()
+ defer s.usernsLock.Unlock()
+ }
+
+ var imageHomeStore ROImageStore
+ var istore ImageStore
+ var istores []ROImageStore
+ var lstores []ROLayerStore
+ var cimage *Image
if image != "" {
- var imageHomeStore ROImageStore
- lstores, err := s.ROLayerStores()
+ var err error
+ lstores, err = s.ROLayerStores()
if err != nil {
return nil, err
}
- istore, err := s.ImageStore()
+ istore, err = s.ImageStore()
if err != nil {
return nil, err
}
- istores, err := s.ROImageStores()
+ istores, err = s.ROImageStores()
if err != nil {
return nil, err
}
@@ -1176,7 +1256,6 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat
return nil, err
}
}
- var cimage *Image
for _, s := range append([]ROImageStore{istore}, istores...) {
store := s
if store == istore {
@@ -1200,7 +1279,21 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat
return nil, errors.Wrapf(ErrImageUnknown, "error locating image with ID %q", id)
}
imageID = cimage.ID
+ }
+
+ if options.AutoUserNs {
+ var err error
+ options.UIDMap, options.GIDMap, err = s.getAutoUserNS(id, &options.AutoUserNsOpts, cimage)
+ if err != nil {
+ return nil, err
+ }
+ }
+ uidMap := options.UIDMap
+ gidMap := options.GIDMap
+
+ idMappingsOptions := options.IDMappingOptions
+ if image != "" {
if cimage.TopLayer != "" {
createMappedLayer := imageHomeStore == istore
ilayer, err := s.imageTopLayerForMapping(cimage, imageHomeStore, createMappedLayer, rlstore, lstores, idMappingsOptions)
@@ -2356,14 +2449,15 @@ func (s *store) DeleteContainer(id string) error {
close(errChan)
}()
+ var errors []error
for {
select {
case err, ok := <-errChan:
if !ok {
- return nil
+ return multierror.Append(nil, errors...).ErrorOrNil()
}
if err != nil {
- return err
+ errors = append(errors, err)
}
}
}
@@ -3305,6 +3399,16 @@ func copyStringInterfaceMap(m map[string]interface{}) map[string]interface{} {
// defaultConfigFile path to the system wide storage.conf file
const defaultConfigFile = "/etc/containers/storage.conf"
+// AutoUserNsMinSize is the minimum size for automatically created user namespaces
+const AutoUserNsMinSize = 1024
+
+// AutoUserNsMaxSize is the maximum size for automatically created user namespaces
+const AutoUserNsMaxSize = 65536
+
+// RootAutoUserNsUser is the default user used for root containers when automatically
+// creating a user namespace.
+const RootAutoUserNsUser = "containers"
+
// DefaultConfigFile returns the path to the storage config file used
func DefaultConfigFile(rootless bool) (string, error) {
if rootless {
@@ -3406,6 +3510,13 @@ func ReloadConfigurationFile(configFile string, storeOptions *StoreOptions) {
} else {
storeOptions.GIDMap = append(storeOptions.GIDMap, gidmap...)
}
+ storeOptions.RootAutoNsUser = config.Storage.Options.RootAutoUsernsUser
+ if config.Storage.Options.AutoUsernsMinSize > 0 {
+ storeOptions.AutoNsMinSize = config.Storage.Options.AutoUsernsMinSize
+ }
+ if config.Storage.Options.AutoUsernsMaxSize > 0 {
+ storeOptions.AutoNsMaxSize = config.Storage.Options.AutoUsernsMaxSize
+ }
storeOptions.GraphDriverOptions = append(storeOptions.GraphDriverOptions, cfg.GetGraphDriverOptions(storeOptions.GraphDriverName, config.Storage.Options)...)
diff --git a/vendor/github.com/containers/storage/userns.go b/vendor/github.com/containers/storage/userns.go
new file mode 100644
index 000000000..18e72efd1
--- /dev/null
+++ b/vendor/github.com/containers/storage/userns.go
@@ -0,0 +1,457 @@
+package storage
+
+import (
+ "os"
+ "os/user"
+ "path/filepath"
+ "strconv"
+
+ drivers "github.com/containers/storage/drivers"
+ "github.com/containers/storage/pkg/idtools"
+ "github.com/containers/storage/pkg/unshare"
+ libcontainerUser "github.com/opencontainers/runc/libcontainer/user"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+// getAdditionalSubIDs looks up the additional IDs configured for
+// the specified user.
+// The argument USERNAME is ignored for rootless users, as it is not
+// possible to use an arbitrary entry in /etc/sub*id.
+// Differently, if the username is not specified for root users, a
+// default name is used.
+func getAdditionalSubIDs(username string) ([]idtools.IDMap, []idtools.IDMap, error) {
+ var uids, gids []idtools.IDMap
+
+ if unshare.IsRootless() {
+ username = os.Getenv("USER")
+ if username == "" {
+ var id string
+ if os.Geteuid() == 0 {
+ id = strconv.Itoa(unshare.GetRootlessUID())
+ } else {
+ id = strconv.Itoa(os.Geteuid())
+ }
+ userID, err := user.LookupId(id)
+ if err == nil {
+ username = userID.Username
+ }
+ }
+ } else if username == "" {
+ username = RootAutoUserNsUser
+ }
+ mappings, err := idtools.NewIDMappings(username, username)
+ if err != nil {
+ logrus.Errorf("cannot find mappings for user %q: %v", username, err)
+ } else {
+ uids = mappings.UIDs()
+ gids = mappings.GIDs()
+ }
+ return uids, gids, nil
+}
+
+// getAvailableMappings returns the list of ranges that are usable by the current user.
+// When running as root, it looks up the additional IDs assigned to the specified user.
+// When running as rootless, the mappings assigned to the unprivileged user are converted
+// to the IDs inside of the initial rootless user namespace.
+func (s *store) getAvailableMappings() ([]idtools.IDMap, []idtools.IDMap, error) {
+ if s.autoUIDMap == nil {
+ uids, gids, err := getAdditionalSubIDs(s.autoUsernsUser)
+ if err != nil {
+ return nil, nil, err
+ }
+ // Store the result so we don't need to look it up again next time
+ s.autoUIDMap, s.autoGIDMap = uids, gids
+ }
+
+ uids := s.autoUIDMap
+ gids := s.autoGIDMap
+
+ if !unshare.IsRootless() {
+ // No mapping to inner namespace needed
+ return copyIDMap(uids), copyIDMap(gids), nil
+ }
+
+ // We are already inside of the rootless user namespace.
+ // We need to remap the configured mappings to what is available
+ // inside of the rootless userns.
+ totaluid := 0
+ totalgid := 0
+ for _, u := range uids {
+ totaluid += u.Size
+ }
+ for _, g := range gids {
+ totalgid += g.Size
+ }
+
+ u := []idtools.IDMap{{ContainerID: 0, HostID: 1, Size: totaluid}}
+ g := []idtools.IDMap{{ContainerID: 0, HostID: 1, Size: totalgid}}
+ return u, g, nil
+}
+
+// parseMountedFiles returns the maximum UID and GID found in the /etc/passwd and
+// /etc/group files.
+func parseMountedFiles(containerMount, passwdFile, groupFile string) uint32 {
+ if passwdFile == "" {
+ passwdFile = filepath.Join(containerMount, "etc/passwd")
+ }
+ if groupFile == "" {
+ groupFile = filepath.Join(groupFile, "etc/group")
+ }
+
+ size := 0
+
+ users, err := libcontainerUser.ParsePasswdFile(passwdFile)
+ if err == nil {
+ for _, u := range users {
+ // Skip the "nobody" user otherwise we end up with 65536
+ // ids with most images
+ if u.Name == "nobody" {
+ continue
+ }
+ if u.Uid > size {
+ size = u.Uid
+ }
+ if u.Gid > size {
+ size = u.Uid
+ }
+ }
+ }
+
+ groups, err := libcontainerUser.ParseGroupFile(groupFile)
+ if err == nil {
+ for _, g := range groups {
+ if g.Name == "nobody" {
+ continue
+ }
+ if g.Gid > size {
+ size = g.Gid
+ }
+ }
+ }
+
+ return uint32(size)
+}
+
+// getMaxSizeFromImage returns the maximum ID used by the specified image.
+// The layer stores must be already locked.
+func (s *store) getMaxSizeFromImage(id string, image *Image, passwdFile, groupFile string) (uint32, error) {
+ lstore, err := s.LayerStore()
+ if err != nil {
+ return 0, err
+ }
+ lstores, err := s.ROLayerStores()
+ if err != nil {
+ return 0, err
+ }
+
+ size := uint32(0)
+
+ var topLayer *Layer
+ layerName := image.TopLayer
+outer:
+ for {
+ for _, ls := range append([]ROLayerStore{lstore}, lstores...) {
+ layer, err := ls.Get(layerName)
+ if err != nil {
+ continue
+ }
+ if image.TopLayer == layerName {
+ topLayer = layer
+ }
+ for _, uid := range layer.UIDs {
+ if uid >= size {
+ size = uid + 1
+ }
+ }
+ for _, gid := range layer.GIDs {
+ if gid >= size {
+ size = gid + 1
+ }
+ }
+ layerName = layer.Parent
+ if layerName == "" {
+ break outer
+ }
+ continue outer
+ }
+ return 0, errors.Errorf("cannot find layer %q", layerName)
+ }
+
+ rlstore, err := s.LayerStore()
+ if err != nil {
+ return 0, err
+ }
+
+ layerOptions := &LayerOptions{
+ IDMappingOptions: IDMappingOptions{
+ HostUIDMapping: true,
+ HostGIDMapping: true,
+ UIDMap: nil,
+ GIDMap: nil,
+ },
+ }
+
+ // We need to create a temporary layer so we can mount it and lookup the
+ // maximum IDs used.
+ clayer, err := rlstore.Create(id, topLayer, nil, "", nil, layerOptions, false)
+ if err != nil {
+ return 0, err
+ }
+ defer rlstore.Delete(clayer.ID)
+
+ mountOptions := drivers.MountOpts{
+ MountLabel: "",
+ UidMaps: nil,
+ GidMaps: nil,
+ Options: nil,
+ }
+
+ mountpoint, err := rlstore.Mount(clayer.ID, mountOptions)
+ if err != nil {
+ return 0, err
+ }
+ defer rlstore.Unmount(clayer.ID, true)
+
+ userFilesSize := parseMountedFiles(mountpoint, passwdFile, groupFile)
+ if userFilesSize > size {
+ size = userFilesSize
+ }
+
+ return size, nil
+}
+
+// subtractHostIDs return the subtraction of the range USED from AVAIL. The range is specified
+// by [HostID, HostID+Size).
+// ContainerID is ignored.
+func subtractHostIDs(avail idtools.IDMap, used idtools.IDMap) []idtools.IDMap {
+ switch {
+ case used.HostID <= avail.HostID && used.HostID+used.Size >= avail.HostID+avail.Size:
+ return nil
+ case used.HostID <= avail.HostID && used.HostID+used.Size > avail.HostID && used.HostID+used.Size < avail.HostID+avail.Size:
+ newContainerID := used.HostID + used.Size
+ newHostID := used.HostID + used.Size
+ r := idtools.IDMap{
+ ContainerID: newContainerID,
+ HostID: newHostID,
+ Size: avail.Size + avail.HostID - newHostID,
+ }
+ return []idtools.IDMap{r}
+ case used.HostID > avail.HostID && used.HostID < avail.HostID+avail.Size && used.HostID+used.Size >= avail.HostID+avail.Size:
+ r := idtools.IDMap{
+ ContainerID: avail.ContainerID,
+ HostID: avail.HostID,
+ Size: used.HostID - avail.HostID,
+ }
+ return []idtools.IDMap{r}
+ case used.HostID > avail.HostID && used.HostID < avail.HostID+avail.Size && used.HostID+used.Size < avail.HostID+avail.Size:
+ r1 := idtools.IDMap{
+ ContainerID: avail.ContainerID,
+ HostID: avail.HostID,
+ Size: used.HostID - avail.HostID,
+ }
+ r2 := idtools.IDMap{
+ ContainerID: used.ContainerID + used.Size,
+ HostID: used.HostID + used.Size,
+ Size: avail.HostID + avail.Size - used.HostID - used.Size,
+ }
+ return []idtools.IDMap{r1, r2}
+ default:
+ r := idtools.IDMap{
+ ContainerID: 0,
+ HostID: avail.HostID,
+ Size: avail.Size,
+ }
+ return []idtools.IDMap{r}
+ }
+}
+
+// subtractContainerIDs return the subtraction of the range USED from AVAIL. The range is specified
+// by [ContainerID, ContainerID+Size).
+// HostID is ignored.
+func subtractContainerIDs(avail idtools.IDMap, used idtools.IDMap) []idtools.IDMap {
+ switch {
+ case used.ContainerID <= avail.ContainerID && used.ContainerID+used.Size >= avail.ContainerID+avail.Size:
+ return nil
+ case used.ContainerID <= avail.ContainerID && used.ContainerID+used.Size > avail.ContainerID && used.ContainerID+used.Size < avail.ContainerID+avail.Size:
+ newContainerID := used.ContainerID + used.Size
+ newHostID := used.HostID + used.Size
+ r := idtools.IDMap{
+ ContainerID: newContainerID,
+ HostID: newHostID,
+ Size: avail.Size + avail.ContainerID - newContainerID,
+ }
+ return []idtools.IDMap{r}
+ case used.ContainerID > avail.ContainerID && used.ContainerID < avail.ContainerID+avail.Size && used.ContainerID+used.Size >= avail.ContainerID+avail.Size:
+ r := idtools.IDMap{
+ ContainerID: avail.ContainerID,
+ HostID: avail.HostID,
+ Size: used.ContainerID - avail.ContainerID,
+ }
+ return []idtools.IDMap{r}
+ case used.ContainerID > avail.ContainerID && used.ContainerID < avail.ContainerID+avail.Size && used.ContainerID+used.Size < avail.ContainerID+avail.Size:
+ r1 := idtools.IDMap{
+ ContainerID: avail.ContainerID,
+ HostID: avail.HostID,
+ Size: used.ContainerID - avail.ContainerID,
+ }
+ r2 := idtools.IDMap{
+ ContainerID: used.ContainerID + used.Size,
+ HostID: used.HostID + used.Size,
+ Size: avail.ContainerID + avail.Size - used.ContainerID - used.Size,
+ }
+ return []idtools.IDMap{r1, r2}
+ default:
+ r := idtools.IDMap{
+ ContainerID: avail.ContainerID,
+ HostID: avail.HostID,
+ Size: avail.Size,
+ }
+ return []idtools.IDMap{r}
+ }
+}
+
+// subtractAll subtracts all usedIDs from the available IDs.
+func subtractAll(availableIDs, usedIDs []idtools.IDMap, host bool) []idtools.IDMap {
+ for _, u := range usedIDs {
+ for i := 0; i < len(availableIDs); {
+ var prev []idtools.IDMap
+ if i > 0 {
+ prev = availableIDs[:i-1]
+ }
+ next := availableIDs[i+1:]
+ cur := availableIDs[i]
+ var newRanges []idtools.IDMap
+ if host {
+ newRanges = subtractHostIDs(cur, u)
+ } else {
+ newRanges = subtractContainerIDs(cur, u)
+ }
+ availableIDs = append(append(prev, newRanges...), next...)
+ i += len(newRanges)
+ }
+ }
+ return availableIDs
+}
+
+// findAvailableIDRange returns the list of IDs that are not used by existing containers.
+// This function is used to lookup both UIDs and GIDs.
+func findAvailableIDRange(size uint32, availableIDs, usedIDs []idtools.IDMap) ([]idtools.IDMap, error) {
+ var avail []idtools.IDMap
+
+ // ContainerID will be adjusted later.
+ for _, i := range availableIDs {
+ n := idtools.IDMap{
+ ContainerID: 0,
+ HostID: i.HostID,
+ Size: i.Size,
+ }
+ avail = append(avail, n)
+ }
+ avail = subtractAll(avail, usedIDs, true)
+
+ currentID := 0
+ remaining := size
+ // We know the size for each intervals, let's adjust the ContainerID for each
+ // of them.
+ for i := 0; i < len(avail); i++ {
+ avail[i].ContainerID = currentID
+ if uint32(avail[i].Size) >= remaining {
+ avail[i].Size = int(remaining)
+ return avail[:i+1], nil
+ }
+ remaining -= uint32(avail[i].Size)
+ }
+
+ return nil, errors.New("could not find enough available IDs")
+}
+
+// findAvailableRange returns both the list of UIDs and GIDs ranges that are not
+// currently used by other containers.
+// It is a wrapper for findAvailableIDRange.
+func findAvailableRange(sizeUID, sizeGID uint32, availableUIDs, availableGIDs, usedUIDs, usedGIDs []idtools.IDMap) ([]idtools.IDMap, []idtools.IDMap, error) {
+ UIDMap, err := findAvailableIDRange(sizeUID, availableUIDs, usedUIDs)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ GIDMap, err := findAvailableIDRange(sizeGID, availableGIDs, usedGIDs)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return UIDMap, GIDMap, nil
+}
+
+// getAutoUserNS creates an automatic user namespace
+func (s *store) getAutoUserNS(id string, options *AutoUserNsOptions, image *Image) ([]idtools.IDMap, []idtools.IDMap, error) {
+ requestedSize := uint32(0)
+ initialSize := uint32(1)
+ if options.Size > 0 {
+ requestedSize = options.Size
+ }
+ if options.InitialSize > 0 {
+ initialSize = options.InitialSize
+ }
+
+ availableUIDs, availableGIDs, err := s.getAvailableMappings()
+ if err != nil {
+ return nil, nil, errors.Wrapf(err, "cannot read mappings")
+ }
+
+ // Look every container that is using a user namespace and store
+ // the intervals that are already used.
+ containers, err := s.Containers()
+ if err != nil {
+ return nil, nil, err
+ }
+ var usedUIDs, usedGIDs []idtools.IDMap
+ for _, c := range containers {
+ usedUIDs = append(usedUIDs, c.UIDMap...)
+ usedGIDs = append(usedGIDs, c.GIDMap...)
+ }
+
+ size := requestedSize
+
+ // If there is no requestedSize, lookup the maximum used IDs in the layers
+ // metadata. Make sure the size is at least s.autoNsMinSize and it is not
+ // bigger than s.autoNsMaxSize.
+ // This is a best effort heuristic.
+ if requestedSize == 0 {
+ size = initialSize
+ if s.autoNsMinSize > size {
+ size = s.autoNsMinSize
+ }
+ if image != nil {
+ sizeFromImage, err := s.getMaxSizeFromImage(id, image, options.PasswdFile, options.GroupFile)
+ if err != nil {
+ return nil, nil, err
+ }
+ if sizeFromImage > size {
+ size = sizeFromImage
+ }
+ }
+ if s.autoNsMaxSize > 0 && size > s.autoNsMaxSize {
+ return nil, nil, errors.Errorf("the container needs a user namespace with size %q that is bigger than the maximum value allowed with userns=auto %q", size, s.autoNsMaxSize)
+ }
+ }
+ // Make sure the specified additional IDs are not used as part of the automatic
+ // mapping
+ usedUIDs = append(usedUIDs, options.AdditionalUIDMappings...)
+ usedGIDs = append(usedGIDs, options.AdditionalGIDMappings...)
+ availableUIDs, availableGIDs, err = findAvailableRange(size, size, availableUIDs, availableGIDs, usedUIDs, usedGIDs)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // We need to make sure the specified container IDs are also dropped from the automatic
+ // namespaces we have found.
+ if len(options.AdditionalUIDMappings) > 0 {
+ availableUIDs = subtractAll(availableUIDs, options.AdditionalUIDMappings, false)
+ }
+ if len(options.AdditionalGIDMappings) > 0 {
+ availableGIDs = subtractAll(availableGIDs, options.AdditionalGIDMappings, false)
+ }
+
+ return append(availableUIDs, options.AdditionalUIDMappings...), append(availableGIDs, options.AdditionalGIDMappings...), nil
+}
diff --git a/vendor/github.com/openshift/imagebuilder/builder.go b/vendor/github.com/openshift/imagebuilder/builder.go
index 81d7b8421..7f2f6e482 100644
--- a/vendor/github.com/openshift/imagebuilder/builder.go
+++ b/vendor/github.com/openshift/imagebuilder/builder.go
@@ -209,12 +209,8 @@ func NewStages(node *parser.Node, b *Builder) (Stages, error) {
stages = append(stages, Stage{
Position: i,
Name: name,
- Builder: &Builder{
- Args: b.Args,
- AllowedArgs: b.AllowedArgs,
- Env: b.Env,
- },
- Node: root,
+ Builder: b.builderForStage(),
+ Node: root,
})
}
return stages, nil
@@ -235,17 +231,30 @@ func (b *Builder) extractHeadingArgsFromNode(node *parser.Node) error {
}
}
+ // Set children equal to everything except the leading ARG nodes
+ node.Children = children
+
+ // Use a separate builder to evaluate the heading args
+ tempBuilder := NewBuilder(b.UserArgs)
+
+ // Evaluate all the heading arg commands
for _, c := range args {
- step := b.Step()
+ step := tempBuilder.Step()
if err := step.Resolve(c); err != nil {
return err
}
- if err := b.Run(step, NoopExecutor, false); err != nil {
+ if err := tempBuilder.Run(step, NoopExecutor, false); err != nil {
return err
}
}
- node.Children = children
+ // Add all of the defined heading args to the original builder's HeadingArgs map
+ for k, v := range tempBuilder.Args {
+ if _, ok := tempBuilder.AllowedArgs[k]; ok {
+ b.HeadingArgs[k] = v
+ }
+ }
+
return nil
}
@@ -264,13 +273,23 @@ func extractNameFromNode(node *parser.Node) (string, bool) {
return n.Next.Value, true
}
+func (b *Builder) builderForStage() *Builder {
+ stageBuilder := NewBuilder(b.UserArgs)
+ for k, v := range b.HeadingArgs {
+ stageBuilder.HeadingArgs[k] = v
+ }
+ return stageBuilder
+}
+
type Builder struct {
RunConfig docker.Config
- Env []string
- Args map[string]string
- CmdSet bool
- Author string
+ Env []string
+ Args map[string]string
+ HeadingArgs map[string]string
+ UserArgs map[string]string
+ CmdSet bool
+ Author string
AllowedArgs map[string]bool
Volumes VolumeSet
@@ -288,12 +307,16 @@ func NewBuilder(args map[string]string) *Builder {
for k, v := range builtinAllowedBuildArgs {
allowed[k] = v
}
- provided := make(map[string]string)
+ userArgs := make(map[string]string)
+ initialArgs := make(map[string]string)
for k, v := range args {
- provided[k] = v
+ userArgs[k] = v
+ initialArgs[k] = v
}
return &Builder{
- Args: provided,
+ Args: initialArgs,
+ UserArgs: userArgs,
+ HeadingArgs: make(map[string]string),
AllowedArgs: allowed,
}
}
diff --git a/vendor/github.com/openshift/imagebuilder/dispatchers.go b/vendor/github.com/openshift/imagebuilder/dispatchers.go
index e7f2f97bf..1d77a193b 100644
--- a/vendor/github.com/openshift/imagebuilder/dispatchers.go
+++ b/vendor/github.com/openshift/imagebuilder/dispatchers.go
@@ -216,7 +216,7 @@ func from(b *Builder, args []string, attributes map[string]bool, flagArgs []stri
// Support ARG before from
argStrs := []string{}
- for n, v := range b.Args {
+ for n, v := range b.HeadingArgs {
argStrs = append(argStrs, n+"="+v)
}
var err error
@@ -598,10 +598,16 @@ func arg(b *Builder, args []string, attributes map[string]bool, flagArgs []strin
// add the arg to allowed list of build-time args from this step on.
b.AllowedArgs[name] = true
+ // If there is still no default value, a value can be assigned from the heading args
+ if val, ok := b.HeadingArgs[name]; ok && !hasDefault {
+ b.Args[name] = val
+ }
+
// If there is a default value associated with this arg then add it to the
- // b.buildArgs if one is not already passed to the builder. The args passed
- // to builder override the default value of 'arg'.
- if _, ok := b.Args[name]; !ok && hasDefault {
+ // b.buildArgs, later default values for the same arg override earlier ones.
+ // The args passed to builder (UserArgs) override the default value of 'arg'
+ // Don't add them here as they were already set in NewBuilder.
+ if _, ok := b.UserArgs[name]; !ok && hasDefault {
b.Args[name] = value
}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index a1c28b023..f474a12cf 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -64,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.5
+# github.com/containers/buildah v1.14.6-0.20200402210551-e9a6703edee2
github.com/containers/buildah
github.com/containers/buildah/bind
github.com/containers/buildah/chroot
@@ -82,10 +82,12 @@ github.com/containers/buildah/pkg/secrets
github.com/containers/buildah/pkg/supplemented
github.com/containers/buildah/pkg/umask
github.com/containers/buildah/util
-# github.com/containers/common v0.6.1
+# github.com/containers/common v0.8.0
+github.com/containers/common/pkg/apparmor
github.com/containers/common/pkg/capabilities
+github.com/containers/common/pkg/cgroupv2
github.com/containers/common/pkg/config
-github.com/containers/common/pkg/unshare
+github.com/containers/common/pkg/sysinfo
# github.com/containers/conmon v2.0.14+incompatible
github.com/containers/conmon/runner/config
# github.com/containers/image/v5 v5.3.1
@@ -148,7 +150,7 @@ github.com/containers/psgo/internal/dev
github.com/containers/psgo/internal/host
github.com/containers/psgo/internal/proc
github.com/containers/psgo/internal/process
-# github.com/containers/storage v1.16.6
+# github.com/containers/storage v1.18.1
github.com/containers/storage
github.com/containers/storage/drivers
github.com/containers/storage/drivers/aufs
@@ -188,6 +190,7 @@ github.com/containers/storage/pkg/stringutils
github.com/containers/storage/pkg/system
github.com/containers/storage/pkg/tarlog
github.com/containers/storage/pkg/truncindex
+github.com/containers/storage/pkg/unshare
# github.com/coreos/go-iptables v0.4.5
github.com/coreos/go-iptables/iptables
# github.com/coreos/go-systemd/v22 v22.0.0
@@ -414,7 +417,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.3
+# github.com/openshift/imagebuilder v1.1.4
github.com/openshift/imagebuilder
github.com/openshift/imagebuilder/dockerfile/command
github.com/openshift/imagebuilder/dockerfile/parser