diff options
362 files changed, 6776 insertions, 1979 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index cbe8bc757..9233d9668 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -30,7 +30,7 @@ env: PRIOR_UBUNTU_NAME: "ubuntu-2004" # Google-cloud VM Images - IMAGE_SUFFIX: "c6233039174893568" + IMAGE_SUFFIX: "c6524344056676352" FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}" PRIOR_FEDORA_CACHE_IMAGE_NAME: "prior-fedora-${IMAGE_SUFFIX}" UBUNTU_CACHE_IMAGE_NAME: "ubuntu-${IMAGE_SUFFIX}" @@ -67,6 +67,7 @@ gcp_credentials: ENCRYPTED[a28959877b2c9c36f151781b0a05407218cda646c7d047fc556e4 ext_svc_check_task: alias: 'ext_svc_check' # int. ref. name - required for depends_on reference name: "Ext. services" # Displayed Title - has no other significance + skip: &tags "$CIRRUS_TAG != ''" # Don't run on tags # Default/small container image to execute tasks with container: &smallcontainer image: ${CTR_FQIN} @@ -90,7 +91,7 @@ ext_svc_check_task: automation_task: alias: 'automation' name: "Check Automation" - skip: &branch "$CIRRUS_PR == '' || $CIRRUS_TAG != ''" # Don't run for branches + skip: &branches_and_tags "$CIRRUS_PR == '' || $CIRRUS_TAG != ''" # Don't run on branches/tags container: *smallcontainer env: TEST_FLAVOR: automation @@ -107,6 +108,7 @@ automation_task: smoke_task: alias: 'smoke' name: "Smoke Test" + skip: *tags container: &bigcontainer image: ${CTR_FQIN} # Leave some resources for smallcontainer @@ -209,6 +211,7 @@ build_task: validate_task: name: "Validate $DISTRO_NV Build" alias: validate + skip: *tags depends_on: - ext_svc_check - automation @@ -237,7 +240,7 @@ bindings_task: name: "Test Bindings" alias: bindings only_if: ¬_docs $CIRRUS_CHANGE_TITLE !=~ '.*CI:DOCS.*' - skip: *branch + skip: *branches_and_tags depends_on: - build gce_instance: *standardvm @@ -275,6 +278,7 @@ swagger_task: vendor_task: name: "Test Vendoring" alias: vendor + skip: *tags depends_on: - build container: *smallcontainer @@ -311,6 +315,10 @@ alt_build_task: ALT_NAME: 'Build Without CGO' - env: ALT_NAME: 'Test build RPM' + - env: + ALT_NAME: 'Alt Arch. Cross' + gopath_cache: *ro_gopath_cache + clone_script: *noop # Comes from cache setup_script: *setup main_script: *main always: *binary_artifacts @@ -373,6 +381,7 @@ osx_alt_build_task: docker-py_test_task: name: Docker-py Compat. alias: docker-py_test + skip: *tags only_if: *not_docs depends_on: - build @@ -393,6 +402,7 @@ docker-py_test_task: unit_test_task: name: "Unit tests on $DISTRO_NV" alias: unit_test + skip: *tags only_if: *not_docs depends_on: - validate @@ -410,6 +420,7 @@ unit_test_task: apiv2_test_task: name: "APIv2 test on $DISTRO_NV" alias: apiv2_test + skip: *tags depends_on: - validate gce_instance: *standardvm @@ -437,6 +448,7 @@ apiv2_test_task: compose_test_task: name: "compose test on $DISTRO_NV" alias: compose_test + skip: *tags depends_on: - validate gce_instance: *standardvm @@ -458,7 +470,7 @@ local_integration_test_task: &local_integration_test_task name: &std_name_fmt "$TEST_FLAVOR $PODBIN_NAME $DISTRO_NV $PRIV_NAME $TEST_ENVIRON" alias: local_integration_test only_if: *not_docs - skip: *branch + skip: *branches_and_tags depends_on: - unit_test matrix: *platform_axis @@ -490,7 +502,7 @@ container_integration_test_task: name: *std_name_fmt alias: container_integration_test only_if: *not_docs - skip: *branch + skip: *branches_and_tags depends_on: - unit_test matrix: &fedora_vm_axis @@ -521,7 +533,7 @@ rootless_integration_test_task: name: *std_name_fmt alias: rootless_integration_test only_if: *not_docs - skip: *branch + skip: *branches_and_tags depends_on: - unit_test matrix: *fedora_vm_axis @@ -544,6 +556,7 @@ rootless_integration_test_task: local_system_test_task: &local_system_test_task name: *std_name_fmt alias: local_system_test + skip: *tags only_if: *not_docs depends_on: - local_integration_test @@ -571,6 +584,7 @@ remote_system_test_task: rootless_system_test_task: name: *std_name_fmt alias: rootless_system_test + skip: *tags only_if: *not_docs depends_on: - rootless_integration_test @@ -656,7 +670,7 @@ success_task: release_task: name: "Verify Release" alias: release - only_if: $CIRRUS_TAG != '' + only_if: *tags depends_on: - success gce_instance: *standardvm @@ -671,7 +685,9 @@ release_task: # When preparing to release a new version, this task may be manually -# activated at the PR stage to verify the code is in a proper state. +# activated at the PR stage to verify the build is proper for a potential +# podman release. +# # Note: This cannot use a YAML alias on 'release_task' as of this # comment, it is incompatible with 'trigger_type: manual' release_test_task: diff --git a/.gitignore b/.gitignore index 208a71f37..6a5ae509c 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,10 @@ release.txt /test/checkseccomp/checkseccomp /test/copyimg/copyimg /test/goecho/goecho +/test/testvol/testvol .vscode* result # Necessary to prevent hack/tree-status.sh false-positive /*runner_stats.log +.install.goimports +.generate-bindings diff --git a/Containerfile-testvol b/Containerfile-testvol new file mode 100644 index 000000000..6ff45064b --- /dev/null +++ b/Containerfile-testvol @@ -0,0 +1,10 @@ +FROM golang:1.15-alpine AS build-img +COPY ./test/testvol/ /go/src/github.com/containers/podman/cmd/testvol/ +COPY ./vendor /go/src/github.com/containers/podman/vendor/ +WORKDIR /go/src/github.com/containers/podman +RUN go build -o /testvol ./cmd/testvol + +FROM alpine +COPY --from=build-img /testvol /usr/local/bin +WORKDIR / +ENTRYPOINT ["/usr/local/bin/testvol"] @@ -106,7 +106,11 @@ CROSS_BUILD_TARGETS := \ bin/podman.cross.linux.arm \ bin/podman.cross.linux.arm64 \ bin/podman.cross.linux.386 \ - bin/podman.cross.linux.s390x + bin/podman.cross.linux.s390x \ + bin/podman.cross.linux.mips \ + bin/podman.cross.linux.mipsle \ + bin/podman.cross.linux.mips64 \ + bin/podman.cross.linux.mips64le .PHONY: all all: binaries docs @@ -176,6 +180,14 @@ gofmt: ## Verify the source code gofmt test/checkseccomp/checkseccomp: .gopathok $(wildcard test/checkseccomp/*.go) $(GO) build $(BUILDFLAGS) -ldflags '$(LDFLAGS_PODMAN)' -tags "$(BUILDTAGS)" -o $@ ./test/checkseccomp +.PHONY: test/testvol/testvol +test/testvol/testvol: .gopathok $(wildcard test/testvol/*.go) + $(GO) build $(BUILDFLAGS) -ldflags '$(LDFLAGS_PODMAN)' -o $@ ./test/testvol + +.PHONY: volume-plugin-test-image +volume-plugin-test-img: + podman build -t quay.io/libpod/volume-plugin-test-img -f Containerfile-testvol . + .PHONY: test/goecho/goecho test/goecho/goecho: .gopathok $(wildcard test/goecho/*.go) $(GO) build $(BUILDFLAGS) -ldflags '$(LDFLAGS_PODMAN)' -o $@ ./test/goecho @@ -194,7 +206,7 @@ endif podman: bin/podman .PHONY: bin/podman-remote -bin/podman-remote: .gopathok $(SOURCES) go.mod go.sum ## Build with podman on remote environment +bin/podman-remote: .gopathok .generate-bindings $(SOURCES) go.mod go.sum ## Build with podman on remote environment $(GO) build $(BUILDFLAGS) -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags "${REMOTETAGS}" -o $@ ./cmd/podman .PHONY: bin/podman-remote-static @@ -221,7 +233,7 @@ bin/podman.cross.%: .gopathok TARGET="$*"; \ GOOS="$${TARGET%%.*}" \ GOARCH="$${TARGET##*.}" \ - $(GO) build $(BUILDFLAGS) -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags '$(BUILDTAGS_CROSS)' -o "$@" ./cmd/podman + CGO_ENABLED=0 $(GO) build $(BUILDFLAGS) -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags '$(BUILDTAGS_CROSS)' -o "$@" ./cmd/podman # Update nix/nixpkgs.json its latest stable commit .PHONY: nixpkgs @@ -264,6 +276,8 @@ clean: ## Clean artifacts libpod/pod_ffjson.go \ libpod/container_easyjson.go \ libpod/pod_easyjson.go \ + .install.goimports \ + .generate-bindings \ docs/build make -C docs clean @@ -403,7 +417,7 @@ release.txt: # X-RELEASE-INFO format depended upon by automated tooling echo -n "X-RELEASE-INFO:" > "$@" for field in "$(RELEASE_BASENAME)" "$(RELEASE_VERSION)" \ - "$(RELEASE_DIST)" "$(RELEASE_DIST_VER)" "$(RELEASE_ARCH)"; do \ + "$(RELEASE_DIST)" "$(RELEASE_DIST_VER)" "$(RELEASE_ARCH)"; do \ echo -n " $$field"; done >> "$@" echo "" >> "$@" @@ -445,6 +459,21 @@ podman-remote-%-release: rm -f release.txt $(MAKE) podman-remote-release-$*.zip +BINDINGS_SOURCE = $(wildcard pkg/bindings/**/types.go) +.generate-bindings: $(BINDINGS_SOURCE) +ifneq ($(shell uname -s), Darwin) + for i in $(BINDINGS_SOURCE); do \ + dirname=$$(dirname $${i}); \ + shortname=$$(basename $${dirname}); \ + pushd $${dirname}>/dev/null; \ + echo $${dirname}; \ + echo $(GO) generate; \ + $(GO) generate; \ + popd > /dev/null; \ + done; +endif + touch .generate-bindings + .PHONY: docker-docs docker-docs: docs (cd docs; ./dckrman.sh ./build/man/*.1) @@ -561,13 +590,19 @@ uninstall: GIT_CHECK_EXCLUDE="./vendor:docs/make.bat" $(GOBIN)/git-validation -run DCO,short-subject,dangling-whitespace -range $(EPOCH_TEST_COMMIT)..$(HEAD) .PHONY: install.tools -install.tools: .install.gitvalidation .install.md2man .install.ginkgo .install.golangci-lint .install.bats ## Install needed tools +install.tools: .install.goimports .install.gitvalidation .install.md2man .install.ginkgo .install.golangci-lint .install.bats ## Install needed tools define go-get env GO111MODULE=off \ $(GO) get -u ${1} endef +.install.goimports: .gopathok + if [ ! -x "$(GOBIN)/goimports" ]; then \ + $(call go-get,golang.org/x/tools/cmd/goimports); \ + fi + touch .install.goimports + .PHONY: .install.ginkgo .install.ginkgo: .gopathok if [ ! -x "$(GOBIN)/ginkgo" ]; then \ @@ -71,20 +71,6 @@ A little configuration by an administrator is required before rootless Podman ca See [Skopeo](https://github.com/containers/skopeo/) for those tasks. * Support for the Kubernetes CRI interface for container management. The [CRI-O](https://github.com/cri-o/cri-o) daemon specializes in that. -* Supporting `docker-compose`. We believe that Kubernetes is the defacto - standard for composing Pods and for orchestrating containers, making - Kubernetes YAML a defacto standard file format. Hence, Podman allows the - creation and execution of Pods from a Kubernetes YAML file (see - [podman-play-kube](https://github.com/containers/podman/blob/master/docs/source/markdown/podman-play-kube.1.md)). - Podman can also generate Kubernetes YAML based on a container or Pod (see - [podman-generate-kube](https://github.com/containers/podman/blob/master/docs/source/markdown/podman-generate-kube.1.md)), - which allows for an easy transition from a local development environment - to a production Kubernetes cluster. If Kubernetes does not fit your requirements, - there are other third-party tools that support the docker-compose format such as - [kompose](https://github.com/kubernetes/kompose/) and - [podman-compose](https://github.com/muayyad-alsadi/podman-compose) - that might be appropriate for your environment. This situation may change with - the addition of the REST API. ## OCI Projects Plans diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index 83fe0723c..d01842998 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -982,9 +982,10 @@ func AutocompletePsFilters(cmd *cobra.Command, args []string, toComplete string) return []string{define.HealthCheckHealthy, define.HealthCheckUnhealthy}, cobra.ShellCompDirectiveNoFileComp }, - "label=": nil, - "exited=": nil, - "until=": nil, + "network=": func(s string) ([]string, cobra.ShellCompDirective) { return getNetworks(cmd, s) }, + "label=": nil, + "exited=": nil, + "until=": nil, } return completeKeyValues(toComplete, kv) } @@ -1004,7 +1005,8 @@ func AutocompletePodPsFilters(cmd *cobra.Command, args []string, toComplete stri "ctr-status=": func(_ string) ([]string, cobra.ShellCompDirective) { return containerStatuses, cobra.ShellCompDirectiveNoFileComp }, - "label=": nil, + "network=": func(s string) ([]string, cobra.ShellCompDirective) { return getNetworks(cmd, s) }, + "label=": nil, } return completeKeyValues(toComplete, kv) } diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go index bbd4f6bae..280175f95 100644 --- a/cmd/podman/common/create.go +++ b/cmd/podman/common/create.go @@ -1,7 +1,6 @@ package common import ( - "fmt" "os" "github.com/containers/common/pkg/auth" @@ -181,7 +180,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { createFlags.StringSliceVar( &cf.Devices, deviceFlagName, devices(), - fmt.Sprintf("Add a host device to the container"), + "Add a host device to the container", ) _ = cmd.RegisterFlagCompletionFunc(deviceFlagName, completion.AutocompleteDefault) @@ -336,7 +335,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { createFlags.BoolVar( &cf.HTTPProxy, - "http-proxy", true, + "http-proxy", containerConfig.Containers.HTTPProxy, "Set proxy environment variables in the container based on the host proxy vars", ) @@ -359,7 +358,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { &cf.InitPath, initPathFlagName, initPath(), // 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"), + "Path to the container-init binary", ) _ = cmd.RegisterFlagCompletionFunc(initPathFlagName, completion.AutocompleteDefault) diff --git a/cmd/podman/common/volumes.go b/cmd/podman/common/volumes.go index dfbb7b1b2..a6e6faeca 100644 --- a/cmd/podman/common/volumes.go +++ b/cmd/podman/common/volumes.go @@ -5,7 +5,7 @@ import ( "path/filepath" "strings" - "github.com/containers/buildah/pkg/parse" + "github.com/containers/common/pkg/parse" "github.com/containers/podman/v2/pkg/specgen" "github.com/containers/podman/v2/pkg/util" spec "github.com/opencontainers/runtime-spec/specs-go" diff --git a/cmd/podman/containers/checkpoint.go b/cmd/podman/containers/checkpoint.go index b6dc21348..14abfd5a7 100644 --- a/cmd/podman/containers/checkpoint.go +++ b/cmd/podman/containers/checkpoint.go @@ -57,6 +57,10 @@ func init() { _ = checkpointCommand.RegisterFlagCompletionFunc(exportFlagName, completion.AutocompleteDefault) flags.BoolVar(&checkpointOptions.IgnoreRootFS, "ignore-rootfs", false, "Do not include root file-system changes when exporting") + flags.BoolVar(&checkpointOptions.IgnoreVolumes, "ignore-volumes", false, "Do not export volumes associated with container") + flags.BoolVarP(&checkpointOptions.PreCheckPoint, "pre-checkpoint", "P", false, "Dump container's memory information only, leave the container running") + flags.BoolVar(&checkpointOptions.WithPrevious, "with-previous", false, "Checkpoint container with pre-checkpoint images") + validate.AddLatestFlag(checkpointCommand, &checkpointOptions.Latest) } @@ -68,6 +72,12 @@ func checkpoint(cmd *cobra.Command, args []string) error { if checkpointOptions.Export == "" && checkpointOptions.IgnoreRootFS { return errors.Errorf("--ignore-rootfs can only be used with --export") } + if checkpointOptions.Export == "" && checkpointOptions.IgnoreVolumes { + return errors.Errorf("--ignore-volumes can only be used with --export") + } + if checkpointOptions.WithPrevious && checkpointOptions.PreCheckPoint { + return errors.Errorf("--with-previous can not be used with --pre-checkpoint") + } responses, err := registry.ContainerEngine().ContainerCheckpoint(context.Background(), args, checkpointOptions) if err != nil { return err diff --git a/cmd/podman/containers/prune.go b/cmd/podman/containers/prune.go index d3842778b..50731dd21 100644 --- a/cmd/podman/containers/prune.go +++ b/cmd/podman/containers/prune.go @@ -18,9 +18,9 @@ import ( ) var ( - pruneDescription = fmt.Sprintf(`podman container prune + pruneDescription = `podman container prune - Removes all non running containers`) + Removes all non running containers` pruneCommand = &cobra.Command{ Use: "prune [options]", Short: "Remove all non running containers", diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go index 5d08e6163..d23771fc5 100644 --- a/cmd/podman/containers/ps.go +++ b/cmd/podman/containers/ps.go @@ -392,6 +392,11 @@ func (l psReporter) Names() string { return l.ListContainer.Names[0] } +// Networks returns the container network names in string format +func (l psReporter) Networks() string { + return strings.Join(l.ListContainer.Networks, ",") +} + // Ports converts from Portmappings to the string form // required by ps func (l psReporter) Ports() string { diff --git a/cmd/podman/containers/rename.go b/cmd/podman/containers/rename.go new file mode 100644 index 000000000..b6c4f792c --- /dev/null +++ b/cmd/podman/containers/rename.go @@ -0,0 +1,55 @@ +package containers + +import ( + "github.com/containers/podman/v2/cmd/podman/common" + "github.com/containers/podman/v2/cmd/podman/registry" + "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + renameDescription = "The podman rename command allows you to rename an existing container" + renameCommand = &cobra.Command{ + Use: "rename CONTAINER NAME", + Short: "Rename an existing container", + Long: renameDescription, + RunE: rename, + Args: cobra.ExactArgs(2), + ValidArgsFunction: common.AutocompletePortCommand, + Example: "podman rename containerA newName", + } + + containerRenameCommand = &cobra.Command{ + Use: renameCommand.Use, + Short: renameCommand.Short, + Long: renameCommand.Long, + RunE: renameCommand.RunE, + Args: renameCommand.Args, + ValidArgsFunction: renameCommand.ValidArgsFunction, + Example: "podman container rename containerA newName", + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: renameCommand, + }) + + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: containerRenameCommand, + Parent: containerCmd, + }) +} + +func rename(cmd *cobra.Command, args []string) error { + if len(args) > 2 { + return errors.Errorf("must provide at least two arguments to rename") + } + renameOpts := entities.ContainerRenameOptions{ + NewName: args[1], + } + return registry.ContainerEngine().ContainerRename(registry.GetContext(), args[0], renameOpts) +} diff --git a/cmd/podman/containers/restore.go b/cmd/podman/containers/restore.go index 6a1d2b319..49c0be88e 100644 --- a/cmd/podman/containers/restore.go +++ b/cmd/podman/containers/restore.go @@ -59,9 +59,14 @@ func init() { flags.StringVarP(&restoreOptions.Name, nameFlagName, "n", "", "Specify new name for container restored from exported checkpoint (only works with --import)") _ = restoreCommand.RegisterFlagCompletionFunc(nameFlagName, completion.AutocompleteNone) + importPreviousFlagName := "import-previous" + flags.StringVar(&restoreOptions.ImportPrevious, importPreviousFlagName, "", "Restore from exported pre-checkpoint archive (tar.gz)") + _ = restoreCommand.RegisterFlagCompletionFunc(importPreviousFlagName, completion.AutocompleteDefault) + 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") + flags.BoolVar(&restoreOptions.IgnoreVolumes, "ignore-volumes", false, "Do not export volumes associated with container") validate.AddLatestFlag(restoreCommand, &restoreOptions.Latest) } @@ -70,9 +75,15 @@ func restore(_ *cobra.Command, args []string) error { if rootless.IsRootless() { return errors.New("restoring a container requires root") } + if restoreOptions.Import == "" && restoreOptions.ImportPrevious != "" { + return errors.Errorf("--import-previous can only be used with --import") + } if restoreOptions.Import == "" && restoreOptions.IgnoreRootFS { return errors.Errorf("--ignore-rootfs can only be used with --import") } + if restoreOptions.Import == "" && restoreOptions.IgnoreVolumes { + return errors.Errorf("--ignore-volumes can only be used with --import") + } if restoreOptions.Import == "" && restoreOptions.Name != "" { return errors.Errorf("--name can only be used with --import") } diff --git a/cmd/podman/containers/stats.go b/cmd/podman/containers/stats.go index ca5c0fdb8..1a436aaef 100644 --- a/cmd/podman/containers/stats.go +++ b/cmd/podman/containers/stats.go @@ -147,13 +147,14 @@ func stats(cmd *cobra.Command, args []string) error { func outputStats(reports []define.ContainerStats) error { headers := report.Headers(define.ContainerStats{}, map[string]string{ - "ID": "ID", - "CPUPerc": "CPU %", - "MemUsage": "MEM USAGE / LIMIT", - "MemPerc": "MEM %", - "NetIO": "NET IO", - "BlockIO": "BLOCK IO", - "PIDS": "PIDS", + "ID": "ID", + "CPUPerc": "CPU %", + "MemUsage": "MEM USAGE / LIMIT", + "MemUsageBytes": "MEM USAGE / LIMIT", + "MemPerc": "MEM %", + "NetIO": "NET IO", + "BlockIO": "BLOCK IO", + "PIDS": "PIDS", }) if !statsOptions.NoReset { tm.Clear() @@ -222,10 +223,15 @@ func (s *containerStats) PIDS() string { } return fmt.Sprintf("%d", s.PIDs) } + func (s *containerStats) MemUsage() string { return combineHumanValues(s.ContainerStats.MemUsage, s.ContainerStats.MemLimit) } +func (s *containerStats) MemUsageBytes() string { + return combineBytesValues(s.ContainerStats.MemUsage, s.ContainerStats.MemLimit) +} + func floatToPercentString(f float64) string { strippedFloat, err := utils.RemoveScientificNotationFromFloat(f) if err != nil || strippedFloat == 0 { @@ -242,6 +248,13 @@ func combineHumanValues(a, b uint64) string { return fmt.Sprintf("%s / %s", units.HumanSize(float64(a)), units.HumanSize(float64(b))) } +func combineBytesValues(a, b uint64) string { + if a == 0 && b == 0 { + return "-- / --" + } + return fmt.Sprintf("%s / %s", units.BytesSize(float64(a)), units.BytesSize(float64(b))) +} + func outputJSON(stats []containerStats) error { type jstat struct { Id string `json:"id"` // nolint diff --git a/cmd/podman/images/build.go b/cmd/podman/images/build.go index 3aca104e3..1029e03d1 100644 --- a/cmd/podman/images/build.go +++ b/cmd/podman/images/build.go @@ -135,6 +135,16 @@ func buildFlags(cmd *cobra.Command) { logrus.Errorf("error setting up build flags: %v", err) os.Exit(1) } + // --http-proxy flag + // containers.conf defaults to true but we want to force false by default for remote, since settings do not apply + if registry.IsRemote() { + flag = fromAndBudFlags.Lookup("http-proxy") + buildOpts.HTTPProxy = false + if err := flag.Value.Set("false"); err != nil { + logrus.Errorf("unable to set --https-proxy to %v: %v", false, err) + } + flag.DefValue = "false" + } flags.AddFlagSet(&fromAndBudFlags) // Add the completion functions fromAndBudFlagsCompletions := buildahCLI.GetFromAndBudFlagsCompletions() @@ -394,7 +404,6 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil AddCapabilities: flags.CapAdd, AdditionalTags: tags, Annotations: flags.Annotation, - Architecture: flags.Arch, Args: args, BlobDirectory: flags.BlobCache, CNIConfigDir: flags.CNIConfigDir, @@ -429,7 +438,6 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil Layers: flags.Layers, NamespaceOptions: nsValues, NoCache: flags.NoCache, - OS: flags.OS, Out: stdout, Output: output, OutputFormat: format, diff --git a/cmd/podman/images/load.go b/cmd/podman/images/load.go index a24f46781..59fc6f54c 100644 --- a/cmd/podman/images/load.go +++ b/cmd/podman/images/load.go @@ -9,9 +9,8 @@ import ( "strings" "github.com/containers/common/pkg/completion" - "github.com/containers/image/v5/docker/reference" - "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" + "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/util" "github.com/pkg/errors" @@ -22,11 +21,11 @@ import ( var ( loadDescription = "Loads an image from a locally stored archive (tar file) into container storage." loadCommand = &cobra.Command{ - Use: "load [options] [NAME[:TAG]]", + Use: "load [options]", Short: "Load image(s) from a tar archive", Long: loadDescription, RunE: load, - Args: cobra.MaximumNArgs(1), + Args: validate.NoArgs, ValidArgsFunction: completion.AutocompleteNone, } @@ -71,22 +70,8 @@ func loadFlags(cmd *cobra.Command) { } func load(cmd *cobra.Command, args []string) error { - if len(args) > 0 { - ref, err := reference.Parse(args[0]) - if err != nil { - return err - } - if t, ok := ref.(reference.Tagged); ok { - loadOpts.Tag = t.Tag() - } else { - loadOpts.Tag = "latest" - } - if r, ok := ref.(reference.Named); ok { - loadOpts.Name = r.Name() - } - } if len(loadOpts.Input) > 0 { - if err := parse.ValidateFileName(loadOpts.Input); err != nil { + if _, err := os.Stat(loadOpts.Input); err != nil { return err } } else { diff --git a/cmd/podman/images/search.go b/cmd/podman/images/search.go index c2ef7d767..c8ea4b04a 100644 --- a/cmd/podman/images/search.go +++ b/cmd/podman/images/search.go @@ -26,6 +26,12 @@ type searchOptionsWrapper struct { Format string // For go templating } +// listEntryTag is a utility structure used for json serialization. +type listEntryTag struct { + Name string + Tags []string +} + var ( searchOptions = searchOptionsWrapper{} searchDescription = `Search registries for a given image. Can search all the default registries or a specific registry. @@ -149,14 +155,13 @@ func imageSearch(cmd *cobra.Command, args []string) error { if len(searchOptions.Filters) != 0 { return errors.Errorf("filters are not applicable to list tags result") } + if report.IsJSON(searchOptions.Format) { + listTagsEntries := buildListTagsJson(searchReport) + return printJson(listTagsEntries) + } row = "{{.Name}}\t{{.Tag}}\n" case report.IsJSON(searchOptions.Format): - prettyJSON, err := json.MarshalIndent(searchReport, "", " ") - if err != nil { - return err - } - fmt.Println(string(prettyJSON)) - return nil + return printJson(searchReport) case cmd.Flags().Changed("format"): renderHeaders = parse.HasTable(searchOptions.Format) row = report.NormalizeFormat(searchOptions.Format) @@ -180,3 +185,33 @@ func imageSearch(cmd *cobra.Command, args []string) error { return tmpl.Execute(w, searchReport) } + +func printJson(v interface{}) error { + prettyJSON, err := json.MarshalIndent(v, "", " ") + if err != nil { + return err + } + fmt.Println(string(prettyJSON)) + return nil +} + +func buildListTagsJson(searchReport []entities.ImageSearchReport) []listEntryTag { + entries := []listEntryTag{} + +ReportLoop: + for _, report := range searchReport { + for idx, entry := range entries { + if entry.Name == report.Name { + entries[idx].Tags = append(entries[idx].Tags, report.Tag) + continue ReportLoop + } + } + newElem := listEntryTag{ + report.Name, + []string{report.Tag}, + } + + entries = append(entries, newElem) + } + return entries +} diff --git a/cmd/podman/play/kube.go b/cmd/podman/play/kube.go index db7280b1d..4c44fa30f 100644 --- a/cmd/podman/play/kube.go +++ b/cmd/podman/play/kube.go @@ -12,6 +12,7 @@ import ( "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/util" + "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -126,28 +127,42 @@ func kube(cmd *cobra.Command, args []string) error { for _, pod := range report.Pods { for _, l := range pod.Logs { - fmt.Fprintf(os.Stderr, l) + fmt.Fprint(os.Stderr, l) } } + ctrsFailed := 0 + for _, pod := range report.Pods { - fmt.Printf("Pod:\n") + fmt.Println("Pod:") fmt.Println(pod.ID) switch len(pod.Containers) { case 0: continue case 1: - fmt.Printf("Container:\n") + fmt.Println("Container:") default: - fmt.Printf("Containers:\n") + fmt.Println("Containers:") } for _, ctr := range pod.Containers { fmt.Println(ctr) } + ctrsFailed += len(pod.ContainerErrors) + // If We have errors, add a newline + if len(pod.ContainerErrors) > 0 { + fmt.Println() + } + for _, err := range pod.ContainerErrors { + fmt.Fprintln(os.Stderr, err) + } // Empty line for space for next block fmt.Println() } + if ctrsFailed > 0 { + return errors.Errorf("failed to start %d containers", ctrsFailed) + } + return nil } diff --git a/cmd/podman/pods/inspect.go b/cmd/podman/pods/inspect.go index 091094ff6..e809be0c9 100644 --- a/cmd/podman/pods/inspect.go +++ b/cmd/podman/pods/inspect.go @@ -2,7 +2,6 @@ package pods import ( "context" - "fmt" "os" "text/tabwriter" "text/template" @@ -21,9 +20,9 @@ var ( ) var ( - inspectDescription = fmt.Sprintf(`Display the configuration for a pod by name or id + inspectDescription = `Display the configuration for a pod by name or id - By default, this will render all results in a JSON array.`) + By default, this will render all results in a JSON array.` inspectCmd = &cobra.Command{ Use: "inspect [options] POD [POD...]", diff --git a/cmd/podman/pods/prune.go b/cmd/podman/pods/prune.go index 965c36398..a040a21b6 100644 --- a/cmd/podman/pods/prune.go +++ b/cmd/podman/pods/prune.go @@ -20,7 +20,7 @@ var ( ) var ( - pruneDescription = fmt.Sprintf(`podman pod prune Removes all exited pods`) + pruneDescription = `podman pod prune Removes all exited pods` pruneCommand = &cobra.Command{ Use: "prune [options]", diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go index 99d324411..a27ab4859 100644 --- a/cmd/podman/pods/ps.go +++ b/cmd/podman/pods/ps.go @@ -191,6 +191,11 @@ func (l ListPodReporter) Labels() map[string]string { return l.ListPodsReport.Labels } +// Networks returns the infra container network names in string format +func (l ListPodReporter) Networks() string { + return strings.Join(l.ListPodsReport.Networks, ",") +} + // NumberOfContainers returns an int representation for // the number of containers belonging to the pod func (l ListPodReporter) NumberOfContainers() int { diff --git a/cmd/podman/pods/rm.go b/cmd/podman/pods/rm.go index ff238aa20..109f18b78 100644 --- a/cmd/podman/pods/rm.go +++ b/cmd/podman/pods/rm.go @@ -25,9 +25,9 @@ type podRmOptionsWrapper struct { var ( rmOptions = podRmOptionsWrapper{} - podRmDescription = fmt.Sprintf(`podman rm will remove one or more stopped pods and their containers from the host. + podRmDescription = `podman rm will remove one or more stopped pods and their containers from the host. - The pod name or ID can be used. A pod with containers will not be removed without --force. If --force is specified, all containers will be stopped, then removed.`) + The pod name or ID can be used. A pod with containers will not be removed without --force. If --force is specified, all containers will be stopped, then removed.` rmCommand = &cobra.Command{ Use: "rm [options] POD [POD...]", Short: "Remove one or more pods", diff --git a/cmd/podman/pods/stats.go b/cmd/podman/pods/stats.go index 79e7cd8ed..5f79fa016 100644 --- a/cmd/podman/pods/stats.go +++ b/cmd/podman/pods/stats.go @@ -75,11 +75,12 @@ func stats(cmd *cobra.Command, args []string) error { doJSON := report.IsJSON(row) headers := report.Headers(entities.PodStatsReport{}, map[string]string{ - "CPU": "CPU %", - "MemUsage": "MEM USAGE/ LIMIT", - "MEM": "MEM %", - "NET IO": "NET IO", - "BlockIO": "BLOCK IO", + "CPU": "CPU %", + "MemUsage": "MEM USAGE/ LIMIT", + "MemUsageBytes": "MEM USAGE/ LIMIT", + "MEM": "MEM %", + "NET IO": "NET IO", + "BlockIO": "BLOCK IO", }) for ; ; time.Sleep(time.Second) { diff --git a/cmd/podman/root.go b/cmd/podman/root.go index 1f613a4c5..0ee530242 100644 --- a/cmd/podman/root.go +++ b/cmd/podman/root.go @@ -158,7 +158,7 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error { // Prep the engines if _, err := registry.NewImageEngine(cmd, args); err != nil { - return err + return errors.Wrapf(err, "Cannot connect to the Podman socket, make sure there is a Podman REST API service running.") } if _, err := registry.NewContainerEngine(cmd, args); err != nil { return err diff --git a/cmd/podman/system/prune.go b/cmd/podman/system/prune.go index 93b4a1157..5e96a654a 100644 --- a/cmd/podman/system/prune.go +++ b/cmd/podman/system/prune.go @@ -13,17 +13,18 @@ import ( "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" dfilters "github.com/containers/podman/v2/pkg/domain/filters" + "github.com/docker/go-units" "github.com/spf13/cobra" ) var ( pruneOptions = entities.SystemPruneOptions{} filters []string - pruneDescription = fmt.Sprintf(` + pruneDescription = ` podman system prune Remove unused data -`) +` pruneCommand = &cobra.Command{ Use: "prune [options]", @@ -90,7 +91,7 @@ Are you sure you want to continue? [y/N] `, volumeString) return err } // Print container prune results - err = utils.PrintContainerPruneResults(response.ContainerPruneReport, true) + err = utils.PrintContainerPruneResults(response.ContainerPruneReports, true) if err != nil { return err } @@ -101,11 +102,17 @@ Are you sure you want to continue? [y/N] `, volumeString) } // Print Volume prune results if pruneOptions.Volume { - err = utils.PrintVolumePruneResults(response.VolumePruneReport, true) + err = utils.PrintVolumePruneResults(response.VolumePruneReports, true) if err != nil { return err } } // Print Images prune results - return utils.PrintImagePruneResults(response.ImagePruneReport, true) + err = utils.PrintImagePruneResults(response.ImagePruneReports, true) + if err != nil { + return err + } + + fmt.Printf("Total reclaimed space: %s\n", units.HumanSize((float64)(response.ReclaimedSpace))) + return nil } diff --git a/cmd/podman/system/service.go b/cmd/podman/system/service.go index f8bdbfa10..f5760e172 100644 --- a/cmd/podman/system/service.go +++ b/cmd/podman/system/service.go @@ -80,7 +80,7 @@ func service(cmd *cobra.Command, args []string) error { } // socket activation uses a unix:// socket in the shipped unit files but apiURI is coded as "" at this layer. - if "unix" == uri.Scheme && !registry.IsRemote() { + if uri.Scheme == "unix" && !registry.IsRemote() { if err := syscall.Unlink(uri.Path); err != nil && !os.IsNotExist(err) { return err } diff --git a/cmd/podman/utils/utils.go b/cmd/podman/utils/utils.go index 2ca2c4c92..f42243f69 100644 --- a/cmd/podman/utils/utils.go +++ b/cmd/podman/utils/utils.go @@ -5,6 +5,7 @@ import ( "os" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" ) // IsDir returns true if the specified path refers to a directory. @@ -41,21 +42,21 @@ func PrintPodPruneResults(podPruneReports []*entities.PodPruneReport, heading bo return errs.PrintErrors() } -func PrintContainerPruneResults(containerPruneReport *entities.ContainerPruneReport, heading bool) error { +func PrintContainerPruneResults(containerPruneReports []*reports.PruneReport, heading bool) error { var errs OutputErrors - if heading && (len(containerPruneReport.ID) > 0 || len(containerPruneReport.Err) > 0) { + if heading && (len(containerPruneReports) > 0) { fmt.Println("Deleted Containers") } - for k := range containerPruneReport.ID { - fmt.Println(k) - } - for _, v := range containerPruneReport.Err { - errs = append(errs, v) + for _, v := range containerPruneReports { + fmt.Println(v.Id) + if v.Err != nil { + errs = append(errs, v.Err) + } } return errs.PrintErrors() } -func PrintVolumePruneResults(volumePruneReport []*entities.VolumePruneReport, heading bool) error { +func PrintVolumePruneResults(volumePruneReport []*reports.PruneReport, heading bool) error { var errs OutputErrors if heading && len(volumePruneReport) > 0 { fmt.Println("Deleted Volumes") @@ -70,18 +71,16 @@ func PrintVolumePruneResults(volumePruneReport []*entities.VolumePruneReport, he return errs.PrintErrors() } -func PrintImagePruneResults(imagePruneReport *entities.ImagePruneReport, heading bool) error { - if heading && (len(imagePruneReport.Report.Id) > 0 || len(imagePruneReport.Report.Err) > 0) { +func PrintImagePruneResults(imagePruneReports []*reports.PruneReport, heading bool) error { + if heading { fmt.Println("Deleted Images") } - for _, i := range imagePruneReport.Report.Id { - fmt.Println(i) - } - for _, e := range imagePruneReport.Report.Err { - fmt.Fprint(os.Stderr, e.Error()+"\n") - } - if imagePruneReport.Size > 0 { - fmt.Fprintf(os.Stdout, "Size: %d\n", imagePruneReport.Size) + for _, r := range imagePruneReports { + fmt.Println(r.Id) + if r.Err != nil { + fmt.Fprint(os.Stderr, r.Err.Error()+"\n") + } } + return nil } diff --git a/contrib/cirrus/required_host_ports.txt b/contrib/cirrus/required_host_ports.txt index 140e2c32f..9248e497a 100644 --- a/contrib/cirrus/required_host_ports.txt +++ b/contrib/cirrus/required_host_ports.txt @@ -2,13 +2,3 @@ github.com 22 docker.io 443 quay.io 443 registry.fedoraproject.org 443 -mirrors.fedoraproject.org 443 -dl.fedoraproject.org 443 -ewr.edge.kernel.org 443 -mirror.clarkson.edu 443 -mirror.umd.edu 443 -mirror.vcu.edu 443 -mirrors.cat.pdx.edu 443 -pubmirror1.math.uh.edu 443 -pubmirror2.math.uh.edu 443 -sjc.edge.kernel.org 443 diff --git a/contrib/cirrus/runner.sh b/contrib/cirrus/runner.sh index 7f9afd1fd..b0060163e 100755 --- a/contrib/cirrus/runner.sh +++ b/contrib/cirrus/runner.sh @@ -178,6 +178,9 @@ function _run_altbuild() { make -f ./.copr/Makefile rpmbuild --rebuild ./podman-*.src.rpm ;; + Alt*Cross) + make local-cross + ;; *Static*) req_env_vars CTR_FQIN [[ "$UID" -eq 0 ]] || \ @@ -199,9 +202,19 @@ function _run_altbuild() { } function _run_release() { - if bin/podman info |& grep -Eq -- '-dev'; then - die "Releases must never contain '-dev' in output of 'podman info'" + # TODO: These tests should come from code external to the podman repo. + # to allow test-changes (and re-runs) in the case of a correctable test + # flaw or flake at release tag-push time. For now, the test is here + # given its simplicity. + msg "podman info:" + bin/podman info + + msg "Checking podman release (or potential release) criteria." + dev=$(bin/podman info |& grep -- -dev) + if [[ -n "$dev" ]]; then + die "Releases must never contain '-dev' in output of 'podman info' ($dev)" fi + msg "All OK" } logformatter() { diff --git a/docs/source/Commands.rst b/docs/source/Commands.rst index cd5d894da..563462377 100644 --- a/docs/source/Commands.rst +++ b/docs/source/Commands.rst @@ -75,6 +75,8 @@ Commands :doc:`push <markdown/podman-push.1>` Push an image to a specified destination +:doc:`rename <markdown/podman-rename.1>` Rename an existing container + :doc:`restart <markdown/podman-restart.1>` Restart one or more containers :doc:`rm <markdown/podman-rm.1>` Remove one or more containers diff --git a/docs/source/managecontainers.rst b/docs/source/managecontainers.rst index 9926f9996..9b3978f25 100644 --- a/docs/source/managecontainers.rst +++ b/docs/source/managecontainers.rst @@ -41,6 +41,8 @@ Manage Containers :doc:`ps <markdown/podman-ps.1>` List containers +:doc:`rename <markdown/podman-rename.1>` Rename an existing container + :doc:`restart <markdown/podman-restart.1>` Restart one or more containers :doc:`restore <markdown/podman-container-restore.1>` Restores one or more containers from a checkpoint diff --git a/docs/source/markdown/links/podman-container-rename.1 b/docs/source/markdown/links/podman-container-rename.1 new file mode 100644 index 000000000..1e71c57b6 --- /dev/null +++ b/docs/source/markdown/links/podman-container-rename.1 @@ -0,0 +1 @@ +.so man1/podman-rename.1 diff --git a/docs/source/markdown/podman-build.1.md b/docs/source/markdown/podman-build.1.md index 67513e386..29f763d54 100644 --- a/docs/source/markdown/podman-build.1.md +++ b/docs/source/markdown/podman-build.1.md @@ -294,7 +294,7 @@ If you specify `-f -`, the Containerfile contents will be read from stdin. #### **--force-rm**=*true|false* Always remove intermediate containers after a build, even if the build fails -(default false). +(default true). #### **--format** @@ -305,6 +305,11 @@ Recognized formats include *oci* (OCI image-spec v1.0, the default) and Note: You can also override the default format by setting the BUILDAH\_FORMAT environment variable. `export BUILDAH_FORMAT=docker` +#### **--from** + +Overrides the first `FROM` instruction within the Containerfile. If there are multiple +FROM instructions in a Containerfile, only the first is changed. + **-h**, **--help** Print usage statement @@ -383,6 +388,11 @@ Adjust the logging level up or down. Valid option values range from -2 to 3, with 3 being roughly equivalent to using the global *--debug* option, and values below 0 omitting even error messages which accompany fatal errors. +#### **--manifest** "manifest" + +Name of the manifest list to which the image will be added. Creates the manifest list +if it does not exist. This option is useful for building multi architecture images. + #### **--memory**, **-m**=*LIMIT* Memory limit (format: <number>[<unit>], where unit = b (bytes), k (kilobytes), m (megabytes), or g (gigabytes)) @@ -524,6 +534,12 @@ layers are not squashed. Squash all of the new image's layers (including those inherited from a base image) into a single new layer. +#### **--stdin** + +Pass stdin into the RUN containers. Sometime commands being RUN within a Containerfile +want to request information from the user. For example apt asking for a confirmation for install. +Use --stdin to be able to interact from the terminal during the build. + #### **--tag**, **-t**=*imageName* Specifies the name which will be assigned to the resulting image if the build @@ -656,6 +672,10 @@ that the UTS namespace in which `podman` itself is being run should be reused, or it can be the path to a UTS namespace which is already in use by another process. +#### **--variant**="" + +Set the architecture variant of the image to be pulled. + #### **--volume**, **-v**[=*[HOST-DIR:CONTAINER-DIR[:OPTIONS]]*] Create a bind mount. If you specify, ` -v /HOST-DIR:/CONTAINER-DIR`, Podman @@ -800,6 +820,12 @@ $ podman build --layers --force-rm -t imageName . $ podman build --no-cache --rm=false -t imageName . ``` +### Building an multi-architecture image using a --manifest option (Requires emulation software) + +podman build --arch arm --manifest myimage /tmp/mysrc +podman build --arch amd64 --manifest myimage /tmp/mysrc +podman build --arch s390x --manifest myimage /tmp/mysrc + ### Building an image using a URL, Git repo, or archive The build context directory can be specified as a URL to a Containerfile, a diff --git a/docs/source/markdown/podman-container-checkpoint.1.md b/docs/source/markdown/podman-container-checkpoint.1.md index bfda782c5..ea05979cd 100644 --- a/docs/source/markdown/podman-container-checkpoint.1.md +++ b/docs/source/markdown/podman-container-checkpoint.1.md @@ -52,12 +52,32 @@ exported to a tar.gz file it is possible with the help of **--ignore-rootfs** to explicitly disable including changes to the root file-system into the checkpoint archive file. +#### **--ignore-volumes** + +This option must be used in combination with the **--export, -e** option. +When this option is specified, the content of volumes associated with +the container will not be included into the checkpoint tar.gz file. + +#### **--pre-checkpoint**, **-P** + +Dump the container's memory information only, leaving the container running. Later +operations will supersede prior dumps. It only works on runc 1.0-rc3 or higher. + +#### **--with-previous** + +Check out the container with previous criu image files in pre-dump. It only works +without **--pre-checkpoint** or **-P**. It only works on runc 1.0-rc3 or higher. + ## EXAMPLE podman container checkpoint mywebserver podman container checkpoint 860a4b23 +podman container checkpoint -P -e pre-checkpoint.tar.gz -l + +podman container checkpoint --with-previous -e checkpoint.tar.gz -l + ## SEE ALSO podman(1), podman-container-restore(1) diff --git a/docs/source/markdown/podman-container-restore.1.md b/docs/source/markdown/podman-container-restore.1.md index 494e7db1e..192b8765b 100644 --- a/docs/source/markdown/podman-container-restore.1.md +++ b/docs/source/markdown/podman-container-restore.1.md @@ -48,6 +48,11 @@ Import a checkpoint tar.gz file, which was exported by Podman. This can be used to import a checkpointed container from another host. Do not specify a *container* argument when using this option. +#### **--import-previous** + +Import a pre-checkpoint tar.gz file which was exported by Podman. This option +must be used with **-i** or **--import**. It only works on runc 1.0-rc3 or higher. + #### **--name**, **-n** This is only available in combination with **--import, -i**. If a container is restored @@ -85,12 +90,21 @@ exported checkpoint with **--name, -n**. Using **--ignore-static-mac** tells Podman to ignore the MAC address if it was configured with **--mac-address** during container creation. + +#### **--ignore-volumes** + +This option must be used in combination with the **--import, -i** option. +When restoring containers from a checkpoint tar.gz file with this option, +the content of associated volumes will not be restored. + ## EXAMPLE podman container restore mywebserver podman container restore 860a4b23 +podman container restore --import-previous pre-checkpoint.tar.gz --import checkpoint.tar.gz + ## SEE ALSO podman(1), podman-container-checkpoint(1) diff --git a/docs/source/markdown/podman-container.1.md b/docs/source/markdown/podman-container.1.md index 9da5db601..e85d69c59 100644 --- a/docs/source/markdown/podman-container.1.md +++ b/docs/source/markdown/podman-container.1.md @@ -33,6 +33,7 @@ The container command allows you to manage containers | port | [podman-port(1)](podman-port.1.md) | List port mappings for the container. | | prune | [podman-container-prune(1)](podman-container-prune.1.md)| Remove all stopped containers from local storage. | | ps | [podman-ps(1)](podman-ps.1.md) | Prints out information about containers. | +| rename | [podman-rename(1)](podman-rename.1.md) | Rename an existing container. | | restart | [podman-restart(1)](podman-restart.1.md) | Restart one or more containers. | | restore | [podman-container-restore(1)](podman-container-restore.1.md) | Restores one or more containers from a checkpoint. | | rm | [podman-rm(1)](podman-rm.1.md) | Remove one or more containers. | @@ -42,7 +43,7 @@ The container command allows you to manage containers | stats | [podman-stats(1)](podman-stats.1.md) | Display a live stream of one or more container's resource usage statistics. | | stop | [podman-stop(1)](podman-stop.1.md) | Stop one or more running containers. | | top | [podman-top(1)](podman-top.1.md) | Display the running processes of a container. | -| unmount | [podman-unmount(1)](podman-unmount.1.md) | Unmount a working container's root filesystem.(Alias unmount) | +| unmount | [podman-unmount(1)](podman-unmount.1.md) | Unmount a working container's root filesystem.(Alias unmount) | | unpause | [podman-unpause(1)](podman-unpause.1.md) | Unpause one or more containers. | | wait | [podman-wait(1)](podman-wait.1.md) | Wait on one or more containers to stop and print their exit codes. | diff --git a/docs/source/markdown/podman-load.1.md b/docs/source/markdown/podman-load.1.md index dc2a632e5..3ff03e9e7 100644 --- a/docs/source/markdown/podman-load.1.md +++ b/docs/source/markdown/podman-load.1.md @@ -4,13 +4,12 @@ podman\-load - Load image(s) from a tar archive into container storage ## SYNOPSIS -**podman load** [*options*] [*name*[:*tag*]] +**podman load** [*options*] -**podman image load** [*options*] [*name*[:*tag*]] +**podman image load** [*options*] ## DESCRIPTION **podman load** loads an image from either an **oci-archive** or a **docker-archive** stored on the local machine into container storage. **podman load** reads from stdin by default or a file if the **input** option is set. -You can also specify a name for the image if the archive is of single image and load will tag an additional image with the name:tag. **podman load** is used for loading from the archive generated by **podman save**, that includes the image parent layers. To load the archive of container's filesystem created by **podman export**, use **podman import**. The local client further supports loading an **oci-dir** or a **docker-dir** as created with **podman save** (1). @@ -23,7 +22,7 @@ Note: `:` is a restricted character and cannot be part of the file name. **podman load [GLOBAL OPTIONS]** -**podman load [OPTIONS] NAME[:TAG]** +**podman load [OPTIONS]** ## OPTIONS @@ -78,7 +77,7 @@ Loaded image: registry.fedoraproject.org/fedora:latest ``` ## SEE ALSO -podman(1), podman-save(1), podman-tag(1) +podman(1), podman-save(1) ## HISTORY July 2017, Originally compiled by Urvashi Mohnani <umohnani@redhat.com> diff --git a/docs/source/markdown/podman-pod-ps.1.md b/docs/source/markdown/podman-pod-ps.1.md index e1d60d765..ab250e1ff 100644 --- a/docs/source/markdown/podman-pod-ps.1.md +++ b/docs/source/markdown/podman-pod-ps.1.md @@ -72,6 +72,8 @@ Valid placeholders for the Go template are listed below: | .Cgroup | Cgroup path of pod | | .Created | Creation time of pod | | .InfraID | Pod infra container ID | +| .Networks | Show all networks connected to the infra container | + #### **--sort** Sort by created, ID, name, status, or number of containers @@ -93,6 +95,7 @@ Valid filters are listed below: | name | [Name] Pod's name (accepts regex) | | label | [Key] or [Key=Value] Label assigned to a container | | status | Pod's status: `stopped`, `running`, `paused`, `exited`, `dead`, `created`, `degraded` | +| network | [Network] name or full ID of network | | ctr-names | Container name within the pod (accepts regex) | | ctr-ids | Container ID within the pod (accepts regex) | | ctr-status | Container status within the pod | diff --git a/docs/source/markdown/podman-pod-stats.1.md b/docs/source/markdown/podman-pod-stats.1.md index b1b23cd06..4ef15fc20 100644 --- a/docs/source/markdown/podman-pod-stats.1.md +++ b/docs/source/markdown/podman-pod-stats.1.md @@ -35,17 +35,18 @@ Pretty-print container statistics to JSON or using a Go template Valid placeholders for the Go template are listed below: -| **Placeholder** | **Description** | -| --------------- | --------------- | -| .Pod | Pod ID | -| .CID | Container ID | -| .Name | Container Name | -| .CPU | CPU percentage | -| .MemUsage | Memory usage | -| .Mem | Memory percentage | -| .NetIO | Network IO | -| .BlockIO | Block IO | -| .PIDS | Number of PIDs | +| **Placeholder** | **Description** | +| --------------- | ------------------ | +| .Pod | Pod ID | +| .CID | Container ID | +| .Name | Container Name | +| .CPU | CPU percentage | +| .MemUsage | Memory usage | +| .MemUsageBytes | Memory usage (IEC) | +| .Mem | Memory percentage | +| .NetIO | Network IO | +| .BlockIO | Block IO | +| .PIDS | Number of PIDs | When using a GO template, you may precede the format with `table` to print headers. ## EXAMPLE diff --git a/docs/source/markdown/podman-ps.1.md b/docs/source/markdown/podman-ps.1.md index 28212b92c..bb8001ad9 100644 --- a/docs/source/markdown/podman-ps.1.md +++ b/docs/source/markdown/podman-ps.1.md @@ -58,6 +58,7 @@ Valid filters are listed below: | volume | [VolumeName] or [MountpointDestination] Volume mounted in container | | health | [Status] healthy or unhealthy | | pod | [Pod] name or full or partial ID of pod | +| network | [Network] name or full ID of network | #### **--format**=*format* @@ -79,6 +80,7 @@ Valid placeholders for the Go template are listed below: | .Ports | Exposed ports | | .Size | Size of container | | .Names | Name of container | +| .Networks | Show all networks connected to the container | | .Labels | All the labels assigned to the container | | .Mounts | Volumes mounted in the container | diff --git a/docs/source/markdown/podman-rename.1.md b/docs/source/markdown/podman-rename.1.md new file mode 100644 index 000000000..fdb0dac89 --- /dev/null +++ b/docs/source/markdown/podman-rename.1.md @@ -0,0 +1,38 @@ +% podman-rename(1) + +## NAME +podman\-rename - Rename an existing container + +## SYNOPSIS +**podman rename** *container* *newname* + +**podman container rename** *container* *newname* + +## DESCRIPTION +Rename changes the name of an existing container. +The old name will be freed, and will be available for use. +This command can be run on containers in any state. +However, running containers may not fully receive the effects until they are restarted - for example, a running container may still use the old name in its logs. +At present, only containers are supported; pods and volumes cannot be renamed. + +## OPTIONS + +## EXAMPLES + +``` +# Rename a container by name +$ podman rename oldContainer aNewName +``` + +``` +# Rename a container by ID +$ podman rename 717716c00a6b testcontainer +``` + +``` +# Use the container rename alias +$ podman container rename 6e7514b47180 databaseCtr +``` + +## SEE ALSO +podman(1), podman-create(1), podman-run(1) diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md index 674079a9d..74c231184 100644 --- a/docs/source/markdown/podman-run.1.md +++ b/docs/source/markdown/podman-run.1.md @@ -1011,8 +1011,11 @@ When set to **true**, Podman will allocate a pseudo-tty and attach to the standa input of the container. This can be used, for example, to run a throwaway interactive shell. The default is **false**. -**NOTE**: The **-t** option is incompatible with a redirection of the Podman client -standard input. +**NOTE**: The --tty flag prevents redirection of standard output. It combines STDOUT and STDERR, it can insert control characters, and it can hang pipes. This option should only be used when run interactively in a terminal. When feeding input to Podman, use -i only, not -it. + +``` +echo "asdf" | podman run --rm -i someimage /bin/cat +``` #### **--tz**=*timezone* @@ -1528,6 +1531,13 @@ weight by **--blkio-weight-device** flag. Use the following command: $ podman run -it --blkio-weight-device "/dev/sda:200" ubuntu ``` +### Using a podman container with input from a pipe + +``` +$ echo "asdf" | podman run --rm -i --entrypoint /bin/cat someimage +asdf +``` + ### Setting Namespaced Kernel Parameters (Sysctls) The **--sysctl** sets namespaced kernel parameters (sysctls) in the diff --git a/docs/source/markdown/podman-stats.1.md b/docs/source/markdown/podman-stats.1.md index d5de8caf2..722027aae 100644 --- a/docs/source/markdown/podman-stats.1.md +++ b/docs/source/markdown/podman-stats.1.md @@ -45,16 +45,17 @@ Pretty-print container statistics to JSON or using a Go template Valid placeholders for the Go template are listed below: -| **Placeholder** | **Description** | -| --------------- | --------------- | -| .ID | Container ID | -| .Name | Container Name | -| .CPUPerc | CPU percentage | -| .MemUsage | Memory usage | -| .MemPerc | Memory percentage | -| .NetIO | Network IO | -| .BlockIO | Block IO | -| .PIDS | Number of PIDs | +| **Placeholder** | **Description** | +| --------------- | ------------------ | +| .ID | Container ID | +| .Name | Container Name | +| .CPUPerc | CPU percentage | +| .MemUsage | Memory usage | +| .MemUsageBytes | Memory usage (IEC) | +| .MemPerc | Memory percentage | +| .NetIO | Network IO | +| .BlockIO | Block IO | +| .PIDS | Number of PIDs | When using a GO template, you may precede the format with `table` to print headers. diff --git a/docs/source/markdown/podman-volume-create.1.md b/docs/source/markdown/podman-volume-create.1.md index 118f024df..a06411000 100644 --- a/docs/source/markdown/podman-volume-create.1.md +++ b/docs/source/markdown/podman-volume-create.1.md @@ -17,7 +17,7 @@ driver options can be set using the **--opt** flag. #### **--driver**=*driver* -Specify the volume driver name (default local). +Specify the volume driver name (default **local**). Setting this to a value other than **local** Podman will attempt to create the volume using a volume plugin with the given name. Such plugins must be defined in the **volume_plugins** section of the **containers.conf**(5) configuration file. #### **--help** @@ -30,13 +30,14 @@ Set metadata for a volume (e.g., --label mykey=value). #### **--opt**=*option*, **-o** Set driver specific options. -For the default driver, `local`, this allows a volume to be configured to mount a filesystem on the host. +For the default driver, **local**, this allows a volume to be configured to mount a filesystem on the host. For the `local` driver the following options are supported: `type`, `device`, and `o`. The `type` option sets the type of the filesystem to be mounted, and is equivalent to the `-t` flag to **mount(8)**. The `device` option sets the device to be mounted, and is equivalent to the `device` argument to **mount(8)**. The `o` option sets options for the mount, and is equivalent to the `-o` flag to **mount(8)** with two exceptions. The `o` option supports `uid` and `gid` options to set the UID and GID of the created volume that are not normally supported by **mount(8)**. -Using volume options with the `local` driver requires root privileges. +Using volume options with the **local** driver requires root privileges. +When not using the **local** driver, the given options will be passed directly to the volume plugin. In this case, supported options will be dictated by the plugin in question, not Podman. ## EXAMPLES @@ -53,7 +54,8 @@ $ podman volume create --label foo=bar myvol ``` ## SEE ALSO -podman-volume(1), mount(8) +**podman-volume**(1), **mount**(8), **containers.conf**(5) ## HISTORY +January 2020, updated with information on volume plugins by Matthew Heon <mheon@redhat.com> November 2018, Originally compiled by Urvashi Mohnani <umohnani@redhat.com> diff --git a/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md index 2688c1720..67484c3ec 100644 --- a/docs/source/markdown/podman.1.md +++ b/docs/source/markdown/podman.1.md @@ -122,7 +122,7 @@ Default root dir configured in `/etc/containers/storage.conf`. #### **--runroot**=*value* -Storage state directory where all state information is stored (default: "/var/run/containers/storage" for UID 0, "/var/run/user/$UID/run" for other users). +Storage state directory where all state information is stored (default: "/run/containers/storage" for UID 0, "/run/user/$UID/run" for other users). Default state dir configured in `/etc/containers/storage.conf`. #### **--runtime**=*value* @@ -247,6 +247,7 @@ the exit codes follow the `chroot` standard, see below: | [podman-ps(1)](podman-ps.1.md) | Prints out information about containers. | | [podman-pull(1)](podman-pull.1.md) | Pull an image from a registry. | | [podman-push(1)](podman-push.1.md) | Push an image from local storage to elsewhere. | +| [podman-rename(1)](podman-rename.1.md) | Rename an existing container. | | [podman-restart(1)](podman-restart.1.md) | Restart one or more containers. | | [podman-rm(1)](podman-rm.1.md) | Remove one or more containers. | | [podman-rmi(1)](podman-rmi.1.md) | Removes one or more locally stored images. | @@ -259,7 +260,7 @@ the exit codes follow the `chroot` standard, see below: | [podman-system(1)](podman-system.1.md) | Manage podman. | | [podman-tag(1)](podman-tag.1.md) | Add an additional name to a local image. | | [podman-top(1)](podman-top.1.md) | Display the running processes of a container. | -| [podman-unmount(1)](podman-unmount.1.md) | Unmount a working container's root filesystem. | +| [podman-unmount(1)](podman-unmount.1.md) | Unmount a working container's root filesystem. | | [podman-unpause(1)](podman-unpause.1.md) | Unpause one or more containers. | | [podman-unshare(1)](podman-unshare.1.md) | Run a command inside of a modified user namespace. | | [podman-untag(1)](podman-untag.1.md) | Removes one or more names from a locally-stored image. | diff --git a/docs/tutorials/podman_tutorial.md b/docs/tutorials/podman_tutorial.md index c15de67a6..7419f445e 100644 --- a/docs/tutorials/podman_tutorial.md +++ b/docs/tutorials/podman_tutorial.md @@ -21,7 +21,7 @@ For installing or building Podman, please see the [installation instructions](ht This sample container will run a very basic httpd server that serves only its index page. ```console -podman run -dt -p 8080:8080/tcp -e HTTPD_VAR_RUN=/var/run/httpd -e HTTPD_MAIN_CONF_D_PATH=/etc/httpd/conf.d \ +podman run -dt -p 8080:8080/tcp -e HTTPD_VAR_RUN=/run/httpd -e HTTPD_MAIN_CONF_D_PATH=/etc/httpd/conf.d \ -e HTTPD_MAIN_CONF_PATH=/etc/httpd/conf \ -e HTTPD_CONTAINER_SCRIPTS_PATH=/usr/share/container-scripts/httpd/ \ registry.fedoraproject.org/f29/httpd /usr/bin/run-httpd diff --git a/docs/tutorials/rootless_tutorial.md b/docs/tutorials/rootless_tutorial.md index 9d8851bc8..cb73fc519 100644 --- a/docs/tutorials/rootless_tutorial.md +++ b/docs/tutorials/rootless_tutorial.md @@ -130,7 +130,7 @@ graphroot="" Default directory to store all writable content created by container storage programs. runroot="" - container storage run dir (default: "/var/run/containers/storage") + container storage run dir (default: "/run/containers/storage") Default directory to store all temporary writable content created by container storage programs. ``` In rootless podman these fields default to @@ -10,18 +10,18 @@ require ( github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect github.com/containernetworking/cni v0.8.0 github.com/containernetworking/plugins v0.9.0 - github.com/containers/buildah v1.18.1-0.20201217112226-67470615779c - github.com/containers/common v0.31.2 + github.com/containers/buildah v1.19.2 + github.com/containers/common v0.33.1 github.com/containers/conmon v2.0.20+incompatible github.com/containers/image/v5 v5.9.0 - github.com/containers/psgo v1.5.1 - github.com/containers/storage v1.24.4 + github.com/containers/psgo v1.5.2 + github.com/containers/storage v1.24.5 github.com/coreos/go-systemd/v22 v22.1.0 - github.com/cri-o/ocicni v0.2.1-0.20201125151022-df072ea5421c + github.com/cri-o/ocicni v0.2.1-0.20201204103948-b6cbe99b9756 github.com/cyphar/filepath-securejoin v0.2.2 github.com/davecgh/go-spew v1.1.1 github.com/docker/distribution v2.7.1+incompatible - github.com/docker/docker v17.12.0-ce-rc1.0.20201020191947-73dc6a680cdd+incompatible + github.com/docker/docker v20.10.0-beta1.0.20201113105859-b6bfff2a628f+incompatible github.com/docker/go-connections v0.4.0 github.com/docker/go-plugins-helpers v0.0.0-20200102110956-c9a8a2d92ccc github.com/docker/go-units v0.4.0 @@ -29,14 +29,14 @@ require ( github.com/ghodss/yaml v1.0.0 github.com/godbus/dbus/v5 v5.0.3 github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf - github.com/google/uuid v1.1.2 + github.com/google/uuid v1.1.5 github.com/gorilla/mux v1.8.0 github.com/gorilla/schema v1.2.0 github.com/hashicorp/go-multierror v1.1.0 github.com/hpcloud/tail v1.0.0 github.com/json-iterator/go v1.1.10 github.com/kr/text v0.2.0 // indirect - github.com/moby/term v0.0.0-20200915141129-7f0af18e79f2 + github.com/moby/term v0.0.0-20201110203204-bea5bbe245bf github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618 github.com/onsi/ginkgo v1.14.2 github.com/onsi/gomega v1.10.4 @@ -53,7 +53,7 @@ require ( github.com/sirupsen/logrus v1.7.0 github.com/spf13/cobra v1.1.1 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.6.1 + github.com/stretchr/testify v1.7.0 github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 github.com/uber/jaeger-client-go v2.25.0+incompatible github.com/uber/jaeger-lib v2.2.0+incompatible // indirect @@ -62,12 +62,12 @@ require ( go.uber.org/atomic v1.7.0 // indirect golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect - golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 + golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3 google.golang.org/appengine v1.6.6 // indirect gopkg.in/square/go-jose.v2 v2.5.1 // indirect gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect k8s.io/api v0.0.0-20190620084959-7cf5895f2711 - k8s.io/apimachinery v0.20.1 + k8s.io/apimachinery v0.20.2 k8s.io/client-go v0.0.0-20190620085101-78d2af792bab ) @@ -23,12 +23,9 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/go-winio v0.4.15-0.20200113171025-3fe6c5262873/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= -github.com/Microsoft/go-winio v0.4.15 h1:qkLXKzb1QoVatRyd/YlXZ/Kg0m5K3SPuoD82jjSOaBc= -github.com/Microsoft/go-winio v0.4.15/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331 h1:3YnB7Hpmh1lPecPE8doMOtYCrMdrpedZOvxfuNES/Vk= github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.9 h1:VrfodqvztU8YSOvygU+DN1BGaSGxmrNfqOv5oOuX2Bk= github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= github.com/Microsoft/hcsshim v0.8.14 h1:lbPVK25c1cu5xTLITwpUcxoA9vKrKErASPYygvouJns= github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= @@ -76,7 +73,6 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f h1:tSNMc+rJDfmYntojat8lljbt1mgKNpTxUZJsSzJ9Y1s= github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59 h1:qWj4qVYZ95vLWwqyNJCQg7rDsG5wPdze0UaPolH7DUk= github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= @@ -96,32 +92,26 @@ github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kw github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/cni v0.8.0 h1:BT9lpgGoH4jw3lFC7Odz2prU5ruiYKcgAjMCbgybcKI= github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/plugins v0.8.7 h1:bU7QieuAp+sACI2vCzESJ3FoT860urYP+lThyZkb/2M= github.com/containernetworking/plugins v0.8.7/go.mod h1:R7lXeZaBzpfqapcAbHRW8/CYwm0dHzbz0XEjofx0uB0= github.com/containernetworking/plugins v0.9.0 h1:c+1gegKhR7+d0Caum9pEHugZlyhXPOG6v3V6xJgIGCI= github.com/containernetworking/plugins v0.9.0/go.mod h1:dbWv4dI0QrBGuVgj+TuVQ6wJRZVOhrCQj91YyC92sxg= -github.com/containers/buildah v1.18.1-0.20201217112226-67470615779c h1:DnJiPjBKeoZbzjkUA6YMf/r5ShYpNacK+EcQ/ui1Mxo= -github.com/containers/buildah v1.18.1-0.20201217112226-67470615779c/go.mod h1:hvIoL3urgYPL0zX8XlK05aWP6qfUnBNqTrsedsYw6OY= -github.com/containers/common v0.31.0/go.mod h1:yT4GTUHsKRmpaDb+mecXRnIMre7W3ZgwXqaYMywXlaA= -github.com/containers/common v0.31.2 h1:sNYwvLA4B7SpEiAWTUvkItPlCrUa2vcxh0FTKXKoC3Q= -github.com/containers/common v0.31.2/go.mod h1:Fehe82hQfJQvDspnRrV9rcdAWG3IalNHEt0F6QWNBHQ= +github.com/containers/buildah v1.19.2 h1:1/ePUtinuqTPSwXiZXPyBJmik688l1e4SUZsoOv716w= +github.com/containers/buildah v1.19.2/go.mod h1:zUMKdtZu4rs6lgKHheKwo+wBlh5ZL+1+/5/IsaNTD74= +github.com/containers/common v0.33.1 h1:XpDiq8Cta8+u1s4kpYSEWdB140ZmqgyIXfWkLqKx3z0= +github.com/containers/common v0.33.1/go.mod h1:mjDo/NKeweL/onaspLhZ38WnHXaYmrELHclIdvSnYpY= github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg= github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= -github.com/containers/image/v5 v5.8.1/go.mod h1:blOEFd/iFdeyh891ByhCVUc+xAcaI3gBegXECwz9UbQ= github.com/containers/image/v5 v5.9.0 h1:dRmUtcluQcmasNo3DpnRoZjfU0rOu1qZeL6wlDJr10Q= github.com/containers/image/v5 v5.9.0/go.mod h1:blOEFd/iFdeyh891ByhCVUc+xAcaI3gBegXECwz9UbQ= 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 v1.0.3 h1:vYgl+RZ9Q3DPMuTfxmN+qp0X2Bj52uuY2vnt6GzVe1c= github.com/containers/ocicrypt v1.0.3/go.mod h1:CUBa+8MRNL/VkpxYIpaMtgn1WgXGyvPQj8jcy0EVG6g= -github.com/containers/psgo v1.5.1 h1:MQNb7FLbXqBdqz6u4lI2QWizVz4RSTzs1+Nk9XT1iVA= -github.com/containers/psgo v1.5.1/go.mod h1:2ubh0SsreMZjSXW1Hif58JrEcFudQyIy9EzPUWfawVU= +github.com/containers/psgo v1.5.2 h1:3aoozst/GIwsrr/5jnFy3FrJay98uujPCu9lTuSZ/Cw= +github.com/containers/psgo v1.5.2/go.mod h1:2ubh0SsreMZjSXW1Hif58JrEcFudQyIy9EzPUWfawVU= github.com/containers/storage v1.23.7/go.mod h1:cUT2zHjtx+WlVri30obWmM2gpqpi8jfPsmIzP1TVpEI= -github.com/containers/storage v1.24.1/go.mod h1:0xJL06Dmd+ZYXIUdnBUPN0JnhHGgwMkLvnnAonJfWJU= -github.com/containers/storage v1.24.3 h1:8UB4S62l4hrU6Yw3dbsLCJtLg7Ofo39IN2HdckBIX4E= -github.com/containers/storage v1.24.3/go.mod h1:0xJL06Dmd+ZYXIUdnBUPN0JnhHGgwMkLvnnAonJfWJU= -github.com/containers/storage v1.24.4 h1:QJn/C/4eNbYNpxYdnIn1u4lElIB7V9IesRraLf68JjY= -github.com/containers/storage v1.24.4/go.mod h1:Y793GKrV3RVM1Jt4QejXtCJHGUPLrDvQ9LAbCyJ9OKs= +github.com/containers/storage v1.24.5 h1:BusfdU0rCS2/Daa/DPw+0iLfGRlYA7UVF7D0el3N7Vk= +github.com/containers/storage v1.24.5/go.mod h1:YC+2pY8SkfEAcZkwycxYbpK8EiRbx5soPPwz9dxe4IQ= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-iptables v0.4.5 h1:DpHb9vJrZQEFMcVLFKAAGMUVX0XoRC0ptCthinRYm38= @@ -138,8 +128,10 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cri-o/ocicni v0.2.1-0.20201125151022-df072ea5421c h1:iGaCU6d3oVT0pl8tmvyDhoA/vTDL3IX08akfsKZIy9o= -github.com/cri-o/ocicni v0.2.1-0.20201125151022-df072ea5421c/go.mod h1:vingr1ztOAzP2WyTgGbpMov9dFhbjNxdLtDv0+PhAvY= +github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= +github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cri-o/ocicni v0.2.1-0.20201204103948-b6cbe99b9756 h1:4T3rzrCSvMgVTR+fm526d+Ed0BurAHGjOaaNFOVoK6E= +github.com/cri-o/ocicni v0.2.1-0.20201204103948-b6cbe99b9756/go.mod h1:vingr1ztOAzP2WyTgGbpMov9dFhbjNxdLtDv0+PhAvY= github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= @@ -158,6 +150,8 @@ github.com/docker/docker v1.4.2-0.20191219165747-a9416c67da9f/go.mod h1:eEKB0N0r github.com/docker/docker v17.12.0-ce-rc1.0.20200505174321-1655290016ac+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v17.12.0-ce-rc1.0.20201020191947-73dc6a680cdd+incompatible h1:+0LETFJcCLdIqdtEbVWF1JIxATqM15Y4sLiMcWOYq2U= github.com/docker/docker v17.12.0-ce-rc1.0.20201020191947-73dc6a680cdd+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.0-beta1.0.20201113105859-b6bfff2a628f+incompatible h1:lwpV3629md5omgAKjxPWX17shI7vMRpE3nyb9WHn8pA= +github.com/docker/docker v20.10.0-beta1.0.20201113105859-b6bfff2a628f+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ= github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= @@ -221,6 +215,7 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -264,8 +259,9 @@ github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf h1:7+FW5aGwISbqUtkfmI github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.5 h1:kxhtnfFVi+rYdOALN0B3k9UT86zVJKfBimRaciULW4I= +github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= @@ -333,10 +329,9 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW 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.11.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.3 h1:dB4Bn0tN3wdCzQxnS8r06kV74qN/TAfaIS0bVE8h3jc= github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.4 h1:kz40R/YWls3iqT9zX9AHN3WoVsrAWVyui5sxuLqiXqU= -github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.5 h1:xNCE0uE6yvTPRS+0wGNMHPo3NIpwnk6aluQZ6R6kRcc= +github.com/klauspost/compress v1.11.5/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -388,8 +383,9 @@ github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+S github.com/moby/sys/mountinfo v0.4.0 h1:1KInV3Huv18akCu58V7lzNlt+jFmqlu1EaErnEHE/VM= github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/term v0.0.0-20200429084858-129dac9f73f6/go.mod h1:or9wGItza1sRcM4Wd3dIv8DsFHYQuFsMHEdxUIlUxms= -github.com/moby/term v0.0.0-20200915141129-7f0af18e79f2 h1:SPoLlS9qUUnXcIY4pvA4CTwYjk0Is5f4UPEkeESr53k= github.com/moby/term v0.0.0-20200915141129-7f0af18e79f2/go.mod h1:TjQg8pa4iejrUrjiz0MCtMV38jdMNW4doKSiBrEvCQQ= +github.com/moby/term v0.0.0-20201110203204-bea5bbe245bf h1:Un6PNx5oMK6CCwO3QTUyPiK2mtZnPrpDl5UnZ64eCkw= +github.com/moby/term v0.0.0-20201110203204-bea5bbe245bf/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= github.com/moby/vpnkit v0.4.0/go.mod h1:KyjUrL9cb6ZSNNAUwZfqRjhwwgJ3BJN+kXh0t43WTUQ= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= @@ -544,8 +540,9 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 h1:b6uOv7YOFK0TYG7HtkIgExQo+2RdLuwRft63jn2HWj8= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= @@ -671,8 +668,8 @@ 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/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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= @@ -715,9 +712,7 @@ golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637 h1:O5hKNaGxIT4A8OTMnuh6UpmBdI3SAPxlZ3g0olDrJVM= golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3 h1:kzM6+9dur93BcC2kVlYl34cHU+TYZLanmpSJHVMmL64= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -786,6 +781,7 @@ google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= @@ -844,8 +840,8 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt k8s.io/api v0.0.0-20190620084959-7cf5895f2711 h1:BblVYz/wE5WtBsD/Gvu54KyBUTJMflolzc5I2DTvh50= k8s.io/api v0.0.0-20190620084959-7cf5895f2711/go.mod h1:TBhBqb1AWbBQbW3XRusr7n7E4v2+5ZY8r8sAMnyFC5A= k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719/go.mod h1:I4A+glKBHiTgiEjQiCCQfCAIcIMFGt291SmsvcrFzJA= -k8s.io/apimachinery v0.20.1 h1:LAhz8pKbgR8tUwn7boK+b2HZdt7MiTu2mkYtFMUjTRQ= -k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.20.2 h1:hFx6Sbt1oG0n6DZ+g4bFt5f6BoMkOjKWsQFu077M3Vg= +k8s.io/apimachinery v0.20.2/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/client-go v0.0.0-20190620085101-78d2af792bab h1:E8Fecph0qbNsAbijJJQryKu4Oi9QTp5cVpjTE+nqg6g= k8s.io/client-go v0.0.0-20190620085101-78d2af792bab/go.mod h1:E95RaSlHr79aHaX0aGSwcPNfygDiPKOVXdmivCIZT0k= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= diff --git a/hack/get_ci_vm.sh b/hack/get_ci_vm.sh index 0755470a2..d1e38eb35 100755 --- a/hack/get_ci_vm.sh +++ b/hack/get_ci_vm.sh @@ -11,6 +11,14 @@ # there are no guarantees it will remain in-sync with other automation-related # scripts. Therefore it may not always function for everybody in every # future scenario without updates/modifications/tweaks. +# +# When successful, you will end up connected to a GCP VM with with a clone of +# the upstream podman repository 'master' branch, using a remote named 'origin'. +# If you want to customize this behavior, you will want to use a "hook" script. +# Please use this example carefully, since git setups vary by person, you +# will probably need to make local edits. +# +# https://gist.github.com/cevich/626a0790c0b476d5cd2a5a76fbdae0a1 set -e diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go index dcb2ff751..b2ee63b08 100644 --- a/libpod/boltdb_state.go +++ b/libpod/boltdb_state.go @@ -304,6 +304,7 @@ func (s *BoltState) Refresh() error { // Reset mount count to 0 oldState.MountCount = 0 + oldState.MountPoint = "" newState, err := json.Marshal(oldState) if err != nil { diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go index c06fedd3e..6014fbef3 100644 --- a/libpod/boltdb_state_internal.go +++ b/libpod/boltdb_state_internal.go @@ -497,6 +497,21 @@ func (s *BoltState) getVolumeFromDB(name []byte, volume *Volume, volBkt *bolt.Bu } } + // Retrieve volume driver + if volume.UsesVolumeDriver() { + plugin, err := s.runtime.getVolumePlugin(volume.config.Driver) + if err != nil { + // We want to fail gracefully here, to ensure that we + // can still remove volumes even if their plugin is + // missing. Otherwise, we end up with volumes that + // cannot even be retrieved from the database and will + // cause things like `volume ls` to fail. + logrus.Errorf("Volume %s uses volume plugin %s, but it cannot be accessed - some functionality may not be available: %v", volume.Name(), volume.config.Driver, err) + } else { + volume.plugin = plugin + } + } + // Get the lock lock, err := s.runtime.lockManager.RetrieveLock(volume.config.LockID) if err != nil { diff --git a/libpod/container.go b/libpod/container.go index 96a21736c..58bf95470 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -1012,6 +1012,9 @@ func (c *Container) IDMappings() (storage.IDMappingOptions, error) { // RootUID returns the root user mapping from container func (c *Container) RootUID() int { + if len(c.config.IDMappings.UIDMap) == 1 && c.config.IDMappings.UIDMap[0].Size == 1 { + return c.config.IDMappings.UIDMap[0].HostID + } for _, uidmap := range c.config.IDMappings.UIDMap { if uidmap.ContainerID == 0 { return uidmap.HostID @@ -1022,6 +1025,9 @@ func (c *Container) RootUID() int { // RootGID returns the root user mapping from container func (c *Container) RootGID() int { + if len(c.config.IDMappings.GIDMap) == 1 && c.config.IDMappings.GIDMap[0].Size == 1 { + return c.config.IDMappings.GIDMap[0].HostID + } for _, gidmap := range c.config.IDMappings.GIDMap { if gidmap.ContainerID == 0 { return gidmap.HostID diff --git a/libpod/container_api.go b/libpod/container_api.go index c3e1a23d2..0d62a2dd7 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -210,7 +210,13 @@ func (c *Container) Kill(signal uint) error { } // TODO: Is killing a paused container OK? - if c.state.State != define.ContainerStateRunning { + switch c.state.State { + case define.ContainerStateRunning, define.ContainerStateStopping: + // Note that killing containers in "stopping" state is okay. + // In that state, the Podman is waiting for the runtime to + // stop the container and if that is taking too long, a user + // may have decided to kill the container after all. + default: return errors.Wrapf(define.ErrCtrStateInvalid, "can only kill running containers. %s is in state %s", c.ID(), c.state.State.String()) } @@ -539,7 +545,7 @@ func (c *Container) Cleanup(ctx context.Context) error { } // Check if state is good - if !c.ensureState(define.ContainerStateConfigured, define.ContainerStateCreated, define.ContainerStateStopped, define.ContainerStateExited) { + if !c.ensureState(define.ContainerStateConfigured, define.ContainerStateCreated, define.ContainerStateStopped, define.ContainerStateStopping, define.ContainerStateExited) { return errors.Wrapf(define.ErrCtrStateInvalid, "container %s is running or paused, refusing to clean up", c.ID()) } @@ -703,6 +709,16 @@ type ContainerCheckpointOptions struct { // important to be able to restore a container multiple // times with '--import --name'. IgnoreStaticMAC bool + // IgnoreVolumes tells the API to not export or not to import + // the content of volumes associated with the container + IgnoreVolumes bool + // Pre Checkpoint container and leave container running + PreCheckPoint bool + // Dump container with Pre Checkpoint images + WithPrevious bool + // ImportPrevious tells the API to restore container with two + // images. One is TargetFile, the other is ImportPrevious. + ImportPrevious string } // Checkpoint checkpoints a container @@ -715,6 +731,12 @@ func (c *Container) Checkpoint(ctx context.Context, options ContainerCheckpointO } } + if options.WithPrevious { + if err := c.canWithPrevious(); err != nil { + return err + } + } + if !c.batched { c.lock.Lock() defer c.lock.Unlock() diff --git a/libpod/container_exec.go b/libpod/container_exec.go index fce26acb0..5aee847e1 100644 --- a/libpod/container_exec.go +++ b/libpod/container_exec.go @@ -8,7 +8,6 @@ import ( "strconv" "time" - "github.com/containers/common/pkg/capabilities" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/events" "github.com/containers/storage/pkg/stringid" @@ -973,20 +972,12 @@ func (c *Container) removeAllExecSessions() error { // Make an ExecOptions struct to start the OCI runtime and prepare its exec // bundle. func prepareForExec(c *Container, session *ExecSession) (*ExecOptions, error) { - // TODO: check logic here - should we set Privileged if the container is - // privileged? - var capList []string - if session.Config.Privileged || c.config.Privileged { - capList = capabilities.AllCapabilities() - } - if err := c.createExecBundle(session.ID()); err != nil { return nil, err } opts := new(ExecOptions) opts.Cmd = session.Config.Command - opts.CapAdd = capList opts.Env = session.Config.Environment opts.Terminal = session.Config.Terminal opts.Cwd = session.Config.WorkDir @@ -995,6 +986,7 @@ func prepareForExec(c *Container, session *ExecSession) (*ExecOptions, error) { opts.DetachKeys = session.Config.DetachKeys opts.ExitCommand = session.Config.ExitCommand opts.ExitCommandDelay = session.Config.ExitCommandDelay + opts.Privileged = session.Config.Privileged return opts, nil } diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go index 51474471b..ac7eae56b 100644 --- a/libpod/container_inspect.go +++ b/libpod/container_inspect.go @@ -49,7 +49,7 @@ func (c *Container) Inspect(size bool) (*define.InspectContainerData, error) { return c.inspectLocked(size) } -func (c *Container) getContainerInspectData(size bool, driverData *driver.Data) (*define.InspectContainerData, error) { +func (c *Container) getContainerInspectData(size bool, driverData *define.DriverData) (*define.InspectContainerData, error) { config := c.config runtimeInfo := c.state ctrSpec, err := c.specFromState() @@ -212,7 +212,12 @@ func (c *Container) getInspectMounts(namedVolumes []*ContainerNamedVolume, image return nil, errors.Wrapf(err, "error looking up volume %s in container %s config", volume.Name, c.ID()) } mountStruct.Driver = volFromDB.Driver() - mountStruct.Source = volFromDB.MountPoint() + + mountPoint, err := volFromDB.MountPoint() + if err != nil { + return nil, err + } + mountStruct.Source = mountPoint parseMountOptionsForInspect(volume.Options, &mountStruct) diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 540230c26..b9ea50783 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -1,6 +1,7 @@ package libpod import ( + "bufio" "bytes" "context" "fmt" @@ -134,6 +135,11 @@ func (c *Container) CheckpointPath() string { return filepath.Join(c.bundlePath(), "checkpoint") } +// PreCheckpointPath returns the path to the directory containing the pre-checkpoint-images +func (c *Container) PreCheckPointPath() string { + return filepath.Join(c.bundlePath(), "pre-checkpoint") +} + // AttachSocketPath retrieves the path of the container's attach socket func (c *Container) AttachSocketPath() (string, error) { return c.ociRuntime.AttachSocketPath(c) @@ -758,7 +764,7 @@ func (c *Container) isStopped() (bool, error) { return true, err } - return !c.ensureState(define.ContainerStateRunning, define.ContainerStatePaused), nil + return !c.ensureState(define.ContainerStateRunning, define.ContainerStatePaused, define.ContainerStateStopping), nil } // save container state to the database @@ -1284,10 +1290,49 @@ func (c *Container) stop(timeout uint) error { return err } + // Set the container state to "stopping" and unlock the container + // before handing it over to conmon to unblock other commands. #8501 + // demonstrates nicely that a high stop timeout will block even simple + // commands such as `podman ps` from progressing if the container lock + // is held when busy-waiting for the container to be stopped. + c.state.State = define.ContainerStateStopping + if err := c.save(); err != nil { + return errors.Wrapf(err, "error saving container %s state before stopping", c.ID()) + } + if !c.batched { + c.lock.Unlock() + } + if err := c.ociRuntime.StopContainer(c, timeout, all); err != nil { return err } + if !c.batched { + c.lock.Lock() + if err := c.syncContainer(); err != nil { + switch errors.Cause(err) { + // If the container has already been removed (e.g., via + // the cleanup process), there's nothing left to do. + case define.ErrNoSuchCtr, define.ErrCtrRemoved: + return nil + default: + return err + } + } + } + + // Since we're now subject to a race condition with other processes who + // may have altered the state (and other data), let's check if the + // state has changed. If so, we should return immediately and log a + // warning. + if c.state.State != define.ContainerStateStopping { + logrus.Warnf( + "Container %q state changed from %q to %q while waiting for it to be stopped: discontinuing stop procedure as another process interfered", + c.ID(), define.ContainerStateStopping, c.state.State, + ) + return nil + } + c.newContainerEvent(events.Stop) c.state.PID = 0 @@ -1535,8 +1580,18 @@ func (c *Container) mountNamedVolume(v *ContainerNamedVolume, mountpoint string) return nil, err } + // HACK HACK HACK - copy up into a volume driver is 100% broken + // right now. + if vol.UsesVolumeDriver() { + logrus.Infof("Not copying up into volume %s as it uses a volume driver", vol.Name()) + return vol, nil + } + // If the volume is not empty, we should not copy up. - volMount := vol.MountPoint() + volMount, err := vol.MountPoint() + if err != nil { + return nil, err + } contents, err := ioutil.ReadDir(volMount) if err != nil { return nil, errors.Wrapf(err, "error listing contents of volume %s mountpoint when copying up from container %s", vol.Name(), c.ID()) @@ -1574,7 +1629,11 @@ func (c *Container) chownVolume(volumeName string) error { return err } - if vol.state.NeedsChown { + // TODO: For now, I've disabled chowning volumes owned by non-Podman + // drivers. This may be safe, but it's really going to be a case-by-case + // thing, I think - safest to leave disabled now and reenable later if + // there is a demand. + if vol.state.NeedsChown && !vol.UsesVolumeDriver() { vol.state.NeedsChown = false uid := int(c.config.Spec.Process.User.UID) @@ -1601,7 +1660,10 @@ func (c *Container) chownVolume(volumeName string) error { return err } - mountPoint := vol.MountPoint() + mountPoint, err := vol.MountPoint() + if err != nil { + return err + } if err := os.Lchown(mountPoint, uid, gid); err != nil { return err @@ -1860,16 +1922,26 @@ func (c *Container) writeStringToStaticDir(filename, contents string) (string, e return destFileName, nil } -// appendStringToRundir appends the provided string to the runtimedir file -func (c *Container) appendStringToRundir(destFile, output string) (string, error) { +// appendStringToRunDir appends the provided string to the runtimedir file +func (c *Container) appendStringToRunDir(destFile, output string) (string, error) { destFileName := filepath.Join(c.state.RunDir, destFile) - f, err := os.OpenFile(destFileName, os.O_APPEND|os.O_WRONLY, 0600) + f, err := os.OpenFile(destFileName, os.O_APPEND|os.O_RDWR, 0600) if err != nil { return "", err } defer f.Close() + compareStr := strings.TrimRight(output, "\n") + scanner := bufio.NewScanner(f) + scanner.Split(bufio.ScanLines) + + for scanner.Scan() { + if strings.Compare(scanner.Text(), compareStr) == 0 { + return filepath.Join(c.state.RunDir, destFile), nil + } + } + if _, err := f.WriteString(output); err != nil { return "", errors.Wrapf(err, "unable to write %s", destFileName) } @@ -2023,6 +2095,12 @@ func (c *Container) checkReadyForRemoval() error { return nil } +// canWithPrevious return the stat of the preCheckPoint dir +func (c *Container) canWithPrevious() error { + _, err := os.Stat(c.PreCheckPointPath()) + return err +} + // writeJSONFile marshalls and writes the given data to a JSON file // in the bundle path func (c *Container) writeJSONFile(v interface{}, file string) error { @@ -2094,7 +2172,7 @@ func (c *Container) sortUserVolumes(ctrSpec *spec.Spec) ([]*ContainerNamedVolume // Check for an exit file, and handle one if present func (c *Container) checkExitFile() error { // If the container's not running, nothing to do. - if !c.ensureState(define.ContainerStateRunning, define.ContainerStatePaused) { + if !c.ensureState(define.ContainerStateRunning, define.ContainerStatePaused, define.ContainerStateStopping) { return nil } diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 05b149e03..0553cc59c 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -40,7 +40,6 @@ import ( "github.com/containers/storage/pkg/idtools" securejoin "github.com/cyphar/filepath-securejoin" runcuser "github.com/opencontainers/runc/libcontainer/user" - "github.com/opencontainers/runtime-spec/specs-go" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/opencontainers/selinux/go-selinux/label" @@ -284,7 +283,7 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { return nil, err } - g := generate.NewFromSpec(c.config.Spec) + g := generate.Generator{Config: c.config.Spec} // If network namespace was requested, add it now if c.config.CreateNetNS { @@ -342,7 +341,10 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { if err != nil { return nil, errors.Wrapf(err, "error retrieving volume %s to add to container %s", namedVol.Name, c.ID()) } - mountPoint := volume.MountPoint() + mountPoint, err := volume.MountPoint() + if err != nil { + return nil, err + } volMount := spec.Mount{ Type: "bind", Source: mountPoint, @@ -400,7 +402,7 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { return nil, errors.Wrapf(err, "failed to create TempDir in the %s directory", c.config.StaticDir) } - var overlayMount specs.Mount + var overlayMount spec.Mount if volume.ReadWrite { overlayMount, err = overlay.Mount(contentDir, mountPoint, volume.Dest, c.RootUID(), c.RootGID(), c.runtime.store.GraphOptions()) } else { @@ -529,14 +531,45 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { } } + availableUIDs, availableGIDs, err := rootless.GetAvailableIDMaps() + if err != nil { + return nil, err + } + g.Config.Linux.UIDMappings = rootless.MaybeSplitMappings(g.Config.Linux.UIDMappings, availableUIDs) + g.Config.Linux.GIDMappings = rootless.MaybeSplitMappings(g.Config.Linux.GIDMappings, availableGIDs) + + // Hostname handling: + // If we have a UTS namespace, set Hostname in the OCI spec. + // Set the HOSTNAME environment variable unless explicitly overridden by + // the user (already present in OCI spec). If we don't have a UTS ns, + // set it to the host's hostname instead. + hostname := c.Hostname() + foundUTS := false + for _, i := range c.config.Spec.Linux.Namespaces { if i.Type == spec.UTSNamespace && i.Path == "" { - hostname := c.Hostname() + foundUTS = true g.SetHostname(hostname) - g.AddProcessEnv("HOSTNAME", hostname) break } } + if !foundUTS { + tmpHostname, err := os.Hostname() + if err != nil { + return nil, err + } + hostname = tmpHostname + } + needEnv := true + for _, checkEnv := range g.Config.Process.Env { + if strings.SplitN(checkEnv, "=", 2)[0] == "HOSTNAME" { + needEnv = false + break + } + } + if needEnv { + g.AddProcessEnv("HOSTNAME", hostname) + } if c.config.UTSNsCtr != "" { if err := c.addNamespaceContainer(&g, UTSNS, c.config.UTSNsCtr, spec.UTSNamespace); err != nil { @@ -767,11 +800,11 @@ func (c *Container) addNamespaceContainer(g *generate.Generator, ns LinuxNS, ctr return nil } -func (c *Container) exportCheckpoint(dest string, ignoreRootfs bool) error { - if (len(c.config.NamedVolumes) > 0) || (len(c.Dependencies()) > 0) { - return errors.Errorf("Cannot export checkpoints of containers with named volumes or dependencies") +func (c *Container) exportCheckpoint(options ContainerCheckpointOptions) error { + if len(c.Dependencies()) > 0 { + return errors.Errorf("Cannot export checkpoints of containers with dependencies") } - logrus.Debugf("Exporting checkpoint image of container %q to %q", c.ID(), dest) + logrus.Debugf("Exporting checkpoint image of container %q to %q", c.ID(), options.TargetFile) includeFiles := []string{ "checkpoint", @@ -781,10 +814,13 @@ func (c *Container) exportCheckpoint(dest string, ignoreRootfs bool) error { "spec.dump", "network.status"} + if options.PreCheckPoint { + includeFiles[0] = "pre-checkpoint" + } // Get root file-system changes included in the checkpoint archive rootfsDiffPath := filepath.Join(c.bundlePath(), "rootfs-diff.tar") deleteFilesList := filepath.Join(c.bundlePath(), "deleted.files") - if !ignoreRootfs { + if !options.IgnoreRootfs { // To correctly track deleted files, let's go through the output of 'podman diff' tarFiles, err := c.runtime.GetDiff("", c.ID()) if err != nil { @@ -847,6 +883,55 @@ func (c *Container) exportCheckpoint(dest string, ignoreRootfs bool) error { } } + // Folder containing archived volumes that will be included in the export + expVolDir := filepath.Join(c.bundlePath(), "volumes") + + // Create an archive for each volume associated with the container + if !options.IgnoreVolumes { + if err := os.MkdirAll(expVolDir, 0700); err != nil { + return errors.Wrapf(err, "error creating volumes export directory %q", expVolDir) + } + + for _, v := range c.config.NamedVolumes { + volumeTarFilePath := filepath.Join("volumes", v.Name+".tar") + volumeTarFileFullPath := filepath.Join(c.bundlePath(), volumeTarFilePath) + + volumeTarFile, err := os.Create(volumeTarFileFullPath) + if err != nil { + return errors.Wrapf(err, "error creating %q", volumeTarFileFullPath) + } + + volume, err := c.runtime.GetVolume(v.Name) + if err != nil { + return err + } + + mp, err := volume.MountPoint() + if err != nil { + return err + } + if mp == "" { + return errors.Wrapf(define.ErrInternal, "volume %s is not mounted, cannot export", volume.Name()) + } + + input, err := archive.TarWithOptions(mp, &archive.TarOptions{ + Compression: archive.Uncompressed, + IncludeSourceDir: true, + }) + if err != nil { + return errors.Wrapf(err, "error reading volume directory %q", v.Dest) + } + + _, err = io.Copy(volumeTarFile, input) + if err != nil { + return err + } + volumeTarFile.Close() + + includeFiles = append(includeFiles, volumeTarFilePath) + } + } + input, err := archive.TarWithOptions(c.bundlePath(), &archive.TarOptions{ Compression: archive.Gzip, IncludeSourceDir: true, @@ -857,13 +942,13 @@ func (c *Container) exportCheckpoint(dest string, ignoreRootfs bool) error { return errors.Wrapf(err, "error reading checkpoint directory %q", c.ID()) } - outFile, err := os.Create(dest) + outFile, err := os.Create(options.TargetFile) if err != nil { - return errors.Wrapf(err, "error creating checkpoint export file %q", dest) + return errors.Wrapf(err, "error creating checkpoint export file %q", options.TargetFile) } defer outFile.Close() - if err := os.Chmod(dest, 0600); err != nil { + if err := os.Chmod(options.TargetFile, 0600); err != nil { return err } @@ -875,15 +960,19 @@ func (c *Container) exportCheckpoint(dest string, ignoreRootfs bool) error { os.Remove(rootfsDiffPath) os.Remove(deleteFilesList) + if !options.IgnoreVolumes { + os.RemoveAll(expVolDir) + } + return nil } func (c *Container) checkpointRestoreSupported() error { if !criu.CheckForCriu() { - return errors.Errorf("Checkpoint/Restore requires at least CRIU %d", criu.MinCriuVersion) + return errors.Errorf("checkpoint/restore requires at least CRIU %d", criu.MinCriuVersion) } if !c.ociRuntime.SupportsCheckpoint() { - return errors.Errorf("Configured runtime does not support checkpoint/restore") + return errors.Errorf("configured runtime does not support checkpoint/restore") } return nil } @@ -915,7 +1004,7 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO } if c.AutoRemove() && options.TargetFile == "" { - return errors.Errorf("Cannot checkpoint containers that have been started with '--rm' unless '--export' is used") + return errors.Errorf("cannot checkpoint containers that have been started with '--rm' unless '--export' is used") } if err := c.checkpointRestoreLabelLog("dump.log"); err != nil { @@ -939,15 +1028,24 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO defer c.newContainerEvent(events.Checkpoint) + // There is a bug from criu: https://github.com/checkpoint-restore/criu/issues/116 + // We have to change the symbolic link from absolute path to relative path + if options.WithPrevious { + os.Remove(path.Join(c.CheckpointPath(), "parent")) + if err := os.Symlink("../pre-checkpoint", path.Join(c.CheckpointPath(), "parent")); err != nil { + return err + } + } + if options.TargetFile != "" { - if err = c.exportCheckpoint(options.TargetFile, options.IgnoreRootfs); err != nil { + if err = c.exportCheckpoint(options); err != nil { return err } } logrus.Debugf("Checkpointed container %s", c.ID()) - if !options.KeepRunning { + if !options.KeepRunning && !options.PreCheckPoint { c.state.State = define.ContainerStateStopped // Cleanup Storage and Network @@ -956,7 +1054,7 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO } } - if !options.Keep { + if !options.Keep && !options.PreCheckPoint { cleanup := []string{ "dump.log", "stats-dump", @@ -992,18 +1090,33 @@ func (c *Container) importCheckpoint(input string) error { } err = archive.Untar(archiveFile, c.bundlePath(), options) if err != nil { - return errors.Wrapf(err, "Unpacking of checkpoint archive %s failed", input) + return errors.Wrapf(err, "unpacking of checkpoint archive %s failed", input) } // Make sure the newly created config.json exists on disk g := generate.Generator{Config: c.config.Spec} if err = c.saveSpec(g.Config); err != nil { - return errors.Wrap(err, "Saving imported container specification for restore failed") + return errors.Wrap(err, "saving imported container specification for restore failed") } return nil } +func (c *Container) importPreCheckpoint(input string) error { + archiveFile, err := os.Open(input) + if err != nil { + return errors.Wrap(err, "failed to open pre-checkpoint archive for import") + } + + defer archiveFile.Close() + + err = archive.Untar(archiveFile, c.bundlePath(), nil) + if err != nil { + return errors.Wrapf(err, "Unpacking of pre-checkpoint archive %s failed", input) + } + return nil +} + func (c *Container) restore(ctx context.Context, options ContainerCheckpointOptions) (retErr error) { if err := c.checkpointRestoreSupported(); err != nil { return err @@ -1013,6 +1126,12 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti return errors.Wrapf(define.ErrCtrStateInvalid, "container %s is running or paused, cannot restore", c.ID()) } + if options.ImportPrevious != "" { + if err := c.importPreCheckpoint(options.ImportPrevious); err != nil { + return err + } + } + if options.TargetFile != "" { if err := c.importCheckpoint(options.TargetFile); err != nil { return err @@ -1022,7 +1141,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti // Let's try to stat() CRIU's inventory file. If it does not exist, it makes // no sense to try a restore. This is a minimal check if a checkpoint exist. if _, err := os.Stat(filepath.Join(c.CheckpointPath(), "inventory.img")); os.IsNotExist(err) { - return errors.Wrapf(err, "A complete checkpoint for this container cannot be found, cannot restore") + return errors.Wrapf(err, "a complete checkpoint for this container cannot be found, cannot restore") } if err := c.checkpointRestoreLabelLog("restore.log"); err != nil { @@ -1170,6 +1289,36 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti return err } + // When restoring from an imported archive, allow restoring the content of volumes. + // Volumes are created in setupContainer() + if options.TargetFile != "" && !options.IgnoreVolumes { + for _, v := range c.config.NamedVolumes { + volumeFilePath := filepath.Join(c.bundlePath(), "volumes", v.Name+".tar") + + volumeFile, err := os.Open(volumeFilePath) + if err != nil { + return errors.Wrapf(err, "failed to open volume file %s", volumeFilePath) + } + defer volumeFile.Close() + + volume, err := c.runtime.GetVolume(v.Name) + if err != nil { + return errors.Wrapf(err, "failed to retrieve volume %s", v.Name) + } + + mountPoint, err := volume.MountPoint() + if err != nil { + return err + } + if mountPoint == "" { + return errors.Wrapf(err, "unable to import volume %s as it is not mounted", volume.Name()) + } + if err := archive.UntarUncompressed(volumeFile, mountPoint, nil); err != nil { + return errors.Wrapf(err, "Failed to extract volume %s to %s", volumeFilePath, mountPoint) + } + } + } + // Before actually restarting the container, apply the root file-system changes if !options.IgnoreRootfs { rootfsDiffPath := filepath.Join(c.bundlePath(), "rootfs-diff.tar") @@ -1222,6 +1371,10 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti if err != nil { logrus.Debugf("Non-fatal: removal of checkpoint directory (%s) failed: %v", c.CheckpointPath(), err) } + err = os.RemoveAll(c.PreCheckPointPath()) + if err != nil { + logrus.Debugf("Non-fatal: removal of pre-checkpoint directory (%s) failed: %v", c.PreCheckPointPath(), err) + } cleanup := [...]string{"restore.log", "dump.log", "stats-dump", "stats-restore", "network.status", "rootfs-diff.tar", "deleted.files"} for _, del := range cleanup { file := filepath.Join(c.bundlePath(), del) @@ -1382,18 +1535,14 @@ func (c *Container) makeBindMounts() error { } if newPasswd != "" { // Make /etc/passwd - if _, ok := c.state.BindMounts["/etc/passwd"]; ok { - // If it already exists, delete so we can recreate - delete(c.state.BindMounts, "/etc/passwd") - } + // If it already exists, delete so we can recreate + delete(c.state.BindMounts, "/etc/passwd") c.state.BindMounts["/etc/passwd"] = newPasswd } if newGroup != "" { // Make /etc/group - if _, ok := c.state.BindMounts["/etc/group"]; ok { - // If it already exists, delete so we can recreate - delete(c.state.BindMounts, "/etc/group") - } + // If it already exists, delete so we can recreate + delete(c.state.BindMounts, "/etc/group") c.state.BindMounts["/etc/group"] = newGroup } @@ -1610,7 +1759,7 @@ func (c *Container) generateHosts(path string) (string, error) { // FIXME. Path should be used by this function,but I am not sure what is correct; remove //lint // once this is fixed func (c *Container) appendHosts(path string, netCtr *Container) (string, error) { //nolint - return c.appendStringToRundir("hosts", netCtr.getHosts()) + return c.appendStringToRunDir("hosts", netCtr.getHosts()) } // getHosts finds the pertinent information for a container's host file in its config and state diff --git a/libpod/container_log.go b/libpod/container_log.go index e58503bd3..f16e08353 100644 --- a/libpod/container_log.go +++ b/libpod/container_log.go @@ -82,7 +82,7 @@ func (c *Container) readFromLogFile(ctx context.Context, options *logs.LogOption if nll.Partial() { partial += nll.Msg continue - } else if !nll.Partial() && len(partial) > 1 { + } else if !nll.Partial() && len(partial) > 0 { nll.Msg = partial + nll.Msg partial = "" } diff --git a/libpod/define/container_inspect.go b/libpod/define/container_inspect.go index c61f7c159..9a93e2ffd 100644 --- a/libpod/define/container_inspect.go +++ b/libpod/define/container_inspect.go @@ -4,7 +4,6 @@ import ( "time" "github.com/containers/image/v5/manifest" - "github.com/containers/podman/v2/libpod/driver" ) // InspectContainerConfig holds further data about how a container was initially @@ -635,7 +634,7 @@ type InspectContainerData struct { EffectiveCaps []string `json:"EffectiveCaps"` BoundingCaps []string `json:"BoundingCaps"` ExecIDs []string `json:"ExecIDs"` - GraphDriver *driver.Data `json:"GraphDriver"` + GraphDriver *DriverData `json:"GraphDriver"` SizeRw *int64 `json:"SizeRw,omitempty"` SizeRootFs int64 `json:"SizeRootFs,omitempty"` Mounts []InspectMount `json:"Mounts"` @@ -700,3 +699,9 @@ type InspectExecProcess struct { // User is the user the exec session was started as. User string `json:"user"` } + +// DriverData handles the data for a storage driver +type DriverData struct { + Name string `json:"Name"` + Data map[string]string `json:"Data"` +} diff --git a/libpod/define/containerstate.go b/libpod/define/containerstate.go index 825e77387..5d2bc9099 100644 --- a/libpod/define/containerstate.go +++ b/libpod/define/containerstate.go @@ -28,6 +28,9 @@ const ( // ContainerStateRemoving indicates the container is in the process of // being removed. ContainerStateRemoving ContainerStatus = iota + // ContainerStateStopping indicates the container is in the process of + // being stopped. + ContainerStateStopping ContainerStatus = iota ) // ContainerStatus returns a string representation for users @@ -50,6 +53,8 @@ func (t ContainerStatus) String() string { return "exited" case ContainerStateRemoving: return "removing" + case ContainerStateStopping: + return "stopping" } return "bad state" } diff --git a/libpod/define/errors.go b/libpod/define/errors.go index b96d36429..d37bc397e 100644 --- a/libpod/define/errors.go +++ b/libpod/define/errors.go @@ -2,6 +2,7 @@ package define import ( "errors" + "fmt" ) var ( @@ -34,6 +35,10 @@ var ( // aliases. ErrNoAliases = errors.New("no aliases for container") + // ErrMissingPlugin indicates that the requested operation requires a + // plugin that is not present on the system or in the configuration. + ErrMissingPlugin = errors.New("required plugin missing") + // ErrCtrExists indicates a container with the same name or ID already // exists ErrCtrExists = errors.New("container already exists") @@ -181,4 +186,16 @@ var ( // ErrNoNetwork indicates that a container has no net namespace, like network=none ErrNoNetwork = errors.New("container has no network namespace") + + // ErrSetSecurityAttribute indicates that a request to set a container's security attribute + // was not possible. + ErrSetSecurityAttribute = fmt.Errorf("%w: unable to assign security attribute", ErrOCIRuntime) + + // ErrGetSecurityAttribute indicates that a request to get a container's security attribute + // was not possible. + ErrGetSecurityAttribute = fmt.Errorf("%w: unable to get security attribute", ErrOCIRuntime) + + // ErrSecurityAttribute indicates that an error processing security attributes + // for the container + ErrSecurityAttribute = fmt.Errorf("%w: unable to process security attribute", ErrOCIRuntime) ) diff --git a/libpod/define/volume_inspect.go b/libpod/define/volume_inspect.go new file mode 100644 index 000000000..20602ea16 --- /dev/null +++ b/libpod/define/volume_inspect.go @@ -0,0 +1,51 @@ +package define + +import ( + "time" +) + +// InspectVolumeData is the output of Inspect() on a volume. It is matched to +// the format of 'docker volume inspect'. +type InspectVolumeData struct { + // Name is the name of the volume. + Name string `json:"Name"` + // Driver is the driver used to create the volume. + // If set to "local" or "", the Local driver (Podman built-in code) is + // used to service the volume; otherwise, a volume plugin with the given + // name is used to mount and manage the volume. + Driver string `json:"Driver"` + // Mountpoint is the path on the host where the volume is mounted. + Mountpoint string `json:"Mountpoint"` + // CreatedAt is the date and time the volume was created at. This is not + // stored for older Libpod volumes; if so, it will be omitted. + CreatedAt time.Time `json:"CreatedAt,omitempty"` + // Status is used to return information on the volume's current state, + // if the volume was created using a volume plugin (uses a Driver that + // is not the local driver). + // Status is provided to us by an external program, so no guarantees are + // made about its format or contents. Further, it is an optional field, + // so it may not be set even in cases where a volume plugin is in use. + Status map[string]interface{} `json:"Status,omitempty"` + // Labels includes the volume's configured labels, key:value pairs that + // can be passed during volume creation to provide information for third + // party tools. + Labels map[string]string `json:"Labels"` + // Scope is unused and provided solely for Docker compatibility. It is + // unconditionally set to "local". + Scope string `json:"Scope"` + // Options is a set of options that were used when creating the volume. + // For the Local driver, these are mount options that will be used to + // determine how a local filesystem is mounted; they are handled as + // parameters to Mount in a manner described in the volume create + // manpage. + // For non-local drivers, these are passed as-is to the volume plugin. + Options map[string]string `json:"Options"` + // UID is the UID that the volume was created with. + UID int `json:"UID,omitempty"` + // GID is the GID that the volume was created with. + GID int `json:"GID,omitempty"` + // Anonymous indicates that the volume was created as an anonymous + // volume for a specific container, and will be be removed when any + // container using it is removed. + Anonymous bool `json:"Anonymous,omitempty"` +} diff --git a/libpod/driver/driver.go b/libpod/driver/driver.go index 85eda5a21..de71c1f6e 100644 --- a/libpod/driver/driver.go +++ b/libpod/driver/driver.go @@ -1,40 +1,17 @@ package driver import ( - cstorage "github.com/containers/storage" + "github.com/containers/podman/v2/libpod/define" + "github.com/containers/storage" ) -// Data handles the data for a storage driver -type Data struct { - Name string `json:"Name"` - Data map[string]string `json:"Data"` -} - -// GetDriverName returns the name of the driver for the given store -func GetDriverName(store cstorage.Store) (string, error) { - driver, err := store.GraphDriver() - if err != nil { - return "", err - } - return driver.String(), nil -} - -// GetDriverMetadata returns the metadata regarding the driver for the layer in the given store -func GetDriverMetadata(store cstorage.Store, layerID string) (map[string]string, error) { +// GetDriverData returns information on a given store's running graph driver. +func GetDriverData(store storage.Store, layerID string) (*define.DriverData, error) { driver, err := store.GraphDriver() if err != nil { return nil, err } - return driver.Metadata(layerID) -} - -// GetDriverData returns the Data struct with information of the driver used by the store -func GetDriverData(store cstorage.Store, layerID string) (*Data, error) { - name, err := GetDriverName(store) - if err != nil { - return nil, err - } - metaData, err := GetDriverMetadata(store, layerID) + metaData, err := driver.Metadata(layerID) if err != nil { return nil, err } @@ -42,8 +19,8 @@ func GetDriverData(store cstorage.Store, layerID string) (*Data, error) { delete(metaData, "MergedDir") } - return &Data{ - Name: name, + return &define.DriverData{ + Name: driver.String(), Data: metaData, }, nil } diff --git a/libpod/events/journal_linux.go b/libpod/events/journal_linux.go index 71c638017..8b7e448b1 100644 --- a/libpod/events/journal_linux.go +++ b/libpod/events/journal_linux.go @@ -84,7 +84,11 @@ func (e EventJournalD) Read(ctx context.Context, options ReadOptions) error { if err != nil { return err } - + defer func() { + if err := j.Close(); err != nil { + logrus.Errorf("Unable to close journal :%v", err) + } + }() // match only podman journal entries podmanJournal := sdjournal.Match{Field: "SYSLOG_IDENTIFIER", Value: "podman"} if err := j.AddMatch(podmanJournal.String()); err != nil { @@ -112,7 +116,6 @@ func (e EventJournalD) Read(ctx context.Context, options ReadOptions) error { if err != nil { return errors.Wrap(err, "failed to get journal cursor") } - for { select { case <-ctx.Done(): diff --git a/libpod/image/image.go b/libpod/image/image.go index 5c3f3b9e4..d732aecfe 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -17,7 +17,6 @@ import ( "github.com/containers/common/pkg/retry" cp "github.com/containers/image/v5/copy" "github.com/containers/image/v5/directory" - "github.com/containers/image/v5/docker/archive" dockerarchive "github.com/containers/image/v5/docker/archive" "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/image" @@ -30,6 +29,7 @@ import ( "github.com/containers/image/v5/transports" "github.com/containers/image/v5/transports/alltransports" "github.com/containers/image/v5/types" + "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/driver" "github.com/containers/podman/v2/libpod/events" "github.com/containers/podman/v2/pkg/inspect" @@ -37,7 +37,6 @@ import ( "github.com/containers/podman/v2/pkg/util" "github.com/containers/storage" digest "github.com/opencontainers/go-digest" - imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" ociv1 "github.com/opencontainers/image-spec/specs-go/v1" opentracing "github.com/opentracing/opentracing-go" "github.com/pkg/errors" @@ -185,7 +184,7 @@ func (ir *Runtime) SaveImages(ctx context.Context, namesOrIDs []string, format s sys := GetSystemContext("", "", false) - archWriter, err := archive.NewWriter(sys, outputFile) + archWriter, err := dockerarchive.NewWriter(sys, outputFile) if err != nil { return err } @@ -291,7 +290,7 @@ func (ir *Runtime) LoadAllImagesFromDockerArchive(ctx context.Context, fileName } sc := GetSystemContext(signaturePolicyPath, "", false) - reader, err := archive.NewReader(sc, fileName) + reader, err := dockerarchive.NewReader(sc, fileName) if err != nil { return nil, err } @@ -497,7 +496,7 @@ func (ir *Runtime) getLocalImage(inputName string) (string, *storage.Image, erro return inputName, repoImage, nil } - return "", nil, errors.Wrapf(ErrNoSuchImage, err.Error()) + return "", nil, err } // ID returns the image ID as a string @@ -972,7 +971,7 @@ func (i *Image) toImageRef(ctx context.Context) (types.Image, error) { } // DriverData gets the driver data from the store on a layer -func (i *Image) DriverData() (*driver.Data, error) { +func (i *Image) DriverData() (*define.DriverData, error) { return driver.GetDriverData(i.imageruntime.store, i.TopLayer()) } @@ -1148,7 +1147,7 @@ func (i *Image) GetLabel(ctx context.Context, label string) (string, error) { } for k, v := range labels { - if strings.ToLower(k) == strings.ToLower(label) { + if strings.EqualFold(k, label) { return v, nil } } @@ -1326,7 +1325,7 @@ func (ir *Runtime) Import(ctx context.Context, path, reference string, writer io annotations := make(map[string]string) - // config imgspecv1.Image + // config ociv1.Image err = updater.ConfigUpdate(imageConfig, annotations) if err != nil { return nil, errors.Wrapf(err, "error updating image config") @@ -1435,7 +1434,7 @@ func (i *Image) IsParent(ctx context.Context) (bool, error) { // historiesMatch returns the number of entries in the histories which have the // same contents -func historiesMatch(a, b []imgspecv1.History) int { +func historiesMatch(a, b []ociv1.History) int { i := 0 for i < len(a) && i < len(b) { if a[i].Created != nil && b[i].Created == nil { @@ -1468,7 +1467,7 @@ func historiesMatch(a, b []imgspecv1.History) int { // areParentAndChild checks diff ID and history in the two images and return // true if the second should be considered to be directly based on the first -func areParentAndChild(parent, child *imgspecv1.Image) bool { +func areParentAndChild(parent, child *ociv1.Image) bool { // the child and candidate parent should share all of the // candidate parent's diff IDs, which together would have // controlled which layers were used @@ -1621,7 +1620,7 @@ func (i *Image) Save(ctx context.Context, source, format, output string, moreTag if err != nil { return errors.Wrapf(err, "error getting the OCI directory ImageReference for (%q, %q)", output, destImageName) } - manifestType = imgspecv1.MediaTypeImageManifest + manifestType = ociv1.MediaTypeImageManifest case "docker-dir": destRef, err = directory.NewReference(output) if err != nil { diff --git a/libpod/image/prune.go b/libpod/image/prune.go index b38265a7e..587c99333 100644 --- a/libpod/image/prune.go +++ b/libpod/image/prune.go @@ -6,6 +6,7 @@ import ( "time" "github.com/containers/podman/v2/libpod/events" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/containers/podman/v2/pkg/timetype" "github.com/containers/storage" "github.com/pkg/errors" @@ -28,7 +29,7 @@ func generatePruneFilterFuncs(filter, filterValue string) (ImageFilter, error) { return false } for labelKey, labelValue := range labels { - if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) { + if labelKey == filterKey && (filterValue == "" || labelValue == filterValue) { return true } } @@ -110,7 +111,8 @@ func (ir *Runtime) GetPruneImages(ctx context.Context, all bool, filterFuncs []I // PruneImages prunes dangling and optionally all unused images from the local // image store -func (ir *Runtime) PruneImages(ctx context.Context, all bool, filter []string) ([]string, error) { +func (ir *Runtime) PruneImages(ctx context.Context, all bool, filter []string) ([]*reports.PruneReport, error) { + preports := make([]*reports.PruneReport, 0) filterFuncs := make([]ImageFilter, 0, len(filter)) for _, f := range filter { filterSplit := strings.SplitN(f, "=", 2) @@ -125,7 +127,6 @@ func (ir *Runtime) PruneImages(ctx context.Context, all bool, filter []string) ( filterFuncs = append(filterFuncs, generatedFunc) } - pruned := []string{} prev := 0 for { toPrune, err := ir.GetPruneImages(ctx, all, filterFuncs) @@ -143,6 +144,13 @@ func (ir *Runtime) PruneImages(ctx context.Context, all bool, filter []string) ( if err != nil { return nil, err } + nameOrID := img.ID() + s, err := img.Size(ctx) + imgSize := *s + if err != nil { + logrus.Warnf("Failed to collect image size for: %s, %s", nameOrID, err) + imgSize = 0 + } if err := img.Remove(ctx, false); err != nil { if errors.Cause(err) == storage.ErrImageUsedByContainer { logrus.Warnf("Failed to prune image %s as it is in use: %v.\nA container associated with containers/storage (e.g., Buildah, CRI-O, etc.) maybe associated with this image.\nUsing the rmi command with the --force option will remove the container and image, but may cause failures for other dependent systems.", img.ID(), err) @@ -151,13 +159,18 @@ func (ir *Runtime) PruneImages(ctx context.Context, all bool, filter []string) ( return nil, errors.Wrap(err, "failed to prune image") } defer img.newImageEvent(events.Prune) - nameOrID := img.ID() + if len(repotags) > 0 { nameOrID = repotags[0] } - pruned = append(pruned, nameOrID) + + preports = append(preports, &reports.PruneReport{ + Id: nameOrID, + Err: nil, + Size: uint64(imgSize), + }) } } - return pruned, nil + return preports, nil } diff --git a/libpod/image/pull.go b/libpod/image/pull.go index c37929927..996b5995a 100644 --- a/libpod/image/pull.go +++ b/libpod/image/pull.go @@ -11,7 +11,6 @@ import ( cp "github.com/containers/image/v5/copy" "github.com/containers/image/v5/directory" "github.com/containers/image/v5/docker" - "github.com/containers/image/v5/docker/archive" dockerarchive "github.com/containers/image/v5/docker/archive" ociarchive "github.com/containers/image/v5/oci/archive" oci "github.com/containers/image/v5/oci/layout" @@ -130,7 +129,7 @@ func (ir *Runtime) getSinglePullRefPairGoal(srcRef types.ImageReference, destNam // getPullRefPairsFromDockerArchiveReference returns a slice of pullRefPairs // for the specified docker reference and the corresponding archive.Reader. -func (ir *Runtime) getPullRefPairsFromDockerArchiveReference(ctx context.Context, reader *archive.Reader, ref types.ImageReference, sc *types.SystemContext) ([]pullRefPair, error) { +func (ir *Runtime) getPullRefPairsFromDockerArchiveReference(ctx context.Context, reader *dockerarchive.Reader, ref types.ImageReference, sc *types.SystemContext) ([]pullRefPair, error) { destNames, err := reader.ManifestTagsForReference(ref) if err != nil { return nil, err @@ -178,7 +177,7 @@ func (ir *Runtime) pullGoalFromImageReference(ctx context.Context, srcRef types. // supports pulling from docker-archive, oci, and registries switch srcRef.Transport().Name() { case DockerArchive: - reader, readerRef, err := archive.NewReaderForReference(sc, srcRef) + reader, readerRef, err := dockerarchive.NewReaderForReference(sc, srcRef) if err != nil { return nil, err } @@ -432,7 +431,7 @@ func checkRemoteImageForLabel(ctx context.Context, label string, imageInfo pullR } // Labels are case insensitive; so we iterate instead of simple lookup for k := range remoteInspect.Labels { - if strings.ToLower(label) == strings.ToLower(k) { + if strings.EqualFold(label, k) { return nil } } diff --git a/libpod/image/utils.go b/libpod/image/utils.go index 727c73a71..5e7fed5c6 100644 --- a/libpod/image/utils.go +++ b/libpod/image/utils.go @@ -45,7 +45,8 @@ func findImageInRepotags(search imageParts, images []*Image) (*storage.Image, er } } if len(candidates) == 0 { - return nil, errors.Errorf("unable to find a name and tag match for %s in repotags", searchName) + + return nil, errors.Wrapf(define.ErrNoSuchImage, "unable to find a name and tag match for %s in repotags", searchName) } // If more then one candidate and the candidates all have same name diff --git a/libpod/in_memory_state.go b/libpod/in_memory_state.go index 6c0cde531..9285589b1 100644 --- a/libpod/in_memory_state.go +++ b/libpod/in_memory_state.go @@ -437,12 +437,8 @@ func (s *InMemoryState) RemoveContainer(ctr *Container) error { } // Remove our network aliases - if _, ok := s.ctrNetworkAliases[ctr.ID()]; ok { - delete(s.ctrNetworkAliases, ctr.ID()) - } - if _, ok := s.ctrNetworks[ctr.ID()]; ok { - delete(s.ctrNetworks, ctr.ID()) - } + delete(s.ctrNetworkAliases, ctr.ID()) + delete(s.ctrNetworks, ctr.ID()) return nil } @@ -680,9 +676,7 @@ func (s *InMemoryState) NetworkDisconnect(ctr *Container, network string) error ctrAliases = make(map[string][]string) s.ctrNetworkAliases[ctr.ID()] = ctrAliases } - if _, ok := ctrAliases[network]; ok { - delete(ctrAliases, network) - } + delete(ctrAliases, network) return nil } @@ -1523,12 +1517,8 @@ func (s *InMemoryState) RemoveContainerFromPod(pod *Pod, ctr *Container) error { } // Remove our network aliases - if _, ok := s.ctrNetworkAliases[ctr.ID()]; ok { - delete(s.ctrNetworkAliases, ctr.ID()) - } - if _, ok := s.ctrNetworks[ctr.ID()]; ok { - delete(s.ctrNetworks, ctr.ID()) - } + delete(s.ctrNetworkAliases, ctr.ID()) + delete(s.ctrNetworks, ctr.ID()) return nil } diff --git a/libpod/network/netconflist.go b/libpod/network/netconflist.go index bf7d03501..165a9067b 100644 --- a/libpod/network/netconflist.go +++ b/libpod/network/netconflist.go @@ -216,7 +216,7 @@ func IfPassesFilter(netconf *libcni.NetworkConfigList, filters map[string][]stri filterValue = "" } for labelKey, labelValue := range labels { - if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) { + if labelKey == filterKey && (filterValue == "" || labelValue == filterValue) { result = true continue outer } diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index be6867399..addf1814c 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -685,7 +685,7 @@ func (r *Runtime) setupNetNS(ctr *Container) error { return errors.Wrapf(err, "failed to generate random netns name") } - nsPath := fmt.Sprintf("/var/run/netns/cni-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) + nsPath := fmt.Sprintf("/run/netns/cni-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) if err := os.MkdirAll(filepath.Dir(nsPath), 0711); err != nil { return err diff --git a/libpod/oci.go b/libpod/oci.go index 157c42c38..6948e6425 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -151,8 +151,6 @@ type OCIRuntime interface { type ExecOptions struct { // Cmd is the command to execute. Cmd []string - // CapAdd is a set of capabilities to add to the executed command. - CapAdd []string // Env is a set of environment variables to add to the container. Env map[string]string // Terminal is whether to create a new TTY for the exec session. @@ -181,6 +179,8 @@ type ExecOptions struct { // ExitCommandDelay is a delay (in seconds) between the exec session // exiting, and the exit command being invoked. ExitCommandDelay uint + // Privileged indicates the execed process will be launched in Privileged mode + Privileged bool } // HTTPAttachStreams informs the HTTPAttach endpoint which of the container's diff --git a/libpod/oci_attach_linux.go b/libpod/oci_attach_linux.go index fbc95510e..4556eba94 100644 --- a/libpod/oci_attach_linux.go +++ b/libpod/oci_attach_linux.go @@ -28,6 +28,15 @@ const ( AttachPipeStderr = 3 ) +func openUnixSocket(path string) (*net.UnixConn, error) { + fd, err := unix.Open(path, unix.O_PATH, 0) + if err != nil { + return nil, err + } + defer unix.Close(fd) + return net.DialUnix("unixpacket", nil, &net.UnixAddr{Name: fmt.Sprintf("/proc/self/fd/%d", fd), Net: "unixpacket"}) +} + // Attach to the given container // Does not check if state is appropriate // started is only required if startContainer is true @@ -52,11 +61,10 @@ func (c *Container) attach(streams *define.AttachStreams, keys string, resize <- if err != nil { return err } - socketPath := buildSocketPath(attachSock) - conn, err := net.DialUnix("unixpacket", nil, &net.UnixAddr{Name: socketPath, Net: "unixpacket"}) + conn, err := openUnixSocket(attachSock) if err != nil { - return errors.Wrapf(err, "failed to connect to container's attach socket: %v", socketPath) + return errors.Wrapf(err, "failed to connect to container's attach socket: %v", attachSock) } defer func() { if err := conn.Close(); err != nil { @@ -124,7 +132,6 @@ func (c *Container) attachToExec(streams *define.AttachStreams, keys *string, se if err != nil { return err } - socketPath := buildSocketPath(sockPath) // 2: read from attachFd that the parent process has set up the console socket if _, err := readConmonPipeData(attachFd, ""); err != nil { @@ -132,9 +139,9 @@ func (c *Container) attachToExec(streams *define.AttachStreams, keys *string, se } // 2: then attach - conn, err := net.DialUnix("unixpacket", nil, &net.UnixAddr{Name: socketPath, Net: "unixpacket"}) + conn, err := openUnixSocket(sockPath) if err != nil { - return errors.Wrapf(err, "failed to connect to container's attach socket: %v", socketPath) + return errors.Wrapf(err, "failed to connect to container's attach socket: %v", sockPath) } defer func() { if err := conn.Close(); err != nil { @@ -182,16 +189,6 @@ func registerResizeFunc(resize <-chan remotecommand.TerminalSize, bundlePath str }) } -func buildSocketPath(socketPath string) string { - maxUnixLength := unixPathLength() - if maxUnixLength < len(socketPath) { - socketPath = socketPath[0:maxUnixLength] - } - - logrus.Debug("connecting to socket ", socketPath) - return socketPath -} - func setupStdioChannels(streams *define.AttachStreams, conn *net.UnixConn, detachKeys []byte) (chan error, chan error) { receiveStdoutError := make(chan error) go func() { diff --git a/libpod/oci_attach_linux_cgo.go b/libpod/oci_attach_linux_cgo.go deleted file mode 100644 index d81243360..000000000 --- a/libpod/oci_attach_linux_cgo.go +++ /dev/null @@ -1,11 +0,0 @@ -//+build linux,cgo - -package libpod - -//#include <sys/un.h> -// extern int unix_path_length(){struct sockaddr_un addr; return sizeof(addr.sun_path) - 1;} -import "C" - -func unixPathLength() int { - return int(C.unix_path_length()) -} diff --git a/libpod/oci_attach_linux_nocgo.go b/libpod/oci_attach_linux_nocgo.go deleted file mode 100644 index a514a555d..000000000 --- a/libpod/oci_attach_linux_nocgo.go +++ /dev/null @@ -1,7 +0,0 @@ -//+build linux,!cgo - -package libpod - -func unixPathLength() int { - return 107 -} diff --git a/libpod/oci_conmon_exec_linux.go b/libpod/oci_conmon_exec_linux.go index f8e7020f7..dc5dd03df 100644 --- a/libpod/oci_conmon_exec_linux.go +++ b/libpod/oci_conmon_exec_linux.go @@ -2,7 +2,6 @@ package libpod import ( "fmt" - "net" "net/http" "os" "os/exec" @@ -387,7 +386,7 @@ func (r *ConmonOCIRuntime) startExec(c *Container, sessionID string, options *Ex finalEnv = append(finalEnv, fmt.Sprintf("%s=%s", k, v)) } - processFile, err := prepareProcessExec(c, options.Cmd, finalEnv, options.Terminal, options.Cwd, options.User, sessionID) + processFile, err := prepareProcessExec(c, options, finalEnv, sessionID) if err != nil { return nil, nil, err } @@ -398,10 +397,6 @@ func (r *ConmonOCIRuntime) startExec(c *Container, sessionID string, options *Ex args = append(args, formatRuntimeOpts("--preserve-fds", fmt.Sprintf("%d", options.PreserveFDs))...) } - for _, capability := range options.CapAdd { - args = append(args, formatRuntimeOpts("--cap", capability)...) - } - if options.Terminal { args = append(args, "-t") } @@ -516,7 +511,6 @@ func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.Resp if err != nil { return err } - socketPath := buildSocketPath(sockPath) // 2: read from attachFd that the parent process has set up the console socket if _, err := readConmonPipeData(pipes.attachPipe, ""); err != nil { @@ -524,9 +518,9 @@ func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.Resp } // 2: then attach - conn, err := net.DialUnix("unixpacket", nil, &net.UnixAddr{Name: socketPath, Net: "unixpacket"}) + conn, err := openUnixSocket(sockPath) if err != nil { - return errors.Wrapf(err, "failed to connect to container's attach socket: %v", socketPath) + return errors.Wrapf(err, "failed to connect to container's attach socket: %v", sockPath) } defer func() { if err := conn.Close(); err != nil { diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go index c99086b33..23bfb29d7 100644 --- a/libpod/oci_conmon_linux.go +++ b/libpod/oci_conmon_linux.go @@ -22,6 +22,7 @@ import ( "text/template" "time" + "github.com/containers/common/pkg/capabilities" "github.com/containers/common/pkg/config" conmonConfig "github.com/containers/conmon/runner/config" "github.com/containers/podman/v2/libpod/define" @@ -193,6 +194,11 @@ func hasCurrentUserMapped(ctr *Container) bool { // CreateContainer creates a container. func (r *ConmonOCIRuntime) CreateContainer(ctr *Container, restoreOptions *ContainerCheckpointOptions) error { + // always make the run dir accessible to the current user so that the PID files can be read without + // being in the rootless user namespace. + if err := makeAccessible(ctr.state.RunDir, 0, 0); err != nil { + return err + } if !hasCurrentUserMapped(ctr) { for _, i := range []string{ctr.state.RunDir, ctr.runtime.config.Engine.TmpDir, ctr.config.StaticDir, ctr.state.Mountpoint, ctr.runtime.config.Engine.VolumePath} { if err := makeAccessible(i, ctr.RootUID(), ctr.RootGID()); err != nil { @@ -523,13 +529,12 @@ func (r *ConmonOCIRuntime) HTTPAttach(ctr *Container, req *http.Request, w http. if err != nil { return err } - socketPath := buildSocketPath(attachSock) var conn *net.UnixConn if streamAttach { - newConn, err := net.DialUnix("unixpacket", nil, &net.UnixAddr{Name: socketPath, Net: "unixpacket"}) + newConn, err := openUnixSocket(attachSock) if err != nil { - return errors.Wrapf(err, "failed to connect to container's attach socket: %v", socketPath) + return errors.Wrapf(err, "failed to connect to container's attach socket: %v", attachSock) } conn = newConn defer func() { @@ -538,7 +543,7 @@ func (r *ConmonOCIRuntime) HTTPAttach(ctr *Container, req *http.Request, w http. } }() - logrus.Debugf("Successfully connected to container %s attach socket %s", ctr.ID(), socketPath) + logrus.Debugf("Successfully connected to container %s attach socket %s", ctr.ID(), attachSock) } detachString := ctr.runtime.config.Engine.DetachKeys @@ -763,10 +768,14 @@ func (r *ConmonOCIRuntime) CheckpointContainer(ctr *Container, options Container } // imagePath is used by CRIU to store the actual checkpoint files imagePath := ctr.CheckpointPath() + if options.PreCheckPoint { + imagePath = ctr.PreCheckPointPath() + } // workPath will be used to store dump.log and stats-dump workPath := ctr.bundlePath() logrus.Debugf("Writing checkpoint to %s", imagePath) logrus.Debugf("Writing checkpoint logs to %s", workPath) + logrus.Debugf("Pre-dump the container %t", options.PreCheckPoint) args := []string{} args = append(args, r.runtimeFlags...) args = append(args, "checkpoint") @@ -780,6 +789,15 @@ func (r *ConmonOCIRuntime) CheckpointContainer(ctr *Container, options Container if options.TCPEstablished { args = append(args, "--tcp-established") } + if !options.PreCheckPoint && options.KeepRunning { + args = append(args, "--leave-running") + } + if options.PreCheckPoint { + args = append(args, "--pre-dump") + } + if !options.PreCheckPoint && options.WithPrevious { + args = append(args, "--parent-path", ctr.PreCheckPointPath()) + } runtimeDir, err := util.GetRuntimeDir() if err != nil { return err @@ -788,6 +806,7 @@ func (r *ConmonOCIRuntime) CheckpointContainer(ctr *Container, options Container return errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR") } args = append(args, ctr.ID()) + logrus.Debugf("the args to checkpoint: %s %s", r.path, strings.Join(args, " ")) return utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, nil, r.path, args...) } @@ -1185,26 +1204,30 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co // prepareProcessExec returns the path of the process.json used in runc exec -p // caller is responsible to close the returned *os.File if needed. -func prepareProcessExec(c *Container, cmd, env []string, tty bool, cwd, user, sessionID string) (*os.File, error) { +func prepareProcessExec(c *Container, options *ExecOptions, env []string, sessionID string) (*os.File, error) { f, err := ioutil.TempFile(c.execBundlePath(sessionID), "exec-process-") if err != nil { return nil, err } - pspec := c.config.Spec.Process + pspec := new(spec.Process) + if err := JSONDeepCopy(c.config.Spec.Process, pspec); err != nil { + return nil, err + } pspec.SelinuxLabel = c.config.ProcessLabel - pspec.Args = cmd + pspec.Args = options.Cmd + // We need to default this to false else it will inherit terminal as true // from the container. pspec.Terminal = false - if tty { + if options.Terminal { pspec.Terminal = true } if len(env) > 0 { pspec.Env = append(pspec.Env, env...) } - if cwd != "" { - pspec.Cwd = cwd + if options.Cwd != "" { + pspec.Cwd = options.Cwd } @@ -1212,6 +1235,7 @@ func prepareProcessExec(c *Container, cmd, env []string, tty bool, cwd, user, se var sgids []uint32 // if the user is empty, we should inherit the user that the container is currently running with + user := options.User if user == "" { user = c.config.User addGroups = c.config.Groups @@ -1247,6 +1271,31 @@ func prepareProcessExec(c *Container, cmd, env []string, tty bool, cwd, user, se pspec.User = processUser } + ctrSpec, err := c.specFromState() + if err != nil { + return nil, err + } + + allCaps := capabilities.AllCapabilities() + if options.Privileged { + pspec.Capabilities.Bounding = allCaps + } else { + pspec.Capabilities.Bounding = ctrSpec.Process.Capabilities.Bounding + } + if execUser.Uid == 0 { + pspec.Capabilities.Effective = pspec.Capabilities.Bounding + pspec.Capabilities.Inheritable = pspec.Capabilities.Bounding + pspec.Capabilities.Permitted = pspec.Capabilities.Bounding + pspec.Capabilities.Ambient = pspec.Capabilities.Bounding + } else { + if user == c.config.User { + pspec.Capabilities.Effective = ctrSpec.Process.Capabilities.Effective + pspec.Capabilities.Inheritable = ctrSpec.Process.Capabilities.Effective + pspec.Capabilities.Permitted = ctrSpec.Process.Capabilities.Effective + pspec.Capabilities.Ambient = ctrSpec.Process.Capabilities.Effective + } + } + hasHomeSet := false for _, s := range pspec.Env { if strings.HasPrefix(s, "HOME=") { @@ -1272,7 +1321,12 @@ func prepareProcessExec(c *Container, cmd, env []string, tty bool, cwd, user, se // configureConmonEnv gets the environment values to add to conmon's exec struct // TODO this may want to be less hardcoded/more configurable in the future func (r *ConmonOCIRuntime) configureConmonEnv(ctr *Container, runtimeDir string) ([]string, []*os.File) { - env := make([]string, 0, 6) + var env []string + for _, e := range os.Environ() { + if strings.HasPrefix(e, "LC_") { + env = append(env, e) + } + } env = append(env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir)) env = append(env, fmt.Sprintf("_CONTAINERS_USERNS_CONFIGURED=%s", os.Getenv("_CONTAINERS_USERNS_CONFIGURED"))) env = append(env, fmt.Sprintf("_CONTAINERS_ROOTLESS_UID=%s", os.Getenv("_CONTAINERS_ROOTLESS_UID"))) @@ -1333,6 +1387,7 @@ func (r *ConmonOCIRuntime) sharedConmonArgs(ctr *Container, cuuid, bundlePath, p logDriverArg = define.NoLogging case define.JSONLogging: fallthrough + //lint:ignore ST1015 the default case has to be here default: //nolint-stylecheck // No case here should happen except JSONLogging, but keep this here in case the options are extended logrus.Errorf("%s logging specified but not supported. Choosing k8s-file logging instead", ctr.LogDriver()) diff --git a/libpod/oci_util.go b/libpod/oci_util.go index 2ba85c4b3..d40cf13bd 100644 --- a/libpod/oci_util.go +++ b/libpod/oci_util.go @@ -126,5 +126,17 @@ func getOCIRuntimeError(runtimeMsg string) error { } return errors.Wrapf(define.ErrOCIRuntimeNotFound, "%s", strings.Trim(errStr, "\n")) } + if match := regexp.MustCompile("`/proc/[a-z0-9-].+/attr.*`").FindString(runtimeMsg); match != "" { + errStr := match + if includeFullOutput { + errStr = runtimeMsg + } + if strings.HasSuffix(match, "/exec`") { + return errors.Wrapf(define.ErrSetSecurityAttribute, "%s", strings.Trim(errStr, "\n")) + } else if strings.HasSuffix(match, "/current`") { + return errors.Wrapf(define.ErrGetSecurityAttribute, "%s", strings.Trim(errStr, "\n")) + } + return errors.Wrapf(define.ErrSecurityAttribute, "%s", strings.Trim(errStr, "\n")) + } return errors.Wrapf(define.ErrOCIRuntime, "%s", strings.Trim(runtimeMsg, "\n")) } diff --git a/libpod/options.go b/libpod/options.go index 8100eee62..c7bac7e1f 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -306,7 +306,7 @@ func WithDefaultMountsFile(mountsFile string) RuntimeOption { // WithTmpDir sets the directory that temporary runtime files which are not // expected to survive across reboots will be stored. -// This should be located on a tmpfs mount (/tmp or /var/run for example). +// This should be located on a tmpfs mount (/tmp or /run for example). func WithTmpDir(dir string) RuntimeOption { return func(rt *Runtime) error { if rt.valid { @@ -910,7 +910,7 @@ func WithUserNSFrom(nsCtr *Container) CtrCreateOption { ctr.config.UserNsCtr = nsCtr.ID() ctr.config.IDMappings = nsCtr.config.IDMappings - g := generate.NewFromSpec(ctr.config.Spec) + g := generate.Generator{Config: ctr.config.Spec} g.ClearLinuxUIDMappings() for _, uidmap := range nsCtr.config.IDMappings.UIDMap { @@ -1549,17 +1549,6 @@ func WithVolumeDriver(driver string) VolumeCreateOption { return define.ErrVolumeFinalized } - // Uncomment when volume plugins are ready for use. - // if driver != define.VolumeDriverLocal { - // if _, err := plugin.GetVolumePlugin(driver); err != nil { - // return err - // } - // } - - if driver != define.VolumeDriverLocal { - return define.ErrNotImplemented - } - volume.config.Driver = driver return nil } diff --git a/libpod/plugin/volume_api.go b/libpod/plugin/volume_api.go index 2500a4f36..c5dec651c 100644 --- a/libpod/plugin/volume_api.go +++ b/libpod/plugin/volume_api.go @@ -2,8 +2,9 @@ package plugin import ( "bytes" - "fmt" + "context" "io/ioutil" + "net" "net/http" "os" "path/filepath" @@ -43,7 +44,6 @@ var ( const ( defaultTimeout = 5 * time.Second - defaultPath = "/run/docker/plugins" volumePluginType = "VolumeDriver" ) @@ -64,6 +64,8 @@ type VolumePlugin struct { Name string // SocketPath is the unix socket at which the plugin is accessed. SocketPath string + // Client is the HTTP client we use to connect to the plugin. + Client *http.Client } // This is the response from the activate endpoint of the API. @@ -76,7 +78,7 @@ type activateResponse struct { func validatePlugin(newPlugin *VolumePlugin) error { // It's a socket. Is it a plugin? // Hit the Activate endpoint to find out if it is, and if so what kind - req, err := http.NewRequest("POST", activatePath, nil) + req, err := http.NewRequest("POST", "http://plugin"+activatePath, nil) if err != nil { return errors.Wrapf(err, "error making request to volume plugin %s activation endpoint", newPlugin.Name) } @@ -84,9 +86,7 @@ func validatePlugin(newPlugin *VolumePlugin) error { req.Header.Set("Host", newPlugin.getURI()) req.Header.Set("Content-Type", sdk.DefaultContentTypeV1_1) - client := new(http.Client) - client.Timeout = defaultTimeout - resp, err := client.Do(req) + resp, err := newPlugin.Client.Do(req) if err != nil { return errors.Wrapf(err, "error sending request to plugin %s activation endpoint", newPlugin.Name) } @@ -121,22 +121,28 @@ func validatePlugin(newPlugin *VolumePlugin) error { return errors.Wrapf(ErrNotVolumePlugin, "plugin %s does not implement volume plugin, instead provides %s", newPlugin.Name, strings.Join(respStruct.Implements, ", ")) } + if plugins == nil { + plugins = make(map[string]*VolumePlugin) + } + plugins[newPlugin.Name] = newPlugin return nil } -// GetVolumePlugin gets a single volume plugin by path. -// TODO: We should not be auto-completing based on a default path; we should -// require volumes to have been pre-specified in containers.conf (will need a -// function to pre-populate the plugins list, and we should probably do a lazy -// initialization there to not slow things down too much). -func GetVolumePlugin(name string) (*VolumePlugin, error) { +// GetVolumePlugin gets a single volume plugin, with the given name, at the +// given path. +func GetVolumePlugin(name string, path string) (*VolumePlugin, error) { pluginsLock.Lock() defer pluginsLock.Unlock() plugin, exists := plugins[name] if exists { + // This shouldn't be possible, but just in case... + if plugin.SocketPath != filepath.Clean(path) { + return nil, errors.Wrapf(define.ErrInvalidArg, "requested path %q for volume plugin %s does not match pre-existing path for plugin, %q", path, name, plugin.SocketPath) + } + return plugin, nil } @@ -144,7 +150,20 @@ func GetVolumePlugin(name string) (*VolumePlugin, error) { newPlugin := new(VolumePlugin) newPlugin.Name = name - newPlugin.SocketPath = filepath.Join(defaultPath, fmt.Sprintf("%s.sock", name)) + newPlugin.SocketPath = filepath.Clean(path) + + // Need an HTTP client to force a Unix connection. + // And since we can reuse it, might as well cache it. + client := new(http.Client) + client.Timeout = defaultTimeout + // This bit borrowed from pkg/bindings/connection.go + client.Transport = &http.Transport{ + DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) { + return (&net.Dialer{}).DialContext(ctx, "unix", newPlugin.SocketPath) + }, + DisableCompression: true, + } + newPlugin.Client = client stat, err := os.Stat(newPlugin.SocketPath) if err != nil { @@ -183,6 +202,7 @@ func (p *VolumePlugin) verifyReachable() error { } // Send a request to the volume plugin for handling. +// Callers *MUST* close the response when they are done. func (p *VolumePlugin) sendRequest(toJSON interface{}, hasBody bool, endpoint string) (*http.Response, error) { var ( reqJSON []byte @@ -196,7 +216,7 @@ func (p *VolumePlugin) sendRequest(toJSON interface{}, hasBody bool, endpoint st } } - req, err := http.NewRequest("POST", endpoint, bytes.NewReader(reqJSON)) + req, err := http.NewRequest("POST", "http://plugin"+endpoint, bytes.NewReader(reqJSON)) if err != nil { return nil, errors.Wrapf(err, "error making request to volume plugin %s endpoint %s", p.Name, endpoint) } @@ -204,13 +224,12 @@ func (p *VolumePlugin) sendRequest(toJSON interface{}, hasBody bool, endpoint st req.Header.Set("Host", p.getURI()) req.Header.Set("Content-Type", sdk.DefaultContentTypeV1_1) - client := new(http.Client) - client.Timeout = defaultTimeout - resp, err := client.Do(req) + resp, err := p.Client.Do(req) if err != nil { return nil, errors.Wrapf(err, "error sending request to volume plugin %s endpoint %s", p.Name, endpoint) } - defer resp.Body.Close() + // We are *deliberately not closing* response here. It is the + // responsibility of the caller to do so after reading the response. return resp, nil } diff --git a/libpod/rootless_cni_linux.go b/libpod/rootless_cni_linux.go index ce8a87759..9a980750f 100644 --- a/libpod/rootless_cni_linux.go +++ b/libpod/rootless_cni_linux.go @@ -110,6 +110,8 @@ func DeallocRootlessCNI(ctx context.Context, c *Container) error { logrus.Warn(err) } logrus.Debugf("rootless CNI: removing infra container %q", infra.ID()) + infra.lock.Lock() + defer infra.lock.Unlock() if err := c.runtime.removeContainer(ctx, infra, true, false, true); err != nil { return err } diff --git a/libpod/runtime.go b/libpod/runtime.go index 1004e4fa7..34c737a67 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -17,6 +17,7 @@ import ( "github.com/containers/podman/v2/libpod/events" "github.com/containers/podman/v2/libpod/image" "github.com/containers/podman/v2/libpod/lock" + "github.com/containers/podman/v2/libpod/plugin" "github.com/containers/podman/v2/libpod/shutdown" "github.com/containers/podman/v2/pkg/cgroups" "github.com/containers/podman/v2/pkg/registries" @@ -888,3 +889,18 @@ func (r *Runtime) reloadStorageConf() error { logrus.Infof("applied new storage configuration: %v", r.storageConfig) return nil } + +// getVolumePlugin gets a specific volume plugin given its name. +func (r *Runtime) getVolumePlugin(name string) (*plugin.VolumePlugin, error) { + // There is no plugin for local. + if name == define.VolumeDriverLocal || name == "" { + return nil, nil + } + + pluginPath, ok := r.config.Engine.VolumePlugins[name] + if !ok { + return nil, errors.Wrapf(define.ErrMissingPlugin, "no volume plugin with name %s available", name) + } + + return plugin.GetVolumePlugin(name, pluginPath) +} diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index 14b537ca2..d2bcd8db3 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -14,6 +14,7 @@ import ( "github.com/containers/podman/v2/libpod/events" "github.com/containers/podman/v2/libpod/shutdown" "github.com/containers/podman/v2/pkg/cgroups" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/containers/podman/v2/pkg/rootless" "github.com/containers/storage" "github.com/containers/storage/pkg/stringid" @@ -71,6 +72,140 @@ func (r *Runtime) RestoreContainer(ctx context.Context, rSpec *spec.Spec, config return r.setupContainer(ctx, ctr) } +// RenameContainer renames the given container. +// The given container object will be rendered unusable, and a new, renamed +// Container will be returned. +func (r *Runtime) RenameContainer(ctx context.Context, ctr *Container, newName string) (*Container, error) { + ctr.lock.Lock() + defer ctr.lock.Unlock() + + if err := ctr.syncContainer(); err != nil { + return nil, err + } + + if newName == "" || !define.NameRegex.MatchString(newName) { + return nil, define.RegexError + } + + // Check if the name is available. + // This is *100% NOT ATOMIC* so any failures in-flight will do + // *VERY BAD THINGS* to the state. So we have to try and catch all we + // can before starting. + if _, err := r.state.LookupContainerID(newName); err == nil { + return nil, errors.Wrapf(define.ErrCtrExists, "name %s is already in use by another container", newName) + } + if _, err := r.state.LookupPod(newName); err == nil { + return nil, errors.Wrapf(define.ErrPodExists, "name %s is already in use by another pod", newName) + } + + // TODO: Investigate if it is possible to remove this limitation. + depCtrs, err := r.state.ContainerInUse(ctr) + if err != nil { + return nil, err + } + if len(depCtrs) > 0 { + return nil, errors.Wrapf(define.ErrCtrExists, "cannot rename container %s as it is in use by other containers: %v", ctr.ID(), strings.Join(depCtrs, ",")) + } + + // We need to pull an updated config, in case another rename fired and + // the config was re-written. + newConf, err := r.state.GetContainerConfig(ctr.ID()) + if err != nil { + return nil, errors.Wrapf(err, "error retrieving container %s configuration from DB to remove", ctr.ID()) + } + ctr.config = newConf + + // TODO: This is going to fail if we have active exec sessions, too. + // Investigate fixing that at a later date. + + var pod *Pod + if ctr.config.Pod != "" { + tmpPod, err := r.state.Pod(ctr.config.Pod) + if err != nil { + return nil, errors.Wrapf(err, "error retrieving container %s pod", ctr.ID()) + } + pod = tmpPod + // Lock pod to ensure it's not removed while we're working + pod.lock.Lock() + defer pod.lock.Unlock() + } + + // Lock all volumes to ensure they are not removed while we're working + volsLocked := make(map[string]bool) + for _, namedVol := range ctr.config.NamedVolumes { + if volsLocked[namedVol.Name] { + continue + } + vol, err := r.state.Volume(namedVol.Name) + if err != nil { + return nil, errors.Wrapf(err, "error retrieving volume used by container %s", ctr.ID()) + } + + volsLocked[vol.Name()] = true + vol.lock.Lock() + defer vol.lock.Unlock() + } + + logrus.Infof("Going to rename container %s from %q to %q", ctr.ID(), ctr.Name(), newName) + + // Step 1: remove the old container. + if pod != nil { + if err := r.state.RemoveContainerFromPod(pod, ctr); err != nil { + return nil, errors.Wrapf(err, "error renaming container %s", ctr.ID()) + } + } else { + if err := r.state.RemoveContainer(ctr); err != nil { + return nil, errors.Wrapf(err, "error renaming container %s", ctr.ID()) + } + } + + // Step 2: Make a new container based on the old one. + // TODO: Should we deep-copy the container config and state, to be safe? + newCtr := new(Container) + newCtr.config = ctr.config + newCtr.state = ctr.state + newCtr.lock = ctr.lock + newCtr.ociRuntime = ctr.ociRuntime + newCtr.runtime = r + newCtr.rootlessSlirpSyncR = ctr.rootlessSlirpSyncR + newCtr.rootlessSlirpSyncW = ctr.rootlessSlirpSyncW + newCtr.rootlessPortSyncR = ctr.rootlessPortSyncR + newCtr.rootlessPortSyncW = ctr.rootlessPortSyncW + + newCtr.valid = true + newCtr.config.Name = newName + + // Step 3: Add that new container to the DB + if pod != nil { + if err := r.state.AddContainerToPod(pod, newCtr); err != nil { + return nil, errors.Wrapf(err, "error renaming container %s", newCtr.ID()) + } + } else { + if err := r.state.AddContainer(newCtr); err != nil { + return nil, errors.Wrapf(err, "error renaming container %s", newCtr.ID()) + } + } + + // Step 4: Save the new container, to force the state to be written to + // the DB. This may not be necessary, depending on DB implementation, + // but let's do it to be safe. + if err := newCtr.save(); err != nil { + return nil, err + } + + // Step 5: rename the container in c/storage. + // This can fail if the name is already in use by a non-Podman + // container. This puts us in a bad spot - we've already renamed the + // container in Podman. We can swap the order, but then we have the + // opposite problem. Atomicity is a real problem here, with no easy + // solution. + if err := r.store.SetNames(newCtr.ID(), []string{newCtr.Name()}); err != nil { + return nil, err + } + + return newCtr, nil +} + func (r *Runtime) initContainerVariables(rSpec *spec.Spec, config *ContainerConfig) (*Container, error) { if rSpec == nil { return nil, errors.Wrapf(define.ErrInvalidArg, "must provide a valid runtime spec to create container") @@ -392,7 +527,7 @@ func (r *Runtime) RemoveContainer(ctx context.Context, c *Container, force bool, // removePod is used only when removing pods. It instructs Podman to ignore // infra container protections, and *not* remove from the database (as pod // remove will handle that). -func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool, removeVolume bool, removePod bool) error { +func (r *Runtime) removeContainer(ctx context.Context, c *Container, force, removeVolume, removePod bool) error { span, _ := opentracing.StartSpanFromContext(ctx, "removeContainer") span.SetTag("type", "runtime") defer span.Finish() @@ -405,6 +540,18 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool, } } + // We need to refresh container config from the DB, to ensure that any + // changes (e.g. a rename) are picked up before we start removing. + // Since HasContainer above succeeded, we can safely assume the + // container exists. + // This is *very iffy* but it should be OK because the container won't + // exist once we're done. + newConf, err := r.state.GetContainerConfig(c.ID()) + if err != nil { + return errors.Wrapf(err, "error retrieving container %s configuration from DB to remove", c.ID()) + } + c.config = newConf + logrus.Debugf("Removing container %s", c.ID()) // We need to lock the pod before we lock the container. @@ -412,7 +559,6 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool, // Don't need to do this in pod removal case - we're evicting the entire // pod. var pod *Pod - var err error runtime := c.runtime if c.config.Pod != "" && !removePod { pod, err = r.state.Pod(c.config.Pod) @@ -884,9 +1030,8 @@ func (r *Runtime) GetExecSessionContainer(id string) (*Container, error) { // PruneContainers removes stopped and exited containers from localstorage. A set of optional filters // can be provided to be more granular. -func (r *Runtime) PruneContainers(filterFuncs []ContainerFilter) (map[string]int64, map[string]error, error) { - pruneErrors := make(map[string]error) - prunedContainers := make(map[string]int64) +func (r *Runtime) PruneContainers(filterFuncs []ContainerFilter) ([]*reports.PruneReport, error) { + preports := make([]*reports.PruneReport, 0) // We add getting the exited and stopped containers via a filter containerStateFilter := func(c *Container) bool { if c.PodID() != "" { @@ -906,23 +1051,28 @@ func (r *Runtime) PruneContainers(filterFuncs []ContainerFilter) (map[string]int filterFuncs = append(filterFuncs, containerStateFilter) delContainers, err := r.GetContainers(filterFuncs...) if err != nil { - return nil, nil, err + return nil, err } for _, c := range delContainers { - ctr := c - size, err := ctr.RWSize() + report := new(reports.PruneReport) + report.Id = c.ID() + report.Err = nil + report.Size = 0 + size, err := c.RWSize() if err != nil { - pruneErrors[ctr.ID()] = err + report.Err = err + preports = append(preports, report) continue } - err = r.RemoveContainer(context.Background(), ctr, false, false) + err = r.RemoveContainer(context.Background(), c, false, false) if err != nil { - pruneErrors[ctr.ID()] = err + report.Err = err } else { - prunedContainers[ctr.ID()] = size + report.Size = (uint64)(size) } + preports = append(preports, report) } - return prunedContainers, pruneErrors, nil + return preports, nil } // MountStorageContainer mounts the storage container's root filesystem diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go index 965333f77..2c5442bd2 100644 --- a/libpod/runtime_img.go +++ b/libpod/runtime_img.go @@ -279,6 +279,7 @@ func (r *Runtime) LoadImage(ctx context.Context, inputFile string, writer io.Wri if newImages, err := r.LoadAllImageFromArchive(ctx, writer, inputFile, signaturePolicy); err == nil { return newImages, nil } + return r.LoadImageFromSingleImageArchive(ctx, writer, inputFile, signaturePolicy) } @@ -293,7 +294,7 @@ func (r *Runtime) LoadAllImageFromArchive(ctx context.Context, writer io.Writer, // LoadImageFromSingleImageArchive load image from the archive of single image that inputFile points to. func (r *Runtime) LoadImageFromSingleImageArchive(ctx context.Context, writer io.Writer, inputFile, signaturePolicy string) (string, error) { - var err error + var saveErr error for _, referenceFn := range []func() (types.ImageReference, error){ func() (types.ImageReference, error) { return dockerarchive.ParseReference(inputFile) @@ -312,10 +313,12 @@ func (r *Runtime) LoadImageFromSingleImageArchive(ctx context.Context, writer io if err == nil && src != nil { if newImages, err := r.ImageRuntime().LoadFromArchiveReference(ctx, src, signaturePolicy, writer); err == nil { return getImageNames(newImages), nil + } else { + saveErr = err } } } - return "", errors.Wrapf(err, "error pulling image") + return "", errors.Wrapf(saveErr, "error pulling image") } func getImageNames(images []*image.Image) string { diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go index 3e4185db1..dd957527d 100644 --- a/libpod/runtime_pod_infra_linux.go +++ b/libpod/runtime_pod_infra_linux.go @@ -159,6 +159,34 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm g.AddMount(devPts) } + // Add default sysctls from containers.conf + defaultSysctls, err := util.ValidateSysctls(r.config.Sysctls()) + if err != nil { + return nil, err + } + for sysctlKey, sysctlVal := range defaultSysctls { + // Ignore mqueue sysctls if not sharing IPC + if !p.config.UsePodIPC && strings.HasPrefix(sysctlKey, "fs.mqueue.") { + logrus.Infof("Sysctl %s=%s ignored in containers.conf, since IPC Namespace for pod is unused", sysctlKey, sysctlVal) + + continue + } + + // Ignore net sysctls if host network or not sharing network + if (p.config.InfraContainer.HostNetwork || !p.config.UsePodNet) && strings.HasPrefix(sysctlKey, "net.") { + logrus.Infof("Sysctl %s=%s ignored in containers.conf, since Network Namespace for pod is unused", sysctlKey, sysctlVal) + continue + } + + // Ignore uts sysctls if not sharing UTS + if !p.config.UsePodUTS && (strings.HasPrefix(sysctlKey, "kernel.domainname") || strings.HasPrefix(sysctlKey, "kernel.hostname")) { + logrus.Infof("Sysctl %s=%s ignored in containers.conf, since UTS Namespace for pod is unused", sysctlKey, sysctlVal) + continue + } + + g.AddLinuxSysctl(sysctlKey, sysctlVal) + } + containerName := p.ID()[:IDTruncLength] + "-infra" options = append(options, r.WithPod(p)) options = append(options, WithRootFSFromImage(imgID, imgName, rawImageName)) diff --git a/libpod/runtime_volume.go b/libpod/runtime_volume.go index 10c32a119..9d985f905 100644 --- a/libpod/runtime_volume.go +++ b/libpod/runtime_volume.go @@ -5,6 +5,7 @@ import ( "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/events" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/pkg/errors" ) @@ -133,22 +134,32 @@ func (r *Runtime) GetAllVolumes() ([]*Volume, error) { } // PruneVolumes removes unused volumes from the system -func (r *Runtime) PruneVolumes(ctx context.Context, filterFuncs []VolumeFilter) (map[string]error, error) { - reports := make(map[string]error) +func (r *Runtime) PruneVolumes(ctx context.Context, filterFuncs []VolumeFilter) ([]*reports.PruneReport, error) { + preports := make([]*reports.PruneReport, 0) vols, err := r.Volumes(filterFuncs...) if err != nil { return nil, err } for _, vol := range vols { + report := new(reports.PruneReport) + volSize, err := vol.Size() + if err != nil { + volSize = 0 + } + report.Size = volSize + report.Id = vol.Name() if err := r.RemoveVolume(ctx, vol, false); err != nil { if errors.Cause(err) != define.ErrVolumeBeingUsed && errors.Cause(err) != define.ErrVolumeRemoved { - reports[vol.Name()] = err + report.Err = err + } else { + // We didn't remove the volume for some reason + continue } - continue + } else { + vol.newVolumeEvent(events.Prune) } - vol.newVolumeEvent(events.Prune) - reports[vol.Name()] = nil + preports = append(preports, report) } - return reports, nil + return preports, nil } diff --git a/libpod/runtime_volume_linux.go b/libpod/runtime_volume_linux.go index 9bf0fd108..4a29f01aa 100644 --- a/libpod/runtime_volume_linux.go +++ b/libpod/runtime_volume_linux.go @@ -11,7 +11,9 @@ import ( "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/events" + volplugin "github.com/containers/podman/v2/libpod/plugin" "github.com/containers/storage/pkg/stringid" + pluginapi "github.com/docker/go-plugins-helpers/volume" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -53,6 +55,14 @@ func (r *Runtime) newVolume(ctx context.Context, options ...VolumeCreateOption) return nil, errors.Wrapf(define.ErrVolumeExists, "volume with name %s already exists", volume.config.Name) } + // Plugin can be nil if driver is local, but that's OK - superfluous + // assignment doesn't hurt much. + plugin, err := r.getVolumePlugin(volume.config.Driver) + if err != nil { + return nil, errors.Wrapf(err, "volume %s uses volume plugin %s but it could not be retrieved", volume.config.Name, volume.config.Driver) + } + volume.plugin = plugin + if volume.config.Driver == define.VolumeDriverLocal { logrus.Debugf("Validating options for local driver") // Validate options @@ -66,25 +76,38 @@ func (r *Runtime) newVolume(ctx context.Context, options ...VolumeCreateOption) } } - // Create the mountpoint of this volume - volPathRoot := filepath.Join(r.config.Engine.VolumePath, volume.config.Name) - if err := os.MkdirAll(volPathRoot, 0700); err != nil { - return nil, errors.Wrapf(err, "error creating volume directory %q", volPathRoot) - } - if err := os.Chown(volPathRoot, volume.config.UID, volume.config.GID); err != nil { - return nil, errors.Wrapf(err, "error chowning volume directory %q to %d:%d", volPathRoot, volume.config.UID, volume.config.GID) - } - fullVolPath := filepath.Join(volPathRoot, "_data") - if err := os.MkdirAll(fullVolPath, 0755); err != nil { - return nil, errors.Wrapf(err, "error creating volume directory %q", fullVolPath) - } - if err := os.Chown(fullVolPath, volume.config.UID, volume.config.GID); err != nil { - return nil, errors.Wrapf(err, "error chowning volume directory %q to %d:%d", fullVolPath, volume.config.UID, volume.config.GID) - } - if err := LabelVolumePath(fullVolPath); err != nil { - return nil, err + // Now we get conditional: we either need to make the volume in the + // volume plugin, or on disk if not using a plugin. + if volume.plugin != nil { + // We can't chown, or relabel, or similar the path the volume is + // using, because it's not managed by us. + // TODO: reevaluate this once we actually have volume plugins in + // use in production - it may be safe, but I can't tell without + // knowing what the actual plugin does... + if err := makeVolumeInPluginIfNotExist(volume.config.Name, volume.config.Options, volume.plugin); err != nil { + return nil, err + } + } else { + // Create the mountpoint of this volume + volPathRoot := filepath.Join(r.config.Engine.VolumePath, volume.config.Name) + if err := os.MkdirAll(volPathRoot, 0700); err != nil { + return nil, errors.Wrapf(err, "error creating volume directory %q", volPathRoot) + } + if err := os.Chown(volPathRoot, volume.config.UID, volume.config.GID); err != nil { + return nil, errors.Wrapf(err, "error chowning volume directory %q to %d:%d", volPathRoot, volume.config.UID, volume.config.GID) + } + fullVolPath := filepath.Join(volPathRoot, "_data") + if err := os.MkdirAll(fullVolPath, 0755); err != nil { + return nil, errors.Wrapf(err, "error creating volume directory %q", fullVolPath) + } + if err := os.Chown(fullVolPath, volume.config.UID, volume.config.GID); err != nil { + return nil, errors.Wrapf(err, "error chowning volume directory %q to %d:%d", fullVolPath, volume.config.UID, volume.config.GID) + } + if err := LabelVolumePath(fullVolPath); err != nil { + return nil, err + } + volume.config.MountPoint = fullVolPath } - volume.config.MountPoint = fullVolPath lock, err := r.lockManager.AllocateLock() if err != nil { @@ -111,6 +134,39 @@ func (r *Runtime) newVolume(ctx context.Context, options ...VolumeCreateOption) return volume, nil } +// makeVolumeInPluginIfNotExist makes a volume in the given volume plugin if it +// does not already exist. +func makeVolumeInPluginIfNotExist(name string, options map[string]string, plugin *volplugin.VolumePlugin) error { + // Ping the volume plugin to see if it exists first. + // If it does, use the existing volume in the plugin. + // Options may not match exactly, but not much we can do about + // that. Not complaining avoids a lot of the sync issues we see + // with c/storage and libpod DB. + needsCreate := true + getReq := new(pluginapi.GetRequest) + getReq.Name = name + if resp, err := plugin.GetVolume(getReq); err == nil { + // TODO: What do we do if we get a 200 response, but the + // Volume is nil? The docs on the Plugin API are very + // nonspecific, so I don't know if this is valid or + // not... + if resp != nil { + needsCreate = false + logrus.Infof("Volume %q already exists in plugin %q, using existing volume", name, plugin.Name) + } + } + if needsCreate { + createReq := new(pluginapi.CreateRequest) + createReq.Name = name + createReq.Options = options + if err := plugin.CreateVolume(createReq); err != nil { + return errors.Wrapf(err, "error creating volume %q in plugin %s", name, plugin.Name) + } + } + + return nil +} + // removeVolume removes the specified volume from state as well tears down its mountpoint and storage func (r *Runtime) removeVolume(ctx context.Context, v *Volume, force bool) error { if !v.valid { @@ -185,9 +241,43 @@ func (r *Runtime) removeVolume(ctx context.Context, v *Volume, force bool) error var removalErr error + // If we use a volume plugin, we need to remove from the plugin. + if v.UsesVolumeDriver() { + canRemove := true + + // Do we have a volume driver? + if v.plugin == nil { + canRemove = false + removalErr = errors.Wrapf(define.ErrMissingPlugin, "cannot remove volume %s from plugin %s, but it has been removed from Podman", v.Name(), v.Driver()) + } else { + // Ping the plugin first to verify the volume still + // exists. + // We're trying to be very tolerant of missing volumes + // in the backend, to avoid the problems we see with + // sync between c/storage and the Libpod DB. + getReq := new(pluginapi.GetRequest) + getReq.Name = v.Name() + if _, err := v.plugin.GetVolume(getReq); err != nil { + canRemove = false + removalErr = errors.Wrapf(err, "volume %s could not be retrieved from plugin %s, but it has been removed from Podman", v.Name(), v.Driver()) + } + } + if canRemove { + req := new(pluginapi.RemoveRequest) + req.Name = v.Name() + if err := v.plugin.RemoveVolume(req); err != nil { + removalErr = errors.Wrapf(err, "volume %s could not be removed from plugin %s, but it has been removed from Podman", v.Name(), v.Driver()) + } + } + } + // Free the volume's lock if err := v.lock.Free(); err != nil { - removalErr = errors.Wrapf(err, "error freeing lock for volume %s", v.Name()) + if removalErr == nil { + removalErr = errors.Wrapf(err, "error freeing lock for volume %s", v.Name()) + } else { + logrus.Errorf("Error freeing lock for volume %q: %v", v.Name(), err) + } } // Delete the mountpoint path of the volume, that is delete the volume @@ -196,7 +286,7 @@ func (r *Runtime) removeVolume(ctx context.Context, v *Volume, force bool) error if removalErr == nil { removalErr = errors.Wrapf(err, "error cleaning up volume storage for %q", v.Name()) } else { - logrus.Errorf("error cleaning up volume storage for volume %q: %v", v.Name(), err) + logrus.Errorf("Error cleaning up volume storage for volume %q: %v", v.Name(), err) } } diff --git a/libpod/volume.go b/libpod/volume.go index 0535bf4db..4c137cb8e 100644 --- a/libpod/volume.go +++ b/libpod/volume.go @@ -1,10 +1,13 @@ package libpod import ( + "os" + "path/filepath" "time" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/lock" + "github.com/containers/podman/v2/libpod/plugin" ) // Volume is a libpod named volume. @@ -16,6 +19,7 @@ type Volume struct { state *VolumeState valid bool + plugin *plugin.VolumePlugin runtime *Runtime lock lock.Locker } @@ -29,7 +33,7 @@ type VolumeConfig struct { // Labels for the volume. Labels map[string]string `json:"labels"` // The volume driver. Empty string or local does not activate a volume - // driver, all other volumes will. + // driver, all other values will. Driver string `json:"volumeDriver"` // The location the volume is mounted at. MountPoint string `json:"mountPoint"` @@ -51,6 +55,10 @@ type VolumeConfig struct { // Volumes are not guaranteed to have a state. Only volumes using the Local // driver that have mount options set will create a state. type VolumeState struct { + // Mountpoint is the location where the volume was mounted. + // This is only used for volumes using a volume plugin, which will mount + // at non-standard locations. + MountPoint string `json:"mountPoint,omitempty"` // MountCount is the number of times this volume has been requested to // be mounted. // It is incremented on mount() and decremented on unmount(). @@ -79,6 +87,18 @@ func (v *Volume) Name() string { return v.config.Name } +// Returns the size on disk of volume +func (v *Volume) Size() (uint64, error) { + var size uint64 + err := filepath.Walk(v.config.MountPoint, func(path string, info os.FileInfo, err error) error { + if err == nil && !info.IsDir() { + size += (uint64)(info.Size()) + } + return err + }) + return size, err +} + // Driver retrieves the volume's driver. func (v *Volume) Driver() string { return v.config.Driver @@ -101,8 +121,20 @@ func (v *Volume) Labels() map[string]string { } // MountPoint returns the volume's mountpoint on the host -func (v *Volume) MountPoint() string { - return v.config.MountPoint +func (v *Volume) MountPoint() (string, error) { + // For the sake of performance, avoid locking unless we have to. + if v.UsesVolumeDriver() { + v.lock.Lock() + defer v.lock.Unlock() + + if err := v.update(); err != nil { + return "", err + } + + return v.state.MountPoint, nil + } + + return v.config.MountPoint, nil } // Options return the volume's options @@ -125,14 +157,19 @@ func (v *Volume) UID() (int, error) { v.lock.Lock() defer v.lock.Unlock() - if !v.valid { - return -1, define.ErrVolumeRemoved + if err := v.update(); err != nil { + return -1, err } + return v.uid(), nil +} + +// Internal, unlocked accessor for UID. +func (v *Volume) uid() int { if v.state.UIDChowned > 0 { - return v.state.UIDChowned, nil + return v.state.UIDChowned } - return v.config.UID, nil + return v.config.UID } // GID returns the GID the volume will be created as. @@ -140,14 +177,19 @@ func (v *Volume) GID() (int, error) { v.lock.Lock() defer v.lock.Unlock() - if !v.valid { - return -1, define.ErrVolumeRemoved + if err := v.update(); err != nil { + return -1, err } + return v.gid(), nil +} + +// Internal, unlocked accessor for GID. +func (v *Volume) gid() int { if v.state.GIDChowned > 0 { - return v.state.GIDChowned, nil + return v.state.GIDChowned } - return v.config.GID, nil + return v.config.GID } // CreatedTime returns the time the volume was created at. It was not tracked @@ -184,3 +226,10 @@ func (v *Volume) IsDangling() (bool, error) { } return len(ctrs) == 0, nil } + +// UsesVolumeDriver determines whether the volume uses a volume driver. Volume +// drivers are pluggable backends for volumes that will manage the storage and +// mounting. +func (v *Volume) UsesVolumeDriver() bool { + return !(v.config.Driver == define.VolumeDriverLocal || v.config.Driver == "") +} diff --git a/libpod/volume_inspect.go b/libpod/volume_inspect.go index c8b20b8f1..2448d1bb5 100644 --- a/libpod/volume_inspect.go +++ b/libpod/volume_inspect.go @@ -1,60 +1,52 @@ package libpod import ( - "time" - "github.com/containers/podman/v2/libpod/define" + pluginapi "github.com/docker/go-plugins-helpers/volume" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) -// InspectVolumeData is the output of Inspect() on a volume. It is matched to -// the format of 'docker volume inspect'. -type InspectVolumeData struct { - // Name is the name of the volume. - Name string `json:"Name"` - // Driver is the driver used to create the volume. - // This will be properly implemented in a future version. - Driver string `json:"Driver"` - // Mountpoint is the path on the host where the volume is mounted. - Mountpoint string `json:"Mountpoint"` - // CreatedAt is the date and time the volume was created at. This is not - // stored for older Libpod volumes; if so, it will be omitted. - CreatedAt time.Time `json:"CreatedAt,omitempty"` - // Status is presently unused and provided only for Docker compatibility. - // In the future it will be used to return information on the volume's - // current state. - Status map[string]string `json:"Status,omitempty"` - // Labels includes the volume's configured labels, key:value pairs that - // can be passed during volume creation to provide information for third - // party tools. - Labels map[string]string `json:"Labels"` - // Scope is unused and provided solely for Docker compatibility. It is - // unconditionally set to "local". - Scope string `json:"Scope"` - // Options is a set of options that were used when creating the volume. - // It is presently not used. - Options map[string]string `json:"Options"` - // UID is the UID that the volume was created with. - UID int `json:"UID,omitempty"` - // GID is the GID that the volume was created with. - GID int `json:"GID,omitempty"` - // Anonymous indicates that the volume was created as an anonymous - // volume for a specific container, and will be be removed when any - // container using it is removed. - Anonymous bool `json:"Anonymous,omitempty"` -} - // Inspect provides detailed information about the configuration of the given // volume. -func (v *Volume) Inspect() (*InspectVolumeData, error) { +func (v *Volume) Inspect() (*define.InspectVolumeData, error) { if !v.valid { return nil, define.ErrVolumeRemoved } - data := new(InspectVolumeData) + v.lock.Lock() + defer v.lock.Unlock() + + if err := v.update(); err != nil { + return nil, err + } + + data := new(define.InspectVolumeData) + + data.Mountpoint = v.config.MountPoint + if v.UsesVolumeDriver() { + logrus.Debugf("Querying volume plugin %s for status", v.config.Driver) + data.Mountpoint = v.state.MountPoint + + if v.plugin == nil { + return nil, errors.Wrapf(define.ErrMissingPlugin, "volume %s uses volume plugin %s but it is not available, cannot inspect", v.Name(), v.config.Driver) + } + + // Retrieve status for the volume. + // Need to query the volume driver. + req := new(pluginapi.GetRequest) + req.Name = v.Name() + resp, err := v.plugin.GetVolume(req) + if err != nil { + return nil, errors.Wrapf(err, "error retrieving volume %s information from plugin %s", v.Name(), v.Driver()) + } + if resp != nil { + data.Status = resp.Status + } + } data.Name = v.config.Name data.Driver = v.config.Driver - data.Mountpoint = v.config.MountPoint data.CreatedAt = v.config.CreatedTime data.Labels = make(map[string]string) for k, v := range v.config.Labels { @@ -65,15 +57,8 @@ func (v *Volume) Inspect() (*InspectVolumeData, error) { for k, v := range v.config.Options { data.Options[k] = v } - var err error - data.UID, err = v.UID() - if err != nil { - return nil, err - } - data.GID, err = v.GID() - if err != nil { - return nil, err - } + data.UID = v.uid() + data.GID = v.gid() data.Anonymous = v.config.IsAnon return data, nil diff --git a/libpod/volume_internal.go b/libpod/volume_internal.go index 95cb752e0..88d940370 100644 --- a/libpod/volume_internal.go +++ b/libpod/volume_internal.go @@ -22,13 +22,24 @@ func newVolume(runtime *Runtime) *Volume { // teardownStorage deletes the volume from volumePath func (v *Volume) teardownStorage() error { + if v.UsesVolumeDriver() { + return nil + } + + // TODO: Should this be converted to use v.config.MountPoint? return os.RemoveAll(filepath.Join(v.runtime.config.Engine.VolumePath, v.Name())) } // Volumes with options set, or a filesystem type, or a device to mount need to // be mounted and unmounted. func (v *Volume) needsMount() bool { - return len(v.config.Options) > 0 && v.config.Driver == define.VolumeDriverLocal + // Non-local driver always needs mount + if v.UsesVolumeDriver() { + return true + } + + // Local driver with options needs mount + return len(v.config.Options) > 0 } // update() updates the volume state from the DB. diff --git a/libpod/volume_internal_linux.go b/libpod/volume_internal_linux.go index bbf47f124..e184505e7 100644 --- a/libpod/volume_internal_linux.go +++ b/libpod/volume_internal_linux.go @@ -8,11 +8,17 @@ import ( "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/rootless" + pluginapi "github.com/docker/go-plugins-helpers/volume" "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) +// This is a pseudo-container ID to use when requesting a mount or unmount from +// the volume plugins. +// This is the shas256 of the string "placeholder\n". +const pseudoCtrID = "2f73349cfc4630255319c6c8dfc1b46a8996ace9d14d8e07563b165915918ec2" + // mount mounts the volume if necessary. // A mount is necessary if a volume has any options set. // If a mount is necessary, v.state.MountCount will be incremented. @@ -20,7 +26,7 @@ import ( // host. Otherwise, we assume it is already mounted. // Must be done while the volume is locked. // Is a no-op on volumes that do not require a mount (as defined by -// volumeNeedsMount()) +// volumeNeedsMount()). func (v *Volume) mount() error { if !v.needsMount() { return nil @@ -44,6 +50,28 @@ func (v *Volume) mount() error { return v.save() } + // Volume plugins implement their own mount counter, based on the ID of + // the mounting container. But we already have one, and honestly I trust + // ours more. So hardcode container ID to something reasonable, and use + // the same one for everything. + if v.UsesVolumeDriver() { + if v.plugin == nil { + return errors.Wrapf(define.ErrMissingPlugin, "volume plugin %s (needed by volume %s) missing", v.Driver(), v.Name()) + } + + req := new(pluginapi.MountRequest) + req.Name = v.Name() + req.ID = pseudoCtrID + mountPoint, err := v.plugin.MountVolume(req) + if err != nil { + return err + } + + v.state.MountCount += 1 + v.state.MountPoint = mountPoint + return v.save() + } + volDevice := v.config.Options["device"] volType := v.config.Options["type"] volOptions := v.config.Options["o"] @@ -132,6 +160,22 @@ func (v *Volume) unmount(force bool) error { logrus.Debugf("Volume %s mount count now at %d", v.Name(), v.state.MountCount) if v.state.MountCount == 0 { + if v.UsesVolumeDriver() { + if v.plugin == nil { + return errors.Wrapf(define.ErrMissingPlugin, "volume plugin %s (needed by volume %s) missing", v.Driver(), v.Name()) + } + + req := new(pluginapi.UnmountRequest) + req.Name = v.Name() + req.ID = pseudoCtrID + if err := v.plugin.UnmountVolume(req); err != nil { + return err + } + + v.state.MountPoint = "" + return v.save() + } + // Unmount the volume if err := unix.Unmount(v.config.MountPoint, unix.MNT_DETACH); err != nil { if err == unix.EINVAL { diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index 0f89c859e..6e1945db1 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -465,3 +465,34 @@ func formatCapabilities(slice []string) { slice[i] = strings.TrimPrefix(slice[i], "CAP_") } } + +func RenameContainer(w http.ResponseWriter, r *http.Request) { + runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value("decoder").(*schema.Decoder) + + name := utils.GetName(r) + query := struct { + Name string `schema:"name"` + }{} + 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 + } + + ctr, err := runtime.LookupContainer(name) + if err != nil { + utils.ContainerNotFound(w, name, err) + return + } + + if _, err := runtime.RenameContainer(r.Context(), ctr, query.Name); err != nil { + if errors.Cause(err) == define.ErrPodExists || errors.Cause(err) == define.ErrCtrExists { + utils.Error(w, "Something went wrong.", http.StatusConflict, err) + return + } + utils.InternalServerError(w, err) + return + } + + utils.WriteResponse(w, http.StatusNoContent, nil) +} diff --git a/pkg/api/handlers/compat/containers_prune.go b/pkg/api/handlers/compat/containers_prune.go index a1e35dd97..7bba38475 100644 --- a/pkg/api/handlers/compat/containers_prune.go +++ b/pkg/api/handlers/compat/containers_prune.go @@ -1,22 +1,19 @@ package compat import ( + "bytes" "net/http" "github.com/containers/podman/v2/libpod" + "github.com/containers/podman/v2/pkg/api/handlers" "github.com/containers/podman/v2/pkg/api/handlers/utils" - "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/containers/podman/v2/pkg/domain/filters" - "github.com/docker/docker/api/types" "github.com/gorilla/schema" "github.com/pkg/errors" ) func PruneContainers(w http.ResponseWriter, r *http.Request) { - var ( - delContainers []string - space int64 - ) runtime := r.Context().Value("runtime").(*libpod.Runtime) decoder := r.Context().Value("decoder").(*schema.Decoder) @@ -37,48 +34,45 @@ func PruneContainers(w http.ResponseWriter, r *http.Request) { filterFuncs = append(filterFuncs, generatedFunc) } + report, err := PruneContainersHelper(r, filterFuncs) + if err != nil { + utils.InternalServerError(w, err) + return + } + // Libpod response differs if utils.IsLibpodRequest(r) { - report, err := PruneContainersHelper(w, r, filterFuncs) - if err != nil { - utils.InternalServerError(w, err) - return - } - utils.WriteResponse(w, http.StatusOK, report) return } - prunedContainers, pruneErrors, err := runtime.PruneContainers(filterFuncs) - if err != nil { - utils.InternalServerError(w, err) - return - } - for ctrID, size := range prunedContainers { - if pruneErrors[ctrID] == nil { - space += size - delContainers = append(delContainers, ctrID) + var payload handlers.ContainersPruneReport + var errorMsg bytes.Buffer + for _, pr := range report { + if pr.Err != nil { + // Docker stops on first error vs. libpod which keeps going. Given API constraints, concatenate all errors + // and return that string. + errorMsg.WriteString(pr.Err.Error()) + errorMsg.WriteString("; ") + continue } + payload.ContainersDeleted = append(payload.ContainersDeleted, pr.Id) + payload.SpaceReclaimed += pr.Size } - report := types.ContainersPruneReport{ - ContainersDeleted: delContainers, - SpaceReclaimed: uint64(space), + if errorMsg.Len() > 0 { + utils.InternalServerError(w, errors.New(errorMsg.String())) + return } - utils.WriteResponse(w, http.StatusOK, report) + + utils.WriteResponse(w, http.StatusOK, payload) } -func PruneContainersHelper(w http.ResponseWriter, r *http.Request, filterFuncs []libpod.ContainerFilter) ( - *entities.ContainerPruneReport, error) { +func PruneContainersHelper(r *http.Request, filterFuncs []libpod.ContainerFilter) ([]*reports.PruneReport, error) { runtime := r.Context().Value("runtime").(*libpod.Runtime) - prunedContainers, pruneErrors, err := runtime.PruneContainers(filterFuncs) + + report, err := runtime.PruneContainers(filterFuncs) if err != nil { - utils.InternalServerError(w, err) return nil, err } - - report := &entities.ContainerPruneReport{ - Err: pruneErrors, - ID: prunedContainers, - } return report, nil } diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go index dc72500e4..0ae0f3bcf 100644 --- a/pkg/api/handlers/compat/images.go +++ b/pkg/api/handlers/compat/images.go @@ -18,7 +18,6 @@ import ( "github.com/containers/podman/v2/pkg/api/handlers/utils" "github.com/containers/podman/v2/pkg/auth" "github.com/containers/podman/v2/pkg/domain/entities" - "github.com/docker/docker/api/types" "github.com/gorilla/schema" "github.com/opencontainers/go-digest" "github.com/pkg/errors" @@ -74,50 +73,6 @@ func ExportImage(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusOK, rdr) } -func PruneImages(w http.ResponseWriter, r *http.Request) { - var ( - filters []string - ) - decoder := r.Context().Value("decoder").(*schema.Decoder) - runtime := r.Context().Value("runtime").(*libpod.Runtime) - - query := struct { - All bool - Filters map[string][]string `schema:"filters"` - }{ - // 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 - } - - idr := []types.ImageDeleteResponseItem{} - for k, v := range query.Filters { - for _, val := range v { - filters = append(filters, fmt.Sprintf("%s=%s", k, val)) - } - } - pruneCids, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, filters) - if err != nil { - utils.InternalServerError(w, err) - return - } - for _, p := range pruneCids { - idr = append(idr, types.ImageDeleteResponseItem{ - Deleted: p, - }) - } - - // FIXME/TODO to do this exactly correct, pruneimages needs to return idrs and space-reclaimed, then we are golden - ipr := types.ImagesPruneReport{ - ImagesDeleted: idr, - SpaceReclaimed: 1, // TODO we cannot supply this right now - } - utils.WriteResponse(w, http.StatusOK, handlers.ImagesPruneReport{ImagesPruneReport: ipr}) -} - func CommitContainer(w http.ResponseWriter, r *http.Request) { var ( destImage string diff --git a/pkg/api/handlers/compat/images_prune.go b/pkg/api/handlers/compat/images_prune.go new file mode 100644 index 000000000..c7e84804b --- /dev/null +++ b/pkg/api/handlers/compat/images_prune.go @@ -0,0 +1,75 @@ +package compat + +import ( + "bytes" + "fmt" + "net/http" + + "github.com/containers/podman/v2/libpod" + "github.com/containers/podman/v2/pkg/api/handlers" + "github.com/containers/podman/v2/pkg/api/handlers/utils" + "github.com/docker/docker/api/types" + "github.com/gorilla/schema" + "github.com/pkg/errors" +) + +func PruneImages(w http.ResponseWriter, r *http.Request) { + var ( + filters []string + ) + decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value("runtime").(*libpod.Runtime) + + query := struct { + All bool + Filters map[string][]string `schema:"filters"` + }{ + // 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 + } + + for k, v := range query.Filters { + for _, val := range v { + filters = append(filters, fmt.Sprintf("%s=%s", k, val)) + } + } + imagePruneReports, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, filters) + if err != nil { + utils.InternalServerError(w, err) + return + } + + idr := make([]types.ImageDeleteResponseItem, len(imagePruneReports)) + var reclaimedSpace uint64 + var errorMsg bytes.Buffer + for _, p := range imagePruneReports { + if p.Err != nil { + // Docker stops on first error vs. libpod which keeps going. Given API constraints, concatenate all errors + // and return that string. + errorMsg.WriteString(p.Err.Error()) + errorMsg.WriteString("; ") + continue + } + + idr = append(idr, types.ImageDeleteResponseItem{ + Deleted: p.Id, + }) + reclaimedSpace = reclaimedSpace + p.Size + } + if errorMsg.Len() > 0 { + utils.InternalServerError(w, errors.New(errorMsg.String())) + return + } + + payload := handlers.ImagesPruneReport{ + ImagesPruneReport: types.ImagesPruneReport{ + ImagesDeleted: idr, + SpaceReclaimed: reclaimedSpace, + }, + } + utils.WriteResponse(w, http.StatusOK, payload) +} diff --git a/pkg/api/handlers/compat/unsupported.go b/pkg/api/handlers/compat/unsupported.go index e5ff266f9..1c518690f 100644 --- a/pkg/api/handlers/compat/unsupported.go +++ b/pkg/api/handlers/compat/unsupported.go @@ -4,9 +4,8 @@ import ( "fmt" "net/http" - "github.com/containers/podman/v2/pkg/domain/entities" - "github.com/containers/podman/v2/pkg/api/handlers/utils" + "github.com/containers/podman/v2/pkg/errorhandling" log "github.com/sirupsen/logrus" ) @@ -14,5 +13,5 @@ func UnsupportedHandler(w http.ResponseWriter, r *http.Request) { msg := fmt.Sprintf("Path %s is not supported", r.URL.Path) log.Infof("Request Failed: %s", msg) - utils.WriteJSON(w, http.StatusNotFound, entities.ErrorModel{Message: msg}) + utils.WriteJSON(w, http.StatusNotFound, errorhandling.ErrorModel{Message: msg}) } diff --git a/pkg/api/handlers/compat/volumes.go b/pkg/api/handlers/compat/volumes.go index f49f06b17..82e70eb90 100644 --- a/pkg/api/handlers/compat/volumes.go +++ b/pkg/api/handlers/compat/volumes.go @@ -1,6 +1,7 @@ package compat import ( + "bytes" "encoding/json" "net/http" "net/url" @@ -8,6 +9,7 @@ import ( "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" + "github.com/containers/podman/v2/pkg/api/handlers" "github.com/containers/podman/v2/pkg/api/handlers/utils" "github.com/containers/podman/v2/pkg/domain/filters" "github.com/containers/podman/v2/pkg/domain/infra/abi/parse" @@ -56,10 +58,15 @@ func ListVolumes(w http.ResponseWriter, r *http.Request) { } volumeConfigs := make([]*docker_api_types.Volume, 0, len(vols)) for _, v := range vols { + mp, err := v.MountPoint() + if err != nil { + utils.InternalServerError(w, err) + return + } config := docker_api_types.Volume{ Name: v.Name(), Driver: v.Driver(), - Mountpoint: v.MountPoint(), + Mountpoint: mp, CreatedAt: v.CreatedTime().Format(time.RFC3339), Labels: v.Labels(), Scope: v.Scope(), @@ -104,11 +111,16 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) { // if using the compat layer and the volume already exists, we // must return a 201 with the same information as create if existingVolume != nil && !utils.IsLibpodRequest(r) { + mp, err := existingVolume.MountPoint() + if err != nil { + utils.InternalServerError(w, err) + return + } response := docker_api_types.Volume{ CreatedAt: existingVolume.CreatedTime().Format(time.RFC3339), Driver: existingVolume.Driver(), Labels: existingVolume.Labels(), - Mountpoint: existingVolume.MountPoint(), + Mountpoint: mp, Name: existingVolume.Name(), Options: existingVolume.Options(), Scope: existingVolume.Scope(), @@ -144,10 +156,15 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) { utils.InternalServerError(w, err) return } + mp, err := vol.MountPoint() + if err != nil { + utils.InternalServerError(w, err) + return + } volResponse := docker_api_types.Volume{ Name: config.Name, Driver: config.Driver, - Mountpoint: config.MountPoint, + Mountpoint: mp, CreatedAt: config.CreatedTime.Format(time.RFC3339), Labels: config.Labels, Options: config.Options, @@ -171,10 +188,15 @@ func InspectVolume(w http.ResponseWriter, r *http.Request) { utils.VolumeNotFound(w, name, err) return } + mp, err := vol.MountPoint() + if err != nil { + utils.InternalServerError(w, err) + return + } volResponse := docker_api_types.Volume{ Name: vol.Name(), Driver: vol.Driver(), - Mountpoint: vol.MountPoint(), + Mountpoint: mp, CreatedAt: vol.CreatedTime().Format(time.RFC3339), Labels: vol.Labels(), Options: vol.Options(), @@ -268,17 +290,29 @@ func PruneVolumes(w http.ResponseWriter, r *http.Request) { utils.InternalServerError(w, err) return } + + var errorMsg bytes.Buffer + var reclaimedSpace uint64 prunedIds := make([]string, 0, len(pruned)) - for k := range pruned { - // XXX: This drops any pruning per-volume error messages on the floor - prunedIds = append(prunedIds, k) + for _, v := range pruned { + if v.Err != nil { + errorMsg.WriteString(v.Err.Error()) + errorMsg.WriteString("; ") + continue + } + prunedIds = append(prunedIds, v.Id) + reclaimedSpace += v.Size } - pruneResponse := docker_api_types.VolumesPruneReport{ - VolumesDeleted: prunedIds, - // TODO: We don't have any insight into how much space was reclaimed - // from `PruneVolumes()` but it's not nullable - SpaceReclaimed: 0, + if errorMsg.Len() > 0 { + utils.InternalServerError(w, errors.New(errorMsg.String())) + return } - utils.WriteResponse(w, http.StatusOK, pruneResponse) + payload := handlers.VolumesPruneReport{ + VolumesPruneReport: docker_api_types.VolumesPruneReport{ + VolumesDeleted: prunedIds, + SpaceReclaimed: reclaimedSpace, + }, + } + utils.WriteResponse(w, http.StatusOK, payload) } diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go index 14eb44831..6b07b1cc5 100644 --- a/pkg/api/handlers/libpod/containers.go +++ b/pkg/api/handlers/libpod/containers.go @@ -275,6 +275,7 @@ func Restore(w http.ResponseWriter, r *http.Request) { Import bool `schema:"import"` Name string `schema:"name"` IgnoreRootFS bool `schema:"ignoreRootFS"` + IgnoreVolumes bool `schema:"ignoreVolumes"` IgnoreStaticIP bool `schema:"ignoreStaticIP"` IgnoreStaticMAC bool `schema:"ignoreStaticMAC"` }{ diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index 0b8712f16..5b15527b7 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -16,7 +16,6 @@ import ( "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/image" - image2 "github.com/containers/podman/v2/libpod/image" "github.com/containers/podman/v2/pkg/api/handlers" "github.com/containers/podman/v2/pkg/api/handlers/utils" "github.com/containers/podman/v2/pkg/auth" @@ -156,12 +155,12 @@ func PruneImages(w http.ResponseWriter, r *http.Request) { } } - cids, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, libpodFilters) + imagePruneReports, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, libpodFilters) if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) return } - utils.WriteResponse(w, http.StatusOK, cids) + utils.WriteResponse(w, http.StatusOK, imagePruneReports) } func ExportImage(w http.ResponseWriter, r *http.Request) { @@ -354,20 +353,7 @@ func ImagesLoad(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to load image")) return } - split := strings.Split(loadedImage, ",") - newImage, err := runtime.ImageRuntime().NewFromLocal(split[0]) - if err != nil { - utils.InternalServerError(w, err) - return - } - // TODO this should go into libpod proper at some point. - if len(query.Reference) > 0 { - if err := newImage.TagImage(query.Reference); err != nil { - utils.InternalServerError(w, err) - return - } - } - utils.WriteResponse(w, http.StatusOK, entities.ImageLoadReport{Names: split}) + utils.WriteResponse(w, http.StatusOK, entities.ImageLoadReport{Names: strings.Split(loadedImage, ",")}) } func ImagesImport(w http.ResponseWriter, r *http.Request) { @@ -524,7 +510,7 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) { utils.Error(w, "failed to get runtime config", http.StatusInternalServerError, errors.Wrap(err, "failed to get runtime config")) return } - sc := image2.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false) + sc := image.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false) tag := "latest" options := libpod.ContainerCommitOptions{ Pause: true, diff --git a/pkg/api/handlers/libpod/images_pull.go b/pkg/api/handlers/libpod/images_pull.go index 5e2727e95..bacba006d 100644 --- a/pkg/api/handlers/libpod/images_pull.go +++ b/pkg/api/handlers/libpod/images_pull.go @@ -115,10 +115,10 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { } } - writer := channel.NewWriter(make(chan []byte, 1)) + writer := channel.NewWriter(make(chan []byte)) defer writer.Close() - stderr := channel.NewWriter(make(chan []byte, 1)) + stderr := channel.NewWriter(make(chan []byte)) defer stderr.Close() images := make([]string, 0, len(imagesToPull)) diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go index 5422411cf..2409d3a20 100644 --- a/pkg/api/handlers/libpod/pods.go +++ b/pkg/api/handlers/libpod/pods.go @@ -43,6 +43,7 @@ func PodCreate(w http.ResponseWriter, r *http.Request) { } func Pods(w http.ResponseWriter, r *http.Request) { + runtime := r.Context().Value("runtime").(*libpod.Runtime) decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { Filters map[string][]string `schema:"filters"` @@ -55,7 +56,11 @@ func Pods(w http.ResponseWriter, r *http.Request) { return } - pods, err := utils.GetPods(w, r) + containerEngine := abi.ContainerEngine{Libpod: runtime} + podPSOptions := entities.PodPSOptions{ + Filters: query.Filters, + } + pods, err := containerEngine.PodPs(r.Context(), podPSOptions) if err != nil { utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) return @@ -235,7 +240,7 @@ func PodRestart(w http.ResponseWriter, r *http.Request) { } func PodPrune(w http.ResponseWriter, r *http.Request) { - reports, err := PodPruneHelper(w, r) + reports, err := PodPruneHelper(r) if err != nil { utils.InternalServerError(w, err) return @@ -243,7 +248,7 @@ func PodPrune(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusOK, reports) } -func PodPruneHelper(w http.ResponseWriter, r *http.Request) ([]*entities.PodPruneReport, error) { +func PodPruneHelper(r *http.Request) ([]*entities.PodPruneReport, error) { var ( runtime = r.Context().Value("runtime").(*libpod.Runtime) ) diff --git a/pkg/api/handlers/libpod/system.go b/pkg/api/handlers/libpod/system.go index b157dfc7b..c48c186ed 100644 --- a/pkg/api/handlers/libpod/system.go +++ b/pkg/api/handlers/libpod/system.go @@ -30,7 +30,7 @@ func SystemPrune(w http.ResponseWriter, r *http.Request) { return } - podPruneReport, err := PodPruneHelper(w, r) + podPruneReport, err := PodPruneHelper(r) if err != nil { utils.InternalServerError(w, err) return @@ -38,35 +38,28 @@ func SystemPrune(w http.ResponseWriter, r *http.Request) { systemPruneReport.PodPruneReport = podPruneReport // We could parallelize this, should we? - containerPruneReport, err := compat.PruneContainersHelper(w, r, nil) + containerPruneReports, err := compat.PruneContainersHelper(r, nil) if err != nil { utils.InternalServerError(w, err) return } - systemPruneReport.ContainerPruneReport = containerPruneReport + systemPruneReport.ContainerPruneReports = containerPruneReports - results, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, nil) + imagePruneReports, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, nil) if err != nil { utils.InternalServerError(w, err) return } - report := entities.ImagePruneReport{ - Report: entities.Report{ - Id: results, - Err: nil, - }, - } - - systemPruneReport.ImagePruneReport = &report + systemPruneReport.ImagePruneReports = imagePruneReports if query.Volumes { - volumePruneReport, err := pruneVolumesHelper(r) + volumePruneReports, err := pruneVolumesHelper(r) if err != nil { utils.InternalServerError(w, err) return } - systemPruneReport.VolumePruneReport = volumePruneReport + systemPruneReport.VolumePruneReports = volumePruneReports } utils.WriteResponse(w, http.StatusOK, systemPruneReport) } diff --git a/pkg/api/handlers/libpod/volumes.go b/pkg/api/handlers/libpod/volumes.go index b02a6a8ce..38fdf1b4d 100644 --- a/pkg/api/handlers/libpod/volumes.go +++ b/pkg/api/handlers/libpod/volumes.go @@ -9,6 +9,7 @@ import ( "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/api/handlers/utils" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/containers/podman/v2/pkg/domain/filters" "github.com/containers/podman/v2/pkg/domain/infra/abi/parse" "github.com/gorilla/schema" @@ -59,20 +60,13 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) { utils.InternalServerError(w, err) return } - config, err := vol.Config() + inspectOut, err := vol.Inspect() if err != nil { utils.InternalServerError(w, err) return } volResponse := entities.VolumeConfigResponse{ - Name: config.Name, - Driver: config.Driver, - Mountpoint: config.MountPoint, - CreatedAt: config.CreatedTime, - Labels: config.Labels, - Options: config.Options, - UID: config.UID, - GID: config.GID, + InspectVolumeData: *inspectOut, } utils.WriteResponse(w, http.StatusCreated, volResponse) } @@ -87,27 +81,13 @@ func InspectVolume(w http.ResponseWriter, r *http.Request) { utils.VolumeNotFound(w, name, err) return } - var uid, gid int - uid, err = vol.UID() + inspectOut, err := vol.Inspect() if err != nil { - utils.Error(w, "Error fetching volume UID", http.StatusInternalServerError, err) - return - } - gid, err = vol.GID() - if err != nil { - utils.Error(w, "Error fetching volume GID", http.StatusInternalServerError, err) + utils.InternalServerError(w, err) return } volResponse := entities.VolumeConfigResponse{ - Name: vol.Name(), - Driver: vol.Driver(), - Mountpoint: vol.MountPoint(), - CreatedAt: vol.CreatedTime(), - Labels: vol.Labels(), - Scope: vol.Scope(), - Options: vol.Options(), - UID: uid, - GID: gid, + InspectVolumeData: *inspectOut, } utils.WriteResponse(w, http.StatusOK, volResponse) } @@ -142,27 +122,13 @@ func ListVolumes(w http.ResponseWriter, r *http.Request) { } volumeConfigs := make([]*entities.VolumeListReport, 0, len(vols)) for _, v := range vols { - var uid, gid int - uid, err = v.UID() + inspectOut, err := v.Inspect() if err != nil { - utils.Error(w, "Error fetching volume UID", http.StatusInternalServerError, err) - return - } - gid, err = v.GID() - if err != nil { - utils.Error(w, "Error fetching volume GID", http.StatusInternalServerError, err) + utils.InternalServerError(w, err) return } config := entities.VolumeConfigResponse{ - Name: v.Name(), - Driver: v.Driver(), - Mountpoint: v.MountPoint(), - CreatedAt: v.CreatedTime(), - Labels: v.Labels(), - Scope: v.Scope(), - Options: v.Options(), - UID: uid, - GID: gid, + InspectVolumeData: *inspectOut, } volumeConfigs = append(volumeConfigs, &entities.VolumeListReport{VolumeConfigResponse: config}) } @@ -178,7 +144,7 @@ func PruneVolumes(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusOK, reports) } -func pruneVolumesHelper(r *http.Request) ([]*entities.VolumePruneReport, error) { +func pruneVolumesHelper(r *http.Request) ([]*reports.PruneReport, error) { var ( runtime = r.Context().Value("runtime").(*libpod.Runtime) decoder = r.Context().Value("decoder").(*schema.Decoder) @@ -199,17 +165,10 @@ func pruneVolumesHelper(r *http.Request) ([]*entities.VolumePruneReport, error) return nil, err } - pruned, err := runtime.PruneVolumes(r.Context(), filterFuncs) + reports, err := runtime.PruneVolumes(r.Context(), filterFuncs) if err != nil { return nil, err } - reports := make([]*entities.VolumePruneReport, 0, len(pruned)) - for k, v := range pruned { - reports = append(reports, &entities.VolumePruneReport{ - Err: v, - Id: k, - }) - } return reports, nil } func RemoveVolume(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/api/handlers/utils/errors.go b/pkg/api/handlers/utils/errors.go index fc77b8ec0..e2c287c45 100644 --- a/pkg/api/handlers/utils/errors.go +++ b/pkg/api/handlers/utils/errors.go @@ -5,7 +5,7 @@ import ( "net/http" "github.com/containers/podman/v2/libpod/define" - "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/errorhandling" "github.com/pkg/errors" log "github.com/sirupsen/logrus" ) @@ -24,7 +24,7 @@ var ( func Error(w http.ResponseWriter, apiMessage string, code int, err error) { // Log detailed message of what happened to machine running podman service log.Infof("Request Failed(%s): %s", http.StatusText(code), err.Error()) - em := entities.ErrorModel{ + em := errorhandling.ErrorModel{ Because: (errors.Cause(err)).Error(), Message: err.Error(), ResponseCode: code, diff --git a/pkg/api/handlers/utils/pods.go b/pkg/api/handlers/utils/pods.go deleted file mode 100644 index 0fe3a308b..000000000 --- a/pkg/api/handlers/utils/pods.go +++ /dev/null @@ -1,87 +0,0 @@ -package utils - -import ( - "net/http" - - "github.com/containers/podman/v2/libpod" - "github.com/containers/podman/v2/pkg/domain/entities" - dfilters "github.com/containers/podman/v2/pkg/domain/filters" - "github.com/gorilla/schema" -) - -func GetPods(w http.ResponseWriter, r *http.Request) ([]*entities.ListPodsReport, error) { - var ( - pods []*libpod.Pod - ) - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) - - query := struct { - All bool - Filters map[string][]string `schema:"filters"` - Digests bool - }{} - - if err := decoder.Decode(&query, r.URL.Query()); err != nil { - return nil, err - } - if _, found := r.URL.Query()["digests"]; found && query.Digests { - UnSupportedParameter("digests") - } - - filters := make([]libpod.PodFilter, 0, len(query.Filters)) - for k, v := range query.Filters { - f, err := dfilters.GeneratePodFilterFunc(k, v) - if err != nil { - return nil, err - } - filters = append(filters, f) - } - pods, err := runtime.Pods(filters...) - if err != nil { - return nil, err - } - - if len(pods) == 0 { - return []*entities.ListPodsReport{}, nil - } - - lps := make([]*entities.ListPodsReport, 0, len(pods)) - for _, pod := range pods { - status, err := pod.GetPodStatus() - if err != nil { - return nil, err - } - ctrs, err := pod.AllContainers() - if err != nil { - return nil, err - } - infraID, err := pod.InfraContainerID() - if err != nil { - return nil, err - } - lp := entities.ListPodsReport{ - Cgroup: pod.CgroupParent(), - Created: pod.CreatedTime(), - Id: pod.ID(), - Name: pod.Name(), - Namespace: pod.Namespace(), - Status: status, - InfraId: infraID, - Labels: pod.Labels(), - } - for _, ctr := range ctrs { - state, err := ctr.State() - if err != nil { - return nil, err - } - lp.Containers = append(lp.Containers, &entities.ListPodContainer{ - Id: ctr.ID(), - Names: ctr.Name(), - Status: state.String(), - }) - } - lps = append(lps, &lp) - } - return lps, nil -} diff --git a/pkg/api/server/docs.go b/pkg/api/server/docs.go index 1aaf31117..a99fefd7b 100644 --- a/pkg/api/server/docs.go +++ b/pkg/api/server/docs.go @@ -13,7 +13,7 @@ // You can then use cURL on the socket using requests documented below. // // NOTE: if you install the package podman-docker, it will create a symbolic -// link for /var/run/docker.sock to /run/podman/podman.sock +// link for /run/docker.sock to /run/podman/podman.sock // // See podman-service(1) for more information. // diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go index b80dea545..74a04b2e6 100644 --- a/pkg/api/server/register_containers.go +++ b/pkg/api/server/register_containers.go @@ -291,9 +291,6 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { r.HandleFunc(VersionedPath("/containers/{name}/pause"), s.APIHandler(compat.PauseContainer)).Methods(http.MethodPost) // Added non version path to URI to support docker non versioned paths r.HandleFunc("/containers/{name}/pause", s.APIHandler(compat.PauseContainer)).Methods(http.MethodPost) - r.HandleFunc(VersionedPath("/containers/{name}/rename"), s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodPost) - // Added non version path to URI to support docker non versioned paths - r.HandleFunc("/containers/{name}/rename", s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodPost) // swagger:operation POST /containers/{name}/restart compat restartContainer // --- // tags: @@ -610,6 +607,36 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // $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) + // swagger:operation POST /containers/{name}/rename compat renameContainer + // --- + // tags: + // - containers (compat) + // summary: Rename an existing container + // description: Change the name of an existing container. + // parameters: + // - in: path + // name: name + // type: string + // required: true + // description: Full or partial ID or full name of the container to rename + // - in: query + // name: name + // type: string + // required: true + // description: New name for the container + // produces: + // - application/json + // responses: + // 204: + // description: no error + // 404: + // $ref: "#/responses/NoSuchContainer" + // 409: + // $ref: "#/responses/ConflictError" + // 500: + // $ref: "#/responses/InternalError" + r.HandleFunc(VersionedPath("/containers/{name}/rename"), s.APIHandler(compat.RenameContainer)).Methods(http.MethodPost) + r.HandleFunc("/containers/{name}/rename", s.APIHandler(compat.RenameContainer)).Methods(http.MethodPost) /* libpod endpoints @@ -1463,5 +1490,34 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // 500: // $ref: "#/responses/InternalError" r.HandleFunc(VersionedPath("/libpod/containers/{name}/init"), s.APIHandler(libpod.InitContainer)).Methods(http.MethodPost) + // swagger:operation POST /libpod/containers/{name}/rename libpod libpodRenameContainer + // --- + // tags: + // - containers + // summary: Rename an existing container + // description: Change the name of an existing container. + // parameters: + // - in: path + // name: name + // type: string + // required: true + // description: Full or partial ID or full name of the container to rename + // - in: query + // name: name + // type: string + // required: true + // description: New name for the container + // produces: + // - application/json + // responses: + // 204: + // description: no error + // 404: + // $ref: "#/responses/NoSuchContainer" + // 409: + // $ref: "#/responses/ConflictError" + // 500: + // $ref: "#/responses/InternalError" + r.HandleFunc(VersionedPath("/libpod/containers/{name}/rename"), s.APIHandler(compat.RenameContainer)).Methods(http.MethodPost) return nil } diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index 7e6de8783..8d0c0800b 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -797,10 +797,6 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // summary: Load image // description: Load an image (oci-archive or docker-archive) stream. // parameters: - // - in: query - // name: reference - // description: "Optional Name[:TAG] for the image" - // type: string // - in: formData // name: upload // description: tarball of container image diff --git a/pkg/api/server/register_networks.go b/pkg/api/server/register_networks.go index e6c85d244..967d7da76 100644 --- a/pkg/api/server/register_networks.go +++ b/pkg/api/server/register_networks.go @@ -9,6 +9,19 @@ import ( ) func (s *APIServer) registerNetworkHandlers(r *mux.Router) error { + // swagger:operation POST /networks/prune compat compatPruneNetwork + // --- + // tags: + // - networks (compat) + // Summary: Delete unused networks + // description: Not supported + // produces: + // - application/json + // responses: + // 404: + // $ref: "#/responses/NoSuchNetwork" + r.HandleFunc(VersionedPath("/networks/prune"), compat.UnsupportedHandler).Methods(http.MethodPost) + r.HandleFunc("/networks/prune", compat.UnsupportedHandler).Methods(http.MethodPost) // swagger:operation DELETE /networks/{name} compat compatRemoveNetwork // --- // tags: diff --git a/pkg/api/server/swagger.go b/pkg/api/server/swagger.go index 6d349bb7d..d4fc33442 100644 --- a/pkg/api/server/swagger.go +++ b/pkg/api/server/swagger.go @@ -4,6 +4,8 @@ import ( "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" + "github.com/containers/podman/v2/pkg/errorhandling" ) // No such image @@ -11,7 +13,7 @@ import ( type swagErrNoSuchImage struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -20,7 +22,7 @@ type swagErrNoSuchImage struct { type swagErrNoSuchContainer struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -29,7 +31,7 @@ type swagErrNoSuchContainer struct { type swagErrNoSuchNetwork struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -38,7 +40,7 @@ type swagErrNoSuchNetwork struct { type swagErrNoSuchExecInstance struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -47,7 +49,7 @@ type swagErrNoSuchExecInstance struct { type swagErrNoSuchVolume struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -56,7 +58,7 @@ type swagErrNoSuchVolume struct { type swagErrNoSuchPod struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -65,7 +67,7 @@ type swagErrNoSuchPod struct { type swagErrNoSuchManifest struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -74,7 +76,7 @@ type swagErrNoSuchManifest struct { type swagInternalError struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -83,7 +85,7 @@ type swagInternalError struct { type swagConflictError struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -92,7 +94,7 @@ type swagConflictError struct { type swagBadParamError struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -101,7 +103,7 @@ type swagBadParamError struct { type swagContainerAlreadyStartedError struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -110,7 +112,7 @@ type swagContainerAlreadyStartedError struct { type swagContainerAlreadyStopped struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -119,7 +121,7 @@ type swagContainerAlreadyStopped struct { type swagPodAlreadyStartedError struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -128,7 +130,7 @@ type swagPodAlreadyStartedError struct { type swagPodAlreadyStopped struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -170,7 +172,7 @@ type ok struct { // swagger:response VolumePruneResponse type swagVolumePruneResponse struct { // in:body - Body []entities.VolumePruneReport + Body []reports.PruneReport } // Volume create response diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go index 650aa9ac5..1081a0e61 100644 --- a/pkg/bindings/containers/containers.go +++ b/pkg/bindings/containers/containers.go @@ -12,6 +12,7 @@ import ( "github.com/containers/podman/v2/pkg/api/handlers" "github.com/containers/podman/v2/pkg/bindings" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -49,11 +50,11 @@ func List(ctx context.Context, options *ListOptions) ([]entities.ListContainer, // used for more granular selection of containers. The main error returned indicates if there were runtime // errors like finding containers. Errors specific to the removal of a container are in the PruneContainerResponse // structure. -func Prune(ctx context.Context, options *PruneOptions) (*entities.ContainerPruneReport, error) { +func Prune(ctx context.Context, options *PruneOptions) ([]*reports.PruneReport, error) { if options == nil { options = new(PruneOptions) } - var reports *entities.ContainerPruneReport + var reports []*reports.PruneReport conn, err := bindings.GetClient(ctx) if err != nil { return nil, err diff --git a/pkg/bindings/containers/rename.go b/pkg/bindings/containers/rename.go new file mode 100644 index 000000000..0e8c7f198 --- /dev/null +++ b/pkg/bindings/containers/rename.go @@ -0,0 +1,28 @@ +package containers + +import ( + "context" + "net/http" + + "github.com/containers/podman/v2/pkg/bindings" +) + +// Rename an existing container. +func Rename(ctx context.Context, nameOrID string, options *RenameOptions) error { + if options == nil { + options = new(RenameOptions) + } + conn, err := bindings.GetClient(ctx) + if err != nil { + return err + } + params, err := options.ToParams() + if err != nil { + return err + } + response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/rename", params, nil, nameOrID) + if err != nil { + return err + } + return response.Process(nil) +} diff --git a/pkg/bindings/containers/types.go b/pkg/bindings/containers/types.go index 24402e982..10f553e52 100644 --- a/pkg/bindings/containers/types.go +++ b/pkg/bindings/containers/types.go @@ -194,6 +194,13 @@ type InitOptions struct{} // ShouldRestartOptions type ShouldRestartOptions struct{} +//go:generate go run ../generator/generator.go RenameOptions +// RenameOptions are options for renaming containers. +// The Name field is required. +type RenameOptions struct { + Name *string +} + //go:generate go run ../generator/generator.go ResizeTTYOptions // ResizeTTYOptions are optional options for resizing // container TTYs diff --git a/pkg/bindings/containers/types_attach_options.go b/pkg/bindings/containers/types_attach_options.go index 4ffb8ab17..6d8c1cb01 100644 --- a/pkg/bindings/containers/types_attach_options.go +++ b/pkg/bindings/containers/types_attach_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:18.566804404 -0600 CST m=+0.000258831 */ // Changed @@ -39,6 +38,7 @@ func (o *AttachOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_checkpoint_options.go b/pkg/bindings/containers/types_checkpoint_options.go index d03dc8231..ec766de4a 100644 --- a/pkg/bindings/containers/types_checkpoint_options.go +++ b/pkg/bindings/containers/types_checkpoint_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:18.714853285 -0600 CST m=+0.000319103 */ // Changed @@ -39,6 +38,7 @@ func (o *CheckpointOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_commit_options.go b/pkg/bindings/containers/types_commit_options.go index a8b215141..b745bebe2 100644 --- a/pkg/bindings/containers/types_commit_options.go +++ b/pkg/bindings/containers/types_commit_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:18.420656951 -0600 CST m=+0.000259662 */ // Changed @@ -39,6 +38,7 @@ func (o *CommitOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_create_options.go b/pkg/bindings/containers/types_create_options.go index 4dbce0203..4b9574cf1 100644 --- a/pkg/bindings/containers/types_create_options.go +++ b/pkg/bindings/containers/types_create_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:19.011789618 -0600 CST m=+0.000259413 */ // Changed @@ -39,6 +38,7 @@ func (o *CreateOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_diff_options.go b/pkg/bindings/containers/types_diff_options.go index be3bbf554..55fa6930d 100644 --- a/pkg/bindings/containers/types_diff_options.go +++ b/pkg/bindings/containers/types_diff_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:19.159128927 -0600 CST m=+0.000255635 */ // Changed @@ -39,6 +38,7 @@ func (o *DiffOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_execinspect_options.go b/pkg/bindings/containers/types_execinspect_options.go index 3c4c870be..c5d1f931a 100644 --- a/pkg/bindings/containers/types_execinspect_options.go +++ b/pkg/bindings/containers/types_execinspect_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:19.303239014 -0600 CST m=+0.000256861 */ // Changed @@ -39,6 +38,7 @@ func (o *ExecInspectOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_execstart_options.go b/pkg/bindings/containers/types_execstart_options.go index 66fdc82cb..9ecb70a3e 100644 --- a/pkg/bindings/containers/types_execstart_options.go +++ b/pkg/bindings/containers/types_execstart_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:19.447714428 -0600 CST m=+0.000257278 */ // Changed @@ -39,6 +38,7 @@ func (o *ExecStartOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_execstartandattach_options.go b/pkg/bindings/containers/types_execstartandattach_options.go index 43900d29d..a5a691e35 100644 --- a/pkg/bindings/containers/types_execstartandattach_options.go +++ b/pkg/bindings/containers/types_execstartandattach_options.go @@ -6,6 +6,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -13,8 +14,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:22.827903078 -0600 CST m=+0.000269906 */ // Changed @@ -41,6 +40,7 @@ func (o *ExecStartAndAttachOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_export_options.go b/pkg/bindings/containers/types_export_options.go index e325bd2cd..55e413c72 100644 --- a/pkg/bindings/containers/types_export_options.go +++ b/pkg/bindings/containers/types_export_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:22.101679998 -0600 CST m=+0.000261669 */ // Changed @@ -39,6 +38,7 @@ func (o *ExportOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_healthcheck_options.go b/pkg/bindings/containers/types_healthcheck_options.go index 8c4300366..9d8b25bf4 100644 --- a/pkg/bindings/containers/types_healthcheck_options.go +++ b/pkg/bindings/containers/types_healthcheck_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:19.593883686 -0600 CST m=+0.000289845 */ // Changed @@ -39,6 +38,7 @@ func (o *HealthCheckOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_init_options.go b/pkg/bindings/containers/types_init_options.go index 655362f62..6fb5795c0 100644 --- a/pkg/bindings/containers/types_init_options.go +++ b/pkg/bindings/containers/types_init_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:22.245077233 -0600 CST m=+0.000255461 */ // Changed @@ -39,6 +38,7 @@ func (o *InitOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_inspect_options.go b/pkg/bindings/containers/types_inspect_options.go index 884f5524d..722372414 100644 --- a/pkg/bindings/containers/types_inspect_options.go +++ b/pkg/bindings/containers/types_inspect_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:20.635987603 -0600 CST m=+0.000260270 */ // Changed @@ -39,6 +38,7 @@ func (o *InspectOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_kill_options.go b/pkg/bindings/containers/types_kill_options.go index 3d6fa6224..dd84f0d9f 100644 --- a/pkg/bindings/containers/types_kill_options.go +++ b/pkg/bindings/containers/types_kill_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:20.781581076 -0600 CST m=+0.000259040 */ // Changed @@ -39,6 +38,7 @@ func (o *KillOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_list_options.go b/pkg/bindings/containers/types_list_options.go index dd74d37b7..43326fa59 100644 --- a/pkg/bindings/containers/types_list_options.go +++ b/pkg/bindings/containers/types_list_options.go @@ -12,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:20.199081744 -0600 CST m=+0.000270626 */ // Changed diff --git a/pkg/bindings/containers/types_log_options.go b/pkg/bindings/containers/types_log_options.go index a6958242f..364f29de4 100644 --- a/pkg/bindings/containers/types_log_options.go +++ b/pkg/bindings/containers/types_log_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:18.273264471 -0600 CST m=+0.000274536 */ // Changed @@ -39,6 +38,7 @@ func (o *LogOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_mount_options.go b/pkg/bindings/containers/types_mount_options.go index c0e253094..6f4349b73 100644 --- a/pkg/bindings/containers/types_mount_options.go +++ b/pkg/bindings/containers/types_mount_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:19.740822464 -0600 CST m=+0.000250074 */ // Changed @@ -39,6 +38,7 @@ func (o *MountOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_mountedcontainerpaths_options.go b/pkg/bindings/containers/types_mountedcontainerpaths_options.go index e368ff131..0d8b69654 100644 --- a/pkg/bindings/containers/types_mountedcontainerpaths_options.go +++ b/pkg/bindings/containers/types_mountedcontainerpaths_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:20.048233253 -0600 CST m=+0.000307223 */ // Changed @@ -39,6 +38,7 @@ func (o *MountedContainerPathsOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_pause_options.go b/pkg/bindings/containers/types_pause_options.go index 26ad86793..0cc65f64e 100644 --- a/pkg/bindings/containers/types_pause_options.go +++ b/pkg/bindings/containers/types_pause_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:20.929891294 -0600 CST m=+0.000261081 */ // Changed @@ -39,6 +38,7 @@ func (o *PauseOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_prune_options.go b/pkg/bindings/containers/types_prune_options.go index e3c0f4de7..10adf0a2a 100644 --- a/pkg/bindings/containers/types_prune_options.go +++ b/pkg/bindings/containers/types_prune_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:20.344278799 -0600 CST m=+0.000263499 */ // Changed @@ -39,6 +38,7 @@ func (o *PruneOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_remove_options.go b/pkg/bindings/containers/types_remove_options.go index 6f59f0ed5..e21fb41f7 100644 --- a/pkg/bindings/containers/types_remove_options.go +++ b/pkg/bindings/containers/types_remove_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:20.489968735 -0600 CST m=+0.000264450 */ // Changed @@ -39,6 +38,7 @@ func (o *RemoveOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_rename_options.go b/pkg/bindings/containers/types_rename_options.go new file mode 100644 index 000000000..b7a723f7a --- /dev/null +++ b/pkg/bindings/containers/types_rename_options.go @@ -0,0 +1,104 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + "strings" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. +*/ + +// Changed +func (o *RenameOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *RenameOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + fieldName = strings.ToLower(fieldName) + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithName +func (o *RenameOptions) WithName(value string) *RenameOptions { + v := &value + o.Name = v + return o +} + +// GetName +func (o *RenameOptions) GetName() string { + var name string + if o.Name == nil { + return name + } + return *o.Name +} diff --git a/pkg/bindings/containers/types_resizeexectty_options.go b/pkg/bindings/containers/types_resizeexectty_options.go index 33bb4e78b..0212adeb2 100644 --- a/pkg/bindings/containers/types_resizeexectty_options.go +++ b/pkg/bindings/containers/types_resizeexectty_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:22.680735758 -0600 CST m=+0.000267081 */ // Changed @@ -39,6 +38,7 @@ func (o *ResizeExecTTYOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_resizetty_options.go b/pkg/bindings/containers/types_resizetty_options.go index 29ec54988..cee607902 100644 --- a/pkg/bindings/containers/types_resizetty_options.go +++ b/pkg/bindings/containers/types_resizetty_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:22.535788375 -0600 CST m=+0.000266528 */ // Changed @@ -39,6 +38,7 @@ func (o *ResizeTTYOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_restart_options.go b/pkg/bindings/containers/types_restart_options.go index 13ac099b1..8dcc6b5b7 100644 --- a/pkg/bindings/containers/types_restart_options.go +++ b/pkg/bindings/containers/types_restart_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:21.076709643 -0600 CST m=+0.000303354 */ // Changed @@ -39,6 +38,7 @@ func (o *RestartOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_restore_options.go b/pkg/bindings/containers/types_restore_options.go index be6e94736..491d678a5 100644 --- a/pkg/bindings/containers/types_restore_options.go +++ b/pkg/bindings/containers/types_restore_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:18.861536405 -0600 CST m=+0.000300026 */ // Changed @@ -39,6 +38,7 @@ func (o *RestoreOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_shouldrestart_options.go b/pkg/bindings/containers/types_shouldrestart_options.go index c833d0d8b..30ab618c7 100644 --- a/pkg/bindings/containers/types_shouldrestart_options.go +++ b/pkg/bindings/containers/types_shouldrestart_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:22.388596051 -0600 CST m=+0.000253693 */ // Changed @@ -39,6 +38,7 @@ func (o *ShouldRestartOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_start_options.go b/pkg/bindings/containers/types_start_options.go index 5918af89b..4050a8993 100644 --- a/pkg/bindings/containers/types_start_options.go +++ b/pkg/bindings/containers/types_start_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:21.221364502 -0600 CST m=+0.000276575 */ // Changed @@ -39,6 +38,7 @@ func (o *StartOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_stats_options.go b/pkg/bindings/containers/types_stats_options.go index f821ea1cd..74f419913 100644 --- a/pkg/bindings/containers/types_stats_options.go +++ b/pkg/bindings/containers/types_stats_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:21.370213399 -0600 CST m=+0.000264334 */ // Changed @@ -39,6 +38,7 @@ func (o *StatsOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_stop_options.go b/pkg/bindings/containers/types_stop_options.go index 14d7633a0..db692dbf0 100644 --- a/pkg/bindings/containers/types_stop_options.go +++ b/pkg/bindings/containers/types_stop_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:21.95469621 -0600 CST m=+0.000261399 */ // Changed @@ -39,6 +38,7 @@ func (o *StopOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_top_options.go b/pkg/bindings/containers/types_top_options.go index 95a1ee686..5f2717c28 100644 --- a/pkg/bindings/containers/types_top_options.go +++ b/pkg/bindings/containers/types_top_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:21.515867629 -0600 CST m=+0.000257106 */ // Changed @@ -39,6 +38,7 @@ func (o *TopOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_unmount_options.go b/pkg/bindings/containers/types_unmount_options.go index a29bd8216..060327c4a 100644 --- a/pkg/bindings/containers/types_unmount_options.go +++ b/pkg/bindings/containers/types_unmount_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:19.891657824 -0600 CST m=+0.000326668 */ // Changed @@ -39,6 +38,7 @@ func (o *UnmountOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_unpause_options.go b/pkg/bindings/containers/types_unpause_options.go index 44c077df2..e02bf2c95 100644 --- a/pkg/bindings/containers/types_unpause_options.go +++ b/pkg/bindings/containers/types_unpause_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:21.661356277 -0600 CST m=+0.000262608 */ // Changed @@ -39,6 +38,7 @@ func (o *UnpauseOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_wait_options.go b/pkg/bindings/containers/types_wait_options.go index 18d36c377..470d67611 100644 --- a/pkg/bindings/containers/types_wait_options.go +++ b/pkg/bindings/containers/types_wait_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" "github.com/containers/podman/v2/libpod/define" jsoniter "github.com/json-iterator/go" @@ -12,8 +13,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:21.809397978 -0600 CST m=+0.000267049 */ // Changed @@ -40,6 +39,7 @@ func (o *WaitOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/errors.go b/pkg/bindings/errors.go index 603299389..e75ce898d 100644 --- a/pkg/bindings/errors.go +++ b/pkg/bindings/errors.go @@ -4,7 +4,7 @@ import ( "encoding/json" "io/ioutil" - "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/errorhandling" "github.com/pkg/errors" ) @@ -13,7 +13,7 @@ var ( ) func handleError(data []byte) error { - e := entities.ErrorModel{} + e := errorhandling.ErrorModel{} if err := json.Unmarshal(data, &e); err != nil { return err } @@ -36,7 +36,7 @@ func (a APIResponse) Process(unmarshalInto interface{}) error { } func CheckResponseCode(inError error) (int, error) { - e, ok := inError.(entities.ErrorModel) + e, ok := inError.(errorhandling.ErrorModel) if !ok { return -1, errors.New("error is not type ErrorModel") } diff --git a/pkg/bindings/generate/types_kube_options.go b/pkg/bindings/generate/types_kube_options.go index 68488aaee..5fb965c9f 100644 --- a/pkg/bindings/generate/types_kube_options.go +++ b/pkg/bindings/generate/types_kube_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:20.522950566 -0600 CST m=+0.000154384 */ // Changed @@ -39,6 +38,7 @@ func (o *KubeOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/generate/types_systemd_options.go b/pkg/bindings/generate/types_systemd_options.go index 0e8a46aa0..ce7286b3a 100644 --- a/pkg/bindings/generate/types_systemd_options.go +++ b/pkg/bindings/generate/types_systemd_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:20.661450253 -0600 CST m=+0.000135779 */ // Changed @@ -39,6 +38,7 @@ func (o *SystemdOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/generator/generator.go b/pkg/bindings/generator/generator.go index 8c79aebae..6a7f600a8 100644 --- a/pkg/bindings/generator/generator.go +++ b/pkg/bindings/generator/generator.go @@ -19,13 +19,10 @@ var bodyTmpl = `package {{.PackageName}} import ( {{range $import := .Imports}} {{$import}} {{end}} - ) /* This file is generated automatically by go generate. Do not edit. - -Created {{.Date}} */ // Changed @@ -52,6 +49,7 @@ func (o *{{.StructName}}) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() @@ -239,7 +237,7 @@ func main() { closed = true // go fmt file - gofmt := exec.Command("gofmt", "-w", "-s", out.Name()) + gofmt := exec.Command("go", "fmt", out.Name()) gofmt.Stderr = os.Stdout if err := gofmt.Run(); err != nil { fmt.Println(err) diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go index 9beb493c8..ae6962c8c 100644 --- a/pkg/bindings/images/images.go +++ b/pkg/bindings/images/images.go @@ -12,6 +12,7 @@ import ( "github.com/containers/podman/v2/pkg/auth" "github.com/containers/podman/v2/pkg/bindings" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/pkg/errors" ) @@ -112,20 +113,13 @@ func History(ctx context.Context, nameOrID string, options *HistoryOptions) ([]* return history, response.Process(&history) } -func Load(ctx context.Context, r io.Reader, options *LoadOptions) (*entities.ImageLoadReport, error) { - if options == nil { - options = new(LoadOptions) - } +func Load(ctx context.Context, r io.Reader) (*entities.ImageLoadReport, error) { var report entities.ImageLoadReport conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params, err := options.ToParams() - if err != nil { - return nil, err - } - response, err := conn.DoRequest(r, http.MethodPost, "/images/load", params, nil) + response, err := conn.DoRequest(r, http.MethodPost, "/images/load", nil, nil) if err != nil { return nil, err } @@ -163,9 +157,9 @@ func Export(ctx context.Context, nameOrIDs []string, w io.Writer, options *Expor // Prune removes unused images from local storage. The optional filters can be used to further // define which images should be pruned. -func Prune(ctx context.Context, options *PruneOptions) ([]string, error) { +func Prune(ctx context.Context, options *PruneOptions) ([]*reports.PruneReport, error) { var ( - deleted []string + deleted []*reports.PruneReport ) if options == nil { options = new(PruneOptions) @@ -182,7 +176,8 @@ func Prune(ctx context.Context, options *PruneOptions) ([]string, error) { if err != nil { return deleted, err } - return deleted, response.Process(&deleted) + err = response.Process(&deleted) + return deleted, err } // Tag adds an additional name to locally-stored image. Both the tag and repo parameters are required. diff --git a/pkg/bindings/images/types_diff_options.go b/pkg/bindings/images/types_diff_options.go index d27c8945e..34a5bf2df 100644 --- a/pkg/bindings/images/types_diff_options.go +++ b/pkg/bindings/images/types_diff_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:26.320022698 -0600 CST m=+0.000277796 */ // Changed @@ -39,6 +38,7 @@ func (o *DiffOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_export_options.go b/pkg/bindings/images/types_export_options.go index 078b27fc0..172cb2b5c 100644 --- a/pkg/bindings/images/types_export_options.go +++ b/pkg/bindings/images/types_export_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:27.173810543 -0600 CST m=+0.000239871 */ // Changed @@ -39,6 +38,7 @@ func (o *ExportOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_get_options.go b/pkg/bindings/images/types_get_options.go index 1161657f7..c91ddb170 100644 --- a/pkg/bindings/images/types_get_options.go +++ b/pkg/bindings/images/types_get_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:26.609005517 -0600 CST m=+0.000241828 */ // Changed @@ -39,6 +38,7 @@ func (o *GetOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_history_options.go b/pkg/bindings/images/types_history_options.go index 6f9854e03..bd4224cd8 100644 --- a/pkg/bindings/images/types_history_options.go +++ b/pkg/bindings/images/types_history_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:26.890854681 -0600 CST m=+0.000243668 */ // Changed @@ -39,6 +38,7 @@ func (o *HistoryOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_import_options.go b/pkg/bindings/images/types_import_options.go index f5e6c8f7e..81eda946e 100644 --- a/pkg/bindings/images/types_import_options.go +++ b/pkg/bindings/images/types_import_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:27.740585278 -0600 CST m=+0.000340441 */ // Changed @@ -39,6 +38,7 @@ func (o *ImportOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_list_options.go b/pkg/bindings/images/types_list_options.go index 209d72e34..5dc4242fc 100644 --- a/pkg/bindings/images/types_list_options.go +++ b/pkg/bindings/images/types_list_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:26.462967928 -0600 CST m=+0.000289760 */ // Changed @@ -39,6 +38,7 @@ func (o *ListOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_load_options.go b/pkg/bindings/images/types_load_options.go index 6bba573d4..7bbd56c09 100644 --- a/pkg/bindings/images/types_load_options.go +++ b/pkg/bindings/images/types_load_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:27.031848205 -0600 CST m=+0.000279409 */ // Changed @@ -39,6 +38,7 @@ func (o *LoadOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_prune_options.go b/pkg/bindings/images/types_prune_options.go index c29fdae12..c290bb379 100644 --- a/pkg/bindings/images/types_prune_options.go +++ b/pkg/bindings/images/types_prune_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:27.316938584 -0600 CST m=+0.000239843 */ // Changed @@ -39,6 +38,7 @@ func (o *PruneOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_pull_options.go b/pkg/bindings/images/types_pull_options.go index 07f3e079d..5163a6341 100644 --- a/pkg/bindings/images/types_pull_options.go +++ b/pkg/bindings/images/types_pull_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" "github.com/containers/common/pkg/config" jsoniter "github.com/json-iterator/go" @@ -12,8 +13,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:28.164648348 -0600 CST m=+0.000243264 */ // Changed @@ -40,6 +39,7 @@ func (o *PullOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_push_options.go b/pkg/bindings/images/types_push_options.go index f9ce1b835..15210f30b 100644 --- a/pkg/bindings/images/types_push_options.go +++ b/pkg/bindings/images/types_push_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:27.881232044 -0600 CST m=+0.000242458 */ // Changed @@ -39,6 +38,7 @@ func (o *PushOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_remove_options.go b/pkg/bindings/images/types_remove_options.go index c9692c2b7..66a6bea7d 100644 --- a/pkg/bindings/images/types_remove_options.go +++ b/pkg/bindings/images/types_remove_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:26.180391541 -0600 CST m=+0.000290244 */ // Changed @@ -39,6 +38,7 @@ func (o *RemoveOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_search_options.go b/pkg/bindings/images/types_search_options.go index e6168ac33..299d27505 100644 --- a/pkg/bindings/images/types_search_options.go +++ b/pkg/bindings/images/types_search_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:28.023569573 -0600 CST m=+0.000245548 */ // Changed @@ -39,6 +38,7 @@ func (o *SearchOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_tag_options.go b/pkg/bindings/images/types_tag_options.go index f6396e590..40cd4a35b 100644 --- a/pkg/bindings/images/types_tag_options.go +++ b/pkg/bindings/images/types_tag_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:27.457906682 -0600 CST m=+0.000245071 */ // Changed @@ -39,6 +38,7 @@ func (o *TagOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_tree_options.go b/pkg/bindings/images/types_tree_options.go index fb2493f85..a671fa4e0 100644 --- a/pkg/bindings/images/types_tree_options.go +++ b/pkg/bindings/images/types_tree_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:26.749625305 -0600 CST m=+0.000267624 */ // Changed @@ -39,6 +38,7 @@ func (o *TreeOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_untag_options.go b/pkg/bindings/images/types_untag_options.go index 8faf5c14e..e38c5f18e 100644 --- a/pkg/bindings/images/types_untag_options.go +++ b/pkg/bindings/images/types_untag_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:27.600379913 -0600 CST m=+0.000251449 */ // Changed @@ -39,6 +38,7 @@ func (o *UntagOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/manifests/types_add_options.go b/pkg/bindings/manifests/types_add_options.go index d45effedc..1e588c668 100644 --- a/pkg/bindings/manifests/types_add_options.go +++ b/pkg/bindings/manifests/types_add_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:57:55.92237379 -0600 CST m=+0.000150701 */ // Changed @@ -39,6 +38,7 @@ func (o *AddOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/manifests/types_create_options.go b/pkg/bindings/manifests/types_create_options.go index da07f1abf..3a564a92b 100644 --- a/pkg/bindings/manifests/types_create_options.go +++ b/pkg/bindings/manifests/types_create_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:57:55.784206871 -0600 CST m=+0.000157049 */ // Changed @@ -39,6 +38,7 @@ func (o *CreateOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/manifests/types_inspect_options.go b/pkg/bindings/manifests/types_inspect_options.go index 0d32d5bb9..2af4190d4 100644 --- a/pkg/bindings/manifests/types_inspect_options.go +++ b/pkg/bindings/manifests/types_inspect_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:57:55.631746481 -0600 CST m=+0.000143104 */ // Changed @@ -39,6 +38,7 @@ func (o *InspectOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/manifests/types_push_options.go b/pkg/bindings/manifests/types_push_options.go index 4226733c9..1d689f699 100644 --- a/pkg/bindings/manifests/types_push_options.go +++ b/pkg/bindings/manifests/types_push_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:57:56.197438009 -0600 CST m=+0.000149060 */ // Changed @@ -39,6 +38,7 @@ func (o *PushOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/manifests/types_remove_options.go b/pkg/bindings/manifests/types_remove_options.go index f99220f6c..3b35c38b8 100644 --- a/pkg/bindings/manifests/types_remove_options.go +++ b/pkg/bindings/manifests/types_remove_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:57:56.059954408 -0600 CST m=+0.000146015 */ // Changed @@ -39,6 +38,7 @@ func (o *RemoveOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/network/types_connect_options.go b/pkg/bindings/network/types_connect_options.go index 6fcc4536e..b6081ba57 100644 --- a/pkg/bindings/network/types_connect_options.go +++ b/pkg/bindings/network/types_connect_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:34.075822786 -0600 CST m=+0.000167237 */ // Changed @@ -39,6 +38,7 @@ func (o *ConnectOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/network/types_create_options.go b/pkg/bindings/network/types_create_options.go index daec6a254..5b0abe870 100644 --- a/pkg/bindings/network/types_create_options.go +++ b/pkg/bindings/network/types_create_options.go @@ -5,6 +5,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -12,8 +13,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:33.37307678 -0600 CST m=+0.000176739 */ // Changed @@ -40,6 +39,7 @@ func (o *CreateOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() @@ -193,9 +193,9 @@ func (o *CreateOptions) WithIPRange(value net.IPNet) *CreateOptions { // GetIPRange func (o *CreateOptions) GetIPRange() net.IPNet { - var ipRange net.IPNet + var iPRange net.IPNet if o.IPRange == nil { - return ipRange + return iPRange } return *o.IPRange } diff --git a/pkg/bindings/network/types_disconnect_options.go b/pkg/bindings/network/types_disconnect_options.go index 821279976..8b2a9cb71 100644 --- a/pkg/bindings/network/types_disconnect_options.go +++ b/pkg/bindings/network/types_disconnect_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:33.936088242 -0600 CST m=+0.000168680 */ // Changed @@ -39,6 +38,7 @@ func (o *DisconnectOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/network/types_inspect_options.go b/pkg/bindings/network/types_inspect_options.go index 7704904ce..cec5ef7b2 100644 --- a/pkg/bindings/network/types_inspect_options.go +++ b/pkg/bindings/network/types_inspect_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:33.520438264 -0600 CST m=+0.000172934 */ // Changed @@ -39,6 +38,7 @@ func (o *InspectOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/network/types_list_options.go b/pkg/bindings/network/types_list_options.go index bbd2a71db..6a33fb7b6 100644 --- a/pkg/bindings/network/types_list_options.go +++ b/pkg/bindings/network/types_list_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:33.796794129 -0600 CST m=+0.000180368 */ // Changed @@ -39,6 +38,7 @@ func (o *ListOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/network/types_remove_options.go b/pkg/bindings/network/types_remove_options.go index b24835ae4..861fe1f2c 100644 --- a/pkg/bindings/network/types_remove_options.go +++ b/pkg/bindings/network/types_remove_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:33.658228151 -0600 CST m=+0.000172527 */ // Changed @@ -39,6 +38,7 @@ func (o *RemoveOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/play/types_kube_options.go b/pkg/bindings/play/types_kube_options.go index 91cdd30aa..5aec4b479 100644 --- a/pkg/bindings/play/types_kube_options.go +++ b/pkg/bindings/play/types_kube_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:02.386833736 -0600 CST m=+0.000171080 */ // Changed @@ -39,6 +38,7 @@ func (o *KubeOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_create_options.go b/pkg/bindings/pods/types_create_options.go index b6cf0fc53..b501d1151 100644 --- a/pkg/bindings/pods/types_create_options.go +++ b/pkg/bindings/pods/types_create_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:40.05490508 -0600 CST m=+0.000156396 */ // Changed @@ -39,6 +38,7 @@ func (o *CreateOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_inspect_options.go b/pkg/bindings/pods/types_inspect_options.go index 1c881ce9c..a2eb25fef 100644 --- a/pkg/bindings/pods/types_inspect_options.go +++ b/pkg/bindings/pods/types_inspect_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:40.258801519 -0600 CST m=+0.000175055 */ // Changed @@ -39,6 +38,7 @@ func (o *InspectOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_kill_options.go b/pkg/bindings/pods/types_kill_options.go index cb5bdfd01..f9cad3579 100644 --- a/pkg/bindings/pods/types_kill_options.go +++ b/pkg/bindings/pods/types_kill_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:40.398857339 -0600 CST m=+0.000160135 */ // Changed @@ -39,6 +38,7 @@ func (o *KillOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_list_options.go b/pkg/bindings/pods/types_list_options.go index d095bf3db..02e7adf2d 100644 --- a/pkg/bindings/pods/types_list_options.go +++ b/pkg/bindings/pods/types_list_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:40.818123838 -0600 CST m=+0.000164328 */ // Changed @@ -39,6 +38,7 @@ func (o *ListOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_pause_options.go b/pkg/bindings/pods/types_pause_options.go index 06ee6f81d..2e4fdb4a6 100644 --- a/pkg/bindings/pods/types_pause_options.go +++ b/pkg/bindings/pods/types_pause_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:40.538407099 -0600 CST m=+0.000193274 */ // Changed @@ -39,6 +38,7 @@ func (o *PauseOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_prune_options.go b/pkg/bindings/pods/types_prune_options.go index 6610aa7cc..616ad6dc9 100644 --- a/pkg/bindings/pods/types_prune_options.go +++ b/pkg/bindings/pods/types_prune_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:40.678507342 -0600 CST m=+0.000183891 */ // Changed @@ -39,6 +38,7 @@ func (o *PruneOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_remove_options.go b/pkg/bindings/pods/types_remove_options.go index 8f18e43c9..6960d8839 100644 --- a/pkg/bindings/pods/types_remove_options.go +++ b/pkg/bindings/pods/types_remove_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:41.80320679 -0600 CST m=+0.000158149 */ // Changed @@ -39,6 +38,7 @@ func (o *RemoveOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_restart_options.go b/pkg/bindings/pods/types_restart_options.go index 9030de1e7..427833044 100644 --- a/pkg/bindings/pods/types_restart_options.go +++ b/pkg/bindings/pods/types_restart_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:40.958315383 -0600 CST m=+0.000168360 */ // Changed @@ -39,6 +38,7 @@ func (o *RestartOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_start_options.go b/pkg/bindings/pods/types_start_options.go index 0fce21099..e98798459 100644 --- a/pkg/bindings/pods/types_start_options.go +++ b/pkg/bindings/pods/types_start_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:41.099102916 -0600 CST m=+0.000159629 */ // Changed @@ -39,6 +38,7 @@ func (o *StartOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_stats_options.go b/pkg/bindings/pods/types_stats_options.go index d38a9a115..845a534a3 100644 --- a/pkg/bindings/pods/types_stats_options.go +++ b/pkg/bindings/pods/types_stats_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:41.658228243 -0600 CST m=+0.000160769 */ // Changed @@ -39,6 +38,7 @@ func (o *StatsOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_stop_options.go b/pkg/bindings/pods/types_stop_options.go index ac698b8c5..86000eb57 100644 --- a/pkg/bindings/pods/types_stop_options.go +++ b/pkg/bindings/pods/types_stop_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:41.237892781 -0600 CST m=+0.000155040 */ // Changed @@ -39,6 +38,7 @@ func (o *StopOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_top_options.go b/pkg/bindings/pods/types_top_options.go index 895f62957..ada0b1e25 100644 --- a/pkg/bindings/pods/types_top_options.go +++ b/pkg/bindings/pods/types_top_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:41.375876994 -0600 CST m=+0.000154839 */ // Changed @@ -39,6 +38,7 @@ func (o *TopOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_unpause_options.go b/pkg/bindings/pods/types_unpause_options.go index 3d647cf25..6a9ee8fcd 100644 --- a/pkg/bindings/pods/types_unpause_options.go +++ b/pkg/bindings/pods/types_unpause_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:41.515225789 -0600 CST m=+0.000158667 */ // Changed @@ -39,6 +38,7 @@ func (o *UnpauseOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/system/types_disk_options.go b/pkg/bindings/system/types_disk_options.go index f7d2cca06..c5eb2f94c 100644 --- a/pkg/bindings/system/types_disk_options.go +++ b/pkg/bindings/system/types_disk_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:08.087362343 -0600 CST m=+0.000150636 */ // Changed @@ -39,6 +38,7 @@ func (o *DiskOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/system/types_events_options.go b/pkg/bindings/system/types_events_options.go index 6dd64b055..2e95339e6 100644 --- a/pkg/bindings/system/types_events_options.go +++ b/pkg/bindings/system/types_events_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:07.675150173 -0600 CST m=+0.000140977 */ // Changed @@ -39,6 +38,7 @@ func (o *EventsOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/system/types_info_options.go b/pkg/bindings/system/types_info_options.go index 3a8960e1b..263513b0f 100644 --- a/pkg/bindings/system/types_info_options.go +++ b/pkg/bindings/system/types_info_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:08.233760126 -0600 CST m=+0.000142369 */ // Changed @@ -39,6 +38,7 @@ func (o *InfoOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/system/types_prune_options.go b/pkg/bindings/system/types_prune_options.go index 2cd3c8000..a9a6a6cda 100644 --- a/pkg/bindings/system/types_prune_options.go +++ b/pkg/bindings/system/types_prune_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:07.812719858 -0600 CST m=+0.000143214 */ // Changed @@ -39,6 +38,7 @@ func (o *PruneOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/system/types_version_options.go b/pkg/bindings/system/types_version_options.go index 4974e8d8f..be07581fa 100644 --- a/pkg/bindings/system/types_version_options.go +++ b/pkg/bindings/system/types_version_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:07.950759332 -0600 CST m=+0.000140376 */ // Changed @@ -39,6 +38,7 @@ func (o *VersionOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/test/common_test.go b/pkg/bindings/test/common_test.go index 232d7136f..c2b1347d2 100644 --- a/pkg/bindings/test/common_test.go +++ b/pkg/bindings/test/common_test.go @@ -182,7 +182,7 @@ func (b *bindingTest) RestoreImagesFromCache() { } } func (b *bindingTest) restoreImageFromCache(i testImage) { - p := b.runPodman([]string{"load", "-i", filepath.Join(ImageCacheDir, i.tarballName), i.name}) + p := b.runPodman([]string{"load", "-i", filepath.Join(ImageCacheDir, i.tarballName)}) p.Wait(45) } diff --git a/pkg/bindings/test/containers_test.go b/pkg/bindings/test/containers_test.go index 2ab5e45d0..fa601e7e5 100644 --- a/pkg/bindings/test/containers_test.go +++ b/pkg/bindings/test/containers_test.go @@ -8,6 +8,7 @@ import ( "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/bindings" "github.com/containers/podman/v2/pkg/bindings/containers" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/containers/podman/v2/pkg/specgen" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -533,8 +534,8 @@ var _ = Describe("Podman containers ", func() { // Prune container should return no errors and one pruned container ID. pruneResponse, err := containers.Prune(bt.conn, nil) Expect(err).To(BeNil()) - Expect(len(pruneResponse.Err)).To(Equal(0)) - Expect(len(pruneResponse.ID)).To(Equal(1)) + Expect(len(reports.PruneReportsErrs(pruneResponse))).To(Equal(0)) + Expect(len(reports.PruneReportsIds(pruneResponse))).To(Equal(1)) }) It("podman prune stopped containers with filters", func() { @@ -558,8 +559,8 @@ var _ = Describe("Podman containers ", func() { } pruneResponse, err = containers.Prune(bt.conn, new(containers.PruneOptions).WithFilters(filtersIncorrect)) Expect(err).To(BeNil()) - Expect(len(pruneResponse.Err)).To(Equal(0)) - Expect(len(pruneResponse.ID)).To(Equal(0)) + Expect(len(reports.PruneReportsIds(pruneResponse))).To(Equal(0)) + Expect(len(reports.PruneReportsErrs(pruneResponse))).To(Equal(0)) // Valid filter params container should be pruned now. filters := map[string][]string{ @@ -567,8 +568,8 @@ var _ = Describe("Podman containers ", func() { } pruneResponse, err = containers.Prune(bt.conn, new(containers.PruneOptions).WithFilters(filters)) Expect(err).To(BeNil()) - Expect(len(pruneResponse.Err)).To(Equal(0)) - Expect(len(pruneResponse.ID)).To(Equal(1)) + Expect(len(reports.PruneReportsErrs(pruneResponse))).To(Equal(0)) + Expect(len(reports.PruneReportsIds(pruneResponse))).To(Equal(1)) }) It("podman prune running containers", func() { @@ -585,7 +586,7 @@ var _ = Describe("Podman containers ", func() { // Prune. Should return no error no prune response ID. pruneResponse, err := containers.Prune(bt.conn, nil) Expect(err).To(BeNil()) - Expect(len(pruneResponse.ID)).To(Equal(0)) + Expect(len(pruneResponse)).To(Equal(0)) }) It("podman inspect bogus container", func() { diff --git a/pkg/bindings/test/images_test.go b/pkg/bindings/test/images_test.go index e178f4219..81959e813 100644 --- a/pkg/bindings/test/images_test.go +++ b/pkg/bindings/test/images_test.go @@ -9,6 +9,7 @@ import ( "github.com/containers/podman/v2/pkg/bindings" "github.com/containers/podman/v2/pkg/bindings/containers" "github.com/containers/podman/v2/pkg/bindings/images" + dreports "github.com/containers/podman/v2/pkg/domain/entities/reports" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/onsi/gomega/gexec" @@ -218,7 +219,7 @@ var _ = Describe("Podman images", func() { f, err := os.Open(filepath.Join(ImageCacheDir, alpine.tarballName)) defer f.Close() Expect(err).To(BeNil()) - names, err := images.Load(bt.conn, f, nil) + names, err := images.Load(bt.conn, f) Expect(err).To(BeNil()) Expect(names.Names[0]).To(Equal(alpine.name)) exists, err = images.Exists(bt.conn, alpine.name) @@ -233,14 +234,9 @@ var _ = Describe("Podman images", func() { exists, err = images.Exists(bt.conn, alpine.name) Expect(err).To(BeNil()) Expect(exists).To(BeFalse()) - newName := "quay.io/newname:fizzle" - options := new(images.LoadOptions).WithReference(newName) - names, err = images.Load(bt.conn, f, options) + names, err = images.Load(bt.conn, f) Expect(err).To(BeNil()) Expect(names.Names[0]).To(Equal(alpine.name)) - exists, err = images.Exists(bt.conn, newName) - Expect(err).To(BeNil()) - Expect(exists).To(BeTrue()) // load with a bad repo name should trigger a 500 f, err = os.Open(filepath.Join(ImageCacheDir, alpine.tarballName)) @@ -250,11 +246,6 @@ var _ = Describe("Podman images", func() { exists, err = images.Exists(bt.conn, alpine.name) Expect(err).To(BeNil()) Expect(exists).To(BeFalse()) - options = new(images.LoadOptions).WithReference("quay.io/newName:fizzle") - _, err = images.Load(bt.conn, f, options) - Expect(err).ToNot(BeNil()) - code, _ := bindings.CheckResponseCode(err) - Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) }) It("Export Image", func() { @@ -355,7 +346,7 @@ var _ = Describe("Podman images", func() { results, err := images.Prune(bt.conn, options) Expect(err).NotTo(HaveOccurred()) Expect(len(results)).To(BeNumerically(">", 0)) - Expect(results).To(ContainElement("docker.io/library/alpine:latest")) + Expect(dreports.PruneReportsIds(results)).To(ContainElement("docker.io/library/alpine:latest")) }) // TODO: we really need to extent to pull tests once we have a more sophisticated CI. diff --git a/pkg/bindings/test/system_test.go b/pkg/bindings/test/system_test.go index 25fda5575..44067b61d 100644 --- a/pkg/bindings/test/system_test.go +++ b/pkg/bindings/test/system_test.go @@ -10,6 +10,7 @@ import ( "github.com/containers/podman/v2/pkg/bindings/system" "github.com/containers/podman/v2/pkg/bindings/volumes" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/onsi/gomega/gexec" @@ -80,12 +81,12 @@ var _ = Describe("Podman system", func() { systemPruneResponse, err := system.Prune(bt.conn, options) Expect(err).To(BeNil()) Expect(len(systemPruneResponse.PodPruneReport)).To(Equal(1)) - Expect(len(systemPruneResponse.ContainerPruneReport.ID)).To(Equal(1)) - Expect(len(systemPruneResponse.ImagePruneReport.Report.Id)). + Expect(len(systemPruneResponse.ContainerPruneReports)).To(Equal(1)) + Expect(len(systemPruneResponse.ImagePruneReports)). To(BeNumerically(">", 0)) - Expect(systemPruneResponse.ImagePruneReport.Report.Id). + Expect(reports.PruneReportsIds(systemPruneResponse.ImagePruneReports)). To(ContainElement("docker.io/library/alpine:latest")) - Expect(len(systemPruneResponse.VolumePruneReport)).To(Equal(0)) + Expect(len(systemPruneResponse.VolumePruneReports)).To(Equal(0)) }) It("podman system prune running alpine container", func() { @@ -114,14 +115,14 @@ var _ = Describe("Podman system", func() { systemPruneResponse, err := system.Prune(bt.conn, options) Expect(err).To(BeNil()) Expect(len(systemPruneResponse.PodPruneReport)).To(Equal(1)) - Expect(len(systemPruneResponse.ContainerPruneReport.ID)).To(Equal(1)) - Expect(len(systemPruneResponse.ImagePruneReport.Report.Id)). + Expect(len(systemPruneResponse.ContainerPruneReports)).To(Equal(1)) + Expect(len(systemPruneResponse.ImagePruneReports)). To(BeNumerically(">", 0)) // Alpine image should not be pruned as used by running container - Expect(systemPruneResponse.ImagePruneReport.Report.Id). + Expect(reports.PruneReportsIds(systemPruneResponse.ImagePruneReports)). ToNot(ContainElement("docker.io/library/alpine:latest")) // Though unused volume is available it should not be pruned as flag set to false. - Expect(len(systemPruneResponse.VolumePruneReport)).To(Equal(0)) + Expect(len(systemPruneResponse.VolumePruneReports)).To(Equal(0)) }) It("podman system prune running alpine container volume prune", func() { @@ -149,14 +150,14 @@ var _ = Describe("Podman system", func() { systemPruneResponse, err := system.Prune(bt.conn, options) Expect(err).To(BeNil()) Expect(len(systemPruneResponse.PodPruneReport)).To(Equal(0)) - Expect(len(systemPruneResponse.ContainerPruneReport.ID)).To(Equal(1)) - Expect(len(systemPruneResponse.ImagePruneReport.Report.Id)). + Expect(len(systemPruneResponse.ContainerPruneReports)).To(Equal(1)) + Expect(len(systemPruneResponse.ImagePruneReports)). To(BeNumerically(">", 0)) // Alpine image should not be pruned as used by running container - Expect(systemPruneResponse.ImagePruneReport.Report.Id). + Expect(reports.PruneReportsIds(systemPruneResponse.ImagePruneReports)). ToNot(ContainElement("docker.io/library/alpine:latest")) // Volume should be pruned now as flag set true - Expect(len(systemPruneResponse.VolumePruneReport)).To(Equal(1)) + Expect(len(systemPruneResponse.VolumePruneReports)).To(Equal(1)) }) It("podman system prune running alpine container volume prune --filter", func() { @@ -197,14 +198,14 @@ var _ = Describe("Podman system", func() { // This check **should** be "Equal(0)" since we are passing label // filters however the Prune function doesn't seem to pass filters // to each component. - Expect(len(systemPruneResponse.ContainerPruneReport.ID)).To(Equal(1)) - Expect(len(systemPruneResponse.ImagePruneReport.Report.Id)). + Expect(len(systemPruneResponse.ContainerPruneReports)).To(Equal(1)) + Expect(len(systemPruneResponse.ImagePruneReports)). To(BeNumerically(">", 0)) // Alpine image should not be pruned as used by running container - Expect(systemPruneResponse.ImagePruneReport.Report.Id). + Expect(reports.PruneReportsIds(systemPruneResponse.ImagePruneReports)). ToNot(ContainElement("docker.io/library/alpine:latest")) // Volume shouldn't be pruned because the PruneOptions filters doesn't match - Expect(len(systemPruneResponse.VolumePruneReport)).To(Equal(0)) + Expect(len(systemPruneResponse.VolumePruneReports)).To(Equal(0)) // Fix filter and re prune f["label"] = []string{"label1=value1"} @@ -213,6 +214,6 @@ var _ = Describe("Podman system", func() { Expect(err).To(BeNil()) // Volume should be pruned because the PruneOptions filters now match - Expect(len(systemPruneResponse.VolumePruneReport)).To(Equal(1)) + Expect(len(systemPruneResponse.VolumePruneReports)).To(Equal(1)) }) }) diff --git a/pkg/bindings/test/volumes_test.go b/pkg/bindings/test/volumes_test.go index e0d854b66..1f1da3cfa 100644 --- a/pkg/bindings/test/volumes_test.go +++ b/pkg/bindings/test/volumes_test.go @@ -10,6 +10,7 @@ import ( "github.com/containers/podman/v2/pkg/bindings/containers" "github.com/containers/podman/v2/pkg/bindings/volumes" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/onsi/gomega/gexec" @@ -166,7 +167,7 @@ var _ = Describe("Podman volumes", func() { session.Wait(45) vols, err = volumes.Prune(connText, nil) Expect(err).To(BeNil()) - Expect(len(vols)).To(BeNumerically("==", 1)) + Expect(len(reports.PruneReportsIds(vols))).To(BeNumerically("==", 1)) _, err = volumes.Inspect(connText, "homer", nil) Expect(err).To(BeNil()) diff --git a/pkg/bindings/volumes/types_create_options.go b/pkg/bindings/volumes/types_create_options.go index 80bdac2d2..171090afe 100644 --- a/pkg/bindings/volumes/types_create_options.go +++ b/pkg/bindings/volumes/types_create_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:14.043860791 -0600 CST m=+0.000188944 */ // Changed @@ -39,6 +38,7 @@ func (o *CreateOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/volumes/types_inspect_options.go b/pkg/bindings/volumes/types_inspect_options.go index ba8c70b63..3a1d396a7 100644 --- a/pkg/bindings/volumes/types_inspect_options.go +++ b/pkg/bindings/volumes/types_inspect_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:14.189902005 -0600 CST m=+0.000151439 */ // Changed @@ -39,6 +38,7 @@ func (o *InspectOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/volumes/types_list_options.go b/pkg/bindings/volumes/types_list_options.go index 99dec132c..56033a575 100644 --- a/pkg/bindings/volumes/types_list_options.go +++ b/pkg/bindings/volumes/types_list_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:14.326721724 -0600 CST m=+0.000172471 */ // Changed @@ -39,6 +38,7 @@ func (o *ListOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/volumes/types_prune_options.go b/pkg/bindings/volumes/types_prune_options.go index cdbc03fc9..c043d69d0 100644 --- a/pkg/bindings/volumes/types_prune_options.go +++ b/pkg/bindings/volumes/types_prune_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:14.463307398 -0600 CST m=+0.000180868 */ // Changed @@ -39,6 +38,7 @@ func (o *PruneOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/volumes/types_remove_options.go b/pkg/bindings/volumes/types_remove_options.go index 923d1353c..1f8ba4e22 100644 --- a/pkg/bindings/volumes/types_remove_options.go +++ b/pkg/bindings/volumes/types_remove_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:14.60278922 -0600 CST m=+0.000134408 */ // Changed @@ -39,6 +38,7 @@ func (o *RemoveOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/volumes/volumes.go b/pkg/bindings/volumes/volumes.go index 626f52d39..fe081eb46 100644 --- a/pkg/bindings/volumes/volumes.go +++ b/pkg/bindings/volumes/volumes.go @@ -7,6 +7,7 @@ import ( "github.com/containers/podman/v2/pkg/bindings" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" jsoniter "github.com/json-iterator/go" ) @@ -77,9 +78,9 @@ func List(ctx context.Context, options *ListOptions) ([]*entities.VolumeListRepo } // Prune removes unused volumes from the local filesystem. -func Prune(ctx context.Context, options *PruneOptions) ([]*entities.VolumePruneReport, error) { +func Prune(ctx context.Context, options *PruneOptions) ([]*reports.PruneReport, error) { var ( - pruned []*entities.VolumePruneReport + pruned []*reports.PruneReport ) conn, err := bindings.GetClient(ctx) if err != nil { diff --git a/pkg/checkpoint/checkpoint_restore.go b/pkg/checkpoint/checkpoint_restore.go index 9de04266f..f6cd3b38f 100644 --- a/pkg/checkpoint/checkpoint_restore.go +++ b/pkg/checkpoint/checkpoint_restore.go @@ -8,6 +8,7 @@ import ( "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/image" + "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/errorhandling" "github.com/containers/podman/v2/pkg/util" "github.com/containers/storage/pkg/archive" @@ -36,10 +37,10 @@ func crImportFromJSON(filePath string, v interface{}) error { // 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, restoreOptions entities.RestoreOptions) ([]*libpod.Container, error) { // First get the container definition from the // tarball to a temporary directory - archiveFile, err := os.Open(input) + archiveFile, err := os.Open(restoreOptions.Import) if err != nil { return nil, errors.Wrap(err, "failed to open checkpoint archive for import") } @@ -53,6 +54,7 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input stri "rootfs-diff.tar", "network.status", "deleted.files", + "volumes", }, } dir, err := ioutil.TempDir("", "checkpoint") @@ -66,7 +68,7 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input stri }() err = archive.Untar(archiveFile, dir, options) if err != nil { - return nil, errors.Wrapf(err, "Unpacking of checkpoint archive %s failed", input) + return nil, errors.Wrapf(err, "Unpacking of checkpoint archive %s failed", restoreOptions.Import) } // Load spec.dump from temporary directory @@ -82,17 +84,30 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input stri } // This should not happen as checkpoints with these options are not exported. - if (len(config.Dependencies) > 0) || (len(config.NamedVolumes) > 0) { - return nil, errors.Errorf("Cannot import checkpoints of containers with named volumes or dependencies") + if len(config.Dependencies) > 0 { + return nil, errors.Errorf("Cannot import checkpoints of containers with dependencies") + } + + // Volumes included in the checkpoint should not exist + if !restoreOptions.IgnoreVolumes { + for _, vol := range config.NamedVolumes { + exists, err := runtime.HasVolume(vol.Name) + if err != nil { + return nil, err + } + if exists { + return nil, errors.Errorf("volume with name %s already exists. Use --ignore-volumes to not restore content of volumes", vol.Name) + } + } } ctrID := config.ID newName := false // Check if the restored container gets a new name - if name != "" { + if restoreOptions.Name != "" { config.ID = "" - config.Name = name + config.Name = restoreOptions.Name newName = true } diff --git a/pkg/domain/entities/container_ps.go b/pkg/domain/entities/container_ps.go index ff3b087ed..6709ca48a 100644 --- a/pkg/domain/entities/container_ps.go +++ b/pkg/domain/entities/container_ps.go @@ -43,6 +43,8 @@ type ListContainer struct { // Namespaces the container belongs to. Requires the // namespace boolean to be true Namespaces ListContainerNamespaces + // The network names assigned to the container + Networks []string // The process id of the container Pid int // If the container is part of Pod, the Pod ID. Requires the pod diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index b8d49d067..d8576c101 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -173,10 +173,13 @@ type CheckpointOptions struct { All bool Export string IgnoreRootFS bool + IgnoreVolumes bool Keep bool Latest bool LeaveRunning bool TCPEstablished bool + PreCheckPoint bool + WithPrevious bool } type CheckpointReport struct { @@ -187,6 +190,7 @@ type CheckpointReport struct { type RestoreOptions struct { All bool IgnoreRootFS bool + IgnoreVolumes bool IgnoreStaticIP bool IgnoreStaticMAC bool Import string @@ -194,6 +198,7 @@ type RestoreOptions struct { Latest bool Name string TCPEstablished bool + ImportPrevious string } type RestoreReport struct { @@ -390,13 +395,6 @@ type ContainerPruneOptions struct { Filters url.Values `json:"filters" schema:"filters"` } -// ContainerPruneReport describes the results after pruning the -// stopped containers. -type ContainerPruneReport struct { - ID map[string]int64 - Err map[string]error -} - // ContainerPortOptions describes the options to obtain // port information on containers type ContainerPortOptions struct { @@ -436,3 +434,9 @@ type ContainerStatsReport struct { // Results, set when there is no error. Stats []define.ContainerStats } + +// ContainerRenameOptions describes input options for renaming a container. +type ContainerRenameOptions struct { + // NewName is the new name that will be given to the container. + NewName string +} diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 80127ea45..d2552770c 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -6,6 +6,7 @@ import ( "github.com/containers/common/pkg/config" "github.com/containers/podman/v2/libpod/define" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/containers/podman/v2/pkg/specgen" "github.com/spf13/cobra" ) @@ -35,7 +36,8 @@ type ContainerEngine interface { ContainerMount(ctx context.Context, nameOrIDs []string, options ContainerMountOptions) ([]*ContainerMountReport, error) ContainerPause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error) ContainerPort(ctx context.Context, nameOrID string, options ContainerPortOptions) ([]*ContainerPortReport, error) - ContainerPrune(ctx context.Context, options ContainerPruneOptions) (*ContainerPruneReport, error) + ContainerPrune(ctx context.Context, options ContainerPruneOptions) ([]*reports.PruneReport, error) + ContainerRename(ctr context.Context, nameOrID string, options ContainerRenameOptions) error ContainerRestart(ctx context.Context, namesOrIds []string, options RestartOptions) ([]*RestartReport, error) ContainerRestore(ctx context.Context, namesOrIds []string, options RestoreOptions) ([]*RestoreReport, error) ContainerRm(ctx context.Context, namesOrIds []string, options RmOptions) ([]*RmReport, error) @@ -85,6 +87,6 @@ type ContainerEngine interface { VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IDOrNameResponse, error) VolumeInspect(ctx context.Context, namesOrIds []string, opts InspectOptions) ([]*VolumeInspectReport, []error, error) VolumeList(ctx context.Context, opts VolumeListOptions) ([]*VolumeListReport, error) - VolumePrune(ctx context.Context, options VolumePruneOptions) ([]*VolumePruneReport, error) + VolumePrune(ctx context.Context, options VolumePruneOptions) ([]*reports.PruneReport, error) VolumeRm(ctx context.Context, namesOrIds []string, opts VolumeRmOptions) ([]*VolumeRmReport, error) } diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go index 7f33d8e9d..26a136f13 100644 --- a/pkg/domain/entities/engine_image.go +++ b/pkg/domain/entities/engine_image.go @@ -4,6 +4,7 @@ import ( "context" "github.com/containers/common/pkg/config" + "github.com/containers/podman/v2/pkg/domain/entities/reports" ) type ImageEngine interface { @@ -17,7 +18,7 @@ type ImageEngine interface { List(ctx context.Context, opts ImageListOptions) ([]*ImageSummary, error) Load(ctx context.Context, opts ImageLoadOptions) (*ImageLoadReport, error) Mount(ctx context.Context, images []string, options ImageMountOptions) ([]*ImageMountReport, error) - Prune(ctx context.Context, opts ImagePruneOptions) (*ImagePruneReport, error) + Prune(ctx context.Context, opts ImagePruneOptions) ([]*reports.PruneReport, error) Pull(ctx context.Context, rawImage string, opts ImagePullOptions) (*ImagePullReport, error) Push(ctx context.Context, source string, destination string, opts ImagePushOptions) error Remove(ctx context.Context, images []string, opts ImageRemoveOptions) (*ImageRemoveReport, []error) diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index 67910a34c..0805152c3 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -247,11 +247,6 @@ type ImagePruneOptions struct { Filter []string `json:"filter" schema:"filter"` } -type ImagePruneReport struct { - Report Report - Size int64 -} - type ImageTagOptions struct{} type ImageUntagOptions struct{} @@ -261,8 +256,6 @@ type ImageInspectReport struct { } type ImageLoadOptions struct { - Name string - Tag string Input string Quiet bool SignaturePolicy string diff --git a/pkg/domain/entities/play.go b/pkg/domain/entities/play.go index 0b42e1a3f..6883fe6c5 100644 --- a/pkg/domain/entities/play.go +++ b/pkg/domain/entities/play.go @@ -40,6 +40,9 @@ type PlayKubePod struct { Containers []string // Logs - non-fatal errors and log messages while processing. Logs []string + // ContainerErrors - any errors that occurred while starting containers + // in the pod. + ContainerErrors []string } // PlayKubeReport contains the results of running play kube. diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go index 426419833..32900d536 100644 --- a/pkg/domain/entities/pods.go +++ b/pkg/domain/entities/pods.go @@ -28,8 +28,10 @@ type ListPodsReport struct { InfraId string //nolint Name string Namespace string - Status string - Labels map[string]string + // Network names connected to infra container + Networks []string + Status string + Labels map[string]string } type ListPodContainer struct { @@ -210,15 +212,16 @@ type PodStatsOptions struct { // PodStatsReport includes pod-resource statistics data. type PodStatsReport struct { - CPU string - MemUsage string - Mem string - NetIO string - BlockIO string - PIDS string - Pod string - CID string - Name string + CPU string + MemUsage string + MemUsageBytes string + Mem string + NetIO string + BlockIO string + PIDS string + Pod string + CID string + Name string } // ValidatePodStatsOptions validates the specified slice and options. Allows diff --git a/pkg/domain/entities/reports/prune.go b/pkg/domain/entities/reports/prune.go new file mode 100644 index 000000000..5494ac3ae --- /dev/null +++ b/pkg/domain/entities/reports/prune.go @@ -0,0 +1,40 @@ +package reports + +type PruneReport struct { + Id string //nolint + Err error + Size uint64 +} + +func PruneReportsIds(r []*PruneReport) []string { + ids := make([]string, 0, len(r)) + for _, v := range r { + if v == nil || v.Id == "" { + continue + } + ids = append(ids, v.Id) + } + return ids +} + +func PruneReportsErrs(r []*PruneReport) []error { + errs := make([]error, 0, len(r)) + for _, v := range r { + if v == nil || v.Err == nil { + continue + } + errs = append(errs, v.Err) + } + return errs +} + +func PruneReportsSize(r []*PruneReport) uint64 { + size := uint64(0) + for _, v := range r { + if v == nil { + continue + } + size = size + v.Size + } + return size +} diff --git a/pkg/domain/entities/system.go b/pkg/domain/entities/system.go index d5118f6a8..99fa947f0 100644 --- a/pkg/domain/entities/system.go +++ b/pkg/domain/entities/system.go @@ -4,6 +4,7 @@ import ( "time" "github.com/containers/podman/v2/libpod/define" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/docker/docker/api/types" "github.com/spf13/cobra" ) @@ -24,10 +25,11 @@ type SystemPruneOptions struct { // SystemPruneReport provides report after system prune is executed. type SystemPruneReport struct { - PodPruneReport []*PodPruneReport - *ContainerPruneReport - *ImagePruneReport - VolumePruneReport []*VolumePruneReport + PodPruneReport []*PodPruneReport + ContainerPruneReports []*reports.PruneReport + ImagePruneReports []*reports.PruneReport + VolumePruneReports []*reports.PruneReport + ReclaimedSpace uint64 } // SystemMigrateOptions describes the options needed for the diff --git a/pkg/domain/entities/types.go b/pkg/domain/entities/types.go index 12135c2b1..e5473dc62 100644 --- a/pkg/domain/entities/types.go +++ b/pkg/domain/entities/types.go @@ -1,7 +1,6 @@ package entities import ( - "errors" "net" "github.com/containers/buildah/imagebuildah" @@ -90,29 +89,6 @@ type ContainerCreateResponse struct { Warnings []string `json:"Warnings"` } -type ErrorModel struct { - // API root cause formatted for automated parsing - // example: API root cause - Because string `json:"cause"` - // human error message, formatted for a human to read - // example: human error message - Message string `json:"message"` - // http response code - ResponseCode int `json:"response"` -} - -func (e ErrorModel) Error() string { - return e.Message -} - -func (e ErrorModel) Cause() error { - return errors.New(e.Because) -} - -func (e ErrorModel) Code() int { - return e.ResponseCode -} - // BuildOptions describe the options for building container images. type BuildOptions struct { imagebuildah.BuildOptions diff --git a/pkg/domain/entities/volumes.go b/pkg/domain/entities/volumes.go index e6b29e374..c826ee389 100644 --- a/pkg/domain/entities/volumes.go +++ b/pkg/domain/entities/volumes.go @@ -2,8 +2,8 @@ package entities import ( "net/url" - "time" + "github.com/containers/podman/v2/libpod/define" docker_api_types "github.com/docker/docker/api/types" docker_api_types_volume "github.com/docker/docker/api/types/volume" ) @@ -26,38 +26,7 @@ type IDOrNameResponse struct { } type VolumeConfigResponse struct { - // Name is the name of the volume. - Name string `json:"Name"` - // Driver is the driver used to create the volume. - // This will be properly implemented in a future version. - Driver string `json:"Driver"` - // Mountpoint is the path on the host where the volume is mounted. - Mountpoint string `json:"Mountpoint"` - // CreatedAt is the date and time the volume was created at. This is not - // stored for older Libpod volumes; if so, it will be omitted. - CreatedAt time.Time `json:"CreatedAt,omitempty"` - // Status is presently unused and provided only for Docker compatibility. - // In the future it will be used to return information on the volume's - // current state. - Status map[string]string `json:"Status,omitempty"` - // Labels includes the volume's configured labels, key:value pairs that - // can be passed during volume creation to provide information for third - // party tools. - Labels map[string]string `json:"Labels"` - // Scope is unused and provided solely for Docker compatibility. It is - // unconditionally set to "local". - Scope string `json:"Scope"` - // Options is a set of options that were used when creating the volume. - // It is presently not used. - Options map[string]string `json:"Options"` - // UID is the UID that the volume was created with. - UID int `json:"UID"` - // GID is the GID that the volume was created with. - GID int `json:"GID"` - // Anonymous indicates that the volume was created as an anonymous - // volume for a specific container, and will be be removed when any - // container using it is removed. - Anonymous bool `json:"Anonymous"` + define.InspectVolumeData } // VolumeInfo Volume list response @@ -116,11 +85,6 @@ type VolumePruneOptions struct { Filters url.Values `json:"filters" schema:"filters"` } -type VolumePruneReport struct { - Err error - Id string //nolint -} - type VolumeListOptions struct { Filter map[string][]string } diff --git a/pkg/domain/filters/containers.go b/pkg/domain/filters/containers.go index 6abdd6b57..1de5aca91 100644 --- a/pkg/domain/filters/containers.go +++ b/pkg/domain/filters/containers.go @@ -7,6 +7,7 @@ import ( "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" + "github.com/containers/podman/v2/libpod/network" "github.com/containers/podman/v2/pkg/timetype" "github.com/containers/podman/v2/pkg/util" "github.com/pkg/errors" @@ -34,7 +35,7 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo filterValue = "" } for labelKey, labelValue := range labels { - if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) { + if labelKey == filterKey && (filterValue == "" || labelValue == filterValue) { matched = true break } @@ -233,7 +234,24 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo } return false }, nil - + case "network": + return func(c *libpod.Container) bool { + networks, _, err := c.Networks() + // if err or no networks, quick out + if err != nil || len(networks) == 0 { + return false + } + for _, net := range networks { + netID := network.GetNetworkID(net) + for _, val := range filterValues { + // match by network name or id + if val == net || val == netID { + return true + } + } + } + return false + }, nil } return nil, errors.Errorf("%s is an invalid filter", filter) } diff --git a/pkg/domain/filters/pods.go b/pkg/domain/filters/pods.go index 7e6b7f2cc..ce7028d2a 100644 --- a/pkg/domain/filters/pods.go +++ b/pkg/domain/filters/pods.go @@ -6,6 +6,7 @@ import ( "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" + "github.com/containers/podman/v2/libpod/network" "github.com/containers/podman/v2/pkg/util" "github.com/pkg/errors" ) @@ -123,7 +124,7 @@ func GeneratePodFilterFunc(filter string, filterValues []string) ( filterValue = "" } for labelKey, labelValue := range labels { - if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) { + if labelKey == filterKey && (filterValue == "" || labelValue == filterValue) { matched = true break } @@ -134,6 +135,29 @@ func GeneratePodFilterFunc(filter string, filterValues []string) ( } return true }, nil + case "network": + return func(p *libpod.Pod) bool { + infra, err := p.InfraContainer() + // no infra, quick out + if err != nil { + return false + } + networks, _, err := infra.Networks() + // if err or no networks, quick out + if err != nil || len(networks) == 0 { + return false + } + for _, net := range networks { + netID := network.GetNetworkID(net) + for _, val := range filterValues { + // match by network name or id + if val == net || val == netID { + return true + } + } + } + return false + }, nil } return nil, errors.Errorf("%s is an invalid filter", filter) } diff --git a/pkg/domain/filters/volumes.go b/pkg/domain/filters/volumes.go index 69bef4961..7890459f5 100644 --- a/pkg/domain/filters/volumes.go +++ b/pkg/domain/filters/volumes.go @@ -39,7 +39,7 @@ func GenerateVolumeFilters(filters url.Values) ([]libpod.VolumeFilter, error) { } vf = append(vf, func(v *libpod.Volume) bool { for labelKey, labelValue := range v.Labels() { - if labelKey == filterKey && ("" == filterVal || labelValue == filterVal) { + if labelKey == filterKey && (filterVal == "" || labelValue == filterVal) { return true } } @@ -56,7 +56,7 @@ func GenerateVolumeFilters(filters url.Values) ([]libpod.VolumeFilter, error) { } vf = append(vf, func(v *libpod.Volume) bool { for labelKey, labelValue := range v.Options() { - if labelKey == filterKey && ("" == filterVal || labelValue == filterVal) { + if labelKey == filterKey && (filterVal == "" || labelValue == filterVal) { return true } } diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index cfb3421ba..a8f4d44a8 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -21,6 +21,7 @@ import ( "github.com/containers/podman/v2/pkg/cgroups" "github.com/containers/podman/v2/pkg/checkpoint" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" dfilters "github.com/containers/podman/v2/pkg/domain/filters" "github.com/containers/podman/v2/pkg/domain/infra/abi/terminal" parallelctr "github.com/containers/podman/v2/pkg/parallel/ctr" @@ -112,15 +113,7 @@ func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []strin } func (ic *ContainerEngine) ContainerPause(ctx context.Context, namesOrIds []string, options entities.PauseUnPauseOptions) ([]*entities.PauseUnpauseReport, error) { - var ( - err error - ) - ctrs := []*libpod.Container{} //nolint - if options.All { - ctrs, err = ic.Libpod.GetAllContainers() - } else { - ctrs, err = getContainersByContext(false, false, namesOrIds, ic.Libpod) - } + ctrs, err := getContainersByContext(options.All, false, namesOrIds, ic.Libpod) if err != nil { return nil, err } @@ -133,15 +126,7 @@ func (ic *ContainerEngine) ContainerPause(ctx context.Context, namesOrIds []stri } func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []string, options entities.PauseUnPauseOptions) ([]*entities.PauseUnpauseReport, error) { - var ( - err error - ) - ctrs := []*libpod.Container{} //nolint - if options.All { - ctrs, err = ic.Libpod.GetAllContainers() - } else { - ctrs, err = getContainersByContext(false, false, namesOrIds, ic.Libpod) - } + ctrs, err := getContainersByContext(options.All, false, namesOrIds, ic.Libpod) if err != nil { return nil, err } @@ -204,7 +189,7 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin return reports, nil } -func (ic *ContainerEngine) ContainerPrune(ctx context.Context, options entities.ContainerPruneOptions) (*entities.ContainerPruneReport, error) { +func (ic *ContainerEngine) ContainerPrune(ctx context.Context, options entities.ContainerPruneOptions) ([]*reports.PruneReport, error) { filterFuncs := make([]libpod.ContainerFilter, 0, len(options.Filters)) for k, v := range options.Filters { generatedFunc, err := dfilters.GenerateContainerFilterFuncs(k, v, ic.Libpod) @@ -213,19 +198,7 @@ func (ic *ContainerEngine) ContainerPrune(ctx context.Context, options entities. } filterFuncs = append(filterFuncs, generatedFunc) } - return ic.pruneContainersHelper(filterFuncs) -} - -func (ic *ContainerEngine) pruneContainersHelper(filterFuncs []libpod.ContainerFilter) (*entities.ContainerPruneReport, error) { - prunedContainers, pruneErrors, err := ic.Libpod.PruneContainers(filterFuncs) - if err != nil { - return nil, err - } - report := entities.ContainerPruneReport{ - ID: prunedContainers, - Err: pruneErrors, - } - return &report, nil + return ic.Libpod.PruneContainers(filterFuncs) } func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []string, options entities.KillOptions) ([]*entities.KillReport, error) { @@ -498,7 +471,10 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [ TCPEstablished: options.TCPEstablished, TargetFile: options.Export, IgnoreRootfs: options.IgnoreRootFS, + IgnoreVolumes: options.IgnoreVolumes, KeepRunning: options.LeaveRunning, + PreCheckPoint: options.PreCheckPoint, + WithPrevious: options.WithPrevious, } if options.All { @@ -536,8 +512,10 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st TargetFile: options.Import, Name: options.Name, IgnoreRootfs: options.IgnoreRootFS, + IgnoreVolumes: options.IgnoreVolumes, IgnoreStaticIP: options.IgnoreStaticIP, IgnoreStaticMAC: options.IgnoreStaticMAC, + ImportPrevious: options.ImportPrevious, } filterFuncs := []libpod.ContainerFilter{ @@ -549,7 +527,7 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st switch { case options.Import != "": - cons, err = checkpoint.CRImportCheckpoint(ctx, ic.Libpod, options.Import, options.Name) + cons, err = checkpoint.CRImportCheckpoint(ctx, ic.Libpod, options) case options.All: cons, err = ic.Libpod.GetContainers(filterFuncs...) default: @@ -924,7 +902,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta if err := ic.Libpod.RemoveContainer(ctx, ctr, false, true); err != nil { if errors.Cause(err) == define.ErrNoSuchCtr || errors.Cause(err) == define.ErrCtrRemoved { - logrus.Warnf("Container %s does not exist: %v", ctr.ID(), err) + logrus.Infof("Container %s was already removed, skipping --rm", ctr.ID()) } else { logrus.Errorf("Error removing container %s: %v", ctr.ID(), err) } @@ -1334,3 +1312,17 @@ func (ic *ContainerEngine) ShouldRestart(ctx context.Context, nameOrID string) ( return &entities.BoolReport{Value: ctr.ShouldRestart(ctx)}, nil } + +// ContainerRename renames the given container. +func (ic *ContainerEngine) ContainerRename(ctx context.Context, nameOrID string, opts entities.ContainerRenameOptions) error { + ctr, err := ic.Libpod.LookupContainer(nameOrID) + if err != nil { + return err + } + + if _, err := ic.Libpod.RenameContainer(ctx, ctr, opts.NewName); err != nil { + return err + } + + return nil +} diff --git a/pkg/domain/infra/abi/containers_stat.go b/pkg/domain/infra/abi/containers_stat.go index 5b43ee2f4..931e77026 100644 --- a/pkg/domain/infra/abi/containers_stat.go +++ b/pkg/domain/infra/abi/containers_stat.go @@ -144,16 +144,29 @@ func resolveContainerPaths(container *libpod.Container, mountPoint string, conta } if volume != nil { logrus.Debugf("Container path %q resolved to volume %q on path %q", containerPath, volume.Name(), searchPath) + + // TODO: We really need to force the volume to mount + // before doing this, but that API is not exposed + // externally right now and doing so is beyond the scope + // of this commit. + mountPoint, err := volume.MountPoint() + if err != nil { + return "", "", err + } + if mountPoint == "" { + return "", "", errors.Errorf("volume %s is not mounted, cannot copy into it", volume.Name()) + } + // We found a matching volume for searchPath. We now // need to first find the relative path of our input // path to the searchPath, and then join it with the // volume's mount point. pathRelativeToVolume := strings.TrimPrefix(pathRelativeToContainerMountPoint, searchPath) - absolutePathOnTheVolumeMount, err := securejoin.SecureJoin(volume.MountPoint(), pathRelativeToVolume) + absolutePathOnTheVolumeMount, err := securejoin.SecureJoin(mountPoint, pathRelativeToVolume) if err != nil { return "", "", err } - return volume.MountPoint(), absolutePathOnTheVolumeMount, nil + return mountPoint, absolutePathOnTheVolumeMount, nil } if mount := findBindMount(container, searchPath); mount != nil { diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 394ba359c..1c233d9d5 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -22,8 +22,8 @@ import ( "github.com/containers/image/v5/types" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/image" - libpodImage "github.com/containers/podman/v2/libpod/image" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" domainUtils "github.com/containers/podman/v2/pkg/domain/utils" "github.com/containers/podman/v2/pkg/rootless" "github.com/containers/podman/v2/pkg/util" @@ -49,19 +49,12 @@ func (ir *ImageEngine) Exists(_ context.Context, nameOrID string) (*entities.Boo return &entities.BoolReport{Value: err == nil}, nil } -func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) { - results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, opts.Filter) +func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) ([]*reports.PruneReport, error) { + reports, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, opts.Filter) if err != nil { return nil, err } - - report := entities.ImagePruneReport{ - Report: entities.Report{ - Id: results, - Err: nil, - }, - } - return &report, nil + return reports, err } func (ir *ImageEngine) History(ctx context.Context, nameOrID string, opts entities.ImageHistoryOptions) (*entities.ImageHistoryReport, error) { @@ -212,7 +205,7 @@ func (ir *ImageEngine) Unmount(ctx context.Context, nameOrIDs []string, options return reports, nil } -func ToDomainHistoryLayer(layer *libpodImage.History) entities.ImageHistoryLayer { +func ToDomainHistoryLayer(layer *image.History) entities.ImageHistoryLayer { l := entities.ImageHistoryLayer{} l.ID = layer.ID l.Created = *layer.Created @@ -460,19 +453,7 @@ func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions) if err != nil { return nil, err } - names := strings.Split(name, ",") - if len(names) <= 1 { - newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(name) - if err != nil { - return nil, errors.Wrap(err, "image loaded but no additional tags were created") - } - if len(opts.Name) > 0 { - if err := newImage.TagImage(fmt.Sprintf("%s:%s", opts.Name, opts.Tag)); err != nil { - return nil, errors.Wrapf(err, "error adding %q to image %q", opts.Name, newImage.InputName) - } - } - } - return &entities.ImageLoadReport{Names: names}, nil + return &entities.ImageLoadReport{Names: strings.Split(name, ",")}, nil } func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOptions) (*entities.ImageImportReport, error) { diff --git a/pkg/domain/infra/abi/images_list.go b/pkg/domain/infra/abi/images_list.go index c4b0b7712..2d3b9f36a 100644 --- a/pkg/domain/infra/abi/images_list.go +++ b/pkg/domain/infra/abi/images_list.go @@ -44,7 +44,10 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) } e.Labels, err = img.Labels(ctx) if err != nil { - return nil, errors.Wrapf(err, "error retrieving label for image %q: you may need to remove the image to resolve the error", img.ID()) + // Ignore empty manifest lists. + if errors.Cause(err) != libpodImage.ErrImageIsBareList { + return nil, errors.Wrapf(err, "error retrieving label for image %q: you may need to remove the image to resolve the error", img.ID()) + } } ctnrs, err := img.Containers() diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go index 0c734d10d..a68ed8788 100644 --- a/pkg/domain/infra/abi/manifest.go +++ b/pkg/domain/infra/abi/manifest.go @@ -13,7 +13,6 @@ import ( "github.com/containers/buildah/manifests" buildahManifests "github.com/containers/buildah/pkg/manifests" - "github.com/containers/buildah/util" buildahUtil "github.com/containers/buildah/util" cp "github.com/containers/image/v5/copy" "github.com/containers/image/v5/docker" @@ -60,7 +59,7 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte } } sc := ir.Libpod.SystemContext() - refs, err := util.ResolveNameToReferences(ir.Libpod.GetStore(), sc, name) + refs, err := buildahUtil.ResolveNameToReferences(ir.Libpod.GetStore(), sc, name) if err != nil { return nil, err } diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index cbc74a2f2..70c7104f1 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -10,6 +10,7 @@ import ( "github.com/containers/image/v5/types" "github.com/containers/podman/v2/libpod" + "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/image" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/specgen/generate" @@ -251,21 +252,13 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY } if options.Start != types.OptionalBoolFalse { - //start the containers + // Start the containers podStartErrors, err := pod.Start(ctx) - if err != nil { + if err != nil && errors.Cause(err) != define.ErrPodPartialFail { return nil, err } - - // Previous versions of playkube started containers individually and then - // looked for errors. Because we now use the uber-Pod start call, we should - // iterate the map of possible errors and return one if there is a problem. This - // keeps the behavior the same - - for _, e := range podStartErrors { - if e != nil { - return nil, e - } + for id, err := range podStartErrors { + playKubePod.ContainerErrors = append(playKubePod.ContainerErrors, errors.Wrapf(err, "error starting container %s", id).Error()) } } diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go index f108b770c..2a8445c9f 100644 --- a/pkg/domain/infra/abi/pods.go +++ b/pkg/domain/infra/abi/pods.go @@ -333,6 +333,17 @@ func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOpti if err != nil { return nil, err } + networks := []string{} + if len(infraID) > 0 { + infra, err := p.InfraContainer() + if err != nil { + return nil, err + } + networks, _, err = infra.Networks() + if err != nil { + return nil, err + } + } reports = append(reports, &entities.ListPodsReport{ Cgroup: p.CgroupParent(), Containers: lpcs, @@ -341,6 +352,7 @@ func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOpti InfraId: infraID, Name: p.Name(), Namespace: p.Namespace(), + Networks: networks, Status: status, Labels: p.Labels(), }) diff --git a/pkg/domain/infra/abi/pods_stats.go b/pkg/domain/infra/abi/pods_stats.go index 16c10710a..29bcbe087 100644 --- a/pkg/domain/infra/abi/pods_stats.go +++ b/pkg/domain/infra/abi/pods_stats.go @@ -44,15 +44,16 @@ func (ic *ContainerEngine) podsToStatsReport(pods []*libpod.Pod) ([]*entities.Po podID := pods[i].ID()[:12] for j := range podStats { r := entities.PodStatsReport{ - CPU: floatToPercentString(podStats[j].CPU), - MemUsage: combineHumanValues(podStats[j].MemUsage, podStats[j].MemLimit), - Mem: floatToPercentString(podStats[j].MemPerc), - NetIO: combineHumanValues(podStats[j].NetInput, podStats[j].NetOutput), - BlockIO: combineHumanValues(podStats[j].BlockInput, podStats[j].BlockOutput), - PIDS: pidsToString(podStats[j].PIDs), - CID: podStats[j].ContainerID[:12], - Name: podStats[j].Name, - Pod: podID, + CPU: floatToPercentString(podStats[j].CPU), + MemUsage: combineHumanValues(podStats[j].MemUsage, podStats[j].MemLimit), + MemUsageBytes: combineBytesValues(podStats[j].MemUsage, podStats[j].MemLimit), + Mem: floatToPercentString(podStats[j].MemPerc), + NetIO: combineHumanValues(podStats[j].NetInput, podStats[j].NetOutput), + BlockIO: combineHumanValues(podStats[j].BlockInput, podStats[j].BlockOutput), + PIDS: pidsToString(podStats[j].PIDs), + CID: podStats[j].ContainerID[:12], + Name: podStats[j].Name, + Pod: podID, } reports = append(reports, &r) } @@ -68,6 +69,13 @@ func combineHumanValues(a, b uint64) string { return fmt.Sprintf("%s / %s", units.HumanSize(float64(a)), units.HumanSize(float64(b))) } +func combineBytesValues(a, b uint64) string { + if a == 0 && b == 0 { + return "-- / --" + } + return fmt.Sprintf("%s / %s", units.BytesSize(float64(a)), units.BytesSize(float64(b))) +} + func floatToPercentString(f float64) string { strippedFloat, err := utils.RemoveScientificNotationFromFloat(f) if err != nil || strippedFloat == 0 { diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go index 5f6c95d4f..f29b98696 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -16,6 +16,7 @@ import ( "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/cgroups" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/containers/podman/v2/pkg/rootless" "github.com/containers/podman/v2/pkg/util" "github.com/containers/podman/v2/utils" @@ -161,15 +162,11 @@ func movePauseProcessToScope(r *libpod.Runtime) error { return utils.RunUnderSystemdScope(int(pid), "user.slice", "podman-pause.scope") } -// checkInput can be used to verify any of the globalopt values -func checkInput() error { // nolint:deadcode,unused - return nil -} - // SystemPrune removes unused data from the system. Pruning pods, containers, volumes and images. func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.SystemPruneOptions) (*entities.SystemPruneReport, error) { var systemPruneReport = new(entities.SystemPruneReport) var filters []string + reclaimedSpace := (uint64)(0) found := true for found { found = false @@ -186,42 +183,26 @@ func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.Sys containerPruneOptions := entities.ContainerPruneOptions{} containerPruneOptions.Filters = (url.Values)(options.Filters) - containerPruneReport, err := ic.ContainerPrune(ctx, containerPruneOptions) + containerPruneReports, err := ic.ContainerPrune(ctx, containerPruneOptions) if err != nil { return nil, err } - if len(containerPruneReport.ID) > 0 { - found = true - } - if systemPruneReport.ContainerPruneReport == nil { - systemPruneReport.ContainerPruneReport = containerPruneReport - } else { - for name, val := range containerPruneReport.ID { - systemPruneReport.ContainerPruneReport.ID[name] = val - } - } + reclaimedSpace = reclaimedSpace + reports.PruneReportsSize(containerPruneReports) + systemPruneReport.ContainerPruneReports = append(systemPruneReport.ContainerPruneReports, containerPruneReports...) for k, v := range options.Filters { filters = append(filters, fmt.Sprintf("%s=%s", k, v[0])) } - results, err := ic.Libpod.ImageRuntime().PruneImages(ctx, options.All, filters) + imagePruneReports, err := ic.Libpod.ImageRuntime().PruneImages(ctx, options.All, filters) + reclaimedSpace = reclaimedSpace + reports.PruneReportsSize(imagePruneReports) if err != nil { return nil, err } - if len(results) > 0 { + if len(imagePruneReports) > 0 { found = true } - if systemPruneReport.ImagePruneReport == nil { - systemPruneReport.ImagePruneReport = &entities.ImagePruneReport{ - Report: entities.Report{ - Id: results, - Err: nil, - }, - } - } else { - systemPruneReport.ImagePruneReport.Report.Id = append(systemPruneReport.ImagePruneReport.Report.Id, results...) - } + systemPruneReport.ImagePruneReports = append(systemPruneReport.ImagePruneReports, imagePruneReports...) if options.Volume { volumePruneOptions := entities.VolumePruneOptions{} volumePruneOptions.Filters = (url.Values)(options.Filters) @@ -232,9 +213,11 @@ func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.Sys if len(volumePruneReport) > 0 { found = true } - systemPruneReport.VolumePruneReport = append(systemPruneReport.VolumePruneReport, volumePruneReport...) + reclaimedSpace = reclaimedSpace + reports.PruneReportsSize(volumePruneReport) + systemPruneReport.VolumePruneReports = append(systemPruneReport.VolumePruneReports, volumePruneReport...) } } + systemPruneReport.ReclaimedSpace = reclaimedSpace return systemPruneReport, nil } @@ -329,7 +312,17 @@ func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.System var reclaimableSize int64 for _, v := range vols { var consInUse int - volSize, err := sizeOfPath(v.MountPoint()) + mountPoint, err := v.MountPoint() + if err != nil { + return nil, err + } + if mountPoint == "" { + // We can't get any info on this volume, as it's not + // mounted. + // TODO: fix this. + continue + } + volSize, err := sizeOfPath(mountPoint) if err != nil { return nil, err } diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go index 515e52754..823605052 100644 --- a/pkg/domain/infra/abi/volumes.go +++ b/pkg/domain/infra/abi/volumes.go @@ -6,6 +6,7 @@ import ( "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/containers/podman/v2/pkg/domain/filters" "github.com/containers/podman/v2/pkg/domain/infra/abi/parse" "github.com/pkg/errors" @@ -102,32 +103,19 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin } reports := make([]*entities.VolumeInspectReport, 0, len(vols)) for _, v := range vols { - var uid, gid int - uid, err = v.UID() - if err != nil { - return nil, nil, err - } - gid, err = v.GID() + inspectOut, err := v.Inspect() if err != nil { return nil, nil, err } config := entities.VolumeConfigResponse{ - Name: v.Name(), - Driver: v.Driver(), - Mountpoint: v.MountPoint(), - CreatedAt: v.CreatedTime(), - Labels: v.Labels(), - Scope: v.Scope(), - Options: v.Options(), - UID: uid, - GID: gid, + InspectVolumeData: *inspectOut, } reports = append(reports, &entities.VolumeInspectReport{VolumeConfigResponse: &config}) } return reports, errs, nil } -func (ic *ContainerEngine) VolumePrune(ctx context.Context, options entities.VolumePruneOptions) ([]*entities.VolumePruneReport, error) { +func (ic *ContainerEngine) VolumePrune(ctx context.Context, options entities.VolumePruneOptions) ([]*reports.PruneReport, error) { filterFuncs, err := filters.GenerateVolumeFilters(options.Filters) if err != nil { return nil, err @@ -135,19 +123,12 @@ func (ic *ContainerEngine) VolumePrune(ctx context.Context, options entities.Vol return ic.pruneVolumesHelper(ctx, filterFuncs) } -func (ic *ContainerEngine) pruneVolumesHelper(ctx context.Context, filterFuncs []libpod.VolumeFilter) ([]*entities.VolumePruneReport, error) { +func (ic *ContainerEngine) pruneVolumesHelper(ctx context.Context, filterFuncs []libpod.VolumeFilter) ([]*reports.PruneReport, error) { pruned, err := ic.Libpod.PruneVolumes(ctx, filterFuncs) if err != nil { return nil, err } - reports := make([]*entities.VolumePruneReport, 0, len(pruned)) - for k, v := range pruned { - reports = append(reports, &entities.VolumePruneReport{ - Err: v, - Id: k, - }) - } - return reports, nil + return pruned, nil } func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeListOptions) ([]*entities.VolumeListReport, error) { @@ -161,25 +142,12 @@ func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeL } reports := make([]*entities.VolumeListReport, 0, len(vols)) for _, v := range vols { - var uid, gid int - uid, err = v.UID() - if err != nil { - return nil, err - } - gid, err = v.GID() + inspectOut, err := v.Inspect() if err != nil { return nil, err } config := entities.VolumeConfigResponse{ - Name: v.Name(), - Driver: v.Driver(), - Mountpoint: v.MountPoint(), - CreatedAt: v.CreatedTime(), - Labels: v.Labels(), - Scope: v.Scope(), - Options: v.Options(), - UID: uid, - GID: gid, + InspectVolumeData: *inspectOut, } reports = append(reports, &entities.VolumeListReport{VolumeConfigResponse: config}) } diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 0db985dff..4ee623703 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -18,6 +18,7 @@ import ( "github.com/containers/podman/v2/pkg/api/handlers" "github.com/containers/podman/v2/pkg/bindings/containers" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/containers/podman/v2/pkg/errorhandling" "github.com/containers/podman/v2/pkg/specgen" "github.com/containers/podman/v2/pkg/util" @@ -196,7 +197,7 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, return reports, nil } -func (ic *ContainerEngine) ContainerPrune(ctx context.Context, opts entities.ContainerPruneOptions) (*entities.ContainerPruneReport, error) { +func (ic *ContainerEngine) ContainerPrune(ctx context.Context, opts entities.ContainerPruneOptions) ([]*reports.PruneReport, error) { options := new(containers.PruneOptions).WithFilters(opts.Filters) return containers.Prune(ic.ClientCtx, options) } @@ -210,7 +211,7 @@ func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []st for _, name := range namesOrIds { inspect, err := containers.Inspect(ic.ClientCtx, name, options) if err != nil { - errModel, ok := err.(entities.ErrorModel) + errModel, ok := err.(errorhandling.ErrorModel) if !ok { return nil, nil, err } @@ -819,3 +820,8 @@ func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []stri func (ic *ContainerEngine) ShouldRestart(_ context.Context, id string) (bool, error) { return containers.ShouldRestart(ic.ClientCtx, id, nil) } + +// ContainerRename renames the given container. +func (ic *ContainerEngine) ContainerRename(ctx context.Context, nameOrID string, opts entities.ContainerRenameOptions) error { + return containers.Rename(ic.ClientCtx, nameOrID, new(containers.RenameOptions).WithName(opts.NewName)) +} diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index 10bf9682c..7a4aa1fbc 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -8,15 +8,15 @@ import ( "strings" "time" - "github.com/containers/podman/v2/libpod/image" - - "github.com/containers/image/v5/types" - "github.com/containers/common/pkg/config" "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/types" + "github.com/containers/podman/v2/libpod/image" images "github.com/containers/podman/v2/pkg/bindings/images" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/containers/podman/v2/pkg/domain/utils" + "github.com/containers/podman/v2/pkg/errorhandling" utils2 "github.com/containers/podman/v2/utils" "github.com/pkg/errors" ) @@ -90,26 +90,18 @@ func (ir *ImageEngine) History(ctx context.Context, nameOrID string, opts entiti return &history, nil } -func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) { +func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) ([]*reports.PruneReport, error) { filters := make(map[string][]string, len(opts.Filter)) for _, filter := range opts.Filter { f := strings.Split(filter, "=") filters[f[0]] = f[1:] } options := new(images.PruneOptions).WithAll(opts.All).WithFilters(filters) - results, err := images.Prune(ir.ClientCtx, options) + reports, err := images.Prune(ir.ClientCtx, options) if err != nil { return nil, err } - - report := entities.ImagePruneReport{ - Report: entities.Report{ - Id: results, - Err: nil, - }, - Size: 0, - } - return &report, nil + return reports, nil } func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, opts entities.ImagePullOptions) (*entities.ImagePullReport, error) { @@ -194,7 +186,7 @@ func (ir *ImageEngine) Inspect(ctx context.Context, namesOrIDs []string, opts en for _, i := range namesOrIDs { r, err := images.GetImage(ir.ClientCtx, i, options) if err != nil { - errModel, ok := err.(entities.ErrorModel) + errModel, ok := err.(errorhandling.ErrorModel) if !ok { return nil, nil, err } @@ -222,12 +214,7 @@ func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions) if fInfo.IsDir() { return nil, errors.Errorf("remote client supports archives only but %q is a directory", opts.Input) } - ref := opts.Name - if len(opts.Tag) > 0 { - ref += ":" + opts.Tag - } - options := new(images.LoadOptions).WithReference(ref) - return images.Load(ir.ClientCtx, f, options) + return images.Load(ir.ClientCtx, f) } func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOptions) (*entities.ImageImportReport, error) { diff --git a/pkg/domain/infra/tunnel/network.go b/pkg/domain/infra/tunnel/network.go index 9afb8db02..d4e827580 100644 --- a/pkg/domain/infra/tunnel/network.go +++ b/pkg/domain/infra/tunnel/network.go @@ -5,6 +5,7 @@ import ( "github.com/containers/podman/v2/pkg/bindings/network" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/errorhandling" "github.com/pkg/errors" ) @@ -22,7 +23,7 @@ func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []stri for _, name := range namesOrIds { report, err := network.Inspect(ic.ClientCtx, name, options) if err != nil { - errModel, ok := err.(entities.ErrorModel) + errModel, ok := err.(errorhandling.ErrorModel) if !ok { return nil, nil, err } diff --git a/pkg/domain/infra/tunnel/volumes.go b/pkg/domain/infra/tunnel/volumes.go index e6ad4e0c5..f21336828 100644 --- a/pkg/domain/infra/tunnel/volumes.go +++ b/pkg/domain/infra/tunnel/volumes.go @@ -5,6 +5,8 @@ import ( "github.com/containers/podman/v2/pkg/bindings/volumes" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" + "github.com/containers/podman/v2/pkg/errorhandling" "github.com/pkg/errors" ) @@ -54,7 +56,7 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin for _, id := range namesOrIds { data, err := volumes.Inspect(ic.ClientCtx, id, nil) if err != nil { - errModel, ok := err.(entities.ErrorModel) + errModel, ok := err.(errorhandling.ErrorModel) if !ok { return nil, nil, err } @@ -69,7 +71,7 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin return reports, errs, nil } -func (ic *ContainerEngine) VolumePrune(ctx context.Context, opts entities.VolumePruneOptions) ([]*entities.VolumePruneReport, error) { +func (ic *ContainerEngine) VolumePrune(ctx context.Context, opts entities.VolumePruneOptions) ([]*reports.PruneReport, error) { options := new(volumes.PruneOptions).WithFilters(opts.Filters) return volumes.Prune(ic.ClientCtx, options) } diff --git a/pkg/errorhandling/errorhandling.go b/pkg/errorhandling/errorhandling.go index 21df261fb..b1923be98 100644 --- a/pkg/errorhandling/errorhandling.go +++ b/pkg/errorhandling/errorhandling.go @@ -70,3 +70,27 @@ func CloseQuiet(f *os.File) { func Contains(err error, sub error) bool { return strings.Contains(err.Error(), sub.Error()) } + +// ErrorModel is used in remote connections with podman +type ErrorModel struct { + // API root cause formatted for automated parsing + // example: API root cause + Because string `json:"cause"` + // human error message, formatted for a human to read + // example: human error message + Message string `json:"message"` + // http response code + ResponseCode int `json:"response"` +} + +func (e ErrorModel) Error() string { + return e.Message +} + +func (e ErrorModel) Cause() error { + return errors.New(e.Because) +} + +func (e ErrorModel) Code() int { + return e.ResponseCode +} diff --git a/pkg/inspect/inspect.go b/pkg/inspect/inspect.go index 27bff46a0..67c6a5c03 100644 --- a/pkg/inspect/inspect.go +++ b/pkg/inspect/inspect.go @@ -4,7 +4,7 @@ import ( "time" "github.com/containers/image/v5/manifest" - "github.com/containers/podman/v2/libpod/driver" + "github.com/containers/podman/v2/libpod/define" "github.com/opencontainers/go-digest" v1 "github.com/opencontainers/image-spec/specs-go/v1" ) @@ -25,7 +25,7 @@ type ImageData struct { Os string `json:"Os"` Size int64 `json:"Size"` VirtualSize int64 `json:"VirtualSize"` - GraphDriver *driver.Data `json:"GraphDriver"` + GraphDriver *define.DriverData `json:"GraphDriver"` RootFS *RootFS `json:"RootFS"` Labels map[string]string `json:"Labels"` Annotations map[string]string `json:"Annotations"` diff --git a/pkg/netns/netns_linux.go b/pkg/netns/netns_linux.go index ed5241632..6817a3abd 100644 --- a/pkg/netns/netns_linux.go +++ b/pkg/netns/netns_linux.go @@ -45,7 +45,7 @@ func getNSRunDir() (string, error) { } return filepath.Join(rootlessDir, "netns"), nil } - return "/var/run/netns", nil + return "/run/netns", nil } // NewNS creates a new persistent (bind-mounted) network namespace and returns @@ -80,7 +80,7 @@ func NewNS() (ns.NetNS, error) { return nil, fmt.Errorf("mount --make-rshared %s failed: %q", nsRunDir, err) } - // Recursively remount /var/run/netns on itself. The recursive flag is + // Recursively remount /run/netns on itself. The recursive flag is // so that any existing netns bindmounts are carried over. err = unix.Mount(nsRunDir, nsRunDir, "none", unix.MS_BIND|unix.MS_REC, "") if err != nil { diff --git a/pkg/ps/ps.go b/pkg/ps/ps.go index 9e0dcb728..dc577890a 100644 --- a/pkg/ps/ps.go +++ b/pkg/ps/ps.go @@ -178,6 +178,11 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities return entities.ListContainer{}, err } + networks, _, err := ctr.Networks() + if err != nil { + return entities.ListContainer{}, err + } + ps := entities.ListContainer{ AutoRemove: ctr.AutoRemove(), Command: conConfig.Command, @@ -192,6 +197,7 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities Labels: conConfig.Labels, Mounts: ctr.UserVolumes(), Names: []string{conConfig.Name}, + Networks: networks, Pid: pid, Pod: conConfig.Pod, Ports: portMappings, diff --git a/pkg/rootless/rootless.go b/pkg/rootless/rootless.go index 799c793d8..df35c0d6b 100644 --- a/pkg/rootless/rootless.go +++ b/pkg/rootless/rootless.go @@ -2,10 +2,12 @@ package rootless import ( "os" + "sort" "sync" "github.com/containers/storage" "github.com/opencontainers/runc/libcontainer/user" + spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" ) @@ -50,24 +52,151 @@ func TryJoinPauseProcess(pausePidPath string) (bool, int, error) { } var ( - availableGids int64 - availableGidsErr error - availableGidsOnce sync.Once + uidMap []user.IDMap + uidMapError error + uidMapOnce sync.Once + + gidMap []user.IDMap + gidMapError error + gidMapOnce sync.Once ) -// GetAvailableGids returns how many GIDs are available in the +// GetAvailableUidMap returns the UID mappings in the // current user namespace. -func GetAvailableGids() (int64, error) { - availableGidsOnce.Do(func() { - idMap, err := user.ParseIDMapFile("/proc/self/gid_map") +func GetAvailableUidMap() ([]user.IDMap, error) { + uidMapOnce.Do(func() { + var err error + uidMap, err = user.ParseIDMapFile("/proc/self/uid_map") if err != nil { - availableGidsErr = err + uidMapError = err return } - availableGids = int64(0) - for _, r := range idMap { - availableGids += r.Count + }) + return uidMap, uidMapError +} + +// GetAvailableGidMap returns the GID mappings in the +// current user namespace. +func GetAvailableGidMap() ([]user.IDMap, error) { + gidMapOnce.Do(func() { + var err error + gidMap, err = user.ParseIDMapFile("/proc/self/gid_map") + if err != nil { + gidMapError = err + return } }) - return availableGids, availableGidsErr + return gidMap, gidMapError +} + +// GetAvailableIDMaps returns the UID and GID mappings in the +// current user namespace. +func GetAvailableIDMaps() ([]user.IDMap, []user.IDMap, error) { + u, err := GetAvailableUidMap() + if err != nil { + return nil, nil, err + } + g, err := GetAvailableGidMap() + if err != nil { + return nil, nil, err + } + return u, g, nil +} + +func countAvailableIDs(mappings []user.IDMap) int64 { + availableUids := int64(0) + for _, r := range mappings { + availableUids += r.Count + } + return availableUids +} + +// GetAvailableUids returns how many UIDs are available in the +// current user namespace. +func GetAvailableUids() (int64, error) { + uids, err := GetAvailableUidMap() + if err != nil { + return -1, err + } + + return countAvailableIDs(uids), nil +} + +// GetAvailableGids returns how many GIDs are available in the +// current user namespace. +func GetAvailableGids() (int64, error) { + gids, err := GetAvailableGidMap() + if err != nil { + return -1, err + } + + return countAvailableIDs(gids), nil +} + +// findIDInMappings find the the mapping that contains the specified ID. +// It assumes availableMappings is sorted by ID. +func findIDInMappings(id int64, availableMappings []user.IDMap) *user.IDMap { + i := sort.Search(len(availableMappings), func(i int) bool { + return availableMappings[i].ID >= id + }) + if i < 0 || i >= len(availableMappings) { + return nil + } + r := &availableMappings[i] + if id >= r.ID && id < r.ID+r.Count { + return r + } + return nil +} + +// MaybeSplitMappings checks whether the specified OCI mappings are possible +// in the current user namespace or the specified ranges must be split. +func MaybeSplitMappings(mappings []spec.LinuxIDMapping, availableMappings []user.IDMap) []spec.LinuxIDMapping { + var ret []spec.LinuxIDMapping + var overflow spec.LinuxIDMapping + overflow.Size = 0 + consumed := 0 + sort.Slice(availableMappings, func(i, j int) bool { + return availableMappings[i].ID < availableMappings[j].ID + }) + for { + cur := overflow + // if there is no overflow left from the previous request, get the next one + if cur.Size == 0 { + if consumed == len(mappings) { + // all done + return ret + } + cur = mappings[consumed] + consumed++ + } + + // Find the range where the first specified ID is present + r := findIDInMappings(int64(cur.HostID), availableMappings) + if r == nil { + // The requested range is not available. Just return the original request + // and let other layers deal with it. + return mappings + } + + offsetInRange := cur.HostID - uint32(r.ID) + + usableIDs := uint32(r.Count) - offsetInRange + + // the current range can satisfy the whole request + if usableIDs >= cur.Size { + // reset the overflow + overflow.Size = 0 + } else { + // the current range can satisfy the request partially + // so move the rest to overflow + overflow.Size = cur.Size - usableIDs + overflow.ContainerID = cur.ContainerID + usableIDs + overflow.HostID = cur.HostID + usableIDs + + // and cap to the usableIDs count + cur.Size = usableIDs + } + ret = append(ret, cur) + } } diff --git a/pkg/rootless/rootless_test.go b/pkg/rootless/rootless_test.go new file mode 100644 index 000000000..ef574099c --- /dev/null +++ b/pkg/rootless/rootless_test.go @@ -0,0 +1,101 @@ +package rootless + +import ( + "reflect" + "testing" + + "github.com/opencontainers/runc/libcontainer/user" + spec "github.com/opencontainers/runtime-spec/specs-go" +) + +func TestMaybeSplitMappings(t *testing.T) { + mappings := []spec.LinuxIDMapping{ + { + ContainerID: 0, + HostID: 0, + Size: 2, + }, + } + desiredMappings := []spec.LinuxIDMapping{ + { + ContainerID: 0, + HostID: 0, + Size: 1, + }, + { + ContainerID: 1, + HostID: 1, + Size: 1, + }, + } + availableMappings := []user.IDMap{ + { + ID: 1, + ParentID: 1000000, + Count: 65536, + }, + { + ID: 0, + ParentID: 1000, + Count: 1, + }, + } + newMappings := MaybeSplitMappings(mappings, availableMappings) + if !reflect.DeepEqual(newMappings, desiredMappings) { + t.Fatal("wrong mappings generated") + } + + mappings = []spec.LinuxIDMapping{ + { + ContainerID: 0, + HostID: 0, + Size: 2, + }, + } + desiredMappings = []spec.LinuxIDMapping{ + { + ContainerID: 0, + HostID: 0, + Size: 2, + }, + } + availableMappings = []user.IDMap{ + { + ID: 0, + ParentID: 1000000, + Count: 65536, + }, + } + newMappings = MaybeSplitMappings(mappings, availableMappings) + + if !reflect.DeepEqual(newMappings, desiredMappings) { + t.Fatal("wrong mappings generated") + } + + mappings = []spec.LinuxIDMapping{ + { + ContainerID: 0, + HostID: 0, + Size: 1, + }, + } + desiredMappings = []spec.LinuxIDMapping{ + { + ContainerID: 0, + HostID: 0, + Size: 1, + }, + } + availableMappings = []user.IDMap{ + { + ID: 10000, + ParentID: 10000, + Count: 65536, + }, + } + + newMappings = MaybeSplitMappings(mappings, availableMappings) + if !reflect.DeepEqual(newMappings, desiredMappings) { + t.Fatal("wrong mappings generated") + } +} diff --git a/pkg/signal/signal_linux_mipsx.go b/pkg/signal/signal_linux_mipsx.go index 67638e30a..45c9d5af1 100644 --- a/pkg/signal/signal_linux_mipsx.go +++ b/pkg/signal/signal_linux_mipsx.go @@ -19,6 +19,8 @@ import ( const ( sigrtmin = 34 sigrtmax = 127 + + SIGWINCH = syscall.SIGWINCH ) // signalMap is a map of Linux signals. diff --git a/pkg/specgen/generate/config_linux.go b/pkg/specgen/generate/config_linux.go index e0b039fb7..1290a8eb6 100644 --- a/pkg/specgen/generate/config_linux.go +++ b/pkg/specgen/generate/config_linux.go @@ -21,9 +21,6 @@ var ( errNotADevice = errors.New("not a device node") ) -func u32Ptr(i int64) *uint32 { u := uint32(i); return &u } -func fmPtr(i int64) *os.FileMode { fm := os.FileMode(i); return &fm } - func addPrivilegedDevices(g *generate.Generator) error { hostDevices, err := getDevices("/dev") if err != nil { diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go index 42fea0277..2feb1d3b2 100644 --- a/pkg/specgen/generate/container.go +++ b/pkg/specgen/generate/container.go @@ -100,15 +100,9 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat if err != nil { return nil, err } - // First transform the os env into a map. We need it for the labels later in - // any case. - osEnv, err := envLib.ParseSlice(os.Environ()) - if err != nil { - return nil, errors.Wrap(err, "error parsing host environment variables") - } // Get Default Environment from containers.conf - defaultEnvs, err := envLib.ParseSlice(rtc.GetDefaultEnv()) + defaultEnvs, err := envLib.ParseSlice(rtc.GetDefaultEnvEx(s.EnvHost, s.HTTPProxy)) if err != nil { return nil, errors.Wrap(err, "error parsing fields in containers.conf") } @@ -133,6 +127,12 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat defaultEnvs = envLib.Join(defaultEnvs, envs) } + // First transform the os env into a map. We need it for the labels later in + // any case. + osEnv, err := envLib.ParseSlice(os.Environ()) + if err != nil { + return nil, errors.Wrap(err, "error parsing host environment variables") + } // Caller Specified defaults if s.EnvHost { defaultEnvs = envLib.Join(defaultEnvs, osEnv) @@ -282,8 +282,8 @@ func finishThrottleDevices(s *specgen.SpecGenerator) error { if err := unix.Stat(k, &statT); err != nil { return err } - v.Major = (int64(unix.Major(statT.Rdev))) - v.Minor = (int64(unix.Minor(statT.Rdev))) + v.Major = (int64(unix.Major(uint64(statT.Rdev)))) + v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) s.ResourceLimits.BlockIO.ThrottleReadBpsDevice = append(s.ResourceLimits.BlockIO.ThrottleReadBpsDevice, v) } } @@ -293,8 +293,8 @@ func finishThrottleDevices(s *specgen.SpecGenerator) error { if err := unix.Stat(k, &statT); err != nil { return err } - v.Major = (int64(unix.Major(statT.Rdev))) - v.Minor = (int64(unix.Minor(statT.Rdev))) + v.Major = (int64(unix.Major(uint64(statT.Rdev)))) + v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) s.ResourceLimits.BlockIO.ThrottleWriteBpsDevice = append(s.ResourceLimits.BlockIO.ThrottleWriteBpsDevice, v) } } @@ -304,8 +304,8 @@ func finishThrottleDevices(s *specgen.SpecGenerator) error { if err := unix.Stat(k, &statT); err != nil { return err } - v.Major = (int64(unix.Major(statT.Rdev))) - v.Minor = (int64(unix.Minor(statT.Rdev))) + v.Major = (int64(unix.Major(uint64(statT.Rdev)))) + v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice, v) } } @@ -315,8 +315,8 @@ func finishThrottleDevices(s *specgen.SpecGenerator) error { if err := unix.Stat(k, &statT); err != nil { return err } - v.Major = (int64(unix.Major(statT.Rdev))) - v.Minor = (int64(unix.Minor(statT.Rdev))) + v.Major = (int64(unix.Major(uint64(statT.Rdev)))) + v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) s.ResourceLimits.BlockIO.ThrottleWriteIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleWriteIOPSDevice, v) } } diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index e5b09dcd8..e39a700eb 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - "github.com/containers/buildah/pkg/parse" + "github.com/containers/common/pkg/parse" "github.com/containers/podman/v2/libpod/image" ann "github.com/containers/podman/v2/pkg/annotations" "github.com/containers/podman/v2/pkg/specgen" @@ -129,24 +129,20 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener // TODO: We don't understand why specgen does not take of this, but // integration tests clearly pointed out that it was required. - s.Command = []string{} imageData, err := opts.Image.Inspect(ctx) if err != nil { return nil, err } s.WorkDir = "/" - // We will use "Docker field name" internally here to avoid confusion - // and reference the "Kubernetes field name" when referencing the YAML - // ref: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#notes - entrypoint := []string{} - cmd := []string{} + // Entrypoint/Command handling is based off of + // https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#notes if imageData != nil && imageData.Config != nil { if imageData.Config.WorkingDir != "" { s.WorkDir = imageData.Config.WorkingDir } // Pull entrypoint and cmd from image - entrypoint = imageData.Config.Entrypoint - cmd = imageData.Config.Cmd + s.Entrypoint = imageData.Config.Entrypoint + s.Command = imageData.Config.Cmd s.Labels = imageData.Config.Labels if len(imageData.Config.StopSignal) > 0 { stopSignal, err := util.ParseSignal(imageData.Config.StopSignal) @@ -158,16 +154,15 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener } // If only the yaml.Command is specified, set it as the entrypoint and drop the image Cmd if len(opts.Container.Command) != 0 { - entrypoint = opts.Container.Command - cmd = []string{} + s.Entrypoint = opts.Container.Command + s.Command = []string{} } // Only override the cmd field if yaml.Args is specified // Keep the image entrypoint, or the yaml.command if specified if len(opts.Container.Args) != 0 { - cmd = opts.Container.Args + s.Command = opts.Container.Args } - s.Command = append(entrypoint, cmd...) // FIXME, // we are currently ignoring imageData.Config.ExposedPorts if opts.Container.WorkingDir != "" { diff --git a/pkg/specgen/generate/kube/volume.go b/pkg/specgen/generate/kube/volume.go index bb8edabb7..f5687f60d 100644 --- a/pkg/specgen/generate/kube/volume.go +++ b/pkg/specgen/generate/kube/volume.go @@ -3,7 +3,7 @@ package kube import ( "os" - "github.com/containers/buildah/pkg/parse" + "github.com/containers/common/pkg/parse" "github.com/containers/podman/v2/libpod" "github.com/pkg/errors" "github.com/sirupsen/logrus" diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go index 036c7b7a1..f66ad6101 100644 --- a/pkg/specgen/generate/namespaces.go +++ b/pkg/specgen/generate/namespaces.go @@ -236,6 +236,9 @@ func namespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod. case specgen.Private: fallthrough case specgen.Bridge: + if postConfigureNetNS && rootless.IsRootless() { + return nil, errors.New("CNI networks not supported with user namespaces") + } portMappings, err := createPortMappings(ctx, s, img) if err != nil { return nil, err @@ -364,7 +367,9 @@ func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt // namespaces? g.SetHostname(hostname) } - g.AddProcessEnv("HOSTNAME", hostname) + if _, ok := s.Env["HOSTNAME"]; !ok && s.Hostname != "" { + g.AddProcessEnv("HOSTNAME", hostname) + } // User switch s.UserNS.NSMode { diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index ba68de6fd..e62131244 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -110,7 +110,7 @@ func makeCommand(ctx context.Context, s *specgen.SpecGenerator, img *image.Image // Only use image command if the user did not manually set an // entrypoint. command := s.Command - if (command == nil || len(command) == 0) && img != nil && (s.Entrypoint == nil || len(s.Entrypoint) == 0) { + if len(command) == 0 && img != nil && len(s.Entrypoint) == 0 { newCmd, err := img.Cmd(ctx) if err != nil { return nil, err @@ -138,10 +138,23 @@ func makeCommand(ctx context.Context, s *specgen.SpecGenerator, img *image.Image return finalCommand, nil } +// canMountSys is a best-effort heuristic to detect whether mounting a new sysfs is permitted in the container +func canMountSys(isRootless, isNewUserns bool, s *specgen.SpecGenerator) bool { + if s.NetNS.IsHost() && (isRootless || isNewUserns) { + return false + } + if isNewUserns { + switch s.NetNS.NSMode { + case specgen.Slirp, specgen.Private, specgen.NoNetwork, specgen.Bridge: + return true + default: + return false + } + } + return true +} + func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, newImage *image.Image, mounts []spec.Mount, pod *libpod.Pod, finalCmd []string) (*spec.Spec, error) { - var ( - inUserNS bool - ) cgroupPerm := "ro" g, err := generate.New("linux") if err != nil { @@ -151,23 +164,11 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt g.RemoveMount("/dev/shm") g.HostSpecific = true addCgroup := true - canMountSys := true isRootless := rootless.IsRootless() - if isRootless { - inUserNS = true - } - if !s.UserNS.IsHost() { - if s.UserNS.IsContainer() || s.UserNS.IsPath() { - inUserNS = true - } - if s.UserNS.IsPrivate() { - inUserNS = true - } - } - if inUserNS && s.NetNS.NSMode != specgen.NoNetwork { - canMountSys = false - } + isNewUserns := s.UserNS.IsContainer() || s.UserNS.IsPath() || s.UserNS.IsPrivate() + + canMountSys := canMountSys(isRootless, isNewUserns, s) if s.Privileged && canMountSys { cgroupPerm = "rw" @@ -232,6 +233,8 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt g.AddMount(devPts) } + inUserNS := isRootless || isNewUserns + if inUserNS && s.IpcNS.IsHost() { g.RemoveMount("/dev/mqueue") devMqueue := spec.Mount{ diff --git a/pkg/specgen/generate/security.go b/pkg/specgen/generate/security.go index 0c97dc496..390b19beb 100644 --- a/pkg/specgen/generate/security.go +++ b/pkg/specgen/generate/security.go @@ -133,13 +133,13 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, configSpec := g.Config configSpec.Process.Capabilities.Ambient = []string{} configSpec.Process.Capabilities.Bounding = caplist - configSpec.Process.Capabilities.Inheritable = caplist user := strings.Split(s.User, ":")[0] if (user == "" && s.UserNS.NSMode != specgen.KeepID) || user == "root" || user == "0" { configSpec.Process.Capabilities.Effective = caplist configSpec.Process.Capabilities.Permitted = caplist + configSpec.Process.Capabilities.Inheritable = caplist } else { userCaps, err := capabilities.MergeCapabilities(nil, s.CapAdd, nil) if err != nil { @@ -147,6 +147,7 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, } configSpec.Process.Capabilities.Effective = userCaps configSpec.Process.Capabilities.Permitted = userCaps + configSpec.Process.Capabilities.Inheritable = userCaps // Ambient capabilities were added to Linux 4.3. Set ambient // capabilities only when the kernel supports them. @@ -172,12 +173,16 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, // Clear default Seccomp profile from Generator for unconfined containers // and privileged containers which do not specify a seccomp profile. - if s.SeccompProfilePath == "unconfined" || (s.Privileged && (s.SeccompProfilePath == config.SeccompOverridePath || s.SeccompProfilePath == config.SeccompDefaultPath)) { + if s.SeccompProfilePath == "unconfined" || (s.Privileged && (s.SeccompProfilePath == "" || s.SeccompProfilePath == config.SeccompOverridePath || s.SeccompProfilePath == config.SeccompDefaultPath)) { configSpec.Linux.Seccomp = nil } g.SetRootReadonly(s.ReadOnlyFilesystem) + noUseIPC := s.IpcNS.NSMode == specgen.FromContainer || s.IpcNS.NSMode == specgen.FromPod || s.IpcNS.NSMode == specgen.Host + noUseNet := s.NetNS.NSMode == specgen.FromContainer || s.NetNS.NSMode == specgen.FromPod || s.NetNS.NSMode == specgen.Host + noUseUTS := s.UtsNS.NSMode == specgen.FromContainer || s.UtsNS.NSMode == specgen.FromPod || s.UtsNS.NSMode == specgen.Host + // Add default sysctls defaultSysctls, err := util.ValidateSysctls(rtc.Sysctls()) if err != nil { @@ -186,20 +191,20 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, for sysctlKey, sysctlVal := range defaultSysctls { // Ignore mqueue sysctls if --ipc=host - if s.IpcNS.IsHost() && strings.HasPrefix(sysctlKey, "fs.mqueue.") { + if noUseIPC && strings.HasPrefix(sysctlKey, "fs.mqueue.") { logrus.Infof("Sysctl %s=%s ignored in containers.conf, since IPC Namespace set to host", sysctlKey, sysctlVal) continue } // Ignore net sysctls if --net=host - if s.NetNS.IsHost() && strings.HasPrefix(sysctlKey, "net.") { + if noUseNet && strings.HasPrefix(sysctlKey, "net.") { logrus.Infof("Sysctl %s=%s ignored in containers.conf, since Network Namespace set to host", sysctlKey, sysctlVal) continue } // Ignore uts sysctls if --uts=host - if s.UtsNS.IsHost() && (strings.HasPrefix(sysctlKey, "kernel.domainname") || strings.HasPrefix(sysctlKey, "kernel.hostname")) { + if noUseUTS && (strings.HasPrefix(sysctlKey, "kernel.domainname") || strings.HasPrefix(sysctlKey, "kernel.hostname")) { logrus.Infof("Sysctl %s=%s ignored in containers.conf, since UTS Namespace set to host", sysctlKey, sysctlVal) continue } diff --git a/pkg/specgen/generate/storage.go b/pkg/specgen/generate/storage.go index f523ac5bf..63713726e 100644 --- a/pkg/specgen/generate/storage.go +++ b/pkg/specgen/generate/storage.go @@ -124,14 +124,10 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru // 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) - } + delete(baseVolumes, dest) } for dest := range unifiedVolumes { - if _, ok := baseMounts[dest]; ok { - delete(baseMounts, dest) - } + delete(baseMounts, dest) } // Supersede volumes-from/image volumes with unified volumes from above. diff --git a/pkg/specgen/generate/validate.go b/pkg/specgen/generate/validate.go index f0ab4b994..77cccad3e 100644 --- a/pkg/specgen/generate/validate.go +++ b/pkg/specgen/generate/validate.go @@ -48,7 +48,7 @@ func verifyContainerResourcesCgroupV1(s *specgen.SpecGenerator) ([]string, error warnings = append(warnings, "Your kernel does not support memory swappiness capabilities, or the cgroup is not mounted. Memory swappiness discarded.") memory.Swappiness = nil } else { - if *memory.Swappiness < 0 || *memory.Swappiness > 100 { + if *memory.Swappiness > 100 { return warnings, errors.Errorf("invalid value: %v, valid memory swappiness range is 0-100", *memory.Swappiness) } } diff --git a/pkg/specgen/volumes.go b/pkg/specgen/volumes.go index a4f42d715..83634b4ef 100644 --- a/pkg/specgen/volumes.go +++ b/pkg/specgen/volumes.go @@ -4,7 +4,7 @@ import ( "path/filepath" "strings" - "github.com/containers/buildah/pkg/parse" + "github.com/containers/common/pkg/parse" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" diff --git a/pkg/systemd/generate/common.go b/pkg/systemd/generate/common.go index 52a214883..8901298db 100644 --- a/pkg/systemd/generate/common.go +++ b/pkg/systemd/generate/common.go @@ -11,6 +11,11 @@ import ( // is set to the unit's (unique) name. const EnvVariable = "PODMAN_SYSTEMD_UNIT" +// minTimeoutStopSec is the minimal stop timeout for generated systemd units. +// Once exceeded, processes of the services are killed and the cgroup(s) are +// cleaned up. +const minTimeoutStopSec = 60 + // RestartPolicies includes all valid restart policies to be used in a unit // file. var RestartPolicies = []string{"no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", "always"} @@ -66,3 +71,30 @@ func quoteArguments(command []string) []string { } return command } + +func removeDetachArg(args []string, argCount int) []string { + // "--detach=false" could also be in the container entrypoint + // split them off so we do not remove it there + realArgs := args[len(args)-argCount:] + flagArgs := removeArg("-d=false", args[:len(args)-argCount]) + flagArgs = removeArg("--detach=false", flagArgs) + return append(flagArgs, realArgs...) +} + +func removeReplaceArg(args []string, argCount int) []string { + // "--replace=false" could also be in the container entrypoint + // split them off so we do not remove it there + realArgs := args[len(args)-argCount:] + flagArgs := removeArg("--replace=false", args[:len(args)-argCount]) + return append(flagArgs, realArgs...) +} + +func removeArg(arg string, args []string) []string { + newArgs := []string{} + for _, a := range args { + if a != arg { + newArgs = append(newArgs, a) + } + } + return newArgs +} diff --git a/pkg/systemd/generate/containers.go b/pkg/systemd/generate/containers.go index d84125fc7..b64b2593c 100644 --- a/pkg/systemd/generate/containers.go +++ b/pkg/systemd/generate/containers.go @@ -14,6 +14,7 @@ import ( "github.com/containers/podman/v2/version" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "github.com/spf13/pflag" ) // containerInfo contains data required for generating a container's systemd @@ -44,6 +45,9 @@ type containerInfo struct { // Executable is the path to the podman executable. Will be auto-filled if // left empty. Executable string + // RootFlags contains the root flags which were used to create the container + // Only used with --new + RootFlags string // TimeStamp at the time of creating the unit file. Will be set internally. TimeStamp string // CreateCommand is the full command plus arguments of the process the @@ -55,6 +59,8 @@ type containerInfo struct { ExecStartPre string // ExecStart of the unit. ExecStart string + // TimeoutStopSec of the unit. + TimeoutStopSec uint // ExecStop of the unit. ExecStop string // ExecStopPost of the unit. @@ -74,6 +80,7 @@ After={{- range $index, $value := .BoundToServices -}}{{if $index}} {{end}}{{ $v [Service] Environment={{.EnvVariable}}=%n Restart={{.RestartPolicy}} +TimeoutStopSec={{.TimeoutStopSec}} {{- if .ExecStartPre}} ExecStartPre={{.ExecStartPre}} {{- end}} @@ -81,7 +88,6 @@ ExecStart={{.ExecStart}} ExecStop={{.ExecStop}} ExecStopPost={{.ExecStopPost}} PIDFile={{.PIDFile}} -KillMode=none Type=forking [Install] @@ -183,22 +189,30 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst info.ContainerIDFile = "%t/" + info.ServiceName + ".ctr-id" // The create command must at least have three arguments: // /usr/bin/podman run $IMAGE - index := 2 - if info.CreateCommand[1] == "container" { - index = 3 + index := 0 + for i, arg := range info.CreateCommand { + if arg == "run" || arg == "create" { + index = i + 1 + break + } } - if len(info.CreateCommand) < index+1 { + if index == 0 { return "", errors.Errorf("container's create command is too short or invalid: %v", info.CreateCommand) } // We're hard-coding the first five arguments and append the // CreateCommand with a stripped command and subcommand. - startCommand := []string{ - info.Executable, + startCommand := []string{info.Executable} + if index > 2 { + // include root flags + info.RootFlags = strings.Join(quoteArguments(info.CreateCommand[1:index-1]), " ") + startCommand = append(startCommand, info.CreateCommand[1:index-1]...) + } + startCommand = append(startCommand, "run", "--conmon-pidfile", "{{.PIDFile}}", "--cidfile", "{{.ContainerIDFile}}", "--cgroups=no-conmon", - } + ) // If the container is in a pod, make sure that the // --pod-id-file is set correctly. if info.pod != nil { @@ -208,22 +222,26 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst } // Presence check for certain flags/options. - hasDetachParam := false - hasNameParam := false - hasReplaceParam := false - for _, p := range info.CreateCommand[index:] { - switch p { - case "--detach", "-d": - hasDetachParam = true - case "--name": - hasNameParam = true - case "--replace": - hasReplaceParam = true - } - if strings.HasPrefix(p, "--name=") { - hasNameParam = true - } + fs := pflag.NewFlagSet("args", pflag.ContinueOnError) + fs.ParseErrorsWhitelist.UnknownFlags = true + fs.Usage = func() {} + fs.SetInterspersed(false) + fs.BoolP("detach", "d", false, "") + fs.String("name", "", "") + fs.Bool("replace", false, "") + fs.Parse(info.CreateCommand[index:]) + + hasDetachParam, err := fs.GetBool("detach") + if err != nil { + return "", err } + hasNameParam := fs.Lookup("name").Changed + hasReplaceParam, err := fs.GetBool("replace") + if err != nil { + return "", err + } + + remainingCmd := info.CreateCommand[index:] if !hasDetachParam { // Enforce detaching @@ -238,6 +256,13 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst // will wait the `podman run` command exit until failed // with timeout error. startCommand = append(startCommand, "-d") + + if fs.Changed("detach") { + // this can only happen if --detach=false is set + // in that case we need to remove it otherwise we + // would overwrite the previous detach arg to false + remainingCmd = removeDetachArg(remainingCmd, fs.NArg()) + } } if hasNameParam && !hasReplaceParam { // Enforce --replace for named containers. This will @@ -245,16 +270,25 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst // start after system crashes (see // github.com/containers/podman/issues/5485). startCommand = append(startCommand, "--replace") + + if fs.Changed("replace") { + // this can only happen if --replace=false is set + // in that case we need to remove it otherwise we + // would overwrite the previous replace arg to false + remainingCmd = removeReplaceArg(remainingCmd, fs.NArg()) + } } - startCommand = append(startCommand, info.CreateCommand[index:]...) + startCommand = append(startCommand, remainingCmd...) startCommand = quoteArguments(startCommand) info.ExecStartPre = "/bin/rm -f {{.PIDFile}} {{.ContainerIDFile}}" info.ExecStart = strings.Join(startCommand, " ") - info.ExecStop = "{{.Executable}} stop --ignore --cidfile {{.ContainerIDFile}} {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}}" - info.ExecStopPost = "{{.Executable}} rm --ignore -f --cidfile {{.ContainerIDFile}}" + info.ExecStop = "{{.Executable}} {{if .RootFlags}}{{ .RootFlags}} {{end}}stop --ignore --cidfile {{.ContainerIDFile}} {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}}" + info.ExecStopPost = "{{.Executable}} {{if .RootFlags}}{{ .RootFlags}} {{end}}rm --ignore -f --cidfile {{.ContainerIDFile}}" } + info.TimeoutStopSec = minTimeoutStopSec + info.StopTimeout + if info.PodmanVersion == "" { info.PodmanVersion = version.Version.String() } diff --git a/pkg/systemd/generate/containers_test.go b/pkg/systemd/generate/containers_test.go index d27062ef3..c8e65bfe3 100644 --- a/pkg/systemd/generate/containers_test.go +++ b/pkg/systemd/generate/containers_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/stretchr/testify/assert" ) func TestValidateRestartPolicyContainer(t *testing.T) { @@ -48,11 +49,11 @@ After=network-online.target [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always +TimeoutStopSec=82 ExecStart=/usr/bin/podman start 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 -ExecStop=/usr/bin/podman stop -t 10 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 -ExecStopPost=/usr/bin/podman stop -t 10 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 -PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid -KillMode=none +ExecStop=/usr/bin/podman stop -t 22 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 +ExecStopPost=/usr/bin/podman stop -t 22 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 +PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid Type=forking [Install] @@ -71,11 +72,11 @@ After=network-online.target [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always +TimeoutStopSec=70 ExecStart=/usr/bin/podman start foobar ExecStop=/usr/bin/podman stop -t 10 foobar ExecStopPost=/usr/bin/podman stop -t 10 foobar -PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid -KillMode=none +PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid Type=forking [Install] @@ -96,11 +97,11 @@ After=a.service b.service c.service pod.service [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always +TimeoutStopSec=70 ExecStart=/usr/bin/podman start foobar ExecStop=/usr/bin/podman stop -t 10 foobar ExecStopPost=/usr/bin/podman stop -t 10 foobar -PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid -KillMode=none +PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid Type=forking [Install] @@ -119,12 +120,12 @@ After=network-online.target [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always +TimeoutStopSec=70 ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d --replace --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN "foo=arg \"with \" space" -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id +ExecStart=/usr/bin/podman container run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d --replace --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN "foo=arg \"with \" space" +ExecStop=/usr/bin/podman container stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 +ExecStopPost=/usr/bin/podman container rm --ignore -f --cidfile %t/jadda-jadda.ctr-id PIDFile=%t/jadda-jadda.pid -KillMode=none Type=forking [Install] @@ -143,12 +144,12 @@ After=network-online.target [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always +TimeoutStopSec=70 ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon --replace -d --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42 +ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id PIDFile=%t/jadda-jadda.pid -KillMode=none Type=forking [Install] @@ -167,12 +168,12 @@ After=network-online.target [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always +TimeoutStopSec=70 ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon --pod-id-file /tmp/pod-foobar.pod-id-file --replace -d --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42 +ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id PIDFile=%t/jadda-jadda.pid -KillMode=none Type=forking [Install] @@ -191,12 +192,12 @@ After=network-online.target [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always +TimeoutStopSec=70 ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon --replace --detach --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42 +ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id PIDFile=%t/jadda-jadda.pid -KillMode=none Type=forking [Install] @@ -215,12 +216,113 @@ After=network-online.target [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always +TimeoutStopSec=70 ExecStartPre=/bin/rm -f %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.pid %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.ctr-id ExecStart=/usr/bin/podman run --conmon-pidfile %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.pid --cidfile %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.ctr-id --cgroups=no-conmon -d awesome-image:latest ExecStop=/usr/bin/podman stop --ignore --cidfile %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.ctr-id -t 10 ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.ctr-id PIDFile=%t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.pid -KillMode=none +Type=forking + +[Install] +WantedBy=multi-user.target default.target +` + + genGoodNewDetach := func(detachparam string) string { + goodNewDetach := `# jadda-jadda.service +# autogenerated by Podman CI + +[Unit] +Description=Podman jadda-jadda.service +Documentation=man:podman-generate-systemd(1) +Wants=network.target +After=network-online.target + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=always +TimeoutStopSec=102 +ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id +ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon ` + + detachparam + + ` awesome-image:latest +ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42 +ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id +PIDFile=%t/jadda-jadda.pid +Type=forking + +[Install] +WantedBy=multi-user.target default.target +` + return goodNewDetach + } + + goodNameNewDetachFalseWithCmd := `# jadda-jadda.service +# autogenerated by Podman CI + +[Unit] +Description=Podman jadda-jadda.service +Documentation=man:podman-generate-systemd(1) +Wants=network.target +After=network-online.target + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=always +TimeoutStopSec=102 +ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id +ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d --replace --name test -p 80:80 awesome-image:latest somecmd --detach=false +ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42 +ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id +PIDFile=%t/jadda-jadda.pid +Type=forking + +[Install] +WantedBy=multi-user.target default.target +` + + goodNewRootFlags := `# jadda-jadda.service +# autogenerated by Podman CI + +[Unit] +Description=Podman jadda-jadda.service +Documentation=man:podman-generate-systemd(1) +Wants=network.target +After=network-online.target + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=always +TimeoutStopSec=102 +ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id +ExecStart=/usr/bin/podman --events-backend none --runroot /root run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d awesome-image:latest +ExecStop=/usr/bin/podman --events-backend none --runroot /root stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42 +ExecStopPost=/usr/bin/podman --events-backend none --runroot /root rm --ignore -f --cidfile %t/jadda-jadda.ctr-id +PIDFile=%t/jadda-jadda.pid +Type=forking + +[Install] +WantedBy=multi-user.target default.target +` + + goodContainerCreate := `# jadda-jadda.service +# autogenerated by Podman CI + +[Unit] +Description=Podman jadda-jadda.service +Documentation=man:podman-generate-systemd(1) +Wants=network.target +After=network-online.target + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=always +TimeoutStopSec=70 +ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id +ExecStart=/usr/bin/podman container run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d awesome-image:latest +ExecStop=/usr/bin/podman container stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 +ExecStopPost=/usr/bin/podman container rm --ignore -f --cidfile %t/jadda-jadda.ctr-id +PIDFile=%t/jadda-jadda.pid Type=forking [Install] @@ -241,8 +343,8 @@ WantedBy=multi-user.target default.target ServiceName: "container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", ContainerNameOrID: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 10, + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 22, PodmanVersion: "CI", EnvVariable: EnvVariable, }, @@ -256,7 +358,7 @@ WantedBy=multi-user.target default.target ServiceName: "container-foobar", ContainerNameOrID: "foobar", RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", EnvVariable: EnvVariable, @@ -271,7 +373,7 @@ WantedBy=multi-user.target default.target ServiceName: "container-foobar", ContainerNameOrID: "foobar", RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", BoundToServices: []string{"pod", "a", "b", "c"}, @@ -286,7 +388,7 @@ WantedBy=multi-user.target default.target Executable: "/usr/bin/podman", ServiceName: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", RestartPolicy: "never", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", EnvVariable: EnvVariable, @@ -301,8 +403,8 @@ WantedBy=multi-user.target default.target ServiceName: "jadda-jadda", ContainerNameOrID: "jadda-jadda", RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 42, + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, PodmanVersion: "CI", CreateCommand: []string{"I'll get stripped", "container", "run", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN", "foo=arg \"with \" space"}, EnvVariable: EnvVariable, @@ -317,10 +419,10 @@ WantedBy=multi-user.target default.target ServiceName: "jadda-jadda", ContainerNameOrID: "jadda-jadda", RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 42, + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, PodmanVersion: "CI", - CreateCommand: []string{"I'll get stripped", "container", "run", "-d", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"}, + CreateCommand: []string{"I'll get stripped", "run", "-d", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"}, EnvVariable: EnvVariable, }, goodWithExplicitShortDetachParam, @@ -333,10 +435,10 @@ WantedBy=multi-user.target default.target ServiceName: "jadda-jadda", ContainerNameOrID: "jadda-jadda", RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 42, + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, PodmanVersion: "CI", - CreateCommand: []string{"I'll get stripped", "container", "run", "-d", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"}, + CreateCommand: []string{"I'll get stripped", "run", "-d", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"}, EnvVariable: EnvVariable, pod: &podInfo{ PodIDFile: "/tmp/pod-foobar.pod-id-file", @@ -352,10 +454,10 @@ WantedBy=multi-user.target default.target ServiceName: "jadda-jadda", ContainerNameOrID: "jadda-jadda", RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 42, + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, PodmanVersion: "CI", - CreateCommand: []string{"I'll get stripped", "container", "run", "--detach", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"}, + CreateCommand: []string{"I'll get stripped", "run", "--detach", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"}, EnvVariable: EnvVariable, }, goodNameNewDetach, @@ -368,16 +470,144 @@ WantedBy=multi-user.target default.target ServiceName: "container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", ContainerNameOrID: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", - CreateCommand: []string{"I'll get stripped", "container", "run", "awesome-image:latest"}, + CreateCommand: []string{"I'll get stripped", "run", "awesome-image:latest"}, EnvVariable: EnvVariable, }, goodIDNew, true, false, }, + {"good with explicit detach=true param", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 42, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "run", "--detach=true", "awesome-image:latest"}, + EnvVariable: EnvVariable, + }, + genGoodNewDetach("--detach=true"), + true, + false, + }, + {"good with explicit detach=false param", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 42, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "run", "--detach=false", "awesome-image:latest"}, + EnvVariable: EnvVariable, + }, + genGoodNewDetach("-d"), + true, + false, + }, + {"good with explicit detach=false param", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 42, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "run", "--name", "test", "-p", "80:80", "--detach=false", "awesome-image:latest", "somecmd", "--detach=false"}, + EnvVariable: EnvVariable, + }, + goodNameNewDetachFalseWithCmd, + true, + false, + }, + {"good with multiple detach=false params", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 42, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "run", "--name", "test", "-p", "80:80", "--detach=false", "--detach=false", "awesome-image:latest", "somecmd", "--detach=false"}, + EnvVariable: EnvVariable, + }, + goodNameNewDetachFalseWithCmd, + true, + false, + }, + {"good with multiple shorthand params detach first", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 42, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "run", "-dti", "awesome-image:latest"}, + EnvVariable: EnvVariable, + }, + genGoodNewDetach("-dti"), + true, + false, + }, + {"good with multiple shorthand params detach last", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 42, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "run", "-tid", "awesome-image:latest"}, + EnvVariable: EnvVariable, + }, + genGoodNewDetach("-tid"), + true, + false, + }, + {"good with root flags", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 42, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "--events-backend", "none", "--runroot", "/root", "run", "awesome-image:latest"}, + EnvVariable: EnvVariable, + }, + goodNewRootFlags, + true, + false, + }, + {"good with container create", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "container", "create", "awesome-image:latest"}, + EnvVariable: EnvVariable, + }, + goodContainerCreate, + true, + false, + }, } for _, tt := range tests { test := tt @@ -390,9 +620,7 @@ WantedBy=multi-user.target default.target t.Errorf("CreateContainerSystemdUnit() error = \n%v, wantErr \n%v", err, test.wantErr) return } - if got != test.want { - t.Errorf("CreateContainerSystemdUnit() = \n%v\n---------> want\n%v", got, test.want) - } + assert.Equal(t, test.want, got) }) } } diff --git a/pkg/systemd/generate/pods.go b/pkg/systemd/generate/pods.go index 3c57b03fb..7678a240f 100644 --- a/pkg/systemd/generate/pods.go +++ b/pkg/systemd/generate/pods.go @@ -14,6 +14,7 @@ import ( "github.com/containers/podman/v2/version" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "github.com/spf13/pflag" ) // podInfo contains data required for generating a pod's systemd @@ -44,6 +45,9 @@ type podInfo struct { // Executable is the path to the podman executable. Will be auto-filled if // left empty. Executable string + // RootFlags contains the root flags which were used to create the container + // Only used with --new + RootFlags string // TimeStamp at the time of creating the unit file. Will be set internally. TimeStamp string // CreateCommand is the full command plus arguments of the process the @@ -60,6 +64,8 @@ type podInfo struct { ExecStartPre2 string // ExecStart of the unit. ExecStart string + // TimeoutStopSec of the unit. + TimeoutStopSec uint // ExecStop of the unit. ExecStop string // ExecStopPost of the unit. @@ -72,6 +78,7 @@ Before={{- range $index, $value := .RequiredServices -}}{{if $index}} {{end}}{{ [Service] Environment={{.EnvVariable}}=%n Restart={{.RestartPolicy}} +TimeoutStopSec={{.TimeoutStopSec}} {{- if .ExecStartPre1}} ExecStartPre={{.ExecStartPre1}} {{- end}} @@ -82,7 +89,6 @@ ExecStart={{.ExecStart}} ExecStop={{.ExecStop}} ExecStopPost={{.ExecStopPost}} PIDFile={{.PIDFile}} -KillMode=none Type=forking [Install] @@ -262,7 +268,8 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions) if podCreateIndex == 0 { return "", errors.Errorf("pod does not appear to be created via `podman pod create`: %v", info.CreateCommand) } - podRootArgs = info.CreateCommand[0 : podCreateIndex-2] + podRootArgs = info.CreateCommand[1 : podCreateIndex-1] + info.RootFlags = strings.Join(quoteArguments(podRootArgs), " ") podCreateArgs = filterPodFlags(info.CreateCommand[podCreateIndex+1:]) } // We're hard-coding the first five arguments and append the @@ -275,17 +282,26 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions) "--pod-id-file", "{{.PodIDFile}}"}...) // Presence check for certain flags/options. - hasNameParam := false - hasReplaceParam := false - for _, p := range podCreateArgs { - switch p { - case "--name": - hasNameParam = true - case "--replace": - hasReplaceParam = true - } + fs := pflag.NewFlagSet("args", pflag.ContinueOnError) + fs.ParseErrorsWhitelist.UnknownFlags = true + fs.Usage = func() {} + fs.SetInterspersed(false) + fs.String("name", "", "") + fs.Bool("replace", false, "") + fs.Parse(podCreateArgs) + + hasNameParam := fs.Lookup("name").Changed + hasReplaceParam, err := fs.GetBool("replace") + if err != nil { + return "", err } if hasNameParam && !hasReplaceParam { + if fs.Changed("replace") { + // this can only happen if --replace=false is set + // in that case we need to remove it otherwise we + // would overwrite the previous replace arg to false + podCreateArgs = removeReplaceArg(podCreateArgs, fs.NArg()) + } podCreateArgs = append(podCreateArgs, "--replace") } @@ -294,10 +310,12 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions) info.ExecStartPre1 = "/bin/rm -f {{.PIDFile}} {{.PodIDFile}}" info.ExecStartPre2 = strings.Join(startCommand, " ") - info.ExecStart = "{{.Executable}} pod start --pod-id-file {{.PodIDFile}}" - info.ExecStop = "{{.Executable}} pod stop --ignore --pod-id-file {{.PodIDFile}} {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}}" - info.ExecStopPost = "{{.Executable}} pod rm --ignore -f --pod-id-file {{.PodIDFile}}" + info.ExecStart = "{{.Executable}} {{if .RootFlags}}{{ .RootFlags}} {{end}}pod start --pod-id-file {{.PodIDFile}}" + info.ExecStop = "{{.Executable}} {{if .RootFlags}}{{ .RootFlags}} {{end}}pod stop --ignore --pod-id-file {{.PodIDFile}} {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}}" + info.ExecStopPost = "{{.Executable}} {{if .RootFlags}}{{ .RootFlags}} {{end}}pod rm --ignore -f --pod-id-file {{.PodIDFile}}" } + info.TimeoutStopSec = minTimeoutStopSec + info.StopTimeout + if info.PodmanVersion == "" { info.PodmanVersion = version.Version.String() } diff --git a/pkg/systemd/generate/pods_test.go b/pkg/systemd/generate/pods_test.go index 7f1f63b7e..1c6330160 100644 --- a/pkg/systemd/generate/pods_test.go +++ b/pkg/systemd/generate/pods_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/stretchr/testify/assert" ) func TestValidateRestartPolicyPod(t *testing.T) { @@ -50,11 +51,11 @@ Before=container-1.service container-2.service [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always +TimeoutStopSec=102 ExecStart=/usr/bin/podman start jadda-jadda-infra -ExecStop=/usr/bin/podman stop -t 10 jadda-jadda-infra -ExecStopPost=/usr/bin/podman stop -t 10 jadda-jadda-infra -PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid -KillMode=none +ExecStop=/usr/bin/podman stop -t 42 jadda-jadda-infra +ExecStopPost=/usr/bin/podman stop -t 42 jadda-jadda-infra +PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid Type=forking [Install] @@ -75,13 +76,67 @@ Before=container-1.service container-2.service [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure +TimeoutStopSec=70 ExecStartPre=/bin/rm -f %t/pod-123abc.pid %t/pod-123abc.pod-id ExecStartPre=/usr/bin/podman pod create --infra-conmon-pidfile %t/pod-123abc.pid --pod-id-file %t/pod-123abc.pod-id --name foo "bar=arg with space" --replace ExecStart=/usr/bin/podman pod start --pod-id-file %t/pod-123abc.pod-id ExecStop=/usr/bin/podman pod stop --ignore --pod-id-file %t/pod-123abc.pod-id -t 10 ExecStopPost=/usr/bin/podman pod rm --ignore -f --pod-id-file %t/pod-123abc.pod-id PIDFile=%t/pod-123abc.pid -KillMode=none +Type=forking + +[Install] +WantedBy=multi-user.target default.target +` + + podGoodNamedNewWithRootArgs := `# pod-123abc.service +# autogenerated by Podman CI + +[Unit] +Description=Podman pod-123abc.service +Documentation=man:podman-generate-systemd(1) +Wants=network.target +After=network-online.target +Requires=container-1.service container-2.service +Before=container-1.service container-2.service + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=on-failure +TimeoutStopSec=70 +ExecStartPre=/bin/rm -f %t/pod-123abc.pid %t/pod-123abc.pod-id +ExecStartPre=/usr/bin/podman --events-backend none --runroot /root pod create --infra-conmon-pidfile %t/pod-123abc.pid --pod-id-file %t/pod-123abc.pod-id --name foo "bar=arg with space" --replace +ExecStart=/usr/bin/podman --events-backend none --runroot /root pod start --pod-id-file %t/pod-123abc.pod-id +ExecStop=/usr/bin/podman --events-backend none --runroot /root pod stop --ignore --pod-id-file %t/pod-123abc.pod-id -t 10 +ExecStopPost=/usr/bin/podman --events-backend none --runroot /root pod rm --ignore -f --pod-id-file %t/pod-123abc.pod-id +PIDFile=%t/pod-123abc.pid +Type=forking + +[Install] +WantedBy=multi-user.target default.target +` + + podGoodNamedNewWithReplaceFalse := `# pod-123abc.service +# autogenerated by Podman CI + +[Unit] +Description=Podman pod-123abc.service +Documentation=man:podman-generate-systemd(1) +Wants=network.target +After=network-online.target +Requires=container-1.service container-2.service +Before=container-1.service container-2.service + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=on-failure +TimeoutStopSec=70 +ExecStartPre=/bin/rm -f %t/pod-123abc.pid %t/pod-123abc.pod-id +ExecStartPre=/usr/bin/podman pod create --infra-conmon-pidfile %t/pod-123abc.pid --pod-id-file %t/pod-123abc.pod-id --name foo --replace +ExecStart=/usr/bin/podman pod start --pod-id-file %t/pod-123abc.pod-id +ExecStop=/usr/bin/podman pod stop --ignore --pod-id-file %t/pod-123abc.pod-id -t 10 +ExecStopPost=/usr/bin/podman pod rm --ignore -f --pod-id-file %t/pod-123abc.pod-id +PIDFile=%t/pod-123abc.pid Type=forking [Install] @@ -101,10 +156,27 @@ WantedBy=multi-user.target default.target ServiceName: "pod-123abc", InfraNameOrID: "jadda-jadda-infra", RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 10, + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 42, PodmanVersion: "CI", RequiredServices: []string{"container-1", "container-2"}, + CreateCommand: []string{"podman", "pod", "create", "--name", "foo", "bar=arg with space"}, + }, + podGood, + false, + false, + }, + {"pod with root args", + podInfo{ + Executable: "/usr/bin/podman", + ServiceName: "pod-123abc", + InfraNameOrID: "jadda-jadda-infra", + RestartPolicy: "always", + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 42, + PodmanVersion: "CI", + RequiredServices: []string{"container-1", "container-2"}, + CreateCommand: []string{"podman", "--events-backend", "none", "--runroot", "/root", "pod", "create", "--name", "foo", "bar=arg with space"}, }, podGood, false, @@ -116,7 +188,7 @@ WantedBy=multi-user.target default.target ServiceName: "pod-123abc", InfraNameOrID: "jadda-jadda-infra", RestartPolicy: "on-failure", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", RequiredServices: []string{"container-1", "container-2"}, @@ -126,6 +198,38 @@ WantedBy=multi-user.target default.target true, false, }, + {"pod --new with root args", + podInfo{ + Executable: "/usr/bin/podman", + ServiceName: "pod-123abc", + InfraNameOrID: "jadda-jadda-infra", + RestartPolicy: "on-failure", + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + RequiredServices: []string{"container-1", "container-2"}, + CreateCommand: []string{"podman", "--events-backend", "none", "--runroot", "/root", "pod", "create", "--name", "foo", "bar=arg with space"}, + }, + podGoodNamedNewWithRootArgs, + true, + false, + }, + {"pod --new with --replace=false", + podInfo{ + Executable: "/usr/bin/podman", + ServiceName: "pod-123abc", + InfraNameOrID: "jadda-jadda-infra", + RestartPolicy: "on-failure", + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + RequiredServices: []string{"container-1", "container-2"}, + CreateCommand: []string{"podman", "pod", "create", "--name", "foo", "--replace=false"}, + }, + podGoodNamedNewWithReplaceFalse, + true, + false, + }, } for _, tt := range tests { @@ -139,9 +243,7 @@ WantedBy=multi-user.target default.target t.Errorf("CreatePodSystemdUnit() error = \n%v, wantErr \n%v", err, test.wantErr) return } - if got != test.want { - t.Errorf("CreatePodSystemdUnit() = \n%v\n---------> want\n%v", got, test.want) - } + assert.Equal(t, test.want, got) }) } } diff --git a/test/apiv2/30-volumes.at b/test/apiv2/30-volumes.at index 33f4ea37f..b38810039 100644 --- a/test/apiv2/30-volumes.at +++ b/test/apiv2/30-volumes.at @@ -12,7 +12,7 @@ t POST libpod/volumes/create name=foo1 201 \ .Mountpoint=$volumepath/foo1/_data \ .CreatedAt~[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.* \ .Labels={} \ - .Options=null + .Options={} t POST libpod/volumes/create '' 201 t POST libpod/volumes/create \ '"Name":"foo2","Label":{"testlabel":"testonly"},"Options":{"type":"tmpfs","o":"nodev,noexec"}}' 201 \ diff --git a/test/apiv2/45-system.at b/test/apiv2/45-system.at index 7d14fd4b3..985d86e56 100644 --- a/test/apiv2/45-system.at +++ b/test/apiv2/45-system.at @@ -19,7 +19,7 @@ t POST libpod/volumes/create name=foo1 201 \ .Mountpoint=$volumepath/foo1/_data \ .CreatedAt~[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.* \ .Labels={} \ - .Options=null + .Options={} t GET system/df 200 '.Volumes[0].Name=foo1' @@ -58,10 +58,10 @@ t GET libpod/system/df 200 '.Volumes | length=3' # -G --data-urlencode 'volumes=true&filters={"label":["testlabel1=testonly"]}' # only foo3 should be pruned because of filter -t POST 'libpod/system/prune?volumes=true&filters=%7B%22label%22:%5B%22testlabel1=testonly%22%5D%7D' params='' 200 .VolumePruneReport[0].Id=foo3 +t POST 'libpod/system/prune?volumes=true&filters=%7B%22label%22:%5B%22testlabel1=testonly%22%5D%7D' params='' 200 .VolumePruneReports[0].Id=foo3 # only foo2 should be pruned because of filter -t POST 'libpod/system/prune?volumes=true&filters=%7B%22label%22:%5B%22testlabel1%22%5D%7D' params='' 200 .VolumePruneReport[0].Id=foo2 +t POST 'libpod/system/prune?volumes=true&filters=%7B%22label%22:%5B%22testlabel1%22%5D%7D' params='' 200 .VolumePruneReports[0].Id=foo2 # foo1, the last remaining volume should be pruned without any filters applied -t POST 'libpod/system/prune?volumes=true' params='' 200 .VolumePruneReport[0].Id=foo1 +t POST 'libpod/system/prune?volumes=true' params='' 200 .VolumePruneReports[0].Id=foo1 # TODO add other system prune tests for pods / images diff --git a/test/apiv2/rest_api/test_rest_v2_0_0.py b/test/apiv2/rest_api/test_rest_v2_0_0.py index 2f9e62149..cc66dd5af 100644 --- a/test/apiv2/rest_api/test_rest_v2_0_0.py +++ b/test/apiv2/rest_api/test_rest_v2_0_0.py @@ -1,13 +1,15 @@ import json +import os import random +import shutil import string import subprocess +import sys +import time import unittest from multiprocessing import Process import requests -import sys -import time from dateutil.parser import parse from test.apiv2.rest_api import Podman @@ -449,7 +451,7 @@ class TestApi(unittest.TestCase): self.assertEqual(inspect.status_code, 404, inspect.content) prune = requests.post(PODMAN_URL + "/v1.40/networks/prune") - self.assertEqual(prune.status_code, 405, prune.content) + self.assertEqual(prune.status_code, 404, prune.content) def test_volumes_compat(self): name = "Volume_" + "".join(random.choice(string.ascii_letters) for i in range(10)) @@ -499,8 +501,18 @@ class TestApi(unittest.TestCase): rm = requests.delete(PODMAN_URL + f"/v1.40/volumes/{name}") self.assertEqual(rm.status_code, 204, rm.content) + # recreate volume with data and then prune it + r = requests.post(PODMAN_URL + "/v1.40/volumes/create", json={"Name": name}) + self.assertEqual(create.status_code, 201, create.content) + create = json.loads(r.content) + with open(os.path.join(create["Mountpoint"], "test_prune"), "w") as file: + file.writelines(["This is a test\n", "This is a good test\n"]) + prune = requests.post(PODMAN_URL + "/v1.40/volumes/prune") self.assertEqual(prune.status_code, 200, prune.content) + payload = json.loads(prune.content) + self.assertIn(name, payload["VolumesDeleted"]) + self.assertGreater(payload["SpaceReclaimed"], 0) def test_auth_compat(self): r = requests.post( @@ -530,6 +542,50 @@ class TestApi(unittest.TestCase): self.assertIn("Volumes", obj) self.assertIn("BuildCache", obj) + def test_prune_compat(self): + name = "Ctnr_" + "".join(random.choice(string.ascii_letters) for i in range(10)) + + r = requests.post( + PODMAN_URL + f"/v1.40/containers/create?name={name}", + json={ + "Cmd": ["cp", "/etc/motd", "/motd.size_test"], + "Image": "alpine:latest", + "NetworkDisabled": True, + }, + ) + self.assertEqual(r.status_code, 201, r.text) + create = json.loads(r.text) + + r = requests.post(PODMAN_URL + f"/v1.40/containers/{create['Id']}/start") + self.assertEqual(r.status_code, 204, r.text) + + r = requests.post(PODMAN_URL + f"/v1.40/containers/{create['Id']}/wait") + self.assertEqual(r.status_code, 200, r.text) + wait = json.loads(r.text) + self.assertEqual(wait["StatusCode"], 0, wait["Error"]["Message"]) + + prune = requests.post(PODMAN_URL + "/v1.40/containers/prune") + self.assertEqual(prune.status_code, 200, prune.status_code) + prune_payload = json.loads(prune.text) + self.assertGreater(prune_payload["SpaceReclaimed"], 0) + self.assertIn(create["Id"], prune_payload["ContainersDeleted"]) + + # Delete any orphaned containers + r = requests.get(PODMAN_URL + "/v1.40/containers/json?all=true") + self.assertEqual(r.status_code, 200, r.text) + for ctnr in json.loads(r.text): + requests.delete(PODMAN_URL + f"/v1.40/containers/{ctnr['Id']}?force=true") + + prune = requests.post(PODMAN_URL + "/v1.40/images/prune") + self.assertEqual(prune.status_code, 200, prune.text) + prune_payload = json.loads(prune.text) + self.assertGreater(prune_payload["SpaceReclaimed"], 0) + + # FIXME need method to determine which image is going to be "pruned" to fix test + # TODO should handler be recursive when deleting images? + # self.assertIn(img["Id"], prune_payload["ImagesDeleted"][1]["Deleted"]) + self.assertIsNotNone(prune_payload["ImagesDeleted"][1]["Deleted"]) + if __name__ == "__main__": unittest.main() diff --git a/test/e2e/build/basicalpine/Containerfile.volume b/test/e2e/build/basicalpine/Containerfile.volume new file mode 100644 index 000000000..6a4fc8242 --- /dev/null +++ b/test/e2e/build/basicalpine/Containerfile.volume @@ -0,0 +1,2 @@ +FROM alpine +VOLUME "/volume0" diff --git a/test/e2e/build_test.go b/test/e2e/build_test.go index 21f98d3d0..71b4c0089 100644 --- a/test/e2e/build_test.go +++ b/test/e2e/build_test.go @@ -72,9 +72,9 @@ var _ = Describe("Podman build", func() { st, err := os.Stat(logfile) Expect(err).To(BeNil()) - Expect(st.Size()).To(Not(Equal(0))) + Expect(st.Size()).To(Not(Equal(int64(0)))) - session = podmanTest.Podman([]string{"rmi", "alpine"}) + session = podmanTest.Podman([]string{"rmi", "test"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) }) diff --git a/test/e2e/checkpoint_test.go b/test/e2e/checkpoint_test.go index 75310b961..3270ce685 100644 --- a/test/e2e/checkpoint_test.go +++ b/test/e2e/checkpoint_test.go @@ -4,6 +4,7 @@ import ( "net" "os" "os/exec" + "strings" "github.com/containers/podman/v2/pkg/criu" . "github.com/containers/podman/v2/test/utils" @@ -622,7 +623,7 @@ var _ = Describe("Podman checkpoint", func() { result := podmanTest.Podman([]string{"container", "checkpoint", "-l"}) result.WaitWithDefaultTimeout() Expect(result).To(ExitWithError()) - Expect(result.ErrorToString()).To(ContainSubstring("Cannot checkpoint containers that have been started with '--rm'")) + Expect(result.ErrorToString()).To(ContainSubstring("cannot checkpoint containers that have been started with '--rm'")) // Checkpointing with --export should still work fileName := "/tmp/checkpoint-" + cid + ".tar.gz" @@ -652,4 +653,173 @@ var _ = Describe("Podman checkpoint", func() { // Remove exported checkpoint os.Remove(fileName) }) + + It("podman checkpoint a container with volumes", func() { + session := podmanTest.Podman([]string{ + "build", "-f", "build/basicalpine/Containerfile.volume", "-t", "test-cr-volume", + }) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Start the container + localRunString := getRunString([]string{ + "--rm", + "-v", "/volume1", + "-v", "my-test-vol:/volume2", + "test-cr-volume", + "top", + }) + session = podmanTest.Podman(localRunString) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + + cid := session.OutputToString() + + // Add file in volume0 + result := podmanTest.Podman([]string{ + "exec", "-l", "/bin/sh", "-c", "echo " + cid + " > /volume0/test.output", + }) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + + // Add file in volume1 + result = podmanTest.Podman([]string{ + "exec", "-l", "/bin/sh", "-c", "echo " + cid + " > /volume1/test.output", + }) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + + // Add file in volume2 + result = podmanTest.Podman([]string{ + "exec", "-l", "/bin/sh", "-c", "echo " + cid + " > /volume2/test.output", + }) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + + checkpointFileName := "/tmp/checkpoint-" + cid + ".tar.gz" + + // Checkpoint the container + result = podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", checkpointFileName}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + Expect(podmanTest.NumberOfContainers()).To(Equal(0)) + + // Restore container should fail because named volume still exists + result = podmanTest.Podman([]string{"container", "restore", "-i", checkpointFileName}) + result.WaitWithDefaultTimeout() + Expect(result).To(ExitWithError()) + Expect(result.ErrorToString()).To(ContainSubstring( + "volume with name my-test-vol already exists. Use --ignore-volumes to not restore content of volumes", + )) + + // Remove named volume + session = podmanTest.Podman([]string{"volume", "rm", "my-test-vol"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Restoring container + result = podmanTest.Podman([]string{"container", "restore", "-i", checkpointFileName}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + Expect(podmanTest.NumberOfContainers()).To(Equal(1)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) + + // Validate volume0 content + result = podmanTest.Podman([]string{"exec", "-l", "cat", "/volume0/test.output"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(result.OutputToString()).To(ContainSubstring(cid)) + + // Validate volume1 content + result = podmanTest.Podman([]string{"exec", "-l", "cat", "/volume1/test.output"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(result.OutputToString()).To(ContainSubstring(cid)) + + // Validate volume2 content + result = podmanTest.Podman([]string{"exec", "-l", "cat", "/volume2/test.output"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(result.OutputToString()).To(ContainSubstring(cid)) + + // Remove exported checkpoint + os.Remove(checkpointFileName) + }) + + It("podman checkpoint container with --pre-checkpoint", func() { + if !strings.Contains(podmanTest.OCIRuntime, "runc") { + Skip("Test only works on runc 1.0-rc3 or higher.") + } + localRunString := getRunString([]string{ALPINE, "top"}) + session := podmanTest.Podman(localRunString) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + cid := session.OutputToString() + + result := podmanTest.Podman([]string{"container", "checkpoint", "-P", cid}) + result.WaitWithDefaultTimeout() + + Expect(result.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) + + result = podmanTest.Podman([]string{"container", "checkpoint", "--with-previous", cid}) + result.WaitWithDefaultTimeout() + + Expect(result.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited")) + + result = podmanTest.Podman([]string{"container", "restore", cid}) + result.WaitWithDefaultTimeout() + + Expect(result.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) + }) + + It("podman checkpoint container with --pre-checkpoint and export (migration)", func() { + if !strings.Contains(podmanTest.OCIRuntime, "runc") { + Skip("Test only works on runc 1.0-rc3 or higher.") + } + localRunString := getRunString([]string{ALPINE, "top"}) + session := podmanTest.Podman(localRunString) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + cid := session.OutputToString() + preCheckpointFileName := "/tmp/pre-checkpoint-" + cid + ".tar.gz" + checkpointFileName := "/tmp/checkpoint-" + cid + ".tar.gz" + + result := podmanTest.Podman([]string{"container", "checkpoint", "-P", "-e", preCheckpointFileName, cid}) + result.WaitWithDefaultTimeout() + + Expect(result.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) + + result = podmanTest.Podman([]string{"container", "checkpoint", "--with-previous", "-e", checkpointFileName, cid}) + result.WaitWithDefaultTimeout() + + Expect(result.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited")) + + result = podmanTest.Podman([]string{"rm", "-f", cid}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + + result = podmanTest.Podman([]string{"container", "restore", "-i", checkpointFileName, "--import-previous", preCheckpointFileName}) + result.WaitWithDefaultTimeout() + + Expect(result.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) + + os.Remove(checkpointFileName) + os.Remove(preCheckpointFileName) + }) }) diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go index a076ada6b..2668b1e7b 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/common_test.go @@ -122,7 +122,7 @@ var _ = SynchronizedBeforeSuite(func() []byte { } // Pull cirros but don't put it into the cache - pullImages := []string{cirros, fedoraToolbox} + pullImages := []string{cirros, fedoraToolbox, volumeTest} pullImages = append(pullImages, CACHE_IMAGES...) for _, image := range pullImages { podman.createArtifact(image) @@ -378,10 +378,17 @@ func GetRandomIPAddress() string { // RunTopContainer runs a simple container in the background that // runs top. If the name passed != "", it will have a name func (p *PodmanTestIntegration) RunTopContainer(name string) *PodmanSessionIntegration { + return p.RunTopContainerWithArgs(name, nil) +} + +// RunTopContainerWithArgs runs a simple container in the background that +// runs top. If the name passed != "", it will have a name, command args can also be passed in +func (p *PodmanTestIntegration) RunTopContainerWithArgs(name string, args []string) *PodmanSessionIntegration { var podmanArgs = []string{"run"} if name != "" { podmanArgs = append(podmanArgs, "--name", name) } + podmanArgs = append(podmanArgs, args...) podmanArgs = append(podmanArgs, "-d", ALPINE, "top") return p.Podman(podmanArgs) } @@ -476,13 +483,7 @@ func (p *PodmanTestIntegration) CleanupVolume() { session := p.Podman([]string{"volume", "rm", "-fa"}) session.Wait(90) - // Stop remove service on volume cleanup - p.StopRemoteService() - - // Nuke tempdir - if err := os.RemoveAll(p.TempDir); err != nil { - fmt.Printf("%q\n", err) - } + p.Cleanup() } // InspectContainerToJSON takes the session output of an inspect @@ -538,12 +539,7 @@ func (p *PodmanTestIntegration) CreatePodWithLabels(name string, labels map[stri } func (p *PodmanTestIntegration) RunTopContainerInPod(name, pod string) *PodmanSessionIntegration { - var podmanArgs = []string{"run", "--pod", pod} - if name != "" { - podmanArgs = append(podmanArgs, "--name", name) - } - podmanArgs = append(podmanArgs, "-d", ALPINE, "top") - return p.Podman(podmanArgs) + return p.RunTopContainerWithArgs(name, []string{"--pod", pod}) } func (p *PodmanTestIntegration) RunHealthCheck(cid string) error { diff --git a/test/e2e/config.go b/test/e2e/config.go index e66cd6846..2552595ad 100644 --- a/test/e2e/config.go +++ b/test/e2e/config.go @@ -15,6 +15,7 @@ var ( healthcheck = "quay.io/libpod/alpine_healthcheck:latest" ImageCacheDir = "/tmp/podman/imagecachedir" fedoraToolbox = "registry.fedoraproject.org/f32/fedora-toolbox:latest" + volumeTest = "quay.io/libpod/volume-plugin-test-img:latest" // This image has seccomp profiles that blocks all syscalls. // The intention behind blocking all syscalls is to prevent diff --git a/test/e2e/config/containers.conf b/test/e2e/config/containers.conf index 35153ba05..5a5e4b7a5 100644 --- a/test/e2e/config/containers.conf +++ b/test/e2e/config/containers.conf @@ -56,3 +56,17 @@ umask = "0002" [engine] network_cmd_options=["allow_host_loopback=true"] + +# We need to ensure each test runs on a separate plugin instance... +# For now, let's just make a bunch of plugin paths and have each test use one. +[engine.volume_plugins] +testvol0 = "/run/docker/plugins/testvol0.sock" +testvol1 = "/run/docker/plugins/testvol1.sock" +testvol2 = "/run/docker/plugins/testvol2.sock" +testvol3 = "/run/docker/plugins/testvol3.sock" +testvol4 = "/run/docker/plugins/testvol4.sock" +testvol5 = "/run/docker/plugins/testvol5.sock" +testvol6 = "/run/docker/plugins/testvol6.sock" +testvol7 = "/run/docker/plugins/testvol7.sock" +testvol8 = "/run/docker/plugins/testvol8.sock" +testvol9 = "/run/docker/plugins/testvol9.sock" diff --git a/test/e2e/exec_test.go b/test/e2e/exec_test.go index f61f52589..b180d881a 100644 --- a/test/e2e/exec_test.go +++ b/test/e2e/exec_test.go @@ -119,6 +119,203 @@ var _ = Describe("Podman exec", func() { Expect(session.ExitCode()).To(Equal(100)) }) + It("podman exec --privileged", func() { + session := podmanTest.Podman([]string{"run", "--privileged", "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + bndPerms := session.OutputToString() + + session = podmanTest.Podman([]string{"run", "--privileged", "--rm", ALPINE, "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + effPerms := session.OutputToString() + + setup := podmanTest.RunTopContainer("test-privileged") + setup.WaitWithDefaultTimeout() + Expect(setup.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"exec", "--privileged", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(effPerms)) + + session = podmanTest.Podman([]string{"exec", "--privileged", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(bndPerms)) + + }) + + It("podman exec --privileged", func() { + session := podmanTest.Podman([]string{"run", "--privileged", "--user=bin", "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + bndPerms := session.OutputToString() + + session = podmanTest.Podman([]string{"run", "--privileged", "--user=bin", "--rm", ALPINE, "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + effPerms := session.OutputToString() + + setup := podmanTest.RunTopContainer("test-privileged") + setup.WaitWithDefaultTimeout() + Expect(setup.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"exec", "--privileged", "--user=bin", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(effPerms)) + + session = podmanTest.Podman([]string{"exec", "--privileged", "--user=bin", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(bndPerms)) + + }) + + It("podman exec --privileged", func() { + session := podmanTest.Podman([]string{"run", "--privileged", "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + bndPerms := session.OutputToString() + + setup := podmanTest.RunTopContainer("test-privileged") + setup.WaitWithDefaultTimeout() + Expect(setup.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"exec", "--privileged", "--user=bin", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("00000000")) + + session = podmanTest.Podman([]string{"exec", "--privileged", "--user=bin", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(bndPerms)) + }) + + It("podman exec --privileged container not running as root", func() { + session := podmanTest.Podman([]string{"run", "--privileged", "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + bndPerms := session.OutputToString() + + setup := podmanTest.RunTopContainerWithArgs("test-privileged", []string{"--user=bin"}) + setup.WaitWithDefaultTimeout() + Expect(setup.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"exec", "--privileged", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("00000000")) + + session = podmanTest.Podman([]string{"exec", "--privileged", "--user=bin", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("00000000")) + + session = podmanTest.Podman([]string{"exec", "--privileged", "--user=root", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(bndPerms)) + + session = podmanTest.Podman([]string{"exec", "--privileged", "--user=bin", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(bndPerms)) + }) + + It("podman exec with user with cap-add", func() { + capAdd := "--cap-add=net_bind_service" + session := podmanTest.Podman([]string{"run", "--user=bin", capAdd, "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + bndPerms := session.OutputToString() + + session = podmanTest.Podman([]string{"run", "--user=bin", capAdd, "--rm", ALPINE, "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + effPerms := session.OutputToString() + + setup := podmanTest.RunTopContainerWithArgs("test-privileged", []string{"--user=bin", capAdd}) + setup.WaitWithDefaultTimeout() + Expect(setup.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"exec", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(bndPerms)) + + session = podmanTest.Podman([]string{"exec", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(effPerms)) + }) + + It("podman exec with user with and cap-drop cap-add", func() { + capAdd := "--cap-add=net_bind_service" + capDrop := "--cap-drop=all" + session := podmanTest.Podman([]string{"run", "--user=bin", capDrop, capAdd, "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + bndPerms := session.OutputToString() + + session = podmanTest.Podman([]string{"run", "--user=bin", capDrop, capAdd, "--rm", ALPINE, "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + effPerms := session.OutputToString() + + setup := podmanTest.RunTopContainerWithArgs("test-privileged", []string{"--user=bin", capDrop, capAdd}) + setup.WaitWithDefaultTimeout() + Expect(setup.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"exec", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(bndPerms)) + + session = podmanTest.Podman([]string{"exec", "--privileged", "test-privileged", "sh", "-c", "grep ^CapInh /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(effPerms)) + + session = podmanTest.Podman([]string{"exec", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(effPerms)) + + session = podmanTest.Podman([]string{"exec", "test-privileged", "sh", "-c", "grep ^CapPrm /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(effPerms)) + + session = podmanTest.Podman([]string{"exec", "test-privileged", "sh", "-c", "grep ^CapAmb /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(effPerms)) + }) + + It("podman exec --privileged with user", func() { + session := podmanTest.Podman([]string{"run", "--privileged", "--user=bin", "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + bindPerms := session.OutputToString() + + setup := podmanTest.RunTopContainerWithArgs("test-privileged", []string{"--privileged", "--user=bin"}) + setup.WaitWithDefaultTimeout() + Expect(setup.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"exec", "--privileged", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(bindPerms)) + + session = podmanTest.Podman([]string{"exec", "--privileged", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) + }) + It("podman exec terminal doesn't hang", func() { setup := podmanTest.Podman([]string{"run", "-dti", "--name", "test1", fedoraMinimal, "sleep", "+Inf"}) setup.WaitWithDefaultTimeout() diff --git a/test/e2e/generate_systemd_test.go b/test/e2e/generate_systemd_test.go index 765844265..be9727591 100644 --- a/test/e2e/generate_systemd_test.go +++ b/test/e2e/generate_systemd_test.go @@ -59,8 +59,7 @@ var _ = Describe("Podman generate systemd", func() { session = podmanTest.Podman([]string{"generate", "systemd", "--restart-policy", "bogus", "foobar"}) session.WaitWithDefaultTimeout() Expect(session).To(ExitWithError()) - found, _ := session.ErrorGrepString("bogus is not a valid restart policy") - Expect(found).Should(BeTrue()) + Expect(session.ErrorToString()).To(ContainSubstring("bogus is not a valid restart policy")) }) It("podman generate systemd good timeout value", func() { @@ -71,9 +70,8 @@ var _ = Describe("Podman generate systemd", func() { session = podmanTest.Podman([]string{"generate", "systemd", "--time", "1234", "foobar"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - - found, _ := session.GrepString(" stop -t 1234 ") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("TimeoutStopSec=1294")) + Expect(session.OutputToString()).To(ContainSubstring(" stop -t 1234 ")) }) It("podman generate systemd", func() { @@ -84,6 +82,9 @@ var _ = Describe("Podman generate systemd", func() { session := podmanTest.Podman([]string{"generate", "systemd", "nginx"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) + + // The podman commands in the unit should not contain the root flags + Expect(session.OutputToString()).ToNot(ContainSubstring(" --runroot")) }) It("podman generate systemd --files --name", func() { @@ -98,9 +99,7 @@ var _ = Describe("Podman generate systemd", func() { for _, file := range session.OutputToStringArray() { os.Remove(file) } - - found, _ := session.GrepString("/container-nginx.service") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("/container-nginx.service")) }) It("podman generate systemd with timeout", func() { @@ -111,9 +110,7 @@ var _ = Describe("Podman generate systemd", func() { session := podmanTest.Podman([]string{"generate", "systemd", "--time", "5", "nginx"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - - found, _ := session.GrepString("podman stop -t 5") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("podman stop -t 5")) }) It("podman generate systemd pod --name", func() { @@ -134,35 +131,19 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.ExitCode()).To(Equal(0)) // Grepping the output (in addition to unit tests) - found, _ := session.GrepString("# pod-foo.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("Requires=container-foo-1.service container-foo-2.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("# container-foo-1.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString(" start foo-1") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("-infra") // infra container - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("# container-foo-2.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString(" stop -t 42 foo-2") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("BindsTo=pod-foo.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("PIDFile=") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("/userdata/conmon.pid") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("# pod-foo.service")) + Expect(session.OutputToString()).To(ContainSubstring("Requires=container-foo-1.service container-foo-2.service")) + Expect(session.OutputToString()).To(ContainSubstring("# container-foo-1.service")) + Expect(session.OutputToString()).To(ContainSubstring(" start foo-1")) + Expect(session.OutputToString()).To(ContainSubstring("-infra")) // infra container + Expect(session.OutputToString()).To(ContainSubstring("# container-foo-2.service")) + Expect(session.OutputToString()).To(ContainSubstring(" stop -t 42 foo-2")) + Expect(session.OutputToString()).To(ContainSubstring("BindsTo=pod-foo.service")) + Expect(session.OutputToString()).To(ContainSubstring("PIDFile=")) + Expect(session.OutputToString()).To(ContainSubstring("/userdata/conmon.pid")) + + // The podman commands in the unit should not contain the root flags + Expect(session.OutputToString()).ToNot(ContainSubstring(" --runroot")) }) It("podman generate systemd pod --name --files", func() { @@ -182,11 +163,8 @@ var _ = Describe("Podman generate systemd", func() { os.Remove(file) } - found, _ := session.GrepString("/pod-foo.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("/container-foo-1.service") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("/pod-foo.service")) + Expect(session.OutputToString()).To(ContainSubstring("/container-foo-1.service")) }) It("podman generate systemd --new --name foo", func() { @@ -199,14 +177,13 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.ExitCode()).To(Equal(0)) // Grepping the output (in addition to unit tests) - found, _ := session.GrepString("# container-foo.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString(" --replace ") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("stop --ignore --cidfile %t/container-foo.ctr-id -t 42") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("# container-foo.service")) + Expect(session.OutputToString()).To(ContainSubstring(" --replace ")) + Expect(session.OutputToString()).To(ContainSubstring(" stop --ignore --cidfile %t/container-foo.ctr-id -t 42")) + if !IsRemote() { + // The podman commands in the unit should contain the root flags if generate systemd --new is used + Expect(session.OutputToString()).To(ContainSubstring(" --runroot")) + } }) It("podman generate systemd --new --name=foo", func() { @@ -219,14 +196,9 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.ExitCode()).To(Equal(0)) // Grepping the output (in addition to unit tests) - found, _ := session.GrepString("# container-foo.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString(" --replace ") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("stop --ignore --cidfile %t/container-foo.ctr-id -t 42") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("# container-foo.service")) + Expect(session.OutputToString()).To(ContainSubstring(" --replace ")) + Expect(session.OutputToString()).To(ContainSubstring(" stop --ignore --cidfile %t/container-foo.ctr-id -t 42")) }) It("podman generate systemd --new without explicit detaching param", func() { @@ -239,8 +211,7 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.ExitCode()).To(Equal(0)) // Grepping the output (in addition to unit tests) - found, _ := session.GrepString("--cgroups=no-conmon -d") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("--cgroups=no-conmon -d")) }) It("podman generate systemd --new with explicit detaching param in middle", func() { @@ -253,8 +224,7 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.ExitCode()).To(Equal(0)) // Grepping the output (in addition to unit tests) - found, _ := session.GrepString("--name foo alpine top") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("--name foo alpine top")) }) It("podman generate systemd --new pod", func() { @@ -277,8 +247,8 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.ExitCode()).To(Equal(0)) // Grepping the output (in addition to unit tests) - found, _ := session.GrepString("# con-foo.service") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("# con-foo.service")) + }) It("podman generate systemd --separator _", func() { @@ -291,8 +261,7 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.ExitCode()).To(Equal(0)) // Grepping the output (in addition to unit tests) - found, _ := session.GrepString("# container_foo.service") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("# container_foo.service")) }) It("podman generate systemd pod --pod-prefix p", func() { @@ -313,17 +282,10 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.ExitCode()).To(Equal(0)) // Grepping the output (in addition to unit tests) - found, _ := session.GrepString("# p-foo.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("Requires=container-foo-1.service container-foo-2.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("# container-foo-1.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("BindsTo=p-foo.service") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("# p-foo.service")) + Expect(session.OutputToString()).To(ContainSubstring("Requires=container-foo-1.service container-foo-2.service")) + Expect(session.OutputToString()).To(ContainSubstring("# container-foo-1.service")) + Expect(session.OutputToString()).To(ContainSubstring("BindsTo=p-foo.service")) }) It("podman generate systemd pod --pod-prefix p --container-prefix con --separator _ change all prefixes/separator", func() { @@ -344,20 +306,11 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.ExitCode()).To(Equal(0)) // Grepping the output (in addition to unit tests) - found, _ := session.GrepString("# p_foo.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("Requires=con_foo-1.service con_foo-2.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("# con_foo-1.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("# con_foo-2.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("BindsTo=p_foo.service") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("# p_foo.service")) + Expect(session.OutputToString()).To(ContainSubstring("Requires=con_foo-1.service con_foo-2.service")) + Expect(session.OutputToString()).To(ContainSubstring("# con_foo-1.service")) + Expect(session.OutputToString()).To(ContainSubstring("# con_foo-2.service")) + Expect(session.OutputToString()).To(ContainSubstring("BindsTo=p_foo.service")) }) It("podman generate systemd pod with containers --new", func() { @@ -383,26 +336,13 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.ExitCode()).To(Equal(0)) // Grepping the output (in addition to unit tests) - found, _ := session.GrepString("# pod-foo.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("Requires=container-foo-1.service container-foo-2.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("BindsTo=pod-foo.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("pod create --infra-conmon-pidfile %t/pod-foo.pid --pod-id-file %t/pod-foo.pod-id --name foo") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("ExecStartPre=/bin/rm -f %t/pod-foo.pid %t/pod-foo.pod-id") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("pod stop --ignore --pod-id-file %t/pod-foo.pod-id -t 10") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("pod rm --ignore -f --pod-id-file %t/pod-foo.pod-id") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("# pod-foo.service")) + Expect(session.OutputToString()).To(ContainSubstring("Requires=container-foo-1.service container-foo-2.service")) + Expect(session.OutputToString()).To(ContainSubstring("BindsTo=pod-foo.service")) + Expect(session.OutputToString()).To(ContainSubstring("pod create --infra-conmon-pidfile %t/pod-foo.pid --pod-id-file %t/pod-foo.pod-id --name foo")) + Expect(session.OutputToString()).To(ContainSubstring("ExecStartPre=/bin/rm -f %t/pod-foo.pid %t/pod-foo.pod-id")) + Expect(session.OutputToString()).To(ContainSubstring("pod stop --ignore --pod-id-file %t/pod-foo.pod-id -t 10")) + Expect(session.OutputToString()).To(ContainSubstring("pod rm --ignore -f --pod-id-file %t/pod-foo.pod-id")) }) It("podman generate systemd --format json", func() { diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index f009e333e..5930462d5 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -825,9 +825,16 @@ var _ = Describe("Podman play kube", func() { inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) + cmd := inspect.OutputToString() + + inspect = podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Entrypoint }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + ep := inspect.OutputToString() + // Use the defined command to override the image's command - correctCmd := "[" + strings.Join(defaultCtrCmd, " ") + " " + strings.Join(defaultCtrArg, " ") - Expect(inspect.OutputToString()).To(ContainSubstring(correctCmd)) + Expect(ep).To(ContainSubstring(strings.Join(defaultCtrCmd, " "))) + Expect(cmd).To(ContainSubstring(strings.Join(defaultCtrArg, " "))) }) // If you do not supply command or args for a Container, the defaults defined in the Docker image are used. @@ -840,12 +847,17 @@ var _ = Describe("Podman play kube", func() { kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) - inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) + // this image's ENTRYPOINT is `/entrypoint.sh` + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Entrypoint }}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.OutputToString()).To(ContainSubstring(`/entrypoint.sh`)) - // this image's ENTRYPOINT is `/entrypoint.sh` and it's COMMAND is `/etc/docker/registry/config.yml` - Expect(inspect.OutputToString()).To(ContainSubstring(`[/entrypoint.sh /etc/docker/registry/config.yml]`)) + // and its COMMAND is `/etc/docker/registry/config.yml` + inspect = podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.OutputToString()).To(ContainSubstring(`[/etc/docker/registry/config.yml]`)) }) // If you supply a command but no args for a Container, only the supplied command is used. @@ -859,12 +871,18 @@ var _ = Describe("Podman play kube", func() { kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) - inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) - inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) // Use the defined command to override the image's command, and don't set the args // so the full command in result should not contains the image's command - Expect(inspect.OutputToString()).To(ContainSubstring(`[echo hello]`)) + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Entrypoint }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.OutputToString()).To(ContainSubstring(`echo hello`)) + + inspect = podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + // an empty command is reported as '[]' + Expect(inspect.OutputToString()).To(ContainSubstring(`[]`)) }) // If you supply only args for a Container, the default Entrypoint defined in the Docker image is run with the args that you supplied. @@ -877,12 +895,16 @@ var _ = Describe("Podman play kube", func() { kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) - inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) + // this image's ENTRYPOINT is `/entrypoint.sh` + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Entrypoint }}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) - // this image's ENTRYPOINT is `/entrypoint.sh` - // so result should be `/entrypoint.sh + withArg(...)` - Expect(inspect.OutputToString()).To(ContainSubstring(`[/entrypoint.sh echo hello]`)) + Expect(inspect.OutputToString()).To(ContainSubstring(`/entrypoint.sh`)) + + inspect = podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.OutputToString()).To(ContainSubstring(`[echo hello]`)) }) // If you supply a command and args, @@ -897,10 +919,15 @@ var _ = Describe("Podman play kube", func() { kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) - inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Entrypoint }}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) - Expect(inspect.OutputToString()).To(ContainSubstring(`[echo hello]`)) + Expect(inspect.OutputToString()).To(ContainSubstring(`echo`)) + + inspect = podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.OutputToString()).To(ContainSubstring(`[hello]`)) }) It("podman play kube test correct output", func() { @@ -917,11 +944,6 @@ var _ = Describe("Podman play kube", func() { logs.WaitWithDefaultTimeout() Expect(logs.ExitCode()).To(Equal(0)) Expect(logs.OutputToString()).To(ContainSubstring("hello world")) - - inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(p), "--format", "'{{ .Config.Cmd }}'"}) - inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) - Expect(inspect.OutputToString()).To(ContainSubstring(`[echo hello world]`)) }) It("podman play kube test restartPolicy", func() { @@ -1286,12 +1308,11 @@ spec: Expect(kube.ExitCode()).To(Equal(0)) podNames := getPodNamesInDeployment(deployment) - inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[0]), "--format", "'{{ .Config.Cmd }}'"}) + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[0]), "--format", "'{{ .Config.Entrypoint }}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) // yaml's command should override the image's Entrypoint - correctCmd := "[" + strings.Join(defaultCtrCmd, " ") + " " + strings.Join(defaultCtrArg, " ") - Expect(inspect.OutputToString()).To(ContainSubstring(correctCmd)) + Expect(inspect.OutputToString()).To(ContainSubstring(strings.Join(defaultCtrCmd, " "))) }) It("podman play kube deployment more than 1 replica test correct command", func() { @@ -1306,12 +1327,11 @@ spec: Expect(kube.ExitCode()).To(Equal(0)) podNames := getPodNamesInDeployment(deployment) - correctCmd := "[" + strings.Join(defaultCtrCmd, " ") + " " + strings.Join(defaultCtrArg, " ") for i = 0; i < numReplicas; i++ { - inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[i]), "--format", "'{{ .Config.Cmd }}'"}) + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[i]), "--format", "'{{ .Config.Entrypoint }}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) - Expect(inspect.OutputToString()).To(ContainSubstring(correctCmd)) + Expect(inspect.OutputToString()).To(ContainSubstring(strings.Join(defaultCtrCmd, " "))) } }) diff --git a/test/e2e/pod_ps_test.go b/test/e2e/pod_ps_test.go index 225da785c..9f63c1d5d 100644 --- a/test/e2e/pod_ps_test.go +++ b/test/e2e/pod_ps_test.go @@ -6,6 +6,7 @@ import ( "sort" . "github.com/containers/podman/v2/test/utils" + "github.com/containers/storage/pkg/stringid" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" . "github.com/onsi/gomega/gexec" @@ -280,6 +281,69 @@ var _ = Describe("Podman ps", func() { Expect(session.OutputToString()).To(Not(ContainSubstring(podid3))) }) + It("podman pod ps filter network", func() { + net := stringid.GenerateNonCryptoID() + session := podmanTest.Podman([]string{"network", "create", net}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + defer podmanTest.removeCNINetwork(net) + + session = podmanTest.Podman([]string{"pod", "create", "--network", net}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + podWithNet := session.OutputToString() + + session = podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + podWithoutNet := session.OutputToString() + + session = podmanTest.Podman([]string{"pod", "ps", "--no-trunc", "--filter", "network=" + net}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + Expect(session.OutputToString()).To(ContainSubstring(podWithNet)) + Expect(session.OutputToString()).To(Not(ContainSubstring(podWithoutNet))) + }) + + It("podman pod ps --format networks", func() { + session := podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + + session = podmanTest.Podman([]string{"pod", "ps", "--format", "{{ .Networks }}"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + if isRootless() { + // rootless container don't have a network by default + Expect(session.OutputToString()).To(Equal("")) + } else { + // default network name is podman + Expect(session.OutputToString()).To(Equal("podman")) + } + + net1 := stringid.GenerateNonCryptoID() + session = podmanTest.Podman([]string{"network", "create", net1}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + defer podmanTest.removeCNINetwork(net1) + net2 := stringid.GenerateNonCryptoID() + session = podmanTest.Podman([]string{"network", "create", net2}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + defer podmanTest.removeCNINetwork(net2) + + session = podmanTest.Podman([]string{"pod", "create", "--network", net1 + "," + net2}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + pid := session.OutputToString() + + session = podmanTest.Podman([]string{"pod", "ps", "--format", "{{ .Networks }}", "--filter", "id=" + pid}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + // the output is not deterministic so check both possible orders + Expect(session.OutputToString()).To(Or(Equal(net1+","+net2), Equal(net2+","+net1))) + }) + It("pod no infra should ps", func() { session := podmanTest.Podman([]string{"pod", "create", "--infra=false"}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/pod_stats_test.go b/test/e2e/pod_stats_test.go index a034ec2d1..1709b4f81 100644 --- a/test/e2e/pod_stats_test.go +++ b/test/e2e/pod_stats_test.go @@ -17,7 +17,7 @@ var _ = Describe("Podman pod stats", func() { ) BeforeEach(func() { - SkipIfRootless("Tests fail with both CGv1/2 + required --cgroup-manager=cgroupfs") + SkipIfRootlessCgroupsV1("Tests fail with both CGv1 + required --cgroup-manager=cgroupfs") if isContainerized() { SkipIfCgroupV1("All tests fail Error: unable to load cgroup at ...: cgroup deleted") } @@ -176,9 +176,7 @@ var _ = Describe("Podman pod stats", func() { }) It("podman stats on net=host post", func() { - // --net=host not supported for rootless pods at present - // problem with sysctls being passed to containers of the pod. - SkipIfCgroupV1("Bug: Error: sysctl net.ipv4.ping_group_range is not allowed in the hosts network namespace: OCI runtime error") + SkipIfRootless("--net=host not supported for rootless pods at present") podName := "testPod" podCreate := podmanTest.Podman([]string{"pod", "create", "--net=host", "--name", podName}) podCreate.WaitWithDefaultTimeout() diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go index 0c5d817ba..13701fc3b 100644 --- a/test/e2e/ps_test.go +++ b/test/e2e/ps_test.go @@ -8,6 +8,7 @@ import ( "strings" . "github.com/containers/podman/v2/test/utils" + "github.com/containers/storage/pkg/stringid" "github.com/docker/go-units" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -724,4 +725,67 @@ var _ = Describe("Podman ps", func() { }) + It("podman ps filter network", func() { + net := stringid.GenerateNonCryptoID() + session := podmanTest.Podman([]string{"network", "create", net}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + defer podmanTest.removeCNINetwork(net) + + session = podmanTest.Podman([]string{"create", "--network", net, ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + ctrWithNet := session.OutputToString() + + session = podmanTest.Podman([]string{"create", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + ctrWithoutNet := session.OutputToString() + + session = podmanTest.Podman([]string{"ps", "--all", "--no-trunc", "--filter", "network=" + net}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + Expect(session.OutputToString()).To(ContainSubstring(ctrWithNet)) + Expect(session.OutputToString()).To(Not(ContainSubstring(ctrWithoutNet))) + }) + + It("podman ps --format networks", func() { + session := podmanTest.Podman([]string{"create", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + + session = podmanTest.Podman([]string{"ps", "--all", "--format", "{{ .Networks }}"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + if isRootless() { + // rootless container don't have a network by default + Expect(session.OutputToString()).To(Equal("")) + } else { + // default network name is podman + Expect(session.OutputToString()).To(Equal("podman")) + } + + net1 := stringid.GenerateNonCryptoID() + session = podmanTest.Podman([]string{"network", "create", net1}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + defer podmanTest.removeCNINetwork(net1) + net2 := stringid.GenerateNonCryptoID() + session = podmanTest.Podman([]string{"network", "create", net2}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + defer podmanTest.removeCNINetwork(net2) + + session = podmanTest.Podman([]string{"create", "--network", net1 + "," + net2, ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + cid := session.OutputToString() + + session = podmanTest.Podman([]string{"ps", "--all", "--format", "{{ .Networks }}", "--filter", "id=" + cid}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + // the output is not deterministic so check both possible orders + Expect(session.OutputToString()).To(Or(Equal(net1+","+net2), Equal(net2+","+net1))) + }) + }) diff --git a/test/e2e/rename_test.go b/test/e2e/rename_test.go new file mode 100644 index 000000000..7affbaf56 --- /dev/null +++ b/test/e2e/rename_test.go @@ -0,0 +1,92 @@ +package integration + +import ( + "fmt" + "os" + + . "github.com/containers/podman/v2/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("podman rename", func() { + var ( + tempdir string + err error + podmanTest *PodmanTestIntegration + ) + + BeforeEach(func() { + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanTestCreate(tempdir) + podmanTest.Setup() + podmanTest.SeedImages() + }) + + AfterEach(func() { + podmanTest.Cleanup() + f := CurrentGinkgoTestDescription() + processTestResult(f) + + }) + + It("podman rename on non-existent container", func() { + session := podmanTest.Podman([]string{"rename", "doesNotExist", "aNewName"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Not(Equal(0))) + }) + + It("Podman rename on existing container with bad name", func() { + ctrName := "testCtr" + ctr := podmanTest.Podman([]string{"create", "--name", ctrName, ALPINE, "top"}) + ctr.WaitWithDefaultTimeout() + Expect(ctr.ExitCode()).To(Equal(0)) + + newName := "invalid<>:char" + rename := podmanTest.Podman([]string{"rename", ctrName, newName}) + rename.WaitWithDefaultTimeout() + Expect(rename.ExitCode()).To(Not(Equal(0))) + + ps := podmanTest.Podman([]string{"ps", "-aq", "--filter", fmt.Sprintf("name=%s", ctrName), "--format", "{{ .Names }}"}) + ps.WaitWithDefaultTimeout() + Expect(ps.ExitCode()).To(Equal(0)) + Expect(ps.OutputToString()).To(ContainSubstring(ctrName)) + }) + + It("Successfully rename a created container", func() { + ctrName := "testCtr" + ctr := podmanTest.Podman([]string{"create", "--name", ctrName, ALPINE, "top"}) + ctr.WaitWithDefaultTimeout() + Expect(ctr.ExitCode()).To(Equal(0)) + + newName := "aNewName" + rename := podmanTest.Podman([]string{"rename", ctrName, newName}) + rename.WaitWithDefaultTimeout() + Expect(rename.ExitCode()).To(Equal(0)) + + ps := podmanTest.Podman([]string{"ps", "-aq", "--filter", fmt.Sprintf("name=%s", newName), "--format", "{{ .Names }}"}) + ps.WaitWithDefaultTimeout() + Expect(ps.ExitCode()).To(Equal(0)) + Expect(ps.OutputToString()).To(ContainSubstring(newName)) + }) + + It("Successfully rename a running container", func() { + ctrName := "testCtr" + ctr := podmanTest.Podman([]string{"run", "-d", "--name", ctrName, ALPINE, "top"}) + ctr.WaitWithDefaultTimeout() + Expect(ctr.ExitCode()).To(Equal(0)) + + newName := "aNewName" + rename := podmanTest.Podman([]string{"rename", ctrName, newName}) + rename.WaitWithDefaultTimeout() + Expect(rename.ExitCode()).To(Equal(0)) + + ps := podmanTest.Podman([]string{"ps", "-aq", "--filter", fmt.Sprintf("name=%s", newName), "--format", "{{ .Names }}"}) + ps.WaitWithDefaultTimeout() + Expect(ps.ExitCode()).To(Equal(0)) + Expect(ps.OutputToString()).To(ContainSubstring(newName)) + }) +}) diff --git a/test/e2e/restart_test.go b/test/e2e/restart_test.go index 114bd481a..584ccd22b 100644 --- a/test/e2e/restart_test.go +++ b/test/e2e/restart_test.go @@ -196,4 +196,33 @@ var _ = Describe("Podman restart", func() { Expect(restartTime.OutputToStringArray()[0]).To(Equal(startTime.OutputToStringArray()[0])) Expect(restartTime.OutputToStringArray()[1]).To(Not(Equal(startTime.OutputToStringArray()[1]))) }) + + It("Podman restart a container in a pod and hosts shouln't duplicated", func() { + // Fixes: https://github.com/containers/podman/issues/8921 + + _, ec, _ := podmanTest.CreatePod("foobar99") + Expect(ec).To(Equal(0)) + + session := podmanTest.RunTopContainerInPod("host-restart-test", "foobar99") + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + testCmd := []string{"exec", "host-restart-test", "sh", "-c", "wc -l < /etc/hosts"} + + // before restart + beforeRestart := podmanTest.Podman(testCmd) + beforeRestart.WaitWithDefaultTimeout() + Expect(beforeRestart.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"restart", "host-restart-test"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + afterRestart := podmanTest.Podman(testCmd) + afterRestart.WaitWithDefaultTimeout() + Expect(afterRestart.ExitCode()).To(Equal(0)) + + // line count should be equal + Expect(beforeRestart.OutputToString()).To(Equal(afterRestart.OutputToString())) + }) }) diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index b8e14530c..cbaae7186 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -622,7 +622,7 @@ var _ = Describe("Podman run networking", func() { It("podman run in custom CNI network with --static-ip", func() { SkipIfRootless("Rootless mode does not support --ip") - netName := "podmantestnetwork" + netName := stringid.GenerateNonCryptoID() ipAddr := "10.25.30.128" create := podmanTest.Podman([]string{"network", "create", "--subnet", "10.25.30.0/24", netName}) create.WaitWithDefaultTimeout() @@ -639,9 +639,27 @@ var _ = Describe("Podman run networking", func() { Expect(create.ExitCode()).To(BeZero()) }) + It("podman rootless fails custom CNI network with --uidmap", func() { + SkipIfNotRootless("The configuration works with rootless") + + netName := stringid.GenerateNonCryptoID() + create := podmanTest.Podman([]string{"network", "create", netName}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(BeZero()) + defer podmanTest.removeCNINetwork(netName) + + run := podmanTest.Podman([]string{"run", "--rm", "--net", netName, "--uidmap", "0:1:4096", ALPINE, "true"}) + run.WaitWithDefaultTimeout() + Expect(run.ExitCode()).To(Equal(125)) + + remove := podmanTest.Podman([]string{"network", "rm", netName}) + remove.WaitWithDefaultTimeout() + Expect(remove.ExitCode()).To(BeZero()) + }) + It("podman run with new:pod and static-ip", func() { SkipIfRootless("Rootless does not support --ip") - netName := "podmantestnetwork2" + netName := stringid.GenerateNonCryptoID() ipAddr := "10.25.40.128" podname := "testpod" create := podmanTest.Podman([]string{"network", "create", "--subnet", "10.25.40.0/24", netName}) diff --git a/test/e2e/run_ns_test.go b/test/e2e/run_ns_test.go index 51657cb1e..29d2d4395 100644 --- a/test/e2e/run_ns_test.go +++ b/test/e2e/run_ns_test.go @@ -105,6 +105,14 @@ var _ = Describe("Podman run ns", func() { Expect(session).To(ExitWithError()) }) + It("podman run mounts fresh cgroup", func() { + session := podmanTest.Podman([]string{"run", fedoraMinimal, "grep", "cgroup", "/proc/self/mountinfo"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + output := session.OutputToString() + Expect(output).ToNot(ContainSubstring("..")) + }) + It("podman run --ipc=host --pid=host", func() { SkipIfRootlessCgroupsV1("Not supported for rootless + CGroupsV1") cmd := exec.Command("ls", "-l", "/proc/self/ns/pid") diff --git a/test/e2e/run_privileged_test.go b/test/e2e/run_privileged_test.go index 760de55b6..48f9ea76e 100644 --- a/test/e2e/run_privileged_test.go +++ b/test/e2e/run_privileged_test.go @@ -16,22 +16,22 @@ import ( // know about at compile time. That is: the kernel may have more caps // available than we are aware of, leading to host=FFF... and ctr=3FF... // because the latter is all we request. Accept that. -func containerCapMatchesHost(ctr_cap string, host_cap string) { +func containerCapMatchesHost(ctrCap string, hostCap string) { if isRootless() { return } - ctr_cap_n, err := strconv.ParseUint(ctr_cap, 16, 64) - Expect(err).NotTo(HaveOccurred(), "Error parsing %q as hex", ctr_cap) + ctrCap_n, err := strconv.ParseUint(ctrCap, 16, 64) + Expect(err).NotTo(HaveOccurred(), "Error parsing %q as hex", ctrCap) - host_cap_n, err := strconv.ParseUint(host_cap, 16, 64) - Expect(err).NotTo(HaveOccurred(), "Error parsing %q as hex", host_cap) + hostCap_n, err := strconv.ParseUint(hostCap, 16, 64) + Expect(err).NotTo(HaveOccurred(), "Error parsing %q as hex", hostCap) // host caps can never be zero (except rootless). // and host caps must always be a superset (inclusive) of container - Expect(host_cap_n).To(BeNumerically(">", 0), "host cap %q should be nonzero", host_cap) - Expect(host_cap_n).To(BeNumerically(">=", ctr_cap_n), "host cap %q should never be less than container cap %q", host_cap, ctr_cap) - host_cap_masked := host_cap_n & (1<<len(capability.List()) - 1) - Expect(ctr_cap_n).To(Equal(host_cap_masked), "container cap %q is not a subset of host cap %q", ctr_cap, host_cap) + Expect(hostCap_n).To(BeNumerically(">", 0), "host cap %q should be nonzero", hostCap) + Expect(hostCap_n).To(BeNumerically(">=", ctrCap_n), "host cap %q should never be less than container cap %q", hostCap, ctrCap) + hostCap_masked := hostCap_n & (1<<len(capability.List()) - 1) + Expect(ctrCap_n).To(Equal(hostCap_masked), "container cap %q is not a subset of host cap %q", ctrCap, hostCap) } var _ = Describe("Podman privileged container tests", func() { @@ -68,38 +68,38 @@ var _ = Describe("Podman privileged container tests", func() { }) It("podman privileged CapEff", func() { - host_cap := SystemExec("awk", []string{"/^CapEff/ { print $2 }", "/proc/self/status"}) - Expect(host_cap.ExitCode()).To(Equal(0)) + hostCap := SystemExec("awk", []string{"/^CapEff/ { print $2 }", "/proc/self/status"}) + Expect(hostCap.ExitCode()).To(Equal(0)) session := podmanTest.Podman([]string{"run", "--privileged", "busybox", "awk", "/^CapEff/ { print $2 }", "/proc/self/status"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - containerCapMatchesHost(session.OutputToString(), host_cap.OutputToString()) + containerCapMatchesHost(session.OutputToString(), hostCap.OutputToString()) }) It("podman cap-add CapEff", func() { // Get caps of current process - host_cap := SystemExec("awk", []string{"/^CapEff/ { print $2 }", "/proc/self/status"}) - Expect(host_cap.ExitCode()).To(Equal(0)) + hostCap := SystemExec("awk", []string{"/^CapEff/ { print $2 }", "/proc/self/status"}) + Expect(hostCap.ExitCode()).To(Equal(0)) session := podmanTest.Podman([]string{"run", "--cap-add", "all", "busybox", "awk", "/^CapEff/ { print $2 }", "/proc/self/status"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - containerCapMatchesHost(session.OutputToString(), host_cap.OutputToString()) + containerCapMatchesHost(session.OutputToString(), hostCap.OutputToString()) }) It("podman cap-add CapEff with --user", func() { // Get caps of current process - host_cap := SystemExec("awk", []string{"/^CapEff/ { print $2 }", "/proc/self/status"}) - Expect(host_cap.ExitCode()).To(Equal(0)) + hostCap := SystemExec("awk", []string{"/^CapEff/ { print $2 }", "/proc/self/status"}) + Expect(hostCap.ExitCode()).To(Equal(0)) session := podmanTest.Podman([]string{"run", "--user=bin", "--cap-add", "all", "busybox", "awk", "/^CapEff/ { print $2 }", "/proc/self/status"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - containerCapMatchesHost(session.OutputToString(), host_cap.OutputToString()) + containerCapMatchesHost(session.OutputToString(), hostCap.OutputToString()) }) It("podman cap-drop CapEff", func() { @@ -110,6 +110,15 @@ var _ = Describe("Podman privileged container tests", func() { Expect("0000000000000000").To(Equal(capEff[1])) }) + It("podman privileged should disable seccomp by default", func() { + hostSeccomp := SystemExec("grep", []string{"-Ei", "^Seccomp:\\s+0$", "/proc/self/status"}) + Expect(hostSeccomp.ExitCode()).To(Equal(0)) + + session := podmanTest.Podman([]string{"run", "--privileged", ALPINE, "grep", "-Ei", "^Seccomp:\\s+0$", "/proc/self/status"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman non-privileged should have very few devices", func() { session := podmanTest.Podman([]string{"run", "-t", "busybox", "ls", "-l", "/dev"}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index 4888a676b..19060ecdc 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -342,6 +342,11 @@ var _ = Describe("Podman run", func() { Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) + session = podmanTest.Podman([]string{"run", "--rm", "--user", "bin", ALPINE, "grep", "CapInh", "/proc/self/status"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) + session = podmanTest.Podman([]string{"run", "--rm", "--user", "root", ALPINE, "grep", "CapBnd", "/proc/self/status"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -352,6 +357,11 @@ var _ = Describe("Podman run", func() { Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb")) + session = podmanTest.Podman([]string{"run", "--rm", "--user", "root", ALPINE, "grep", "CapInh", "/proc/self/status"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb")) + session = podmanTest.Podman([]string{"run", "--rm", ALPINE, "grep", "CapBnd", "/proc/self/status"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -367,10 +377,10 @@ var _ = Describe("Podman run", func() { Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring("0000000000000002")) - session = podmanTest.Podman([]string{"run", "--user=1000:1000", "--rm", ALPINE, "grep", "CapAmb", "/proc/self/status"}) + session = podmanTest.Podman([]string{"run", "--user=1000:1000", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapInh", "/proc/self/status"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) + Expect(session.OutputToString()).To(ContainSubstring("0000000000000002")) session = podmanTest.Podman([]string{"run", "--user=0", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapAmb", "/proc/self/status"}) session.WaitWithDefaultTimeout() @@ -382,6 +392,11 @@ var _ = Describe("Podman run", func() { Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) + session = podmanTest.Podman([]string{"run", "--user=0:0", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapInh", "/proc/self/status"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb")) + if os.Geteuid() > 0 { if os.Getenv("SKIP_USERNS") != "" { Skip("Skip userns tests.") @@ -393,6 +408,16 @@ var _ = Describe("Podman run", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring("0000000000000002")) + + session = podmanTest.Podman([]string{"run", "--userns=keep-id", "--privileged", "--rm", ALPINE, "grep", "CapInh", "/proc/self/status"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) + + session = podmanTest.Podman([]string{"run", "--userns=keep-id", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapInh", "/proc/self/status"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("0000000000000002")) } }) @@ -1415,4 +1440,12 @@ WORKDIR /madethis` Expect(session.ExitCode()).To(Equal(0)) Expect(session.ErrorToString()).To(ContainSubstring("Trying to pull")) }) + + It("podman run container with hostname and hostname environment variable", func() { + hostnameEnv := "test123" + session := podmanTest.Podman([]string{"run", "--hostname", "testctr", "--env", fmt.Sprintf("HOSTNAME=%s", hostnameEnv), ALPINE, "printenv", "HOSTNAME"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(hostnameEnv)) + }) }) diff --git a/test/e2e/search_test.go b/test/e2e/search_test.go index f809c5afe..1d86ae744 100644 --- a/test/e2e/search_test.go +++ b/test/e2e/search_test.go @@ -124,6 +124,16 @@ registries = ['{{.Host}}:{{.Port}}']` Expect(search.OutputToString()).To(ContainSubstring("docker.io/library/alpine")) }) + It("podman search format json list tags", func() { + search := podmanTest.Podman([]string{"search", "--list-tags", "--format", "json", "alpine"}) + search.WaitWithDefaultTimeout() + Expect(search.ExitCode()).To(Equal(0)) + Expect(search.IsJSONOutputValid()).To(BeTrue()) + Expect(search.OutputToString()).To(ContainSubstring("docker.io/library/alpine")) + Expect(search.OutputToString()).To(ContainSubstring("3.10")) + Expect(search.OutputToString()).To(ContainSubstring("2.7")) + }) + It("podman search no-trunc flag", func() { search := podmanTest.Podman([]string{"search", "--no-trunc", "alpine"}) search.WaitWithDefaultTimeout() diff --git a/test/e2e/systemd_test.go b/test/e2e/systemd_test.go index 48294943b..49ab3b8ed 100644 --- a/test/e2e/systemd_test.go +++ b/test/e2e/systemd_test.go @@ -13,10 +13,10 @@ import ( var _ = Describe("Podman systemd", func() { var ( - tempdir string - err error - podmanTest *PodmanTestIntegration - systemd_unit_file string + tempdir string + err error + podmanTest *PodmanTestIntegration + systemdUnitFile string ) BeforeEach(func() { @@ -27,7 +27,7 @@ var _ = Describe("Podman systemd", func() { podmanTest = PodmanTestCreate(tempdir) podmanTest.Setup() podmanTest.SeedImages() - systemd_unit_file = `[Unit] + systemdUnitFile = `[Unit] Description=redis container [Service] Restart=always @@ -50,7 +50,7 @@ WantedBy=multi-user.target SkipIfRootless("rootless can not write to /etc") SkipIfContainerized("test does not have systemd as pid 1") - sys_file := ioutil.WriteFile("/etc/systemd/system/redis.service", []byte(systemd_unit_file), 0644) + sys_file := ioutil.WriteFile("/etc/systemd/system/redis.service", []byte(systemdUnitFile), 0644) Expect(sys_file).To(BeNil()) defer func() { stop := SystemExec("bash", []string{"-c", "systemctl stop redis"}) @@ -131,6 +131,21 @@ WantedBy=multi-user.target Expect(conData[0].Config.SystemdMode).To(BeTrue()) }) + It("podman create container with --uidmap and conmon PidFile accessible", func() { + ctrName := "testCtrUidMap" + run := podmanTest.Podman([]string{"run", "-d", "--uidmap=0:1:1000", "--name", ctrName, ALPINE, "top"}) + run.WaitWithDefaultTimeout() + Expect(run.ExitCode()).To(Equal(0)) + + session := podmanTest.Podman([]string{"inspect", "--format", "{{.ConmonPidFile}}", ctrName}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + pidFile := strings.TrimSuffix(session.OutputToString(), "\n") + _, err := ioutil.ReadFile(pidFile) + Expect(err).To(BeNil()) + }) + It("podman create container with systemd=always triggers systemd mode", func() { ctrName := "testCtr" run := podmanTest.Podman([]string{"create", "--name", ctrName, "--systemd", "always", ALPINE}) diff --git a/test/e2e/volume_plugin_test.go b/test/e2e/volume_plugin_test.go new file mode 100644 index 000000000..16edab27c --- /dev/null +++ b/test/e2e/volume_plugin_test.go @@ -0,0 +1,184 @@ +package integration + +import ( + "fmt" + "os" + "path/filepath" + + . "github.com/containers/podman/v2/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Podman volume plugins", func() { + var ( + tempdir string + err error + podmanTest *PodmanTestIntegration + ) + + BeforeEach(func() { + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanTestCreate(tempdir) + podmanTest.Setup() + podmanTest.SeedImages() + os.Setenv("CONTAINERS_CONF", "config/containers.conf") + SkipIfRemote("Volume plugins only supported as local") + SkipIfRootless("Root is required for volume plugin testing") + os.MkdirAll("/run/docker/plugins", 0755) + }) + + AfterEach(func() { + podmanTest.CleanupVolume() + f := CurrentGinkgoTestDescription() + processTestResult(f) + os.Unsetenv("CONTAINERS_CONF") + }) + + It("volume create with nonexistent plugin errors", func() { + session := podmanTest.Podman([]string{"volume", "create", "--driver", "notexist", "test_volume_name"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Not(Equal(0))) + }) + + It("volume create with not-running plugin does not error", func() { + session := podmanTest.Podman([]string{"volume", "create", "--driver", "testvol0", "test_volume_name"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Not(Equal(0))) + }) + + It("volume create and remove with running plugin succeeds", func() { + podmanTest.AddImageToRWStore(volumeTest) + + pluginStatePath := filepath.Join(podmanTest.TempDir, "volumes") + os.Mkdir(pluginStatePath, 0755) + + // Keep this distinct within tests to avoid multiple tests using the same plugin. + pluginName := "testvol1" + plugin := podmanTest.Podman([]string{"run", "--security-opt", "label=disable", "-v", "/run/docker/plugins:/run/docker/plugins", "-v", fmt.Sprintf("%v:%v", pluginStatePath, pluginStatePath), "-d", volumeTest, "--sock-name", pluginName, "--path", pluginStatePath}) + plugin.WaitWithDefaultTimeout() + Expect(plugin.ExitCode()).To(Equal(0)) + + volName := "testVolume1" + create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + ls1 := podmanTest.Podman([]string{"volume", "ls", "-q"}) + ls1.WaitWithDefaultTimeout() + Expect(ls1.ExitCode()).To(Equal(0)) + arrOutput := ls1.OutputToStringArray() + Expect(len(arrOutput)).To(Equal(1)) + Expect(arrOutput[0]).To(ContainSubstring(volName)) + + remove := podmanTest.Podman([]string{"volume", "rm", volName}) + remove.WaitWithDefaultTimeout() + Expect(remove.ExitCode()).To(Equal(0)) + + ls2 := podmanTest.Podman([]string{"volume", "ls", "-q"}) + ls2.WaitWithDefaultTimeout() + Expect(ls2.ExitCode()).To(Equal(0)) + Expect(len(ls2.OutputToStringArray())).To(Equal(0)) + }) + + It("volume inspect with running plugin succeeds", func() { + podmanTest.AddImageToRWStore(volumeTest) + + pluginStatePath := filepath.Join(podmanTest.TempDir, "volumes") + os.Mkdir(pluginStatePath, 0755) + + // Keep this distinct within tests to avoid multiple tests using the same plugin. + pluginName := "testvol2" + plugin := podmanTest.Podman([]string{"run", "--security-opt", "label=disable", "-v", "/run/docker/plugins:/run/docker/plugins", "-v", fmt.Sprintf("%v:%v", pluginStatePath, pluginStatePath), "-d", volumeTest, "--sock-name", pluginName, "--path", pluginStatePath}) + plugin.WaitWithDefaultTimeout() + Expect(plugin.ExitCode()).To(Equal(0)) + + volName := "testVolume1" + create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + volInspect := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .Driver }}", volName}) + volInspect.WaitWithDefaultTimeout() + Expect(volInspect.ExitCode()).To(Equal(0)) + Expect(volInspect.OutputToString()).To(ContainSubstring(pluginName)) + }) + + It("remove plugin with stopped plugin succeeds", func() { + podmanTest.AddImageToRWStore(volumeTest) + + pluginStatePath := filepath.Join(podmanTest.TempDir, "volumes") + os.Mkdir(pluginStatePath, 0755) + + // Keep this distinct within tests to avoid multiple tests using the same plugin. + pluginName := "testvol3" + ctrName := "pluginCtr" + plugin := podmanTest.Podman([]string{"run", "--name", ctrName, "--security-opt", "label=disable", "-v", "/run/docker/plugins:/run/docker/plugins", "-v", fmt.Sprintf("%v:%v", pluginStatePath, pluginStatePath), "-d", volumeTest, "--sock-name", pluginName, "--path", pluginStatePath}) + plugin.WaitWithDefaultTimeout() + Expect(plugin.ExitCode()).To(Equal(0)) + + volName := "testVolume1" + create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + ls1 := podmanTest.Podman([]string{"volume", "ls", "-q"}) + ls1.WaitWithDefaultTimeout() + Expect(ls1.ExitCode()).To(Equal(0)) + arrOutput := ls1.OutputToStringArray() + Expect(len(arrOutput)).To(Equal(1)) + Expect(arrOutput[0]).To(ContainSubstring(volName)) + + stop := podmanTest.Podman([]string{"stop", "--timeout", "0", ctrName}) + stop.WaitWithDefaultTimeout() + Expect(stop.ExitCode()).To(Equal(0)) + + // Remove should exit non-zero because missing plugin + remove := podmanTest.Podman([]string{"volume", "rm", volName}) + remove.WaitWithDefaultTimeout() + Expect(remove.ExitCode()).To(Not(Equal(0))) + + // But the volume should still be gone + ls2 := podmanTest.Podman([]string{"volume", "ls", "-q"}) + ls2.WaitWithDefaultTimeout() + Expect(ls2.ExitCode()).To(Equal(0)) + Expect(len(ls2.OutputToStringArray())).To(Equal(0)) + }) + + It("use plugin in containers", func() { + podmanTest.AddImageToRWStore(volumeTest) + + pluginStatePath := filepath.Join(podmanTest.TempDir, "volumes") + os.Mkdir(pluginStatePath, 0755) + + // Keep this distinct within tests to avoid multiple tests using the same plugin. + pluginName := "testvol4" + plugin := podmanTest.Podman([]string{"run", "--security-opt", "label=disable", "-v", "/run/docker/plugins:/run/docker/plugins", "-v", fmt.Sprintf("%v:%v", pluginStatePath, pluginStatePath), "-d", volumeTest, "--sock-name", pluginName, "--path", pluginStatePath}) + plugin.WaitWithDefaultTimeout() + Expect(plugin.ExitCode()).To(Equal(0)) + + volName := "testVolume1" + create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + ctr1 := podmanTest.Podman([]string{"run", "--security-opt", "label=disable", "-v", fmt.Sprintf("%v:/test", volName), ALPINE, "sh", "-c", "touch /test/testfile && echo helloworld > /test/testfile"}) + ctr1.WaitWithDefaultTimeout() + Expect(ctr1.ExitCode()).To(Equal(0)) + + ctr2 := podmanTest.Podman([]string{"run", "--security-opt", "label=disable", "-v", fmt.Sprintf("%v:/test", volName), ALPINE, "cat", "/test/testfile"}) + ctr2.WaitWithDefaultTimeout() + Expect(ctr2.ExitCode()).To(Equal(0)) + Expect(ctr2.OutputToString()).To(ContainSubstring("helloworld")) + + // HACK: `volume rm -f` is timing out trying to remove containers using the volume. + // Solution: remove them manually... + // TODO: fix this when I get back + rmAll := podmanTest.Podman([]string{"rm", "-af"}) + rmAll.WaitWithDefaultTimeout() + Expect(rmAll.ExitCode()).To(Equal(0)) + }) +}) diff --git a/test/python/docker/test_containers.py b/test/python/docker/test_containers.py index 5a9f761a6..01e049ed4 100644 --- a/test/python/docker/test_containers.py +++ b/test/python/docker/test_containers.py @@ -179,11 +179,3 @@ class TestContainers(unittest.TestCase): filters = {"name": "top"} ctnrs = self.client.containers.list(all=True, filters=filters) self.assertEqual(len(ctnrs), 1) - - def test_rename_container(self): - top = self.client.containers.get(TestContainers.topContainerId) - - # rename bogus container - with self.assertRaises(errors.APIError) as error: - top.rename(name="newname") - self.assertEqual(error.exception.response.status_code, 404) diff --git a/test/system/010-images.bats b/test/system/010-images.bats index 76caf282b..e7c88408e 100644 --- a/test/system/010-images.bats +++ b/test/system/010-images.bats @@ -228,4 +228,17 @@ Labels.created_at | 20[0-9-]\\\+T[0-9:]\\\+Z run_podman rmi ${aaa_name}:${aaa_tag} ${zzz_name}:${zzz_tag} } +# Regression test for #8931 +@test "podman images - bare manifest list" { + # Create an empty manifest list and list images. + + run_podman inspect --format '{{.ID}}' $IMAGE + iid=$output + + run_podman manifest create test:1.0 + run_podman images --format '{{.ID}}' --no-trunc + [[ "$output" == *"sha256:$iid"* ]] + + run_podman rmi test:1.0 +} # vim: filetype=sh diff --git a/test/system/050-stop.bats b/test/system/050-stop.bats index f604ea2e2..548fd56ee 100644 --- a/test/system/050-stop.bats +++ b/test/system/050-stop.bats @@ -67,4 +67,32 @@ load helpers done } +# Regression test for #8501 +@test "podman stop - unlock while waiting for timeout" { + # Test that the container state transitions to "stopping" and that other + # commands can get the container's lock. To do that, run a container that + # ingores SIGTERM such that the Podman would wait 20 seconds for the stop + # to finish. This gives us enough time to try some commands and inspect + # the container's status. + + run_podman run --name stopme -d $IMAGE sh -c \ + "trap 'echo Received SIGTERM, ignoring' SIGTERM; echo READY; while :; do sleep 1; done" + + # Stop the container in the background + $PODMAN stop -t 20 stopme & + + # Other commands can acquire the lock + run_podman ps -a + + # The container state transitioned to "stopping" + run_podman inspect --format '{{.State.Status}}' stopme + is "$output" "stopping" "Status of container should be 'stopping'" + + run_podman kill stopme + + # Exit code should be 137 as it was killed + run_podman inspect --format '{{.State.ExitCode}}' stopme + is "$output" "137" "Exit code of killed container" +} + # vim: filetype=sh diff --git a/test/system/120-load.bats b/test/system/120-load.bats index 272e2ae93..902cd9f5e 100644 --- a/test/system/120-load.bats +++ b/test/system/120-load.bats @@ -59,15 +59,13 @@ verify_iid_and_name() { local new_tag=t1$(random_string 6 | tr A-Z a-z) run_podman rmi $fqin - new_fqin=localhost/$new_name:$new_tag - run_podman load -i $archive $new_fqin + run_podman load -i $archive run_podman images --format '{{.Repository}}:{{.Tag}}' --sort tag is "${lines[0]}" "$IMAGE" "image is preserved" is "${lines[1]}" "$fqin" "image is reloaded with old fqin" - is "${lines[2]}" "$new_fqin" "image is reloaded with new fqin too" # Clean up - run_podman rmi $fqin $new_fqin + run_podman rmi $fqin } @@ -118,28 +116,6 @@ verify_iid_and_name() { verify_iid_and_name $img_name } -@test "podman load - NAME and NAME:TAG arguments work" { - get_iid_and_name - run_podman save $iid -o $archive - run_podman rmi $iid - - # Load with just a name (note: names must be lower-case) - random_name=$(random_string 20 | tr A-Z a-z) - run_podman load -i $archive $random_name - verify_iid_and_name "localhost/$random_name:latest" - - # Load with NAME:TAG arg - run_podman rmi $iid - random_tag=$(random_string 10 | tr A-Z a-z) - run_podman load -i $archive $random_name:$random_tag - verify_iid_and_name "localhost/$random_name:$random_tag" - - # Cleanup: restore desired image name - run_podman tag $iid $img_name - run_podman rmi "$random_name:$random_tag" -} - - @test "podman load - will not read from tty" { if [ ! -t 0 ]; then skip "STDIN is not a tty" diff --git a/test/system/400-unprivileged-access.bats b/test/system/400-unprivileged-access.bats index 20fdd068f..6a89247e6 100644 --- a/test/system/400-unprivileged-access.bats +++ b/test/system/400-unprivileged-access.bats @@ -75,7 +75,7 @@ EOF is "$output" "/var/lib/containers/storage" "GraphRoot in expected place" GRAPH_ROOT="$output" run_podman info --format '{{.Store.RunRoot}}' - is "$output" "/var/run/containers/storage" "RunRoot in expected place" + is "$output" ".*/run/containers/storage" "RunRoot in expected place" RUN_ROOT="$output" # The main test: find all world-writable files or directories underneath diff --git a/test/testvol/main.go b/test/testvol/main.go new file mode 100644 index 000000000..14f253aa7 --- /dev/null +++ b/test/testvol/main.go @@ -0,0 +1,309 @@ +package main + +import ( + "io/ioutil" + "os" + "path/filepath" + "sync" + "time" + + "github.com/docker/go-plugins-helpers/volume" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +var rootCmd = &cobra.Command{ + Use: "testvol", + Short: "testvol - volume plugin for Podman", + Long: `Creates simple directory volumes using the Volume Plugin API for testing volume plugin functionality`, + RunE: func(cmd *cobra.Command, args []string) error { + return startServer(config.sockName) + }, + PersistentPreRunE: before, +} + +// Configuration for the volume plugin +type cliConfig struct { + logLevel string + sockName string + path string +} + +// Default configuration is stored here. Will be overwritten by flags. +var config cliConfig = cliConfig{ + logLevel: "error", + sockName: "test-volume-plugin", +} + +func init() { + rootCmd.Flags().StringVar(&config.sockName, "sock-name", config.sockName, "Name of unix socket for plugin") + rootCmd.Flags().StringVar(&config.path, "path", "", "Path to initialize state and mount points") + rootCmd.PersistentFlags().StringVar(&config.logLevel, "log-level", config.logLevel, "Log messages including and over the specified level: debug, info, warn, error, fatal, panic") +} + +func before(cmd *cobra.Command, args []string) error { + if config.logLevel == "" { + config.logLevel = "error" + } + + level, err := logrus.ParseLevel(config.logLevel) + if err != nil { + return err + } + + logrus.SetLevel(level) + + return nil +} + +func main() { + if err := rootCmd.Execute(); err != nil { + logrus.Errorf("Error running volume plugin: %v", err) + os.Exit(1) + } + + os.Exit(0) +} + +// startServer runs the HTTP server and responds to requests +func startServer(socketPath string) error { + logrus.Debugf("Starting server...") + + if config.path == "" { + path, err := ioutil.TempDir("", "test_volume_plugin") + if err != nil { + return errors.Wrapf(err, "error getting directory for plugin") + } + config.path = path + } else { + pathStat, err := os.Stat(config.path) + if err != nil { + return errors.Wrapf(err, "unable to access requested plugin state directory") + } + if !pathStat.IsDir() { + return errors.Errorf("cannot use %v as plugin state dir as it is not a directory", config.path) + } + } + + handle, err := makeDirDriver(config.path) + if err != nil { + return errors.Wrapf(err, "error making volume driver") + } + logrus.Infof("Using %s for volume path", config.path) + + server := volume.NewHandler(handle) + if err := server.ServeUnix(socketPath, 0); err != nil { + return errors.Wrapf(err, "error starting server") + } + return nil +} + +// DirDriver is a trivial volume driver implementation. +// the volumes field maps name to volume +type DirDriver struct { + lock sync.Mutex + volumesPath string + volumes map[string]*dirVol +} + +type dirVol struct { + name string + path string + options map[string]string + mounts map[string]bool + createTime time.Time +} + +// Make a new DirDriver. +func makeDirDriver(path string) (volume.Driver, error) { + drv := new(DirDriver) + drv.volumesPath = path + drv.volumes = make(map[string]*dirVol) + + return drv, nil +} + +// Capabilities returns the capabilities of the driver. +func (d *DirDriver) Capabilities() *volume.CapabilitiesResponse { + logrus.Infof("Hit Capabilities() endpoint") + + return &volume.CapabilitiesResponse{ + volume.Capability{ + "local", + }, + } +} + +// Create creates a volume. +func (d *DirDriver) Create(opts *volume.CreateRequest) error { + d.lock.Lock() + defer d.lock.Unlock() + + logrus.Infof("Hit Create() endpoint") + + if _, exists := d.volumes[opts.Name]; exists { + return errors.Errorf("volume with name %s already exists", opts.Name) + } + + newVol := new(dirVol) + newVol.name = opts.Name + newVol.mounts = make(map[string]bool) + newVol.options = make(map[string]string) + newVol.createTime = time.Now() + for k, v := range opts.Options { + newVol.options[k] = v + } + + volPath := filepath.Join(d.volumesPath, opts.Name) + if err := os.Mkdir(volPath, 0755); err != nil { + return errors.Wrapf(err, "error making volume directory") + } + newVol.path = volPath + + d.volumes[opts.Name] = newVol + + logrus.Debugf("Made volume with name %s and path %s", newVol.name, newVol.path) + + return nil +} + +// List lists all volumes available. +func (d *DirDriver) List() (*volume.ListResponse, error) { + d.lock.Lock() + defer d.lock.Unlock() + + logrus.Infof("Hit List() endpoint") + + vols := new(volume.ListResponse) + vols.Volumes = []*volume.Volume{} + + for _, vol := range d.volumes { + newVol := new(volume.Volume) + newVol.Name = vol.name + newVol.Mountpoint = vol.path + newVol.CreatedAt = vol.createTime.String() + vols.Volumes = append(vols.Volumes, newVol) + logrus.Debugf("Adding volume %s to list response", newVol.Name) + } + + return vols, nil +} + +// Get retrieves a single volume. +func (d *DirDriver) Get(req *volume.GetRequest) (*volume.GetResponse, error) { + d.lock.Lock() + defer d.lock.Unlock() + + logrus.Infof("Hit Get() endpoint") + + vol, exists := d.volumes[req.Name] + if !exists { + logrus.Debugf("Did not find volume %s", req.Name) + return nil, errors.Errorf("no volume with name %s found", req.Name) + } + + logrus.Debugf("Found volume %s", req.Name) + + resp := new(volume.GetResponse) + resp.Volume = new(volume.Volume) + resp.Volume.Name = vol.name + resp.Volume.Mountpoint = vol.path + resp.Volume.CreatedAt = vol.createTime.String() + + return resp, nil +} + +// Remove removes a single volume. +func (d *DirDriver) Remove(req *volume.RemoveRequest) error { + d.lock.Lock() + defer d.lock.Unlock() + + logrus.Infof("Hit Remove() endpoint") + + vol, exists := d.volumes[req.Name] + if !exists { + logrus.Debugf("Did not find volume %s", req.Name) + return errors.Errorf("no volume with name %s found") + } + logrus.Debugf("Found volume %s", req.Name) + + if len(vol.mounts) > 0 { + logrus.Debugf("Cannot remove %s, is mounted", req.Name) + return errors.Errorf("volume %s is mounted and cannot be removed") + } + + delete(d.volumes, req.Name) + + if err := os.RemoveAll(vol.path); err != nil { + return errors.Wrapf(err, "error removing mountpoint of volume %s", req.Name) + } + + logrus.Debugf("Removed volume %s", req.Name) + + return nil +} + +// Path returns the path a single volume is mounted at. +func (d *DirDriver) Path(req *volume.PathRequest) (*volume.PathResponse, error) { + d.lock.Lock() + defer d.lock.Unlock() + + logrus.Infof("Hit Path() endpoint") + + // TODO: Should we return error if not mounted? + + vol, exists := d.volumes[req.Name] + if !exists { + logrus.Debugf("Cannot locate volume %s", req.Name) + return nil, errors.Errorf("no volume with name %s found", req.Name) + } + + return &volume.PathResponse{ + vol.path, + }, nil +} + +// Mount mounts the volume. +func (d *DirDriver) Mount(req *volume.MountRequest) (*volume.MountResponse, error) { + d.lock.Lock() + defer d.lock.Unlock() + + logrus.Infof("Hit Mount() endpoint") + + vol, exists := d.volumes[req.Name] + if !exists { + logrus.Debugf("Cannot locate volume %s", req.Name) + return nil, errors.Errorf("no volume with name %s found", req.Name) + } + + vol.mounts[req.ID] = true + + return &volume.MountResponse{ + vol.path, + }, nil +} + +// Unmount unmounts the volume. +func (d *DirDriver) Unmount(req *volume.UnmountRequest) error { + d.lock.Lock() + defer d.lock.Unlock() + + logrus.Infof("Hit Unmount() endpoint") + + vol, exists := d.volumes[req.Name] + if !exists { + logrus.Debugf("Cannot locate volume %s", req.Name) + return errors.Errorf("no volume with name %s found", req.Name) + } + + mount := vol.mounts[req.ID] + if !mount { + logrus.Debugf("Volume %s is not mounted by %s", req.Name, req.ID) + return errors.Errorf("volume %s is not mounted by %s", req.Name, req.ID) + } + + delete(vol.mounts, req.ID) + + return nil +} diff --git a/troubleshooting.md b/troubleshooting.md index 78e22fa2f..c028a7714 100644 --- a/troubleshooting.md +++ b/troubleshooting.md @@ -344,7 +344,7 @@ After creating a container with Podman's storage directories mounted in from the When running Podman inside a container, it is recommended to mount at a minimum `/var/lib/containers/storage/` as a volume. Typically, you will not mount in the host version of the directory, but if you wish to share containers with the host, you can do so. -If you do mount in the host's `/var/lib/containers/storage`, however, you must also mount in the host's `/var/run/libpod` and `/var/run/containers/storage` directories. +If you do mount in the host's `/var/lib/containers/storage`, however, you must also mount in the host's `/run/libpod` and `/run/containers/storage` directories. Not doing this will cause Podman in the container to detect that temporary files have been cleared, leading it to assume a system restart has taken place. This can cause Podman to reset container states and lose track of running containers. diff --git a/vendor/github.com/containers/buildah/.cirrus.yml b/vendor/github.com/containers/buildah/.cirrus.yml index 589de9c61..ed2fac11c 100644 --- a/vendor/github.com/containers/buildah/.cirrus.yml +++ b/vendor/github.com/containers/buildah/.cirrus.yml @@ -6,7 +6,7 @@ env: #### Global variables used for all tasks #### # Name of the ultimate destination branch for this CI run, PR or post-merge. - DEST_BRANCH: "master" + DEST_BRANCH: "release-1.19" GOPATH: "/var/tmp/go" GOSRC: "${GOPATH}/src/github.com/containers/buildah" # Overrides default location (/tmp/cirrus) for repo clone @@ -196,6 +196,7 @@ gce_instance: buildah_version_script: '$GOSRC/$SCRIPT_BASE/logcollector.sh buildah_version' buildah_info_script: '$GOSRC/$SCRIPT_BASE/logcollector.sh buildah_info' package_versions_script: '$GOSRC/$SCRIPT_BASE/logcollector.sh packages' + golang_version_script: '$GOSRC/$SCRIPT_BASE/logcollector.sh golang' 'cirrus-ci/required/in_podman_task': diff --git a/vendor/github.com/containers/buildah/CHANGELOG.md b/vendor/github.com/containers/buildah/CHANGELOG.md index f37f387e7..25f02db19 100644 --- a/vendor/github.com/containers/buildah/CHANGELOG.md +++ b/vendor/github.com/containers/buildah/CHANGELOG.md @@ -2,6 +2,65 @@ # Changelog +## v1.19.0 (2021-01-08) + Update vendor of containers/storage and containers/common + Buildah inspect should be able to inspect manifests + Make buildah push support pushing manifests lists and digests + Fix handling of TMPDIR environment variable + Add support for --manifest flags + Upper directory should match mode of destination directory + Only grab the OS, Arch if the user actually specified them + Use --arch and --os and --variant options to select architecture and os + Cirrus: Track libseccomp and golang version + copier.PutOptions: add an "IgnoreDevices" flag + fix: `rmi --prune` when parent image is in store. + build(deps): bump github.com/containers/storage from 1.24.3 to 1.24.4 + build(deps): bump github.com/containers/common from 0.31.1 to 0.31.2 + Allow users to specify stdin into containers + Drop log message on failure to mount on /sys file systems to info + Spelling + SELinux no longer requires a tag. + build(deps): bump github.com/opencontainers/selinux from 1.6.0 to 1.8.0 + build(deps): bump github.com/containers/common from 0.31.0 to 0.31.1 + Update nix pin with `make nixpkgs` + Switch references of /var/run -> /run + Allow FROM to be overriden with from option + copier: don't assume we can chroot() on Unixy systems + copier: add PutOptions.NoOverwriteDirNonDir, Get/PutOptions.Rename + copier: handle replacing directories with not-directories + copier: Put: skip entries with zero-length names + build(deps): bump github.com/containers/storage from 1.24.2 to 1.24.3 + Add U volume flag to chown source volumes + Turn off PRIOR_UBUNTU Test until vm is updated + pkg, cli: rootless uses correct isolation + build(deps): bump github.com/onsi/gomega from 1.10.3 to 1.10.4 + update installation doc to reflect current status + Move away from using docker.io + enable short-name aliasing + build(deps): bump github.com/containers/storage from 1.24.1 to 1.24.2 + build(deps): bump github.com/containers/common from 0.30.0 to 0.31.0 + Throw errors when using bogus --network flags + pkg/supplemented test: replace our null blobinfocache + build(deps): bump github.com/containers/common from 0.29.0 to 0.30.0 + inserts forgotten quotation mark + Not prefer use local image create/add manifest + Add container information to .containerenv + Add --ignorefile flag to use alternate .dockerignore flags + Add a source debug build + Fix crash on invalid filter commands + build(deps): bump github.com/containers/common from 0.27.0 to 0.29.0 + Switch to using containers/common pkg's + fix: non-portable shebang #2812 + Remove copy/paste errors that leaked `Podman` into man pages. + Add suggests cpp to spec file + Apply suggestions from code review + update docs for debian testing and unstable + imagebuildah: disable pseudo-terminals for RUN + Compute diffID for mapped-layer at creating image source + intermediateImageExists: ignore images whose history we can't read + Bump to v1.19.0-dev + build(deps): bump github.com/containers/common from 0.26.3 to 0.27.0 + ## v1.18.0 (2020-11-16) Fix testing error caused by simultanious merge Vendor in containers/storage v1.24.0 diff --git a/vendor/github.com/containers/buildah/CONTRIBUTING.md b/vendor/github.com/containers/buildah/CONTRIBUTING.md index e5171f8a4..57f82191e 100644 --- a/vendor/github.com/containers/buildah/CONTRIBUTING.md +++ b/vendor/github.com/containers/buildah/CONTRIBUTING.md @@ -37,7 +37,7 @@ Once you have decided to contribute to Buildah by working on an issue, check our backlog of [open issues](https://github.com/containers/buildah/issues) looking for any that do not have an "In Progress" label attached to it. Often issues will be assigned to someone, to be worked on at a later time. If you have the -time to work on the issue now add yourself as an assignee, and set the +time to work on the issue now, add yourself as an assignee, and set the "In Progress" label if you’re a member of the “Containers” GitHub organization. If you can not set the label, just add a quick comment in the issue asking that the “In Progress” label be set and a member will do so for you. diff --git a/vendor/github.com/containers/buildah/Makefile b/vendor/github.com/containers/buildah/Makefile index 45f8a8ec8..2768a2917 100644 --- a/vendor/github.com/containers/buildah/Makefile +++ b/vendor/github.com/containers/buildah/Makefile @@ -1,9 +1,8 @@ export GOPROXY=https://proxy.golang.org -SELINUXTAG := $(shell ./selinux_tag.sh) APPARMORTAG := $(shell hack/apparmor_tag.sh) STORAGETAGS := $(shell ./btrfs_tag.sh) $(shell ./btrfs_installed_tag.sh) $(shell ./libdm_tag.sh) -SECURITYTAGS ?= seccomp $(SELINUXTAG) $(APPARMORTAG) +SECURITYTAGS ?= seccomp $(APPARMORTAG) TAGS ?= $(SECURITYTAGS) $(STORAGETAGS) BUILDTAGS += $(TAGS) PREFIX := /usr/local @@ -52,7 +51,7 @@ all: bin/buildah bin/imgtype docs # Update nix/nixpkgs.json its latest stable commit .PHONY: nixpkgs nixpkgs: - @nix run -f channel:nixos-20.03 nix-prefetch-git -c nix-prefetch-git \ + @nix run -f channel:nixos-20.09 nix-prefetch-git -c nix-prefetch-git \ --no-deepClone https://github.com/nixos/nixpkgs > nix/nixpkgs.json # Build statically linked binary diff --git a/vendor/github.com/containers/buildah/add.go b/vendor/github.com/containers/buildah/add.go index 6cfd6a09f..dd69d45cf 100644 --- a/vendor/github.com/containers/buildah/add.go +++ b/vendor/github.com/containers/buildah/add.go @@ -20,6 +20,7 @@ import ( "github.com/containers/storage/pkg/fileutils" "github.com/containers/storage/pkg/idtools" "github.com/hashicorp/go-multierror" + rsystem "github.com/opencontainers/runc/libcontainer/system" "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -347,12 +348,13 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption _, putErr = io.Copy(hasher, pipeReader) } else { putOptions := copier.PutOptions{ - UIDMap: destUIDMap, - GIDMap: destGIDMap, - ChownDirs: nil, - ChmodDirs: nil, - ChownFiles: nil, - ChmodFiles: nil, + UIDMap: destUIDMap, + GIDMap: destGIDMap, + ChownDirs: nil, + ChmodDirs: nil, + ChownFiles: nil, + ChmodFiles: nil, + IgnoreDevices: rsystem.RunningInUserNS(), } putErr = copier.Put(mountPoint, extractDirectory, putOptions, io.TeeReader(pipeReader, hasher)) } @@ -482,6 +484,7 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption ChmodDirs: nil, ChownFiles: nil, ChmodFiles: nil, + IgnoreDevices: rsystem.RunningInUserNS(), } putErr = copier.Put(mountPoint, extractDirectory, putOptions, io.TeeReader(pipeReader, hasher)) } diff --git a/vendor/github.com/containers/buildah/buildah.go b/vendor/github.com/containers/buildah/buildah.go index 10e3f17ed..89fc860dd 100644 --- a/vendor/github.com/containers/buildah/buildah.go +++ b/vendor/github.com/containers/buildah/buildah.go @@ -28,7 +28,7 @@ const ( Package = "buildah" // Version for the Package. Bump version in contrib/rpm/buildah.spec // too. - Version = "1.19.0-dev" + Version = "1.19.2" // 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 f59d426ae..ce2f2696f 100644 --- a/vendor/github.com/containers/buildah/changelog.txt +++ b/vendor/github.com/containers/buildah/changelog.txt @@ -1,3 +1,62 @@ +- Changelog for v1.19.0 (2021-01-08) + * Update vendor of containers/storage and containers/common + * Buildah inspect should be able to inspect manifests + * Make buildah push support pushing manifests lists and digests + * Fix handling of TMPDIR environment variable + * Add support for --manifest flags + * Upper directory should match mode of destination directory + * Only grab the OS, Arch if the user actually specified them + * Use --arch and --os and --variant options to select architecture and os + * Cirrus: Track libseccomp and golang version + * copier.PutOptions: add an "IgnoreDevices" flag + * fix: `rmi --prune` when parent image is in store. + * build(deps): bump github.com/containers/storage from 1.24.3 to 1.24.4 + * build(deps): bump github.com/containers/common from 0.31.1 to 0.31.2 + * Allow users to specify stdin into containers + * Drop log message on failure to mount on /sys file systems to info + * Spelling + * SELinux no longer requires a tag. + * build(deps): bump github.com/opencontainers/selinux from 1.6.0 to 1.8.0 + * build(deps): bump github.com/containers/common from 0.31.0 to 0.31.1 + * Update nix pin with `make nixpkgs` + * Switch references of /var/run -> /run + * Allow FROM to be overriden with from option + * copier: don't assume we can chroot() on Unixy systems + * copier: add PutOptions.NoOverwriteDirNonDir, Get/PutOptions.Rename + * copier: handle replacing directories with not-directories + * copier: Put: skip entries with zero-length names + * build(deps): bump github.com/containers/storage from 1.24.2 to 1.24.3 + * Add U volume flag to chown source volumes + * Turn off PRIOR_UBUNTU Test until vm is updated + * pkg, cli: rootless uses correct isolation + * build(deps): bump github.com/onsi/gomega from 1.10.3 to 1.10.4 + * update installation doc to reflect current status + * Move away from using docker.io + * enable short-name aliasing + * build(deps): bump github.com/containers/storage from 1.24.1 to 1.24.2 + * build(deps): bump github.com/containers/common from 0.30.0 to 0.31.0 + * Throw errors when using bogus --network flags + * pkg/supplemented test: replace our null blobinfocache + * build(deps): bump github.com/containers/common from 0.29.0 to 0.30.0 + * inserts forgotten quotation mark + * Not prefer use local image create/add manifest + * Add container information to .containerenv + * Add --ignorefile flag to use alternate .dockerignore flags + * Add a source debug build + * Fix crash on invalid filter commands + * build(deps): bump github.com/containers/common from 0.27.0 to 0.29.0 + * Switch to using containers/common pkg's + * fix: non-portable shebang #2812 + * Remove copy/paste errors that leaked `Podman` into man pages. + * Add suggests cpp to spec file + * Apply suggestions from code review + * update docs for debian testing and unstable + * imagebuildah: disable pseudo-terminals for RUN + * Compute diffID for mapped-layer at creating image source + * intermediateImageExists: ignore images whose history we can't read + * Bump to v1.19.0-dev + * build(deps): bump github.com/containers/common from 0.26.3 to 0.27.0 + - Changelog for v1.18.0 (2020-11-16) * Fix testing error caused by simultanious merge * Vendor in containers/storage v1.24.0 diff --git a/vendor/github.com/containers/buildah/chroot/run.go b/vendor/github.com/containers/buildah/chroot/run.go index e8842f7a9..39ad88b2b 100644 --- a/vendor/github.com/containers/buildah/chroot/run.go +++ b/vendor/github.com/containers/buildah/chroot/run.go @@ -1107,7 +1107,12 @@ func setupChrootBindMounts(spec *specs.Spec, bundlePath string) (undoBinds func( } subSys := filepath.Join(spec.Root.Path, m.Mountpoint) if err := unix.Mount(m.Mountpoint, subSys, "bind", sysFlags, ""); err != nil { - logrus.Warningf("could not bind mount %q, skipping: %v", m.Mountpoint, err) + msg := fmt.Sprintf("could not bind mount %q, skipping: %v", m.Mountpoint, err) + if strings.HasPrefix(m.Mountpoint, "/sys") { + logrus.Infof(msg) + } else { + logrus.Warningf(msg) + } continue } if err := makeReadOnly(subSys, sysFlags); err != nil { diff --git a/vendor/github.com/containers/buildah/chroot/selinux.go b/vendor/github.com/containers/buildah/chroot/selinux.go index 08e8f998b..ef96a0e7a 100644 --- a/vendor/github.com/containers/buildah/chroot/selinux.go +++ b/vendor/github.com/containers/buildah/chroot/selinux.go @@ -1,4 +1,4 @@ -// +build linux,selinux +// +build linux package chroot diff --git a/vendor/github.com/containers/buildah/chroot/selinux_unsupported.go b/vendor/github.com/containers/buildah/chroot/selinux_unsupported.go index 1c6f48912..41d2b86be 100644 --- a/vendor/github.com/containers/buildah/chroot/selinux_unsupported.go +++ b/vendor/github.com/containers/buildah/chroot/selinux_unsupported.go @@ -1,4 +1,4 @@ -// +build !linux !selinux +// +build !linux package chroot diff --git a/vendor/github.com/containers/buildah/commit.go b/vendor/github.com/containers/buildah/commit.go index 7a57021aa..9c6831601 100644 --- a/vendor/github.com/containers/buildah/commit.go +++ b/vendor/github.com/containers/buildah/commit.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "github.com/containers/buildah/manifests" "github.com/containers/buildah/pkg/blobcache" "github.com/containers/buildah/util" "github.com/containers/image/v5/docker" @@ -18,6 +19,7 @@ import ( "github.com/containers/image/v5/signature" is "github.com/containers/image/v5/storage" "github.com/containers/image/v5/transports" + "github.com/containers/image/v5/transports/alltransports" "github.com/containers/image/v5/types" encconfig "github.com/containers/ocicrypt/config" "github.com/containers/storage" @@ -83,6 +85,8 @@ type CommitOptions struct { OmitTimestamp bool // SignBy is the fingerprint of a GPG key to use for signing the image. SignBy string + // Manifest list to add the image to. + Manifest string // MaxRetries is the maximum number of attempts we'll make to commit // the image to an external registry if the first attempt fails. MaxRetries int @@ -220,12 +224,59 @@ func checkRegistrySourcesAllows(forWhat string, dest types.ImageReference) (inse return false, nil } +func (b *Builder) addManifest(ctx context.Context, manifestName string, imageSpec string) error { + var create bool + systemContext := &types.SystemContext{} + var list manifests.List + _, listImage, err := util.FindImage(b.store, "", systemContext, manifestName) + if err != nil { + create = true + list = manifests.Create() + } else { + _, list, err = manifests.LoadFromImage(b.store, listImage.ID) + if err != nil { + return err + } + } + + names, err := util.ExpandNames([]string{manifestName}, "", systemContext, b.store) + if err != nil { + return errors.Wrapf(err, "error encountered while expanding image name %q", manifestName) + } + + ref, err := alltransports.ParseImageName(imageSpec) + if err != nil { + if ref, err = alltransports.ParseImageName(util.DefaultTransport + imageSpec); err != nil { + // check if the local image exists + if ref, _, err = util.FindImage(b.store, "", systemContext, imageSpec); err != nil { + return err + } + } + } + + if _, err = list.Add(ctx, systemContext, ref, true); err != nil { + return err + } + var imageID string + if create { + imageID, err = list.SaveToImage(b.store, "", names, manifest.DockerV2ListMediaType) + } else { + imageID, err = list.SaveToImage(b.store, listImage.ID, nil, "") + } + if err == nil { + fmt.Printf("%s\n", imageID) + } + return err +} + // Commit writes the contents of the container, along with its updated // configuration, to a new image in the specified location, and if we know how, // add any additional tags that were specified. Returns the ID of the new image // if commit was successful and the image destination was local. func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options CommitOptions) (string, reference.Canonical, digest.Digest, error) { - var imgID string + var ( + imgID string + ) // If we weren't given a name, build a destination reference using a // temporary name that we'll remove later. The correct thing to do @@ -437,6 +488,11 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options } } + if options.Manifest != "" { + if err := b.addManifest(ctx, options.Manifest, imgID); err != nil { + return imgID, nil, "", err + } + } return imgID, ref, manifestDigest, nil } diff --git a/vendor/github.com/containers/buildah/config.go b/vendor/github.com/containers/buildah/config.go index 08beeb407..9bed6542f 100644 --- a/vendor/github.com/containers/buildah/config.go +++ b/vendor/github.com/containers/buildah/config.go @@ -529,7 +529,7 @@ func (b *Builder) SetHistoryComment(comment string) { } // StopSignal returns the signal which will be set in the container and in -// containers built using images buiilt from the container +// containers built using images built from the container func (b *Builder) StopSignal() string { return b.Docker.Config.StopSignal } diff --git a/vendor/github.com/containers/buildah/copier/copier.go b/vendor/github.com/containers/buildah/copier/copier.go index ef0e4778d..63cdb1974 100644 --- a/vendor/github.com/containers/buildah/copier/copier.go +++ b/vendor/github.com/containers/buildah/copier/copier.go @@ -289,6 +289,7 @@ type PutOptions struct { ChmodFiles *os.FileMode // set permissions on newly-created files StripXattrs bool // don't bother trying to set extended attributes of items being copied IgnoreXattrErrors bool // ignore any errors encountered when attempting to set extended attributes + IgnoreDevices bool // ignore items which are character or block devices NoOverwriteDirNonDir bool // instead of quietly overwriting directories with non-directories, return an error Rename map[string]string // rename items with the specified names, or under the specified names } @@ -364,7 +365,7 @@ func cleanerReldirectory(candidate string) string { return cleaned } -// convertToRelSubirectory returns the path of directory, bound and relative to +// convertToRelSubdirectory returns the path of directory, bound and relative to // root, as a relative path, or an error if that path can't be computed or if // the two directories are on different volumes func convertToRelSubdirectory(root, directory string) (relative string, err error) { @@ -1402,11 +1403,14 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM } } }() + ignoredItems := make(map[string]struct{}) tr := tar.NewReader(bulkReader) hdr, err := tr.Next() for err == nil { + nameBeforeRenaming := hdr.Name if len(hdr.Name) == 0 { // no name -> ignore the entry + ignoredItems[nameBeforeRenaming] = struct{}{} hdr, err = tr.Next() continue } @@ -1465,6 +1469,11 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM } case tar.TypeLink: var linkTarget string + if _, ignoredTarget := ignoredItems[hdr.Linkname]; ignoredTarget { + // hard link to an ignored item: skip this, too + ignoredItems[nameBeforeRenaming] = struct{}{} + goto nextHeader + } if req.PutOptions.Rename != nil { hdr.Linkname = handleRename(req.PutOptions.Rename, hdr.Linkname) } @@ -1497,6 +1506,10 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM } } case tar.TypeChar: + if req.PutOptions.IgnoreDevices { + ignoredItems[nameBeforeRenaming] = struct{}{} + goto nextHeader + } if err = mknod(path, chrMode(0600), int(mkdev(devMajor, devMinor))); err != nil && os.IsExist(err) { if req.PutOptions.NoOverwriteDirNonDir { if st, err := os.Lstat(path); err == nil && st.IsDir() { @@ -1508,6 +1521,10 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM } } case tar.TypeBlock: + if req.PutOptions.IgnoreDevices { + ignoredItems[nameBeforeRenaming] = struct{}{} + goto nextHeader + } if err = mknod(path, blkMode(0600), int(mkdev(devMajor, devMinor))); err != nil && os.IsExist(err) { if req.PutOptions.NoOverwriteDirNonDir { if st, err := os.Lstat(path); err == nil && st.IsDir() { diff --git a/vendor/github.com/containers/buildah/go.mod b/vendor/github.com/containers/buildah/go.mod index ea9a956be..135926116 100644 --- a/vendor/github.com/containers/buildah/go.mod +++ b/vendor/github.com/containers/buildah/go.mod @@ -5,10 +5,10 @@ go 1.12 require ( github.com/containerd/containerd v1.4.1 // indirect github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784 - github.com/containers/common v0.31.0 - github.com/containers/image/v5 v5.8.1 + github.com/containers/common v0.33.1 + github.com/containers/image/v5 v5.9.0 github.com/containers/ocicrypt v1.0.3 - github.com/containers/storage v1.24.3 + github.com/containers/storage v1.24.5 github.com/docker/distribution v2.7.1+incompatible github.com/docker/go-units v0.4.0 github.com/docker/libnetwork v0.8.0-dev.2.0.20190625141545-5a177b73e316 @@ -16,7 +16,6 @@ require ( github.com/ghodss/yaml v1.0.0 github.com/hashicorp/go-multierror v1.1.0 github.com/ishidawataru/sctp v0.0.0-20191218070446-00ab2ac2db07 // indirect - github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect github.com/mattn/go-shellwords v1.0.10 github.com/moby/sys/mount v0.1.1 // indirect github.com/moby/term v0.0.0-20200915141129-7f0af18e79f2 // indirect @@ -27,7 +26,7 @@ require ( github.com/opencontainers/runc v1.0.0-rc91 github.com/opencontainers/runtime-spec v1.0.3-0.20200710190001-3e4195d92445 github.com/opencontainers/runtime-tools v0.9.0 - github.com/opencontainers/selinux v1.6.0 + github.com/opencontainers/selinux v1.8.0 github.com/openshift/imagebuilder v1.1.8 github.com/pkg/errors v0.9.1 github.com/seccomp/libseccomp-golang v0.9.2-0.20200616122406-847368b35ebf @@ -39,7 +38,7 @@ require ( go.etcd.io/bbolt v1.3.5 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a - golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13 + golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3 gotest.tools/v3 v3.0.3 // indirect k8s.io/klog v1.0.0 // indirect ) diff --git a/vendor/github.com/containers/buildah/go.sum b/vendor/github.com/containers/buildah/go.sum index c2082c5ef..6a5f70a36 100644 --- a/vendor/github.com/containers/buildah/go.sum +++ b/vendor/github.com/containers/buildah/go.sum @@ -22,10 +22,12 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/go-winio v0.4.15-0.20200113171025-3fe6c5262873 h1:93nQ7k53GjoMQ07HVP8g6Zj1fQZDDj7Xy2VkNNtvX8o= github.com/Microsoft/go-winio v0.4.15-0.20200113171025-3fe6c5262873/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= -github.com/Microsoft/go-winio v0.4.15 h1:qkLXKzb1QoVatRyd/YlXZ/Kg0m5K3SPuoD82jjSOaBc= -github.com/Microsoft/go-winio v0.4.15/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= +github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331 h1:3YnB7Hpmh1lPecPE8doMOtYCrMdrpedZOvxfuNES/Vk= +github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/Microsoft/hcsshim v0.8.9 h1:VrfodqvztU8YSOvygU+DN1BGaSGxmrNfqOv5oOuX2Bk= github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= +github.com/Microsoft/hcsshim v0.8.14 h1:lbPVK25c1cu5xTLITwpUcxoA9vKrKErASPYygvouJns= +github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= @@ -53,11 +55,14 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5O github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= github.com/cilium/ebpf v0.0.0-20200507155900-a9f01edf17e3/go.mod h1:XT+cAw5wfvsodedcijoh1l9cf7v1x9FlFB/3VmF/O8s= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f h1:tSNMc+rJDfmYntojat8lljbt1mgKNpTxUZJsSzJ9Y1s= github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= +github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59 h1:qWj4qVYZ95vLWwqyNJCQg7rDsG5wPdze0UaPolH7DUk= +github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/console v1.0.0/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= @@ -73,19 +78,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.31.0 h1:SRnjfoqbjfaojpY9YJq9JBPEslwB5hoXJbaE+5zMFwM= -github.com/containers/common v0.31.0/go.mod h1:yT4GTUHsKRmpaDb+mecXRnIMre7W3ZgwXqaYMywXlaA= -github.com/containers/image/v5 v5.8.1 h1:aHW8a/Kd0dTJ7PTL/fc6y12sJqHxWgqilu+XyHfjD8Q= -github.com/containers/image/v5 v5.8.1/go.mod h1:blOEFd/iFdeyh891ByhCVUc+xAcaI3gBegXECwz9UbQ= +github.com/containers/common v0.33.1 h1:XpDiq8Cta8+u1s4kpYSEWdB140ZmqgyIXfWkLqKx3z0= +github.com/containers/common v0.33.1/go.mod h1:mjDo/NKeweL/onaspLhZ38WnHXaYmrELHclIdvSnYpY= +github.com/containers/image/v5 v5.9.0 h1:dRmUtcluQcmasNo3DpnRoZjfU0rOu1qZeL6wlDJr10Q= +github.com/containers/image/v5 v5.9.0/go.mod h1:blOEFd/iFdeyh891ByhCVUc+xAcaI3gBegXECwz9UbQ= 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 v1.0.3 h1:vYgl+RZ9Q3DPMuTfxmN+qp0X2Bj52uuY2vnt6GzVe1c= github.com/containers/ocicrypt v1.0.3/go.mod h1:CUBa+8MRNL/VkpxYIpaMtgn1WgXGyvPQj8jcy0EVG6g= github.com/containers/storage v1.23.7/go.mod h1:cUT2zHjtx+WlVri30obWmM2gpqpi8jfPsmIzP1TVpEI= -github.com/containers/storage v1.24.1 h1:1+f8fy6ly35c8SLet5jzZ8t0WJJs5+xSpfMAYw0R3kc= -github.com/containers/storage v1.24.1/go.mod h1:0xJL06Dmd+ZYXIUdnBUPN0JnhHGgwMkLvnnAonJfWJU= -github.com/containers/storage v1.24.3 h1:8UB4S62l4hrU6Yw3dbsLCJtLg7Ofo39IN2HdckBIX4E= -github.com/containers/storage v1.24.3/go.mod h1:0xJL06Dmd+ZYXIUdnBUPN0JnhHGgwMkLvnnAonJfWJU= +github.com/containers/storage v1.24.5 h1:BusfdU0rCS2/Daa/DPw+0iLfGRlYA7UVF7D0el3N7Vk= +github.com/containers/storage v1.24.5/go.mod h1:YC+2pY8SkfEAcZkwycxYbpK8EiRbx5soPPwz9dxe4IQ= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -233,6 +236,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/compress v1.11.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.3 h1:dB4Bn0tN3wdCzQxnS8r06kV74qN/TAfaIS0bVE8h3jc= github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.5 h1:xNCE0uE6yvTPRS+0wGNMHPo3NIpwnk6aluQZ6R6kRcc= +github.com/klauspost/compress v1.11.5/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= @@ -303,8 +308,6 @@ github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9k github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA= -github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/onsi/gomega v1.10.4 h1:NiTx7EEvBzu9sFOD1zORteLSt3o8gnlvZZwSE9TnY9U= github.com/onsi/gomega v1.10.4/go.mod h1:g/HbgYopi++010VEqkFgJHKC09uJiW9UkXvMUuKHUCQ= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -319,6 +322,7 @@ github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59P github.com/opencontainers/runc v1.0.0-rc91 h1:Tp8LWs5G8rFpzTsbRjAtQkPVexhCu0bnANE5IfIhJ6g= github.com/opencontainers/runc v1.0.0-rc91/go.mod h1:3Sm6Dt7OT8z88EbdQqqcRN2oCT54jbi72tT/HqgflT8= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20200710190001-3e4195d92445 h1:y8cfsJRmn8g3VkM4IDpusKSgMUZEXhudm/BuYANLozE= github.com/opencontainers/runtime-spec v1.0.3-0.20200710190001-3e4195d92445/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= @@ -327,6 +331,8 @@ github.com/opencontainers/runtime-tools v0.9.0/go.mod h1:r3f7wjNzSs2extwzU3Y+6pK github.com/opencontainers/selinux v1.5.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g= github.com/opencontainers/selinux v1.6.0 h1:+bIAS/Za3q5FTwWym4fTB0vObnfCf3G/NC7K6Jx62mY= github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= +github.com/opencontainers/selinux v1.8.0 h1:+77ba4ar4jsCbL1GLbFL8fFM57w6suPfSS9PDLDY7KM= +github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= github.com/openshift/imagebuilder v1.1.8 h1:gjiIl8pbNj0eC4XWvFJHATdDvYm64p9/pLDLQWoLZPA= github.com/openshift/imagebuilder v1.1.8/go.mod h1:9aJRczxCH0mvT6XQ+5STAQaPWz7OsWcU5/mRkt8IWeo= github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913 h1:TnbXhKzrTOyuvWrjI8W6pcoI9XPbLHFXCdN2dtUw7Rw= @@ -408,6 +414,7 @@ github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ= github.com/ulikunitz/xz v0.5.8/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/urfave/cli v1.22.2/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/v5 v5.3.0 h1:vgrEJjUzHaSZKDRRxul5Oh4C72Yy/5VEMb0em+9M0mQ= @@ -418,6 +425,8 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7Zo github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243 h1:R43TdZy32XXSXjJn7M/HhALJ9imq6ztLnChfYJpVDnM= github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE= +github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= 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= @@ -484,8 +493,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 h1:wBouT66WTYFXdxfVdz9sVWARVd/2vfGcmI45D2gj45M= -golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -519,8 +526,10 @@ golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/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-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -532,8 +541,8 @@ golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13 h1:5jaG59Zhd+8ZXe8C+lgiAGqkOaZBruqrWclLkgAww34= -golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3 h1:kzM6+9dur93BcC2kVlYl34cHU+TYZLanmpSJHVMmL64= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= diff --git a/vendor/github.com/containers/buildah/imagebuildah/build.go b/vendor/github.com/containers/buildah/imagebuildah/build.go index 1ec21e786..7af3fb8d9 100644 --- a/vendor/github.com/containers/buildah/imagebuildah/build.go +++ b/vendor/github.com/containers/buildah/imagebuildah/build.go @@ -58,6 +58,8 @@ type BuildOptions struct { // IgnoreUnrecognizedInstructions tells us to just log instructions we // don't recognize, and try to keep going. IgnoreUnrecognizedInstructions bool + // Manifest Name to which the image will be added. + Manifest string // Quiet tells us whether or not to announce steps as we go through them. Quiet bool // Isolation controls how Run() runs things. @@ -187,6 +189,9 @@ type BuildOptions struct { LogRusage bool // Excludes is a list of excludes to be used instead of the .dockerignore file. Excludes []string + // From is the image name to use to replace the value specified in the first + // FROM instruction in the Containerfile + From string } // BuildDockerfiles parses a set of one or more Dockerfiles (which may be diff --git a/vendor/github.com/containers/buildah/imagebuildah/executor.go b/vendor/github.com/containers/buildah/imagebuildah/executor.go index 3c41ec1d2..a72e24eea 100644 --- a/vendor/github.com/containers/buildah/imagebuildah/executor.go +++ b/vendor/github.com/containers/buildah/imagebuildah/executor.go @@ -114,6 +114,7 @@ type Executor struct { logRusage bool imageInfoLock sync.Mutex imageInfoCache map[string]imageTypeAndHistoryAndDiffIDs + fromOverride string } type imageTypeAndHistoryAndDiffIDs struct { @@ -229,6 +230,7 @@ func NewExecutor(store storage.Store, options BuildOptions, mainNode *parser.Nod jobs: jobs, logRusage: options.LogRusage, imageInfoCache: make(map[string]imageTypeAndHistoryAndDiffIDs), + fromOverride: options.From, } if exec.err == nil { exec.err = os.Stderr @@ -245,6 +247,7 @@ func NewExecutor(store storage.Store, options BuildOptions, mainNode *parser.Nod fmt.Fprintf(exec.out, prefix+format+suffix, args...) } } + for arg := range options.Args { if _, isBuiltIn := builtinAllowedBuildArgs[arg]; !isBuiltIn { exec.unusedArgs[arg] = struct{}{} @@ -522,6 +525,12 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image switch strings.ToUpper(child.Value) { // first token - instruction case "FROM": if child.Next != nil { // second token on this line + // If we have a fromOverride, replace the value of + // image name for the first FROM in the Containerfile. + if b.fromOverride != "" { + child.Next.Value = b.fromOverride + b.fromOverride = "" + } base := child.Next.Value if base != "scratch" { // TODO: this didn't undergo variable and arg diff --git a/vendor/github.com/containers/buildah/new.go b/vendor/github.com/containers/buildah/new.go index aab17fea2..4d70e0146 100644 --- a/vendor/github.com/containers/buildah/new.go +++ b/vendor/github.com/containers/buildah/new.go @@ -103,27 +103,27 @@ func newContainerIDMappingOptions(idmapOptions *IDMappingOptions) storage.IDMapp return options } -func resolveLocalImage(systemContext *types.SystemContext, store storage.Store, options BuilderOptions) (types.ImageReference, string, *storage.Image, error) { +func resolveLocalImage(systemContext *types.SystemContext, store storage.Store, options BuilderOptions) (types.ImageReference, string, string, *storage.Image, error) { candidates, _, _, err := util.ResolveName(options.FromImage, options.Registry, systemContext, store) if err != nil { - return nil, "", nil, errors.Wrapf(err, "error resolving local image %q", options.FromImage) + return nil, "", "", nil, errors.Wrapf(err, "error resolving local image %q", options.FromImage) } - for _, image := range candidates { - img, err := store.Image(image) + for _, imageName := range candidates { + img, err := store.Image(imageName) if err != nil { if errors.Cause(err) == storage.ErrImageUnknown { continue } - return nil, "", nil, err + return nil, "", "", nil, err } ref, err := is.Transport.ParseStoreReference(store, img.ID) if err != nil { - return nil, "", nil, errors.Wrapf(err, "error parsing reference to image %q", img.ID) + return nil, "", "", nil, errors.Wrapf(err, "error parsing reference to image %q", img.ID) } - return ref, ref.Transport().Name(), img, nil + return ref, ref.Transport().Name(), imageName, img, nil } - return nil, "", nil, nil + return nil, "", "", nil, nil } func resolveImage(ctx context.Context, systemContext *types.SystemContext, store storage.Store, options BuilderOptions) (types.ImageReference, string, *storage.Image, error) { @@ -145,7 +145,7 @@ func resolveImage(ctx context.Context, systemContext *types.SystemContext, store } } - localImageRef, _, localImage, err := resolveLocalImage(systemContext, store, options) + localImageRef, _, localImageName, localImage, err := resolveLocalImage(systemContext, store, options) if err != nil { return nil, "", nil, err } @@ -166,6 +166,12 @@ func resolveImage(ctx context.Context, systemContext *types.SystemContext, store } } + // If we found a local image, we must use it's name. + // See #2904. + if localImageRef != nil { + fromImage = localImageName + } + resolved, err := shortnames.Resolve(systemContext, fromImage) if err != nil { return nil, "", nil, err diff --git a/vendor/github.com/containers/buildah/pkg/cli/common.go b/vendor/github.com/containers/buildah/pkg/cli/common.go index 1e2db58c4..59cb0e5a5 100644 --- a/vendor/github.com/containers/buildah/pkg/cli/common.go +++ b/vendor/github.com/containers/buildah/pkg/cli/common.go @@ -51,7 +51,6 @@ type NameSpaceResults struct { // BudResults represents the results for Bud flags type BudResults struct { Annotation []string - Arch string Authfile string BuildArg []string CacheFrom string @@ -63,14 +62,14 @@ type BudResults struct { IgnoreFile string File []string Format string + From string Iidfile string Label []string Logfile string Loglevel int + Manifest string NoCache bool Timestamp int64 - OS string - Platform string Pull bool PullAlways bool PullNever bool @@ -81,6 +80,7 @@ type BudResults struct { SignaturePolicy string SignBy string Squash bool + Stdin bool Tag []string Target string TLSVerify bool @@ -110,8 +110,6 @@ type FromAndBudResults struct { Isolation string Memory string MemorySwap string - OverrideArch string - OverrideOS string SecurityOpt []string ShmSize string Ulimit []string @@ -177,7 +175,7 @@ func GetLayerFlags(flags *LayerResults) pflag.FlagSet { // GetBudFlags returns common bud flags func GetBudFlags(flags *BudResults) pflag.FlagSet { fs := pflag.FlagSet{} - fs.StringVar(&flags.Arch, "arch", runtime.GOARCH, "set the ARCH of the image to the provided value instead of the architecture of the host") + fs.String("arch", runtime.GOARCH, "set the ARCH of the image to the provided value instead of the architecture of the host") fs.StringArrayVar(&flags.Annotation, "annotation", []string{}, "Set metadata for an image (default [])") fs.StringVar(&flags.Authfile, "authfile", auth.GetDefaultAuthFile(), "path of the authentication file.") fs.StringArrayVar(&flags.BuildArg, "build-arg", []string{}, "`argument=value` to supply to the builder") @@ -187,6 +185,7 @@ func GetBudFlags(flags *BudResults) pflag.FlagSet { fs.StringVar(&flags.Creds, "creds", "", "use `[username[:password]]` for accessing the registry") fs.BoolVarP(&flags.DisableCompression, "disable-compression", "D", true, "don't compress layers by default") fs.BoolVar(&flags.DisableContentTrust, "disable-content-trust", false, "This is a Docker specific option and is a NOOP") + fs.StringVar(&flags.From, "from", "", "image name used to replace the value in the first FROM instruction in the Containerfile") fs.StringVar(&flags.IgnoreFile, "ignorefile", "", "path to an alternate .dockerignore file") fs.StringSliceVarP(&flags.File, "file", "f", []string{}, "`pathname or URL` of a Dockerfile") fs.StringVar(&flags.Format, "format", DefaultFormat(), "`format` of the built image's manifest and metadata. Use BUILDAH_FORMAT environment variable to override.") @@ -199,9 +198,10 @@ func GetBudFlags(flags *BudResults) pflag.FlagSet { if err := fs.MarkHidden("log-rusage"); err != nil { panic(fmt.Sprintf("error marking the log-rusage flag as hidden: %v", err)) } + fs.StringVar(&flags.Manifest, "manifest", "", "add the image to the specified manifest list. Creates manifest if it does not exist") fs.BoolVar(&flags.NoCache, "no-cache", false, "Do not use existing cached images for the container build. Build from the start with a new set of cached layers.") - fs.StringVar(&flags.OS, "os", runtime.GOOS, "set the OS to the provided value instead of the current operating system of the host") - fs.StringVar(&flags.Platform, "platform", parse.DefaultPlatform(), "set the OS/ARCH to the provided value instead of the current operating system and architecture of the host (for example `linux/arm`)") + fs.String("os", runtime.GOOS, "set the OS to the provided value instead of the current operating system of the host") + fs.String("platform", parse.DefaultPlatform(), "set the OS/ARCH to the provided value instead of the current operating system and architecture of the host (for example `linux/arm`)") fs.BoolVar(&flags.Pull, "pull", true, "pull the image from the registry if newer or not present in store, if false, only pull the image if not present") fs.BoolVar(&flags.PullAlways, "pull-always", false, "pull the image even if the named image is present in store") fs.BoolVar(&flags.PullNever, "pull-never", false, "do not pull the image, use the image present in store if available") @@ -215,10 +215,12 @@ func GetBudFlags(flags *BudResults) pflag.FlagSet { panic(fmt.Sprintf("error marking the signature-policy flag as hidden: %v", err)) } fs.BoolVar(&flags.Squash, "squash", false, "squash newly built layers into a single new layer") + fs.BoolVar(&flags.Stdin, "stdin", false, "pass stdin into containers") fs.StringArrayVarP(&flags.Tag, "tag", "t", []string{}, "tagged `name` to apply to the built image") fs.StringVar(&flags.Target, "target", "", "set the target build stage to build") fs.Int64Var(&flags.Timestamp, "timestamp", 0, "set created timestamp to the specified epoch seconds to allow for deterministic builds, defaults to current time") fs.BoolVar(&flags.TLSVerify, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry") + fs.String("variant", "", "override the `variant` of the specified image") return fs } @@ -233,6 +235,7 @@ func GetBudFlagsCompletions() commonComp.FlagCompletions { flagCompletion["cert-dir"] = commonComp.AutocompleteDefault flagCompletion["creds"] = commonComp.AutocompleteNone flagCompletion["file"] = commonComp.AutocompleteDefault + flagCompletion["from"] = commonComp.AutocompleteDefault flagCompletion["format"] = commonComp.AutocompleteNone flagCompletion["ignorefile"] = commonComp.AutocompleteDefault flagCompletion["iidfile"] = commonComp.AutocompleteDefault @@ -240,6 +243,7 @@ func GetBudFlagsCompletions() commonComp.FlagCompletions { flagCompletion["label"] = commonComp.AutocompleteNone flagCompletion["logfile"] = commonComp.AutocompleteDefault flagCompletion["loglevel"] = commonComp.AutocompleteDefault + flagCompletion["manifest"] = commonComp.AutocompleteDefault flagCompletion["os"] = commonComp.AutocompleteNone flagCompletion["platform"] = commonComp.AutocompleteNone flagCompletion["runtime-flag"] = commonComp.AutocompleteNone @@ -248,6 +252,7 @@ func GetBudFlagsCompletions() commonComp.FlagCompletions { flagCompletion["tag"] = commonComp.AutocompleteNone flagCompletion["target"] = commonComp.AutocompleteNone flagCompletion["timestamp"] = commonComp.AutocompleteNone + flagCompletion["variant"] = commonComp.AutocompleteNone return flagCompletion } @@ -281,14 +286,9 @@ func GetFromAndBudFlags(flags *FromAndBudResults, usernsResults *UserNSResults, fs.StringVar(&flags.Isolation, "isolation", DefaultIsolation(), "`type` of process isolation to use. Use BUILDAH_ISOLATION environment variable to override.") fs.StringVarP(&flags.Memory, "memory", "m", "", "memory limit (format: <number>[<unit>], where unit = b, k, m or g)") fs.StringVar(&flags.MemorySwap, "memory-swap", "", "swap limit equal to memory plus swap: '-1' to enable unlimited swap") - fs.StringVar(&flags.OverrideOS, "override-os", runtime.GOOS, "prefer `OS` instead of the running OS when pulling images") - if err := fs.MarkHidden("override-os"); err != nil { - panic(fmt.Sprintf("error marking override-os as hidden: %v", err)) - } - fs.StringVar(&flags.OverrideArch, "override-arch", runtime.GOARCH, "prefer `ARCH` instead of the architecture of the machine when pulling images") - if err := fs.MarkHidden("override-arch"); err != nil { - panic(fmt.Sprintf("error marking override-arch as hidden: %v", err)) - } + fs.String("arch", runtime.GOARCH, "set the ARCH of the image to the provided value instead of the architecture of the host") + fs.String("os", runtime.GOOS, "prefer `OS` instead of the running OS when pulling images") + fs.String("variant", "", "override the `variant` of the specified image") fs.StringArrayVar(&flags.SecurityOpt, "security-opt", []string{}, "security options (default [])") fs.StringVar(&flags.ShmSize, "shm-size", defaultContainerConfig.Containers.ShmSize, "size of '/dev/shm'. The format is `<number><unit>`.") fs.StringSliceVar(&flags.Ulimit, "ulimit", defaultContainerConfig.Containers.DefaultUlimits, "ulimit options") @@ -306,6 +306,7 @@ func GetFromAndBudFlags(flags *FromAndBudResults, usernsResults *UserNSResults, // GetFromAndBudFlagsCompletions returns the FlagCompletions for the from and bud flags func GetFromAndBudFlagsCompletions() commonComp.FlagCompletions { flagCompletion := commonComp.FlagCompletions{} + flagCompletion["arch"] = commonComp.AutocompleteNone flagCompletion["add-host"] = commonComp.AutocompleteNone flagCompletion["blob-cache"] = commonComp.AutocompleteNone flagCompletion["cap-add"] = commonComp.AutocompleteCapabilities @@ -324,10 +325,12 @@ func GetFromAndBudFlagsCompletions() commonComp.FlagCompletions { flagCompletion["isolation"] = commonComp.AutocompleteNone flagCompletion["memory"] = commonComp.AutocompleteNone flagCompletion["memory-swap"] = commonComp.AutocompleteNone + flagCompletion["os"] = commonComp.AutocompleteNone flagCompletion["security-opt"] = commonComp.AutocompleteNone flagCompletion["shm-size"] = commonComp.AutocompleteNone flagCompletion["ulimit"] = commonComp.AutocompleteNone flagCompletion["volume"] = commonComp.AutocompleteDefault + flagCompletion["variant"] = commonComp.AutocompleteNone // Add in the usernamespace and namespace flag completions userNsComp := GetUserNSFlagsCompletions() @@ -396,6 +399,12 @@ func AliasFlags(f *pflag.FlagSet, name string) pflag.NormalizedName { switch name { case "net": name = "network" + case "override-arch": + name = "arch" + case "override-os": + name = "os" + case "purge": + name = "rm" } return pflag.NormalizedName(name) } diff --git a/vendor/github.com/containers/buildah/pkg/overlay/overlay.go b/vendor/github.com/containers/buildah/pkg/overlay/overlay.go index 07aa91e20..a3e5866ee 100644 --- a/vendor/github.com/containers/buildah/pkg/overlay/overlay.go +++ b/vendor/github.com/containers/buildah/pkg/overlay/overlay.go @@ -77,6 +77,15 @@ func mountHelper(contentDir, source, dest string, _, _ int, graphOptions []strin // Read-write overlay mounts want a lower, upper and a work layer. workDir := filepath.Join(contentDir, "work") upperDir := filepath.Join(contentDir, "upper") + st, err := os.Stat(dest) + if err == nil { + if err := os.Chmod(upperDir, st.Mode()); err != nil { + return mount, err + } + } + if !os.IsNotExist(err) { + return mount, err + } overlayOptions = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s,private", source, upperDir, workDir) } diff --git a/vendor/github.com/containers/buildah/pkg/parse/parse.go b/vendor/github.com/containers/buildah/pkg/parse/parse.go index f256e6c2a..b5e30b0d3 100644 --- a/vendor/github.com/containers/buildah/pkg/parse/parse.go +++ b/vendor/github.com/containers/buildah/pkg/parse/parse.go @@ -612,12 +612,38 @@ func SystemContextFromOptions(c *cobra.Command) (*types.SystemContext, error) { ctx.RegistriesDirPath = regConfDir } ctx.DockerRegistryUserAgent = fmt.Sprintf("Buildah/%s", buildah.Version) - if os, err := c.Flags().GetString("override-os"); err == nil { - ctx.OSChoice = os + if c.Flag("os") != nil && c.Flag("os").Changed { + if os, err := c.Flags().GetString("os"); err == nil { + ctx.OSChoice = os + } } - if arch, err := c.Flags().GetString("override-arch"); err == nil { - ctx.ArchitectureChoice = arch + if c.Flag("arch") != nil && c.Flag("arch").Changed { + if arch, err := c.Flags().GetString("arch"); err == nil { + ctx.ArchitectureChoice = arch + } } + if c.Flag("variant") != nil && c.Flag("variant").Changed { + if variant, err := c.Flags().GetString("variant"); err == nil { + ctx.VariantChoice = variant + } + } + if c.Flag("platform") != nil && c.Flag("platform").Changed { + if platform, err := c.Flags().GetString("platform"); err == nil { + os, arch, variant, err := parsePlatform(platform) + if err != nil { + return nil, err + } + if ctx.OSChoice != "" || + ctx.ArchitectureChoice != "" || + ctx.VariantChoice != "" { + return nil, errors.Errorf("invalid --platform may not be used with --os, --arch, or --variant") + } + ctx.OSChoice = os + ctx.ArchitectureChoice = arch + ctx.VariantChoice = variant + } + } + ctx.BigFilesTemporaryDir = GetTempDir() return ctx, nil } @@ -632,23 +658,27 @@ func getAuthFile(authfile string) string { // PlatformFromOptions parses the operating system (os) and architecture (arch) // from the provided command line options. func PlatformFromOptions(c *cobra.Command) (os, arch string, err error) { - os = runtime.GOOS - arch = runtime.GOARCH - if selectedOS, err := c.Flags().GetString("os"); err == nil && selectedOS != runtime.GOOS { - os = selectedOS + if c.Flag("os").Changed { + if selectedOS, err := c.Flags().GetString("os"); err == nil { + os = selectedOS + } } - if selectedArch, err := c.Flags().GetString("arch"); err == nil && selectedArch != runtime.GOARCH { - arch = selectedArch + if c.Flag("arch").Changed { + if selectedArch, err := c.Flags().GetString("arch"); err == nil { + arch = selectedArch + } } - if pf, err := c.Flags().GetString("platform"); err == nil && pf != DefaultPlatform() { - selectedOS, selectedArch, err := parsePlatform(pf) - if err != nil { - return "", "", errors.Wrap(err, "unable to parse platform") + if c.Flag("platform").Changed { + if pf, err := c.Flags().GetString("platform"); err == nil { + selectedOS, selectedArch, _, err := parsePlatform(pf) + if err != nil { + return "", "", errors.Wrap(err, "unable to parse platform") + } + arch = selectedArch + os = selectedOS } - arch = selectedArch - os = selectedOS } return os, arch, nil @@ -661,12 +691,17 @@ func DefaultPlatform() string { return runtime.GOOS + platformSep + runtime.GOARCH } -func parsePlatform(platform string) (os, arch string, err error) { +func parsePlatform(platform string) (os, arch, variant string, err error) { split := strings.Split(platform, platformSep) - if len(split) != 2 { - return "", "", errors.Errorf("invalid platform syntax for %q (use OS/ARCH)", platform) + if len(split) < 2 { + return "", "", "", errors.Errorf("invalid platform syntax for %q (use OS/ARCH)", platform) + } + os = split[0] + arch = split[1] + if len(split) == 3 { + variant = split[2] } - return split[0], split[1], nil + return } func parseCreds(creds string) (string, string) { @@ -938,7 +973,7 @@ func IsolationOption(isolation string) (buildah.Isolation, error) { } // Device parses device mapping string to a src, dest & permissions string -// Valid values for device looklike: +// Valid values for device look like: // '/dev/sdc" // '/dev/sdc:/dev/xvdc" // '/dev/sdc:/dev/xvdc:rwm" diff --git a/vendor/github.com/containers/buildah/pull.go b/vendor/github.com/containers/buildah/pull.go index d1fec145e..d75011296 100644 --- a/vendor/github.com/containers/buildah/pull.go +++ b/vendor/github.com/containers/buildah/pull.go @@ -292,7 +292,7 @@ func pullImage(ctx context.Context, store storage.Store, srcRef types.ImageRefer }() logrus.Debugf("copying %q to %q", transports.ImageName(srcRef), destName) - if _, err := retryCopyImage(ctx, policyContext, maybeCachedDestRef, srcRef, srcRef, getCopyOptions(store, options.ReportWriter, sc, nil, "", options.RemoveSignatures, "", nil, nil, options.OciDecryptConfig), options.MaxRetries, options.RetryDelay); err != nil { + if _, err := retryCopyImage(ctx, policyContext, maybeCachedDestRef, srcRef, srcRef, getCopyOptions(store, options.ReportWriter, sc, sc, "", options.RemoveSignatures, "", nil, nil, options.OciDecryptConfig), options.MaxRetries, options.RetryDelay); err != nil { logrus.Debugf("error copying src image [%q] to dest image [%q] err: %v", transports.ImageName(srcRef), destName, err) return nil, err } diff --git a/vendor/github.com/containers/buildah/run.go b/vendor/github.com/containers/buildah/run.go index 15417bba6..d1fee2360 100644 --- a/vendor/github.com/containers/buildah/run.go +++ b/vendor/github.com/containers/buildah/run.go @@ -27,7 +27,7 @@ const ( WithTerminal ) -// String converts a TerminalPoliicy into a string. +// String converts a TerminalPolicy into a string. func (t TerminalPolicy) String() string { switch t { case DefaultTerminal: diff --git a/vendor/github.com/containers/buildah/run_linux.go b/vendor/github.com/containers/buildah/run_linux.go index dc2f5c5ad..66c856884 100644 --- a/vendor/github.com/containers/buildah/run_linux.go +++ b/vendor/github.com/containers/buildah/run_linux.go @@ -489,14 +489,14 @@ func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, bundlePath st return errors.Wrapf(err, "error determining work directory for container %q", b.ContainerID) } - // Figure out which UID and GID to tell the subscritions package to use + // Figure out which UID and GID to tell the subscriptions package to use // for files that it creates. rootUID, rootGID, err := util.GetHostRootIDs(spec) if err != nil { return err } - // Get the list of subscriptionss mounts. + // Get the list of subscriptions mounts. secretMounts := subscriptions.MountsWithUIDGID(b.MountLabel, cdir, b.DefaultMountsFilePath, mountPoint, int(rootUID), int(rootGID), unshare.IsRootless(), false) // Add temporary copies of the contents of volume locations at the diff --git a/vendor/github.com/containers/buildah/selinux.go b/vendor/github.com/containers/buildah/selinux.go index fea863165..00903203e 100644 --- a/vendor/github.com/containers/buildah/selinux.go +++ b/vendor/github.com/containers/buildah/selinux.go @@ -1,4 +1,4 @@ -// +build selinux,linux +// +build linux package buildah diff --git a/vendor/github.com/containers/buildah/selinux_unsupported.go b/vendor/github.com/containers/buildah/selinux_unsupported.go index fb9213e29..264614837 100644 --- a/vendor/github.com/containers/buildah/selinux_unsupported.go +++ b/vendor/github.com/containers/buildah/selinux_unsupported.go @@ -1,4 +1,4 @@ -// +build !selinux !linux +// +build !linux package buildah diff --git a/vendor/github.com/containers/common/pkg/auth/auth.go b/vendor/github.com/containers/common/pkg/auth/auth.go index 21b988187..8daaf4c08 100644 --- a/vendor/github.com/containers/common/pkg/auth/auth.go +++ b/vendor/github.com/containers/common/pkg/auth/auth.go @@ -16,10 +16,17 @@ import ( "golang.org/x/crypto/ssh/terminal" ) -// GetDefaultAuthFile returns env value REGISTRY_AUTH_FILE as default --authfile path -// used in multiple --authfile flag definitions +// GetDefaultAuthFile returns env value REGISTRY_AUTH_FILE as default +// --authfile path used in multiple --authfile flag definitions +// Will fail over to DOCKER_CONFIG if REGISTRY_AUTH_FILE environment is not set func GetDefaultAuthFile() string { - return os.Getenv("REGISTRY_AUTH_FILE") + authfile := os.Getenv("REGISTRY_AUTH_FILE") + if authfile == "" { + if authfile, ok := os.LookupEnv("DOCKER_CONFIG"); ok { + logrus.Infof("Using DOCKER_CONFIG environment variable for authfile path %s", authfile) + } + } + return authfile } // CheckAuthFile validates filepath given by --authfile diff --git a/vendor/github.com/containers/common/pkg/completion/completion.go b/vendor/github.com/containers/common/pkg/completion/completion.go index 07451e992..90fe2f111 100644 --- a/vendor/github.com/containers/common/pkg/completion/completion.go +++ b/vendor/github.com/containers/common/pkg/completion/completion.go @@ -91,3 +91,51 @@ func AutocompleteSubgidName(cmd *cobra.Command, args []string, toComplete string func AutocompleteSubuidName(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return autocompleteSubIDName("/etc/subuid") } + +// AutocompleteArch - Autocomplete platform supported by container engines +func AutocompletePlatform(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + completions := []string{ + "linux/386", + "linux/amd64", + "linux/arm", + "linux/arm64", + "linux/ppc64", + "linux/ppc64le", + "linux/mips", + "linux/mipsle", + "linux/mips64", + "linux/mips64le", + "linux/riscv64", + "linux/s390x", + "windows/386", + "windows/amd64", + "windows/arm", + } + return completions, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteArch - Autocomplete architectures supported by container engines +func AutocompleteArch(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + completions := []string{ + "386", + "amd64", + "arm", + "arm64", + "ppc64", + "ppc64le", + "mips", + "mipsle", + "mips64", + "mips64le", + "riscv64", + "s390x", + } + + return completions, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteOS - Autocomplete OS supported by container engines +func AutocompleteOS(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + completions := []string{"linux", "windows"} + return completions, cobra.ShellCompDirectiveNoFileComp +} diff --git a/vendor/github.com/containers/common/pkg/config/config.go b/vendor/github.com/containers/common/pkg/config/config.go index ea08ab6ad..3b8baf87a 100644 --- a/vendor/github.com/containers/common/pkg/config/config.go +++ b/vendor/github.com/containers/common/pkg/config/config.go @@ -318,7 +318,7 @@ type EngineConfig struct { // RuntimeSupportsNoCgroups is a list of OCI runtimes that support // running containers without CGroups. - RuntimeSupportsNoCgroups []string `toml:"runtime_supports_nocgroupv2,omitempty"` + RuntimeSupportsNoCgroups []string `toml:"runtime_supports_nocgroup,omitempty"` // RuntimeSupportsKVM is a list of OCI runtimes that support // KVM separation for containers. @@ -746,13 +746,20 @@ func (c *Config) FindConmon() (string, error) { } // GetDefaultEnv returns the environment variables for the container. -// It will checn the HTTPProxy and HostEnv booleans and add the appropriate +// It will check the HTTPProxy and HostEnv booleans and add the appropriate // environment variables to the container. func (c *Config) GetDefaultEnv() []string { + return c.GetDefaultEnvEx(c.Containers.EnvHost, c.Containers.HTTPProxy) +} + +// GetDefaultEnvEx returns the environment variables for the container. +// It will check the HTTPProxy and HostEnv boolean parameters and return the appropriate +// environment variables for the container. +func (c *Config) GetDefaultEnvEx(envHost, httpProxy bool) []string { var env []string - if c.Containers.EnvHost { + if envHost { env = append(env, os.Environ()...) - } else if c.Containers.HTTPProxy { + } else if httpProxy { proxy := []string{"http_proxy", "https_proxy", "ftp_proxy", "no_proxy", "HTTP_PROXY", "HTTPS_PROXY", "FTP_PROXY", "NO_PROXY"} for _, p := range proxy { if val, ok := os.LookupEnv(p); ok { diff --git a/vendor/github.com/containers/common/pkg/config/default.go b/vendor/github.com/containers/common/pkg/config/default.go index 6b7aee987..2e26fb7b8 100644 --- a/vendor/github.com/containers/common/pkg/config/default.go +++ b/vendor/github.com/containers/common/pkg/config/default.go @@ -184,7 +184,7 @@ func DefaultConfig() (*Config, error) { "TERM=xterm", }, EnvHost: false, - HTTPProxy: false, + HTTPProxy: true, Init: false, InitPath: "", IPCNS: "private", diff --git a/vendor/github.com/containers/common/pkg/parse/parse.go b/vendor/github.com/containers/common/pkg/parse/parse.go new file mode 100644 index 000000000..611b2e84b --- /dev/null +++ b/vendor/github.com/containers/common/pkg/parse/parse.go @@ -0,0 +1,157 @@ +package parse + +// this package contains functions that parse and validate +// user input and is shared either amongst container engine subcommands + +import ( + "os" + "path/filepath" + "strings" + + "github.com/pkg/errors" +) + +// ValidateVolumeOpts validates a volume's options +func ValidateVolumeOpts(options []string) ([]string, error) { + var foundRootPropagation, foundRWRO, foundLabelChange, bindType, foundExec, foundDev, foundSuid int + finalOpts := make([]string, 0, len(options)) + for _, opt := range options { + switch opt { + case "noexec", "exec": + foundExec++ + if foundExec > 1 { + return nil, errors.Errorf("invalid options %q, can only specify 1 'noexec' or 'exec' option", strings.Join(options, ", ")) + } + case "nodev", "dev": + foundDev++ + if foundDev > 1 { + return nil, errors.Errorf("invalid options %q, can only specify 1 'nodev' or 'dev' option", strings.Join(options, ", ")) + } + case "nosuid", "suid": + foundSuid++ + if foundSuid > 1 { + return nil, errors.Errorf("invalid options %q, can only specify 1 'nosuid' or 'suid' option", strings.Join(options, ", ")) + } + case "rw", "ro": + foundRWRO++ + if foundRWRO > 1 { + return nil, errors.Errorf("invalid options %q, can only specify 1 'rw' or 'ro' option", strings.Join(options, ", ")) + } + case "z", "Z", "O": + foundLabelChange++ + if foundLabelChange > 1 { + return nil, errors.Errorf("invalid options %q, can only specify 1 'z', 'Z', or 'O' option", strings.Join(options, ", ")) + } + case "private", "rprivate", "shared", "rshared", "slave", "rslave", "unbindable", "runbindable": + foundRootPropagation++ + if foundRootPropagation > 1 { + return nil, errors.Errorf("invalid options %q, can only specify 1 '[r]shared', '[r]private', '[r]slave' or '[r]unbindable' option", strings.Join(options, ", ")) + } + case "bind", "rbind": + bindType++ + if bindType > 1 { + return nil, errors.Errorf("invalid options %q, can only specify 1 '[r]bind' option", strings.Join(options, ", ")) + } + case "cached", "delegated": + // The discarded ops are OS X specific volume options + // introduced in a recent Docker version. + // They have no meaning on Linux, so here we silently + // drop them. This matches Docker's behavior (the options + // are intended to be always safe to use, even not on OS + // X). + continue + default: + return nil, errors.Errorf("invalid option type %q", opt) + } + finalOpts = append(finalOpts, opt) + } + return finalOpts, nil +} + +// Device parses device mapping string to a src, dest & permissions string +// Valid values for device looklike: +// '/dev/sdc" +// '/dev/sdc:/dev/xvdc" +// '/dev/sdc:/dev/xvdc:rwm" +// '/dev/sdc:rm" +func Device(device string) (src, dest, permissions string, err error) { + permissions = "rwm" + arr := strings.Split(device, ":") + switch len(arr) { + case 3: + if !isValidDeviceMode(arr[2]) { + return "", "", "", errors.Errorf("invalid device mode: %s", arr[2]) + } + permissions = arr[2] + fallthrough + case 2: + if isValidDeviceMode(arr[1]) { + permissions = arr[1] + } else { + if arr[1] == "" || arr[1][0] != '/' { + return "", "", "", errors.Errorf("invalid device mode: %s", arr[1]) + } + dest = arr[1] + } + fallthrough + case 1: + if len(arr[0]) > 0 { + src = arr[0] + break + } + fallthrough + default: + return "", "", "", errors.Errorf("invalid device specification: %s", device) + } + + if dest == "" { + dest = src + } + return src, dest, permissions, nil +} + +// isValidDeviceMode checks if the mode for device is valid or not. +// isValid mode is a composition of r (read), w (write), and m (mknod). +func isValidDeviceMode(mode string) bool { + var legalDeviceMode = map[rune]bool{ + 'r': true, + 'w': true, + 'm': true, + } + if mode == "" { + return false + } + for _, c := range mode { + if !legalDeviceMode[c] { + return false + } + legalDeviceMode[c] = false + } + return true +} + +// ValidateVolumeHostDir validates a volume mount's source directory +func ValidateVolumeHostDir(hostDir string) error { + if hostDir == "" { + return errors.Errorf("host directory cannot be empty") + } + if filepath.IsAbs(hostDir) { + if _, err := os.Stat(hostDir); err != nil { + return errors.Wrapf(err, "error checking path %q", hostDir) + } + } + // If hostDir is not an absolute path, that means the user wants to create a + // named volume. This will be done later on in the code. + return nil +} + +// ValidateVolumeCtrDir validates a volume mount's destination directory. +func ValidateVolumeCtrDir(ctrDir string) error { + if ctrDir == "" { + return errors.Errorf("container directory cannot be empty") + } + if !filepath.IsAbs(ctrDir) { + return errors.Errorf("invalid container path %q, must be an absolute path", ctrDir) + } + return nil +} diff --git a/vendor/github.com/containers/common/pkg/parse/parse_unix.go b/vendor/github.com/containers/common/pkg/parse/parse_unix.go new file mode 100644 index 000000000..880fbf674 --- /dev/null +++ b/vendor/github.com/containers/common/pkg/parse/parse_unix.go @@ -0,0 +1,51 @@ +// +build linux darwin + +package parse + +import ( + "os" + "path/filepath" + + "github.com/containers/storage/pkg/unshare" + "github.com/opencontainers/runc/libcontainer/configs" + "github.com/opencontainers/runc/libcontainer/devices" + "github.com/pkg/errors" +) + +func DeviceFromPath(device string) ([]configs.Device, error) { + var devs []configs.Device + src, dst, permissions, err := Device(device) + if err != nil { + return nil, err + } + if unshare.IsRootless() && src != dst { + return nil, errors.Errorf("Renaming device %s to %s is not supported in rootless containers", src, dst) + } + srcInfo, err := os.Stat(src) + if err != nil { + return nil, errors.Wrapf(err, "error getting info of source device %s", src) + } + + if !srcInfo.IsDir() { + + dev, err := devices.DeviceFromPath(src, permissions) + if err != nil { + return nil, errors.Wrapf(err, "%s is not a valid device", src) + } + dev.Path = dst + devs = append(devs, *dev) + return devs, nil + } + + // If source device is a directory + srcDevices, err := devices.GetDevices(src) + if err != nil { + return nil, errors.Wrapf(err, "error getting source devices from directory %s", src) + } + for _, d := range srcDevices { + d.Path = filepath.Join(dst, filepath.Base(d.Path)) + d.Permissions = configs.DevicePermissions(permissions) + devs = append(devs, *d) + } + return devs, nil +} diff --git a/vendor/github.com/containers/common/pkg/retry/retry.go b/vendor/github.com/containers/common/pkg/retry/retry.go index f6ecab0c0..8eb2da975 100644 --- a/vendor/github.com/containers/common/pkg/retry/retry.go +++ b/vendor/github.com/containers/common/pkg/retry/retry.go @@ -30,7 +30,7 @@ func RetryIfNecessary(ctx context.Context, operation func() error, retryOptions if retryOptions.Delay != 0 { delay = retryOptions.Delay } - logrus.Infof("Warning: failed, retrying in %s ... (%d/%d). Error: %v", delay, attempt+1, retryOptions.MaxRetry, err) + logrus.Warnf("failed, retrying in %s ... (%d/%d). Error: %v", delay, attempt+1, retryOptions.MaxRetry, err) select { case <-time.After(delay): break @@ -69,7 +69,7 @@ func isRetryable(err error) bool { } return isRetryable(e.Err) case syscall.Errno: - return e != syscall.ECONNREFUSED + return isErrnoRetryable(e) case errcode.Errors: // if this error is a group of errors, process them all in turn for i := range e { @@ -93,3 +93,11 @@ func isRetryable(err error) bool { return false } + +func isErrnoRetryable(e error) bool { + switch e { + case syscall.ECONNREFUSED, syscall.EINTR, syscall.EAGAIN, syscall.EBUSY, syscall.ENETDOWN, syscall.ENETUNREACH, syscall.ENETRESET, syscall.ECONNABORTED, syscall.ECONNRESET, syscall.ETIMEDOUT, syscall.EHOSTDOWN, syscall.EHOSTUNREACH: + return true + } + return isErrnoERESTART(e) +} diff --git a/vendor/github.com/containers/common/pkg/retry/retry_linux.go b/vendor/github.com/containers/common/pkg/retry/retry_linux.go new file mode 100644 index 000000000..b7942f7f4 --- /dev/null +++ b/vendor/github.com/containers/common/pkg/retry/retry_linux.go @@ -0,0 +1,9 @@ +package retry + +import ( + "syscall" +) + +func isErrnoERESTART(e error) bool { + return e == syscall.ERESTART +} diff --git a/vendor/github.com/containers/common/pkg/retry/retry_unsupported.go b/vendor/github.com/containers/common/pkg/retry/retry_unsupported.go new file mode 100644 index 000000000..676980975 --- /dev/null +++ b/vendor/github.com/containers/common/pkg/retry/retry_unsupported.go @@ -0,0 +1,7 @@ +// +build !linux + +package retry + +func isErrnoERESTART(e error) bool { + return false +} diff --git a/vendor/github.com/containers/common/pkg/seccomp/default_linux.go b/vendor/github.com/containers/common/pkg/seccomp/default_linux.go index a127571b5..5c4427318 100644 --- a/vendor/github.com/containers/common/pkg/seccomp/default_linux.go +++ b/vendor/github.com/containers/common/pkg/seccomp/default_linux.go @@ -378,7 +378,6 @@ func DefaultProfile() *Seccomp { "utimensat_time64", "utimes", "vfork", - "vmsplice", "wait4", "waitid", "waitpid", diff --git a/vendor/github.com/containers/common/pkg/seccomp/seccomp.json b/vendor/github.com/containers/common/pkg/seccomp/seccomp.json index 8fb509345..d6f3f4938 100644 --- a/vendor/github.com/containers/common/pkg/seccomp/seccomp.json +++ b/vendor/github.com/containers/common/pkg/seccomp/seccomp.json @@ -378,7 +378,6 @@ "utimensat_time64", "utimes", "vfork", - "vmsplice", "wait4", "waitid", "waitpid", diff --git a/vendor/github.com/containers/common/version/version.go b/vendor/github.com/containers/common/version/version.go index 4366848ea..7d7cf59f1 100644 --- a/vendor/github.com/containers/common/version/version.go +++ b/vendor/github.com/containers/common/version/version.go @@ -1,4 +1,4 @@ package version // Version is the version of the build. -const Version = "0.31.2" +const Version = "0.33.1" diff --git a/vendor/github.com/containers/psgo/internal/capabilities/capabilities.go b/vendor/github.com/containers/psgo/internal/capabilities/capabilities.go index c46468930..1a60b96c4 100644 --- a/vendor/github.com/containers/psgo/internal/capabilities/capabilities.go +++ b/vendor/github.com/containers/psgo/internal/capabilities/capabilities.go @@ -13,7 +13,7 @@ // limitations under the License. // Package capabilities provides a mapping from common kernel bit masks to the -// alphanumerical represenation of kernel capabilities. See capabilities(7) +// alphanumerical representation of kernel capabilities. See capabilities(7) // for additional information. package capabilities diff --git a/vendor/github.com/containers/psgo/internal/dev/tty.go b/vendor/github.com/containers/psgo/internal/dev/tty.go index b7d6f28ac..863767f75 100644 --- a/vendor/github.com/containers/psgo/internal/dev/tty.go +++ b/vendor/github.com/containers/psgo/internal/dev/tty.go @@ -113,8 +113,9 @@ func TTYs() (*[]TTY, error) { } s := fi.Sys().(*syscall.Stat_t) t := TTY{ - Minor: minDevNum(s.Rdev), - Major: majDevNum(s.Rdev), + // Rdev is type uint32 on mips arch so we have to cast to uint64 + Minor: minDevNum(uint64(s.Rdev)), + Major: majDevNum(uint64(s.Rdev)), Path: dev, } ttys = append(ttys, t) diff --git a/vendor/github.com/containers/psgo/internal/proc/status.go b/vendor/github.com/containers/psgo/internal/proc/status.go index 29d059361..df31139f8 100644 --- a/vendor/github.com/containers/psgo/internal/proc/status.go +++ b/vendor/github.com/containers/psgo/internal/proc/status.go @@ -24,7 +24,7 @@ import ( "github.com/pkg/errors" ) -// Status is a direct translation of a `/proc/[pid]/status`, wich provides much +// Status is a direct translation of a `/proc/[pid]/status`, which provides much // of the information in /proc/[pid]/stat and /proc/[pid]/statm in a format // that's easier for humans to parse. type Status struct { diff --git a/vendor/github.com/containers/psgo/internal/process/process.go b/vendor/github.com/containers/psgo/internal/process/process.go index b46a39f46..a581921d5 100644 --- a/vendor/github.com/containers/psgo/internal/process/process.go +++ b/vendor/github.com/containers/psgo/internal/process/process.go @@ -31,9 +31,9 @@ type Process struct { Pid string // Stat contains data from /proc/$pid/stat. Stat proc.Stat - // Status containes data from /proc/$pid/status. + // Status contains data from /proc/$pid/status. Status proc.Status - // CmdLine containes data from /proc/$pid/cmdline. + // CmdLine contains data from /proc/$pid/cmdline. CmdLine []string // Label containers data from /proc/$pid/attr/current. Label string diff --git a/vendor/github.com/containers/psgo/psgo.go b/vendor/github.com/containers/psgo/psgo.go index c75fc3815..7c74fd716 100644 --- a/vendor/github.com/containers/psgo/psgo.go +++ b/vendor/github.com/containers/psgo/psgo.go @@ -482,7 +482,7 @@ func JoinNamespaceAndProcessInfoByPidsWithOptions(pids []string, descriptors []s // catch race conditions continue } - return nil, errors.Wrapf(err, "error extracing PID namespace") + return nil, errors.Wrapf(err, "error extracting PID namespace") } if _, exists := nsMap[ns]; !exists { nsMap[ns] = true @@ -759,7 +759,7 @@ func processVSZ(p *process.Process, ctx *psContext) (string, error) { } // parseCAP parses cap (a string bit mask) and returns the associated set of -// capabilities. If all capabilties are set, "full" is returned. If no +// capabilities. If all capabilities are set, "full" is returned. If no // capability is enabled, "none" is returned. func parseCAP(cap string) (string, error) { mask, err := strconv.ParseUint(cap, 16, 64) @@ -777,36 +777,36 @@ func parseCAP(cap string) (string, error) { return strings.Join(caps, ","), nil } -// processCAPAMB returns the set of ambient capabilties associated with -// process p. If all capabilties are set, "full" is returned. If no +// processCAPAMB returns the set of ambient capabilities associated with +// process p. If all capabilities are set, "full" is returned. If no // capability is enabled, "none" is returned. func processCAPAMB(p *process.Process, ctx *psContext) (string, error) { return parseCAP(p.Status.CapAmb) } -// processCAPINH returns the set of inheritable capabilties associated with -// process p. If all capabilties are set, "full" is returned. If no +// processCAPINH returns the set of inheritable capabilities associated with +// process p. If all capabilities are set, "full" is returned. If no // capability is enabled, "none" is returned. func processCAPINH(p *process.Process, ctx *psContext) (string, error) { return parseCAP(p.Status.CapInh) } -// processCAPPRM returns the set of permitted capabilties associated with -// process p. If all capabilties are set, "full" is returned. If no +// processCAPPRM returns the set of permitted capabilities associated with +// process p. If all capabilities are set, "full" is returned. If no // capability is enabled, "none" is returned. func processCAPPRM(p *process.Process, ctx *psContext) (string, error) { return parseCAP(p.Status.CapPrm) } -// processCAPEFF returns the set of effective capabilties associated with -// process p. If all capabilties are set, "full" is returned. If no +// processCAPEFF returns the set of effective capabilities associated with +// process p. If all capabilities are set, "full" is returned. If no // capability is enabled, "none" is returned. func processCAPEFF(p *process.Process, ctx *psContext) (string, error) { return parseCAP(p.Status.CapEff) } -// processCAPBND returns the set of bounding capabilties associated with -// process p. If all capabilties are set, "full" is returned. If no +// processCAPBND returns the set of bounding capabilities associated with +// process p. If all capabilities are set, "full" is returned. If no // capability is enabled, "none" is returned. func processCAPBND(p *process.Process, ctx *psContext) (string, error) { return parseCAP(p.Status.CapBnd) diff --git a/vendor/github.com/containers/storage/VERSION b/vendor/github.com/containers/storage/VERSION index 2f4320f67..6521720b4 100644 --- a/vendor/github.com/containers/storage/VERSION +++ b/vendor/github.com/containers/storage/VERSION @@ -1 +1 @@ -1.24.4 +1.24.5 diff --git a/vendor/github.com/containers/storage/drivers/overlay/overlay.go b/vendor/github.com/containers/storage/drivers/overlay/overlay.go index c1895c364..6e5a76cf3 100644 --- a/vendor/github.com/containers/storage/drivers/overlay/overlay.go +++ b/vendor/github.com/containers/storage/drivers/overlay/overlay.go @@ -761,19 +761,29 @@ func (d *Driver) optsAppendMappings(opts string, uidMaps, gidMaps []idtools.IDMa } if uidMaps != nil { var uids, gids bytes.Buffer - for _, i := range uidMaps { - if uids.Len() > 0 { - uids.WriteString(":") + if len(uidMaps) == 1 && uidMaps[0].Size == 1 { + uids.WriteString(fmt.Sprintf("squash_to_uid=%d", uidMaps[0].HostID)) + } else { + uids.WriteString("uidmapping=") + for _, i := range uidMaps { + if uids.Len() > 0 { + uids.WriteString(":") + } + uids.WriteString(fmt.Sprintf("%d:%d:%d", i.ContainerID, i.HostID, i.Size)) } - uids.WriteString(fmt.Sprintf("%d:%d:%d", i.ContainerID, i.HostID, i.Size)) } - for _, i := range gidMaps { - if gids.Len() > 0 { - gids.WriteString(":") + if len(gidMaps) == 1 && gidMaps[0].Size == 1 { + gids.WriteString(fmt.Sprintf("squash_to_gid=%d", gidMaps[0].HostID)) + } else { + gids.WriteString("gidmapping=") + for _, i := range gidMaps { + if gids.Len() > 0 { + gids.WriteString(":") + } + gids.WriteString(fmt.Sprintf("%d:%d:%d", i.ContainerID, i.HostID, i.Size)) } - gids.WriteString(fmt.Sprintf("%d:%d:%d", i.ContainerID, i.HostID, i.Size)) } - return fmt.Sprintf("%s,uidmapping=%s,gidmapping=%s", opts, uids.String(), gids.String()) + return fmt.Sprintf("%s,%s,%s", opts, uids.String(), gids.String()) } return opts } diff --git a/vendor/github.com/containers/storage/go.mod b/vendor/github.com/containers/storage/go.mod index b19b4a7c4..8af8ceddb 100644 --- a/vendor/github.com/containers/storage/go.mod +++ b/vendor/github.com/containers/storage/go.mod @@ -8,7 +8,7 @@ require ( github.com/Microsoft/hcsshim v0.8.14 github.com/docker/go-units v0.4.0 github.com/hashicorp/go-multierror v1.1.0 - github.com/klauspost/compress v1.11.4 + github.com/klauspost/compress v1.11.5 github.com/klauspost/pgzip v1.2.5 github.com/mattn/go-shellwords v1.0.10 github.com/mistifyio/go-zfs v2.1.1+incompatible diff --git a/vendor/github.com/containers/storage/go.sum b/vendor/github.com/containers/storage/go.sum index a7be24d40..c786686bc 100644 --- a/vendor/github.com/containers/storage/go.sum +++ b/vendor/github.com/containers/storage/go.sum @@ -58,8 +58,8 @@ github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 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.11.4 h1:kz40R/YWls3iqT9zX9AHN3WoVsrAWVyui5sxuLqiXqU= -github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.5 h1:xNCE0uE6yvTPRS+0wGNMHPo3NIpwnk6aluQZ6R6kRcc= +github.com/klauspost/compress v1.11.5/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= diff --git a/vendor/github.com/containers/storage/pkg/idtools/idtools.go b/vendor/github.com/containers/storage/pkg/idtools/idtools.go index 0958c0c5d..0cd386929 100644 --- a/vendor/github.com/containers/storage/pkg/idtools/idtools.go +++ b/vendor/github.com/containers/storage/pkg/idtools/idtools.go @@ -77,13 +77,23 @@ func MkdirAllAndChownNew(path string, mode os.FileMode, ids IDPair) error { // GetRootUIDGID retrieves the remapped root uid/gid pair from the set of maps. // If the maps are empty, then the root uid/gid will default to "real" 0/0 func GetRootUIDGID(uidMap, gidMap []IDMap) (int, int, error) { - uid, err := toHost(0, uidMap) - if err != nil { - return -1, -1, err + var uid, gid int + var err error + if len(uidMap) == 1 && uidMap[0].Size == 1 { + uid = uidMap[0].HostID + } else { + uid, err = toHost(0, uidMap) + if err != nil { + return -1, -1, err + } } - gid, err := toHost(0, gidMap) - if err != nil { - return -1, -1, err + if len(gidMap) == 1 && gidMap[0].Size == 1 { + gid = gidMap[0].HostID + } else { + gid, err = toHost(0, gidMap) + if err != nil { + return -1, -1, err + } } return uid, gid, nil } diff --git a/vendor/github.com/containers/storage/store.go b/vendor/github.com/containers/storage/store.go index 0b53d81ce..fa595355d 100644 --- a/vendor/github.com/containers/storage/store.go +++ b/vendor/github.com/containers/storage/store.go @@ -3522,10 +3522,11 @@ func ReloadConfigurationFile(configFile string, storeOptions *StoreOptions) { fmt.Printf("Failed to parse %s %v\n", configFile, err.Error()) return } + if config.Storage.Driver != "" { + storeOptions.GraphDriverName = config.Storage.Driver + } if os.Getenv("STORAGE_DRIVER") != "" { config.Storage.Driver = os.Getenv("STORAGE_DRIVER") - } - if config.Storage.Driver != "" { storeOptions.GraphDriverName = config.Storage.Driver } if storeOptions.GraphDriverName == "" { diff --git a/vendor/github.com/containers/storage/utils.go b/vendor/github.com/containers/storage/utils.go index bd6c4feb1..ecfcf45e3 100644 --- a/vendor/github.com/containers/storage/utils.go +++ b/vendor/github.com/containers/storage/utils.go @@ -211,18 +211,27 @@ func getRootlessStorageOpts(rootlessUID int, systemOpts StoreOptions) (StoreOpti } else { opts.GraphRoot = filepath.Join(dataDir, "containers", "storage") } - if path, err := exec.LookPath("fuse-overlayfs"); err == nil { - opts.GraphDriverName = "overlay" - opts.GraphDriverOptions = []string{fmt.Sprintf("overlay.mount_program=%s", path)} - for _, o := range systemOpts.GraphDriverOptions { - if strings.Contains(o, "ignore_chown_errors") { - opts.GraphDriverOptions = append(opts.GraphDriverOptions, o) - break + opts.GraphDriverName = os.Getenv("STORAGE_DRIVER") + if opts.GraphDriverName == "" || opts.GraphDriverName == "overlay" { + if path, err := exec.LookPath("fuse-overlayfs"); err == nil { + opts.GraphDriverName = "overlay" + opts.GraphDriverOptions = []string{fmt.Sprintf("overlay.mount_program=%s", path)} + for _, o := range systemOpts.GraphDriverOptions { + if strings.Contains(o, "ignore_chown_errors") { + opts.GraphDriverOptions = append(opts.GraphDriverOptions, o) + break + } } } - } else { + } + if opts.GraphDriverName == "" { opts.GraphDriverName = "vfs" } + + if os.Getenv("STORAGE_OPTS") != "" { + opts.GraphDriverOptions = append(opts.GraphDriverOptions, strings.Split(os.Getenv("STORAGE_OPTS"), ",")...) + } + return opts, nil } diff --git a/vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go b/vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go index 7a9f97d1c..d9c1d37db 100644 --- a/vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go +++ b/vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go @@ -102,7 +102,7 @@ func (plugin *cniNetworkPlugin) podUnlock(podNetwork PodNetwork) { fullPodName := buildFullPodName(podNetwork) lock, ok := plugin.pods[fullPodName] if !ok { - logrus.Warningf("Unbalanced pod lock unref for %s", fullPodName) + logrus.Errorf("Cannot find reference in refcount map for %s. Refcount cannot be determined.", fullPodName) return } else if lock.refcount == 0 { // This should never ever happen, but handle it anyway @@ -121,12 +121,12 @@ func newWatcher(confDir string) (*fsnotify.Watcher, error) { // Ensure plugin directory exists, because the following monitoring logic // relies on that. if err := os.MkdirAll(confDir, 0755); err != nil { - return nil, fmt.Errorf("failed to create %q: %v", confDir, err) + return nil, fmt.Errorf("failed to create directory %q: %v", confDir, err) } watcher, err := fsnotify.NewWatcher() if err != nil { - return nil, fmt.Errorf("could not create new watcher %v", err) + return nil, fmt.Errorf("failed to create new watcher %v", err) } defer func() { // Close watcher on error @@ -275,13 +275,13 @@ func loadNetworks(confDir string, cni *libcni.CNIConfig) (map[string]*cniNetwork if strings.HasSuffix(confFile, ".conflist") { confList, err = libcni.ConfListFromFile(confFile) if err != nil { - logrus.Warningf("Error loading CNI config list file %s: %v", confFile, err) + logrus.Errorf("Error loading CNI config list file %s: %v", confFile, err) continue } } else { conf, err := libcni.ConfFromFile(confFile) if err != nil { - logrus.Warningf("Error loading CNI config file %s: %v", confFile, err) + logrus.Errorf("Error loading CNI config file %s: %v", confFile, err) continue } if conf.Network.Type == "" { @@ -290,7 +290,7 @@ func loadNetworks(confDir string, cni *libcni.CNIConfig) (map[string]*cniNetwork } confList, err = libcni.ConfListFromConf(conf) if err != nil { - logrus.Warningf("Error converting CNI config file %s to list: %v", confFile, err) + logrus.Errorf("Error converting CNI config file %s to list: %v", confFile, err) continue } } @@ -321,7 +321,7 @@ func loadNetworks(confDir string, cni *libcni.CNIConfig) (map[string]*cniNetwork if _, ok := networks[confList.Name]; !ok { networks[confList.Name] = cniNet } else { - logrus.Infof("Ignore CNI network %s (type=%v) at %s because already exists", confList.Name, confList.Plugins[0].Network.Type, confFile) + logrus.Infof("Ignored CNI network %s (type=%v) at %s because already exists", confList.Name, confList.Plugins[0].Network.Type, confFile) } if defaultNetName == "" { @@ -348,7 +348,7 @@ func (plugin *cniNetworkPlugin) syncNetworkConfig() error { // Update defaultNetName if it is changeable if plugin.defaultNetName.changeable { plugin.defaultNetName.name = defaultNetName - logrus.Infof("Update default CNI network name to %s", defaultNetName) + logrus.Infof("Updated default CNI network name to %s", defaultNetName) } else { logrus.Debugf("Default CNI network name %s is unchangeable", plugin.defaultNetName.name) } @@ -479,8 +479,8 @@ func (plugin *cniNetworkPlugin) forEachNetwork(podNetwork *PodNetwork, fromCache var newRt *libcni.RuntimeConf cniNet, newRt, err = plugin.loadNetworkFromCache(network.Name, rt) if err != nil { - logrus.Debugf("error loading cached network config: %v", err) - logrus.Debugf("falling back to loading from existing plugins on disk") + logrus.Errorf("error loading cached network config: %v", err) + logrus.Warningf("falling back to loading from existing plugins on disk") } else { // Use the updated RuntimeConf rt = newRt @@ -570,7 +570,7 @@ func (plugin *cniNetworkPlugin) getCachedNetworkInfo(containerID string) ([]NetA cacheFile := filepath.Join(dirPath, fname) bytes, err := ioutil.ReadFile(cacheFile) if err != nil { - logrus.Warningf("failed to read CNI cache file %s: %v", cacheFile, err) + logrus.Errorf("failed to read CNI cache file %s: %v", cacheFile, err) continue } @@ -582,7 +582,7 @@ func (plugin *cniNetworkPlugin) getCachedNetworkInfo(containerID string) ([]NetA }{} if err := json.Unmarshal(bytes, &cachedInfo); err != nil { - logrus.Warningf("failed to unmarshal CNI cache file %s: %v", cacheFile, err) + logrus.Errorf("failed to unmarshal CNI cache file %s: %v", cacheFile, err) continue } if cachedInfo.Kind != libcni.CNICacheV1 { @@ -632,13 +632,12 @@ func (plugin *cniNetworkPlugin) TearDownPodWithContext(ctx context.Context, podN if err := tearDownLoopback(podNetwork.NetNS); err != nil { // ignore error - logrus.Errorf("Ignoring error tearing down loopback interface: %v", err) + logrus.Warningf("Ignoring error tearing down loopback interface: %v", err) } return plugin.forEachNetwork(&podNetwork, true, func(network *cniNetwork, podNetwork *PodNetwork, rt *libcni.RuntimeConf) error { if err := network.deleteFromNetwork(ctx, rt, plugin.cniConfig); err != nil { - logrus.Errorf("Error while removing pod from CNI network %q: %s", network.name, err) - return err + return fmt.Errorf("Error while removing pod from CNI network %q: %s", network.name, err) } return nil }) @@ -718,7 +717,7 @@ func (network *cniNetwork) checkNetwork(ctx context.Context, rt *libcni.RuntimeC result, err = cni.GetNetworkListCachedResult(network.config, rt) if err != nil { - logrus.Errorf("Error GetNetworkListCachedResult: %v", err) + logrus.Errorf("Error getting network list cached result: %v", err) return nil, err } else if result != nil { return result, nil @@ -771,7 +770,6 @@ func (network *cniNetwork) checkNetwork(ctx context.Context, rt *libcni.RuntimeC func (network *cniNetwork) deleteFromNetwork(ctx context.Context, rt *libcni.RuntimeConf, cni *libcni.CNIConfig) error { logrus.Infof("About to del CNI network %s (type=%v)", network.name, network.config.Plugins[0].Network.Type) if err := cni.DelNetworkList(ctx, network.config, rt); err != nil { - logrus.Errorf("Error deleting network: %v", err) return err } return nil diff --git a/vendor/github.com/docker/docker/api/swagger.yaml b/vendor/github.com/docker/docker/api/swagger.yaml index 0ff649013..9f1019681 100644 --- a/vendor/github.com/docker/docker/api/swagger.yaml +++ b/vendor/github.com/docker/docker/api/swagger.yaml @@ -8023,7 +8023,7 @@ paths: API-Version: type: "string" description: "Max API Version the server supports" - BuildKit-Version: + Builder-Version: type: "string" description: "Default version of docker image builder" Docker-Experimental: @@ -8062,7 +8062,7 @@ paths: API-Version: type: "string" description: "Max API Version the server supports" - BuildKit-Version: + Builder-Version: type: "string" description: "Default version of docker image builder" Docker-Experimental: diff --git a/vendor/github.com/docker/docker/api/types/mount/mount.go b/vendor/github.com/docker/docker/api/types/mount/mount.go index ab4446b38..443b8d07a 100644 --- a/vendor/github.com/docker/docker/api/types/mount/mount.go +++ b/vendor/github.com/docker/docker/api/types/mount/mount.go @@ -113,7 +113,7 @@ type TmpfsOptions struct { // TODO(stevvooe): There are several more tmpfs flags, specified in the // daemon, that are accepted. Only the most basic are added for now. // - // From docker/docker/pkg/mount/flags.go: + // From https://github.com/moby/sys/blob/mount/v0.1.1/mount/flags.go#L47-L56 // // var validFlags = map[string]bool{ // "": true, diff --git a/vendor/github.com/docker/docker/pkg/system/chtimes_unix.go b/vendor/github.com/docker/docker/pkg/system/chtimes_nowindows.go index d5fab96f9..d5fab96f9 100644 --- a/vendor/github.com/docker/docker/pkg/system/chtimes_unix.go +++ b/vendor/github.com/docker/docker/pkg/system/chtimes_nowindows.go diff --git a/vendor/github.com/docker/docker/pkg/system/rm_unix.go b/vendor/github.com/docker/docker/pkg/system/rm.go index 6ba3fb3ae..c5d80ebda 100644 --- a/vendor/github.com/docker/docker/pkg/system/rm_unix.go +++ b/vendor/github.com/docker/docker/pkg/system/rm.go @@ -1,4 +1,4 @@ -// +build !windows +// +build !darwin,!windows package system // import "github.com/docker/docker/pkg/system" diff --git a/vendor/github.com/google/uuid/hash.go b/vendor/github.com/google/uuid/hash.go index b17461631..b404f4bec 100644 --- a/vendor/github.com/google/uuid/hash.go +++ b/vendor/github.com/google/uuid/hash.go @@ -26,8 +26,8 @@ var ( // NewMD5 and NewSHA1. func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID { h.Reset() - h.Write(space[:]) - h.Write(data) + h.Write(space[:]) //nolint:errcheck + h.Write(data) //nolint:errcheck s := h.Sum(nil) var uuid UUID copy(uuid[:], s) diff --git a/vendor/github.com/google/uuid/sql.go b/vendor/github.com/google/uuid/sql.go index f326b54db..2e02ec06c 100644 --- a/vendor/github.com/google/uuid/sql.go +++ b/vendor/github.com/google/uuid/sql.go @@ -9,7 +9,7 @@ import ( "fmt" ) -// Scan implements sql.Scanner so UUIDs can be read from databases transparently +// Scan implements sql.Scanner so UUIDs can be read from databases transparently. // Currently, database types that map to string and []byte are supported. Please // consult database-specific driver documentation for matching types. func (uuid *UUID) Scan(src interface{}) error { diff --git a/vendor/github.com/google/uuid/uuid.go b/vendor/github.com/google/uuid/uuid.go index 524404cc5..60d26bb50 100644 --- a/vendor/github.com/google/uuid/uuid.go +++ b/vendor/github.com/google/uuid/uuid.go @@ -35,6 +35,12 @@ const ( var rander = rand.Reader // random function +type invalidLengthError struct{ len int } + +func (err invalidLengthError) Error() string { + return fmt.Sprintf("invalid UUID length: %d", err.len) +} + // Parse decodes s into a UUID or returns an error. Both the standard UUID // forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the @@ -68,7 +74,7 @@ func Parse(s string) (UUID, error) { } return uuid, nil default: - return uuid, fmt.Errorf("invalid UUID length: %d", len(s)) + return uuid, invalidLengthError{len(s)} } // s is now at least 36 bytes long // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx @@ -112,7 +118,7 @@ func ParseBytes(b []byte) (UUID, error) { } return uuid, nil default: - return uuid, fmt.Errorf("invalid UUID length: %d", len(b)) + return uuid, invalidLengthError{len(b)} } // s is now at least 36 bytes long // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx diff --git a/vendor/github.com/klauspost/compress/zstd/decoder.go b/vendor/github.com/klauspost/compress/zstd/decoder.go index cdda0de58..62fd37324 100644 --- a/vendor/github.com/klauspost/compress/zstd/decoder.go +++ b/vendor/github.com/klauspost/compress/zstd/decoder.go @@ -85,6 +85,10 @@ func NewReader(r io.Reader, opts ...DOption) (*Decoder, error) { d.current.output = make(chan decodeOutput, d.o.concurrent) d.current.flushed = true + if r == nil { + d.current.err = ErrDecoderNilInput + } + // Transfer option dicts. d.dicts = make(map[uint32]dict, len(d.o.dicts)) for _, dc := range d.o.dicts { @@ -111,7 +115,7 @@ func NewReader(r io.Reader, opts ...DOption) (*Decoder, error) { // When the stream is done, io.EOF will be returned. func (d *Decoder) Read(p []byte) (int, error) { if d.stream == nil { - return 0, errors.New("no input has been initialized") + return 0, ErrDecoderNilInput } var n int for { @@ -152,12 +156,20 @@ func (d *Decoder) Read(p []byte) (int, error) { // Reset will reset the decoder the supplied stream after the current has finished processing. // Note that this functionality cannot be used after Close has been called. +// Reset can be called with a nil reader to release references to the previous reader. +// After being called with a nil reader, no other operations than Reset or DecodeAll or Close +// should be used. func (d *Decoder) Reset(r io.Reader) error { if d.current.err == ErrDecoderClosed { return d.current.err } + + d.drainOutput() + if r == nil { - return errors.New("nil Reader sent as input") + d.current.err = ErrDecoderNilInput + d.current.flushed = true + return nil } if d.stream == nil { @@ -166,8 +178,6 @@ func (d *Decoder) Reset(r io.Reader) error { go d.startStreamDecoder(d.stream) } - d.drainOutput() - // If bytes buffer and < 1MB, do sync decoding anyway. if bb, ok := r.(*bytes.Buffer); ok && bb.Len() < 1<<20 { if debug { @@ -249,7 +259,7 @@ func (d *Decoder) drainOutput() { // Any error encountered during the write is also returned. func (d *Decoder) WriteTo(w io.Writer) (int64, error) { if d.stream == nil { - return 0, errors.New("no input has been initialized") + return 0, ErrDecoderNilInput } var n int64 for { diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec.go b/vendor/github.com/klauspost/compress/zstd/seqdec.go index b5c8ef133..1dd39e63b 100644 --- a/vendor/github.com/klauspost/compress/zstd/seqdec.go +++ b/vendor/github.com/klauspost/compress/zstd/seqdec.go @@ -181,11 +181,18 @@ func (s *sequenceDecs) decode(seqs int, br *bitReader, hist []byte) error { return fmt.Errorf("output (%d) bigger than max block size", size) } if size > cap(s.out) { - // Not enough size, will be extremely rarely triggered, + // Not enough size, which can happen under high volume block streaming conditions // but could be if destination slice is too small for sync operations. - // We add maxBlockSize to the capacity. - s.out = append(s.out, make([]byte, maxBlockSize)...) - s.out = s.out[:len(s.out)-maxBlockSize] + // over-allocating here can create a large amount of GC pressure so we try to keep + // it as contained as possible + used := len(s.out) - startSize + addBytes := 256 + ll + ml + used>>2 + // Clamp to max block size. + if used+addBytes > maxBlockSize { + addBytes = maxBlockSize - used + } + s.out = append(s.out, make([]byte, addBytes)...) + s.out = s.out[:len(s.out)-addBytes] } if ml > maxMatchLen { return fmt.Errorf("match len (%d) bigger than max allowed length", ml) diff --git a/vendor/github.com/klauspost/compress/zstd/zstd.go b/vendor/github.com/klauspost/compress/zstd/zstd.go index 0807719c8..0c761dd62 100644 --- a/vendor/github.com/klauspost/compress/zstd/zstd.go +++ b/vendor/github.com/klauspost/compress/zstd/zstd.go @@ -73,6 +73,10 @@ var ( // ErrDecoderClosed will be returned if the Decoder was used after // Close has been called. ErrDecoderClosed = errors.New("decoder used after Close") + + // ErrDecoderNilInput is returned when a nil Reader was provided + // and an operation other than Reset/DecodeAll/Close was attempted. + ErrDecoderNilInput = errors.New("nil input provided as reader") ) func println(a ...interface{}) { diff --git a/vendor/github.com/moby/term/go.mod b/vendor/github.com/moby/term/go.mod index 4088df8db..f45320433 100644 --- a/vendor/github.com/moby/term/go.mod +++ b/vendor/github.com/moby/term/go.mod @@ -4,7 +4,7 @@ go 1.13 require ( github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 - github.com/creack/pty v1.1.9 + github.com/creack/pty v1.1.11 github.com/google/go-cmp v0.4.0 github.com/pkg/errors v0.9.1 // indirect golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a diff --git a/vendor/github.com/moby/term/go.sum b/vendor/github.com/moby/term/go.sum index 64d7ef7fa..441e06137 100644 --- a/vendor/github.com/moby/term/go.sum +++ b/vendor/github.com/moby/term/go.sum @@ -1,7 +1,7 @@ github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= +github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= diff --git a/vendor/github.com/moby/term/term_windows.go b/vendor/github.com/moby/term/term_windows.go index 2e512759e..ba82960d4 100644 --- a/vendor/github.com/moby/term/term_windows.go +++ b/vendor/github.com/moby/term/term_windows.go @@ -71,19 +71,22 @@ func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) { // go-ansiterm hasn't switch to x/sys/windows. // TODO: switch back to x/sys/windows once go-ansiterm has switched if emulateStdin { - stdIn = windowsconsole.NewAnsiReader(windows.STD_INPUT_HANDLE) + h := uint32(windows.STD_INPUT_HANDLE) + stdIn = windowsconsole.NewAnsiReader(int(h)) } else { stdIn = os.Stdin } if emulateStdout { - stdOut = windowsconsole.NewAnsiWriter(windows.STD_OUTPUT_HANDLE) + h := uint32(windows.STD_OUTPUT_HANDLE) + stdOut = windowsconsole.NewAnsiWriter(int(h)) } else { stdOut = os.Stdout } if emulateStderr { - stdErr = windowsconsole.NewAnsiWriter(windows.STD_ERROR_HANDLE) + h := uint32(windows.STD_ERROR_HANDLE) + stdErr = windowsconsole.NewAnsiWriter(int(h)) } else { stdErr = os.Stderr } diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare.go b/vendor/github.com/stretchr/testify/assert/assertion_compare.go index dc200395c..41649d267 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_compare.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_compare.go @@ -13,12 +13,42 @@ const ( compareGreater ) +var ( + intType = reflect.TypeOf(int(1)) + int8Type = reflect.TypeOf(int8(1)) + int16Type = reflect.TypeOf(int16(1)) + int32Type = reflect.TypeOf(int32(1)) + int64Type = reflect.TypeOf(int64(1)) + + uintType = reflect.TypeOf(uint(1)) + uint8Type = reflect.TypeOf(uint8(1)) + uint16Type = reflect.TypeOf(uint16(1)) + uint32Type = reflect.TypeOf(uint32(1)) + uint64Type = reflect.TypeOf(uint64(1)) + + float32Type = reflect.TypeOf(float32(1)) + float64Type = reflect.TypeOf(float64(1)) + + stringType = reflect.TypeOf("") +) + func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { + obj1Value := reflect.ValueOf(obj1) + obj2Value := reflect.ValueOf(obj2) + + // throughout this switch we try and avoid calling .Convert() if possible, + // as this has a pretty big performance impact switch kind { case reflect.Int: { - intobj1 := obj1.(int) - intobj2 := obj2.(int) + intobj1, ok := obj1.(int) + if !ok { + intobj1 = obj1Value.Convert(intType).Interface().(int) + } + intobj2, ok := obj2.(int) + if !ok { + intobj2 = obj2Value.Convert(intType).Interface().(int) + } if intobj1 > intobj2 { return compareGreater, true } @@ -31,8 +61,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { } case reflect.Int8: { - int8obj1 := obj1.(int8) - int8obj2 := obj2.(int8) + int8obj1, ok := obj1.(int8) + if !ok { + int8obj1 = obj1Value.Convert(int8Type).Interface().(int8) + } + int8obj2, ok := obj2.(int8) + if !ok { + int8obj2 = obj2Value.Convert(int8Type).Interface().(int8) + } if int8obj1 > int8obj2 { return compareGreater, true } @@ -45,8 +81,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { } case reflect.Int16: { - int16obj1 := obj1.(int16) - int16obj2 := obj2.(int16) + int16obj1, ok := obj1.(int16) + if !ok { + int16obj1 = obj1Value.Convert(int16Type).Interface().(int16) + } + int16obj2, ok := obj2.(int16) + if !ok { + int16obj2 = obj2Value.Convert(int16Type).Interface().(int16) + } if int16obj1 > int16obj2 { return compareGreater, true } @@ -59,8 +101,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { } case reflect.Int32: { - int32obj1 := obj1.(int32) - int32obj2 := obj2.(int32) + int32obj1, ok := obj1.(int32) + if !ok { + int32obj1 = obj1Value.Convert(int32Type).Interface().(int32) + } + int32obj2, ok := obj2.(int32) + if !ok { + int32obj2 = obj2Value.Convert(int32Type).Interface().(int32) + } if int32obj1 > int32obj2 { return compareGreater, true } @@ -73,8 +121,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { } case reflect.Int64: { - int64obj1 := obj1.(int64) - int64obj2 := obj2.(int64) + int64obj1, ok := obj1.(int64) + if !ok { + int64obj1 = obj1Value.Convert(int64Type).Interface().(int64) + } + int64obj2, ok := obj2.(int64) + if !ok { + int64obj2 = obj2Value.Convert(int64Type).Interface().(int64) + } if int64obj1 > int64obj2 { return compareGreater, true } @@ -87,8 +141,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { } case reflect.Uint: { - uintobj1 := obj1.(uint) - uintobj2 := obj2.(uint) + uintobj1, ok := obj1.(uint) + if !ok { + uintobj1 = obj1Value.Convert(uintType).Interface().(uint) + } + uintobj2, ok := obj2.(uint) + if !ok { + uintobj2 = obj2Value.Convert(uintType).Interface().(uint) + } if uintobj1 > uintobj2 { return compareGreater, true } @@ -101,8 +161,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { } case reflect.Uint8: { - uint8obj1 := obj1.(uint8) - uint8obj2 := obj2.(uint8) + uint8obj1, ok := obj1.(uint8) + if !ok { + uint8obj1 = obj1Value.Convert(uint8Type).Interface().(uint8) + } + uint8obj2, ok := obj2.(uint8) + if !ok { + uint8obj2 = obj2Value.Convert(uint8Type).Interface().(uint8) + } if uint8obj1 > uint8obj2 { return compareGreater, true } @@ -115,8 +181,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { } case reflect.Uint16: { - uint16obj1 := obj1.(uint16) - uint16obj2 := obj2.(uint16) + uint16obj1, ok := obj1.(uint16) + if !ok { + uint16obj1 = obj1Value.Convert(uint16Type).Interface().(uint16) + } + uint16obj2, ok := obj2.(uint16) + if !ok { + uint16obj2 = obj2Value.Convert(uint16Type).Interface().(uint16) + } if uint16obj1 > uint16obj2 { return compareGreater, true } @@ -129,8 +201,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { } case reflect.Uint32: { - uint32obj1 := obj1.(uint32) - uint32obj2 := obj2.(uint32) + uint32obj1, ok := obj1.(uint32) + if !ok { + uint32obj1 = obj1Value.Convert(uint32Type).Interface().(uint32) + } + uint32obj2, ok := obj2.(uint32) + if !ok { + uint32obj2 = obj2Value.Convert(uint32Type).Interface().(uint32) + } if uint32obj1 > uint32obj2 { return compareGreater, true } @@ -143,8 +221,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { } case reflect.Uint64: { - uint64obj1 := obj1.(uint64) - uint64obj2 := obj2.(uint64) + uint64obj1, ok := obj1.(uint64) + if !ok { + uint64obj1 = obj1Value.Convert(uint64Type).Interface().(uint64) + } + uint64obj2, ok := obj2.(uint64) + if !ok { + uint64obj2 = obj2Value.Convert(uint64Type).Interface().(uint64) + } if uint64obj1 > uint64obj2 { return compareGreater, true } @@ -157,8 +241,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { } case reflect.Float32: { - float32obj1 := obj1.(float32) - float32obj2 := obj2.(float32) + float32obj1, ok := obj1.(float32) + if !ok { + float32obj1 = obj1Value.Convert(float32Type).Interface().(float32) + } + float32obj2, ok := obj2.(float32) + if !ok { + float32obj2 = obj2Value.Convert(float32Type).Interface().(float32) + } if float32obj1 > float32obj2 { return compareGreater, true } @@ -171,8 +261,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { } case reflect.Float64: { - float64obj1 := obj1.(float64) - float64obj2 := obj2.(float64) + float64obj1, ok := obj1.(float64) + if !ok { + float64obj1 = obj1Value.Convert(float64Type).Interface().(float64) + } + float64obj2, ok := obj2.(float64) + if !ok { + float64obj2 = obj2Value.Convert(float64Type).Interface().(float64) + } if float64obj1 > float64obj2 { return compareGreater, true } @@ -185,8 +281,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { } case reflect.String: { - stringobj1 := obj1.(string) - stringobj2 := obj2.(string) + stringobj1, ok := obj1.(string) + if !ok { + stringobj1 = obj1Value.Convert(stringType).Interface().(string) + } + stringobj2, ok := obj2.(string) + if !ok { + stringobj2 = obj2Value.Convert(stringType).Interface().(string) + } if stringobj1 > stringobj2 { return compareGreater, true } @@ -240,6 +342,24 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs) } +// Positive asserts that the specified element is positive +// +// assert.Positive(t, 1) +// assert.Positive(t, 1.23) +func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { + zero := reflect.Zero(reflect.TypeOf(e)) + return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs) +} + +// Negative asserts that the specified element is negative +// +// assert.Negative(t, -1) +// assert.Negative(t, -1.23) +func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { + zero := reflect.Zero(reflect.TypeOf(e)) + return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs) +} + func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go index 49370eb16..4dfd1229a 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -114,6 +114,24 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) bool { return Error(t, err, append([]interface{}{msg}, args...)...) } +// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. +// This is a wrapper for errors.As. +func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return ErrorAs(t, err, target, append([]interface{}{msg}, args...)...) +} + +// ErrorIsf asserts that at least one of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return ErrorIs(t, err, target, append([]interface{}{msg}, args...)...) +} + // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // @@ -321,6 +339,54 @@ func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsil return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...) } +// IsDecreasingf asserts that the collection is decreasing +// +// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return IsDecreasing(t, object, append([]interface{}{msg}, args...)...) +} + +// IsIncreasingf asserts that the collection is increasing +// +// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return IsIncreasing(t, object, append([]interface{}{msg}, args...)...) +} + +// IsNonDecreasingf asserts that the collection is not decreasing +// +// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return IsNonDecreasing(t, object, append([]interface{}{msg}, args...)...) +} + +// IsNonIncreasingf asserts that the collection is not increasing +// +// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return IsNonIncreasing(t, object, append([]interface{}{msg}, args...)...) +} + // IsTypef asserts that the specified objects are of the same type. func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { @@ -375,6 +441,17 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args . return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...) } +// Negativef asserts that the specified element is negative +// +// assert.Negativef(t, -1, "error message %s", "formatted") +// assert.Negativef(t, -1.23, "error message %s", "formatted") +func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Negative(t, e, append([]interface{}{msg}, args...)...) +} + // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // @@ -476,6 +553,15 @@ func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg s return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...) } +// NotErrorIsf asserts that at none of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotErrorIs(t, err, target, append([]interface{}{msg}, args...)...) +} + // NotNilf asserts that the specified object is not nil. // // assert.NotNilf(t, err, "error message %s", "formatted") @@ -572,6 +658,17 @@ func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg str return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...) } +// Positivef asserts that the specified element is positive +// +// assert.Positivef(t, 1, "error message %s", "formatted") +// assert.Positivef(t, 1.23, "error message %s", "formatted") +func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Positive(t, e, append([]interface{}{msg}, args...)...) +} + // Regexpf asserts that a specified regexp matches a string. // // assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go index 9db889427..25337a6f0 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -204,6 +204,42 @@ func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool { return Error(a.t, err, msgAndArgs...) } +// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. +// This is a wrapper for errors.As. +func (a *Assertions) ErrorAs(err error, target interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ErrorAs(a.t, err, target, msgAndArgs...) +} + +// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. +// This is a wrapper for errors.As. +func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ErrorAsf(a.t, err, target, msg, args...) +} + +// ErrorIs asserts that at least one of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ErrorIs(a.t, err, target, msgAndArgs...) +} + +// ErrorIsf asserts that at least one of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ErrorIsf(a.t, err, target, msg, args...) +} + // Errorf asserts that a function returned an error (i.e. not `nil`). // // actualObj, err := SomeFunction() @@ -631,6 +667,102 @@ func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilo return InEpsilonf(a.t, expected, actual, epsilon, msg, args...) } +// IsDecreasing asserts that the collection is decreasing +// +// a.IsDecreasing([]int{2, 1, 0}) +// a.IsDecreasing([]float{2, 1}) +// a.IsDecreasing([]string{"b", "a"}) +func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsDecreasing(a.t, object, msgAndArgs...) +} + +// IsDecreasingf asserts that the collection is decreasing +// +// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") +// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") +func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsDecreasingf(a.t, object, msg, args...) +} + +// IsIncreasing asserts that the collection is increasing +// +// a.IsIncreasing([]int{1, 2, 3}) +// a.IsIncreasing([]float{1, 2}) +// a.IsIncreasing([]string{"a", "b"}) +func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsIncreasing(a.t, object, msgAndArgs...) +} + +// IsIncreasingf asserts that the collection is increasing +// +// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") +// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") +func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsIncreasingf(a.t, object, msg, args...) +} + +// IsNonDecreasing asserts that the collection is not decreasing +// +// a.IsNonDecreasing([]int{1, 1, 2}) +// a.IsNonDecreasing([]float{1, 2}) +// a.IsNonDecreasing([]string{"a", "b"}) +func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsNonDecreasing(a.t, object, msgAndArgs...) +} + +// IsNonDecreasingf asserts that the collection is not decreasing +// +// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") +func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsNonDecreasingf(a.t, object, msg, args...) +} + +// IsNonIncreasing asserts that the collection is not increasing +// +// a.IsNonIncreasing([]int{2, 1, 1}) +// a.IsNonIncreasing([]float{2, 1}) +// a.IsNonIncreasing([]string{"b", "a"}) +func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsNonIncreasing(a.t, object, msgAndArgs...) +} + +// IsNonIncreasingf asserts that the collection is not increasing +// +// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") +func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsNonIncreasingf(a.t, object, msg, args...) +} + // IsType asserts that the specified objects are of the same type. func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { @@ -739,6 +871,28 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i return Lessf(a.t, e1, e2, msg, args...) } +// Negative asserts that the specified element is negative +// +// a.Negative(-1) +// a.Negative(-1.23) +func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Negative(a.t, e, msgAndArgs...) +} + +// Negativef asserts that the specified element is negative +// +// a.Negativef(-1, "error message %s", "formatted") +// a.Negativef(-1.23, "error message %s", "formatted") +func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Negativef(a.t, e, msg, args...) +} + // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // @@ -941,6 +1095,24 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str return NotEqualf(a.t, expected, actual, msg, args...) } +// NotErrorIs asserts that at none of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotErrorIs(a.t, err, target, msgAndArgs...) +} + +// NotErrorIsf asserts that at none of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotErrorIsf(a.t, err, target, msg, args...) +} + // NotNil asserts that the specified object is not nil. // // a.NotNil(err) @@ -1133,6 +1305,28 @@ func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) b return Panicsf(a.t, f, msg, args...) } +// Positive asserts that the specified element is positive +// +// a.Positive(1) +// a.Positive(1.23) +func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Positive(a.t, e, msgAndArgs...) +} + +// Positivef asserts that the specified element is positive +// +// a.Positivef(1, "error message %s", "formatted") +// a.Positivef(1.23, "error message %s", "formatted") +func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Positivef(a.t, e, msg, args...) +} + // Regexp asserts that a specified regexp matches a string. // // a.Regexp(regexp.MustCompile("start"), "it's starting") diff --git a/vendor/github.com/stretchr/testify/assert/assertion_order.go b/vendor/github.com/stretchr/testify/assert/assertion_order.go new file mode 100644 index 000000000..1c3b47182 --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/assertion_order.go @@ -0,0 +1,81 @@ +package assert + +import ( + "fmt" + "reflect" +) + +// isOrdered checks that collection contains orderable elements. +func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool { + objKind := reflect.TypeOf(object).Kind() + if objKind != reflect.Slice && objKind != reflect.Array { + return false + } + + objValue := reflect.ValueOf(object) + objLen := objValue.Len() + + if objLen <= 1 { + return true + } + + value := objValue.Index(0) + valueInterface := value.Interface() + firstValueKind := value.Kind() + + for i := 1; i < objLen; i++ { + prevValue := value + prevValueInterface := valueInterface + + value = objValue.Index(i) + valueInterface = value.Interface() + + compareResult, isComparable := compare(prevValueInterface, valueInterface, firstValueKind) + + if !isComparable { + return Fail(t, fmt.Sprintf("Can not compare type \"%s\" and \"%s\"", reflect.TypeOf(value), reflect.TypeOf(prevValue)), msgAndArgs...) + } + + if !containsValue(allowedComparesResults, compareResult) { + return Fail(t, fmt.Sprintf(failMessage, prevValue, value), msgAndArgs...) + } + } + + return true +} + +// IsIncreasing asserts that the collection is increasing +// +// assert.IsIncreasing(t, []int{1, 2, 3}) +// assert.IsIncreasing(t, []float{1, 2}) +// assert.IsIncreasing(t, []string{"a", "b"}) +func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs) +} + +// IsNonIncreasing asserts that the collection is not increasing +// +// assert.IsNonIncreasing(t, []int{2, 1, 1}) +// assert.IsNonIncreasing(t, []float{2, 1}) +// assert.IsNonIncreasing(t, []string{"b", "a"}) +func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs) +} + +// IsDecreasing asserts that the collection is decreasing +// +// assert.IsDecreasing(t, []int{2, 1, 0}) +// assert.IsDecreasing(t, []float{2, 1}) +// assert.IsDecreasing(t, []string{"b", "a"}) +func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs) +} + +// IsNonDecreasing asserts that the collection is not decreasing +// +// assert.IsNonDecreasing(t, []int{1, 1, 2}) +// assert.IsNonDecreasing(t, []float{1, 2}) +// assert.IsNonDecreasing(t, []string{"a", "b"}) +func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs) +} diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go index 914a10d83..bcac4401f 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -172,8 +172,8 @@ func isTest(name, prefix string) bool { if len(name) == len(prefix) { // "Test" is ok return true } - rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) - return !unicode.IsLower(rune) + r, _ := utf8.DecodeRuneInString(name[len(prefix):]) + return !unicode.IsLower(r) } func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { @@ -1622,6 +1622,7 @@ var spewConfig = spew.ConfigState{ DisableCapacities: true, SortKeys: true, DisableMethods: true, + MaxDepth: 10, } type tHelper interface { @@ -1693,3 +1694,81 @@ func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.D } } } + +// ErrorIs asserts that at least one of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if errors.Is(err, target) { + return true + } + + var expectedText string + if target != nil { + expectedText = target.Error() + } + + chain := buildErrorChainString(err) + + return Fail(t, fmt.Sprintf("Target error should be in err chain:\n"+ + "expected: %q\n"+ + "in chain: %s", expectedText, chain, + ), msgAndArgs...) +} + +// NotErrorIs asserts that at none of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if !errors.Is(err, target) { + return true + } + + var expectedText string + if target != nil { + expectedText = target.Error() + } + + chain := buildErrorChainString(err) + + return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ + "found: %q\n"+ + "in chain: %s", expectedText, chain, + ), msgAndArgs...) +} + +// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. +// This is a wrapper for errors.As. +func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if errors.As(err, target) { + return true + } + + chain := buildErrorChainString(err) + + return Fail(t, fmt.Sprintf("Should be in error chain:\n"+ + "expected: %q\n"+ + "in chain: %s", target, chain, + ), msgAndArgs...) +} + +func buildErrorChainString(err error) string { + if err == nil { + return "" + } + + e := errors.Unwrap(err) + chain := fmt.Sprintf("%q", err.Error()) + for e != nil { + chain += fmt.Sprintf("\n\t%q", e.Error()) + e = errors.Unwrap(e) + } + return chain +} diff --git a/vendor/github.com/stretchr/testify/require/require.go b/vendor/github.com/stretchr/testify/require/require.go index ec4624b28..51820df2e 100644 --- a/vendor/github.com/stretchr/testify/require/require.go +++ b/vendor/github.com/stretchr/testify/require/require.go @@ -256,6 +256,54 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) { t.FailNow() } +// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. +// This is a wrapper for errors.As. +func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ErrorAs(t, err, target, msgAndArgs...) { + return + } + t.FailNow() +} + +// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. +// This is a wrapper for errors.As. +func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ErrorAsf(t, err, target, msg, args...) { + return + } + t.FailNow() +} + +// ErrorIs asserts that at least one of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func ErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ErrorIs(t, err, target, msgAndArgs...) { + return + } + t.FailNow() +} + +// ErrorIsf asserts that at least one of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ErrorIsf(t, err, target, msg, args...) { + return + } + t.FailNow() +} + // Errorf asserts that a function returned an error (i.e. not `nil`). // // actualObj, err := SomeFunction() @@ -806,6 +854,126 @@ func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon fl t.FailNow() } +// IsDecreasing asserts that the collection is decreasing +// +// assert.IsDecreasing(t, []int{2, 1, 0}) +// assert.IsDecreasing(t, []float{2, 1}) +// assert.IsDecreasing(t, []string{"b", "a"}) +func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsDecreasing(t, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// IsDecreasingf asserts that the collection is decreasing +// +// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsDecreasingf(t, object, msg, args...) { + return + } + t.FailNow() +} + +// IsIncreasing asserts that the collection is increasing +// +// assert.IsIncreasing(t, []int{1, 2, 3}) +// assert.IsIncreasing(t, []float{1, 2}) +// assert.IsIncreasing(t, []string{"a", "b"}) +func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsIncreasing(t, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// IsIncreasingf asserts that the collection is increasing +// +// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsIncreasingf(t, object, msg, args...) { + return + } + t.FailNow() +} + +// IsNonDecreasing asserts that the collection is not decreasing +// +// assert.IsNonDecreasing(t, []int{1, 1, 2}) +// assert.IsNonDecreasing(t, []float{1, 2}) +// assert.IsNonDecreasing(t, []string{"a", "b"}) +func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsNonDecreasing(t, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// IsNonDecreasingf asserts that the collection is not decreasing +// +// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsNonDecreasingf(t, object, msg, args...) { + return + } + t.FailNow() +} + +// IsNonIncreasing asserts that the collection is not increasing +// +// assert.IsNonIncreasing(t, []int{2, 1, 1}) +// assert.IsNonIncreasing(t, []float{2, 1}) +// assert.IsNonIncreasing(t, []string{"b", "a"}) +func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsNonIncreasing(t, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// IsNonIncreasingf asserts that the collection is not increasing +// +// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsNonIncreasingf(t, object, msg, args...) { + return + } + t.FailNow() +} + // IsType asserts that the specified objects are of the same type. func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { @@ -944,6 +1112,34 @@ func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...inter t.FailNow() } +// Negative asserts that the specified element is negative +// +// assert.Negative(t, -1) +// assert.Negative(t, -1.23) +func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Negative(t, e, msgAndArgs...) { + return + } + t.FailNow() +} + +// Negativef asserts that the specified element is negative +// +// assert.Negativef(t, -1, "error message %s", "formatted") +// assert.Negativef(t, -1.23, "error message %s", "formatted") +func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Negativef(t, e, msg, args...) { + return + } + t.FailNow() +} + // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // @@ -1200,6 +1396,30 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, t.FailNow() } +// NotErrorIs asserts that at none of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func NotErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotErrorIs(t, err, target, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotErrorIsf asserts that at none of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotErrorIsf(t, err, target, msg, args...) { + return + } + t.FailNow() +} + // NotNil asserts that the specified object is not nil. // // assert.NotNil(t, err) @@ -1446,6 +1666,34 @@ func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{} t.FailNow() } +// Positive asserts that the specified element is positive +// +// assert.Positive(t, 1) +// assert.Positive(t, 1.23) +func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Positive(t, e, msgAndArgs...) { + return + } + t.FailNow() +} + +// Positivef asserts that the specified element is positive +// +// assert.Positivef(t, 1, "error message %s", "formatted") +// assert.Positivef(t, 1.23, "error message %s", "formatted") +func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Positivef(t, e, msg, args...) { + return + } + t.FailNow() +} + // Regexp asserts that a specified regexp matches a string. // // assert.Regexp(t, regexp.MustCompile("start"), "it's starting") diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go b/vendor/github.com/stretchr/testify/require/require_forward.go index 103d7dcb6..ed54a9d83 100644 --- a/vendor/github.com/stretchr/testify/require/require_forward.go +++ b/vendor/github.com/stretchr/testify/require/require_forward.go @@ -205,6 +205,42 @@ func (a *Assertions) Error(err error, msgAndArgs ...interface{}) { Error(a.t, err, msgAndArgs...) } +// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. +// This is a wrapper for errors.As. +func (a *Assertions) ErrorAs(err error, target interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ErrorAs(a.t, err, target, msgAndArgs...) +} + +// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. +// This is a wrapper for errors.As. +func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ErrorAsf(a.t, err, target, msg, args...) +} + +// ErrorIs asserts that at least one of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ErrorIs(a.t, err, target, msgAndArgs...) +} + +// ErrorIsf asserts that at least one of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ErrorIsf(a.t, err, target, msg, args...) +} + // Errorf asserts that a function returned an error (i.e. not `nil`). // // actualObj, err := SomeFunction() @@ -632,6 +668,102 @@ func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilo InEpsilonf(a.t, expected, actual, epsilon, msg, args...) } +// IsDecreasing asserts that the collection is decreasing +// +// a.IsDecreasing([]int{2, 1, 0}) +// a.IsDecreasing([]float{2, 1}) +// a.IsDecreasing([]string{"b", "a"}) +func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsDecreasing(a.t, object, msgAndArgs...) +} + +// IsDecreasingf asserts that the collection is decreasing +// +// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") +// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") +func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsDecreasingf(a.t, object, msg, args...) +} + +// IsIncreasing asserts that the collection is increasing +// +// a.IsIncreasing([]int{1, 2, 3}) +// a.IsIncreasing([]float{1, 2}) +// a.IsIncreasing([]string{"a", "b"}) +func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsIncreasing(a.t, object, msgAndArgs...) +} + +// IsIncreasingf asserts that the collection is increasing +// +// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") +// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") +func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsIncreasingf(a.t, object, msg, args...) +} + +// IsNonDecreasing asserts that the collection is not decreasing +// +// a.IsNonDecreasing([]int{1, 1, 2}) +// a.IsNonDecreasing([]float{1, 2}) +// a.IsNonDecreasing([]string{"a", "b"}) +func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsNonDecreasing(a.t, object, msgAndArgs...) +} + +// IsNonDecreasingf asserts that the collection is not decreasing +// +// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") +func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsNonDecreasingf(a.t, object, msg, args...) +} + +// IsNonIncreasing asserts that the collection is not increasing +// +// a.IsNonIncreasing([]int{2, 1, 1}) +// a.IsNonIncreasing([]float{2, 1}) +// a.IsNonIncreasing([]string{"b", "a"}) +func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsNonIncreasing(a.t, object, msgAndArgs...) +} + +// IsNonIncreasingf asserts that the collection is not increasing +// +// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") +func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsNonIncreasingf(a.t, object, msg, args...) +} + // IsType asserts that the specified objects are of the same type. func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { @@ -740,6 +872,28 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i Lessf(a.t, e1, e2, msg, args...) } +// Negative asserts that the specified element is negative +// +// a.Negative(-1) +// a.Negative(-1.23) +func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Negative(a.t, e, msgAndArgs...) +} + +// Negativef asserts that the specified element is negative +// +// a.Negativef(-1, "error message %s", "formatted") +// a.Negativef(-1.23, "error message %s", "formatted") +func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Negativef(a.t, e, msg, args...) +} + // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // @@ -942,6 +1096,24 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str NotEqualf(a.t, expected, actual, msg, args...) } +// NotErrorIs asserts that at none of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotErrorIs(a.t, err, target, msgAndArgs...) +} + +// NotErrorIsf asserts that at none of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotErrorIsf(a.t, err, target, msg, args...) +} + // NotNil asserts that the specified object is not nil. // // a.NotNil(err) @@ -1134,6 +1306,28 @@ func (a *Assertions) Panicsf(f assert.PanicTestFunc, msg string, args ...interfa Panicsf(a.t, f, msg, args...) } +// Positive asserts that the specified element is positive +// +// a.Positive(1) +// a.Positive(1.23) +func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Positive(a.t, e, msgAndArgs...) +} + +// Positivef asserts that the specified element is positive +// +// a.Positivef(1, "error message %s", "formatted") +// a.Positivef(1.23, "error message %s", "formatted") +func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Positivef(a.t, e, msg, args...) +} + // Regexp asserts that a specified regexp matches a string. // // a.Regexp(regexp.MustCompile("start"), "it's starting") diff --git a/vendor/modules.txt b/vendor/modules.txt index 9688f84e3..fc2b14d96 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -70,7 +70,7 @@ github.com/containernetworking/plugins/pkg/utils/hwaddr github.com/containernetworking/plugins/pkg/utils/sysctl github.com/containernetworking/plugins/plugins/ipam/host-local/backend github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator -# github.com/containers/buildah v1.18.1-0.20201217112226-67470615779c +# github.com/containers/buildah v1.19.2 github.com/containers/buildah github.com/containers/buildah/bind github.com/containers/buildah/chroot @@ -89,7 +89,7 @@ github.com/containers/buildah/pkg/parse github.com/containers/buildah/pkg/rusage github.com/containers/buildah/pkg/supplemented github.com/containers/buildah/util -# github.com/containers/common v0.31.2 +# github.com/containers/common v0.33.1 github.com/containers/common/pkg/apparmor github.com/containers/common/pkg/apparmor/internal/supported github.com/containers/common/pkg/auth @@ -97,6 +97,7 @@ github.com/containers/common/pkg/capabilities github.com/containers/common/pkg/cgroupv2 github.com/containers/common/pkg/completion github.com/containers/common/pkg/config +github.com/containers/common/pkg/parse github.com/containers/common/pkg/report github.com/containers/common/pkg/report/camelcase github.com/containers/common/pkg/retry @@ -163,7 +164,7 @@ github.com/containers/ocicrypt/keywrap/pgp github.com/containers/ocicrypt/keywrap/pkcs7 github.com/containers/ocicrypt/spec github.com/containers/ocicrypt/utils -# github.com/containers/psgo v1.5.1 +# github.com/containers/psgo v1.5.2 github.com/containers/psgo github.com/containers/psgo/internal/capabilities github.com/containers/psgo/internal/cgroups @@ -171,7 +172,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.24.4 +# github.com/containers/storage v1.24.5 github.com/containers/storage github.com/containers/storage/drivers github.com/containers/storage/drivers/aufs @@ -223,7 +224,7 @@ github.com/coreos/go-systemd/v22/dbus github.com/coreos/go-systemd/v22/internal/dlopen github.com/coreos/go-systemd/v22/journal github.com/coreos/go-systemd/v22/sdjournal -# github.com/cri-o/ocicni v0.2.1-0.20201125151022-df072ea5421c +# github.com/cri-o/ocicni v0.2.1-0.20201204103948-b6cbe99b9756 github.com/cri-o/ocicni/pkg/ocicni # github.com/cyphar/filepath-securejoin v0.2.2 github.com/cyphar/filepath-securejoin @@ -241,7 +242,7 @@ github.com/docker/distribution/registry/client/auth/challenge github.com/docker/distribution/registry/client/transport github.com/docker/distribution/registry/storage/cache github.com/docker/distribution/registry/storage/cache/memory -# github.com/docker/docker v17.12.0-ce-rc1.0.20201020191947-73dc6a680cdd+incompatible +# github.com/docker/docker v20.10.0-beta1.0.20201113105859-b6bfff2a628f+incompatible github.com/docker/docker/api github.com/docker/docker/api/types github.com/docker/docker/api/types/blkiodev @@ -318,7 +319,7 @@ github.com/golang/protobuf/ptypes/timestamp github.com/google/gofuzz # github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf github.com/google/shlex -# github.com/google/uuid v1.1.2 +# github.com/google/uuid v1.1.5 github.com/google/uuid # github.com/gorilla/mux v1.8.0 github.com/gorilla/mux @@ -347,7 +348,7 @@ github.com/json-iterator/go # github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a github.com/juju/ansiterm github.com/juju/ansiterm/tabwriter -# github.com/klauspost/compress v1.11.4 +# github.com/klauspost/compress v1.11.5 github.com/klauspost/compress/flate github.com/klauspost/compress/fse github.com/klauspost/compress/huff0 @@ -378,7 +379,7 @@ github.com/mistifyio/go-zfs github.com/moby/sys/mount # github.com/moby/sys/mountinfo v0.4.0 github.com/moby/sys/mountinfo -# github.com/moby/term v0.0.0-20200915141129-7f0af18e79f2 +# github.com/moby/term v0.0.0-20201110203204-bea5bbe245bf github.com/moby/term github.com/moby/term/windows # github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd @@ -524,7 +525,7 @@ github.com/sirupsen/logrus/hooks/syslog github.com/spf13/cobra # github.com/spf13/pflag v1.0.5 github.com/spf13/pflag -# github.com/stretchr/testify v1.6.1 +# github.com/stretchr/testify v1.7.0 github.com/stretchr/testify/assert github.com/stretchr/testify/require # github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 @@ -627,7 +628,7 @@ golang.org/x/net/proxy # golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d golang.org/x/oauth2 golang.org/x/oauth2/internal -# golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 +# golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 golang.org/x/sync/errgroup golang.org/x/sync/semaphore # golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3 @@ -722,7 +723,7 @@ gopkg.in/yaml.v3 # k8s.io/api v0.0.0-20190620084959-7cf5895f2711 k8s.io/api/apps/v1 k8s.io/api/core/v1 -# k8s.io/apimachinery v0.20.1 +# k8s.io/apimachinery v0.20.2 k8s.io/apimachinery/pkg/api/errors k8s.io/apimachinery/pkg/api/resource k8s.io/apimachinery/pkg/apis/meta/v1 |