diff options
132 files changed, 2724 insertions, 779 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 18cb889ad..9233d9668 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -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..f87c8974f 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,5 @@ release.txt result # Necessary to prevent hack/tree-status.sh false-positive /*runner_stats.log +.install.goimports +.generate-bindings @@ -225,7 +225,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 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 4a477eb10..14abfd5a7 100644 --- a/cmd/podman/containers/checkpoint.go +++ b/cmd/podman/containers/checkpoint.go @@ -58,6 +58,9 @@ func init() { 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) } @@ -72,6 +75,9 @@ func checkpoint(cmd *cobra.Command, args []string) error { 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/restore.go b/cmd/podman/containers/restore.go index 5245a68fa..49c0be88e 100644 --- a/cmd/podman/containers/restore.go +++ b/cmd/podman/containers/restore.go @@ -59,6 +59,10 @@ 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") @@ -71,6 +75,9 @@ 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") } 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..c0aa27ca1 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() 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/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/system/prune.go b/cmd/podman/system/prune.go index 87bb947ed..5e96a654a 100644 --- a/cmd/podman/system/prune.go +++ b/cmd/podman/system/prune.go @@ -20,11 +20,11 @@ import ( var ( pruneOptions = entities.SystemPruneOptions{} filters []string - pruneDescription = fmt.Sprintf(` + pruneDescription = ` podman system prune Remove unused data -`) +` pruneCommand = &cobra.Command{ Use: "prune [options]", 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/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/markdown/podman-container-checkpoint.1.md b/docs/source/markdown/podman-container-checkpoint.1.md index 6a9469156..ea05979cd 100644 --- a/docs/source/markdown/podman-container-checkpoint.1.md +++ b/docs/source/markdown/podman-container-checkpoint.1.md @@ -58,12 +58,26 @@ 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 0593e6fe9..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 @@ -98,6 +103,8 @@ 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-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-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-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. @@ -11,13 +11,13 @@ require ( github.com/containernetworking/cni v0.8.0 github.com/containernetworking/plugins v0.9.0 github.com/containers/buildah v1.18.1-0.20201222143428-b9fdee076426 - github.com/containers/common v0.31.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.2 - github.com/containers/storage v1.24.4 + 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 @@ -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 @@ -68,6 +68,6 @@ require ( 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 ) @@ -99,8 +99,8 @@ github.com/containernetworking/plugins v0.9.0/go.mod h1:dbWv4dI0QrBGuVgj+TuVQ6wJ github.com/containers/buildah v1.18.1-0.20201222143428-b9fdee076426 h1:hgNSbIO7KUJ9jHSEHwM5D2qii5t/5f2yfxZepJFYm18= github.com/containers/buildah v1.18.1-0.20201222143428-b9fdee076426/go.mod h1:AM7JcGaUtTJgR6fZL2zBg5PCSCSDiX/sNdMSyrkoJ10= github.com/containers/common v0.31.1/go.mod h1:Fehe82hQfJQvDspnRrV9rcdAWG3IalNHEt0F6QWNBHQ= -github.com/containers/common v0.31.2 h1:sNYwvLA4B7SpEiAWTUvkItPlCrUa2vcxh0FTKXKoC3Q= -github.com/containers/common v0.31.2/go.mod h1:Fehe82hQfJQvDspnRrV9rcdAWG3IalNHEt0F6QWNBHQ= +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.9.0 h1:dRmUtcluQcmasNo3DpnRoZjfU0rOu1qZeL6wlDJr10Q= @@ -113,8 +113,8 @@ 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.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= @@ -131,8 +131,8 @@ 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/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= @@ -257,7 +257,6 @@ 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.4 h1:0ecGp3skIrHWPNGPJDaBIghfA6Sp7Ruo2Io8eLKzWm0= github.com/google/uuid v1.1.4/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -329,8 +328,8 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL 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/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= @@ -540,6 +539,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P 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= @@ -837,8 +838,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/libpod/container_api.go b/libpod/container_api.go index 12207ed0a..0d62a2dd7 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -712,6 +712,13 @@ type ContainerCheckpointOptions struct { // 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 @@ -724,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_inspect.go b/libpod/container_inspect.go index 51474471b..870d92ca9 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() diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 028710849..6d4d3ade0 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) @@ -1899,16 +1905,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) } @@ -2062,6 +2078,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 { diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index ac20e1f25..ce2b52234 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 { @@ -400,7 +399,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 { @@ -812,6 +811,9 @@ func (c *Container) exportCheckpoint(options ContainerCheckpointOptions) 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") @@ -1015,6 +1017,15 @@ 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); err != nil { return err @@ -1023,7 +1034,7 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO logrus.Debugf("Checkpointed container %s", c.ID()) - if !options.KeepRunning { + if !options.KeepRunning && !options.PreCheckPoint { c.state.State = define.ContainerStateStopped // Cleanup Storage and Network @@ -1032,7 +1043,7 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO } } - if !options.Keep { + if !options.Keep && !options.PreCheckPoint { cleanup := []string{ "dump.log", "stats-dump", @@ -1080,6 +1091,21 @@ func (c *Container) importCheckpoint(input string) error { 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 @@ -1089,6 +1115,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 @@ -1322,6 +1354,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) @@ -1482,18 +1518,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 } @@ -1710,7 +1742,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/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/errors.go b/libpod/define/errors.go index b96d36429..568f8e88d 100644 --- a/libpod/define/errors.go +++ b/libpod/define/errors.go @@ -2,6 +2,7 @@ package define import ( "errors" + "fmt" ) var ( @@ -181,4 +182,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/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/image/image.go b/libpod/image/image.go index a9082b2c6..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 } @@ -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 3c06a89c2..587c99333 100644 --- a/libpod/image/prune.go +++ b/libpod/image/prune.go @@ -29,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 } } 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/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/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 d6b63f25e..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" @@ -512,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 { @@ -520,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 16b9f0eac..23bfb29d7 100644 --- a/libpod/oci_conmon_linux.go +++ b/libpod/oci_conmon_linux.go @@ -529,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() { @@ -544,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 @@ -769,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") @@ -786,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 @@ -794,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...) } @@ -1374,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 ef7db3235..31c0b9ac9 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -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 { 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/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/libpod/images.go b/pkg/api/handlers/libpod/images.go index 979a8adc4..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" @@ -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/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/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/swagger.go b/pkg/api/server/swagger.go index 45253e01a..d4fc33442 100644 --- a/pkg/api/server/swagger.go +++ b/pkg/api/server/swagger.go @@ -5,6 +5,7 @@ import ( "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 @@ -12,7 +13,7 @@ import ( type swagErrNoSuchImage struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -21,7 +22,7 @@ type swagErrNoSuchImage struct { type swagErrNoSuchContainer struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -30,7 +31,7 @@ type swagErrNoSuchContainer struct { type swagErrNoSuchNetwork struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -39,7 +40,7 @@ type swagErrNoSuchNetwork struct { type swagErrNoSuchExecInstance struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -48,7 +49,7 @@ type swagErrNoSuchExecInstance struct { type swagErrNoSuchVolume struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -57,7 +58,7 @@ type swagErrNoSuchVolume struct { type swagErrNoSuchPod struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -66,7 +67,7 @@ type swagErrNoSuchPod struct { type swagErrNoSuchManifest struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -75,7 +76,7 @@ type swagErrNoSuchManifest struct { type swagInternalError struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -84,7 +85,7 @@ type swagInternalError struct { type swagConflictError struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -93,7 +94,7 @@ type swagConflictError struct { type swagBadParamError struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -102,7 +103,7 @@ type swagBadParamError struct { type swagContainerAlreadyStartedError struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -111,7 +112,7 @@ type swagContainerAlreadyStartedError struct { type swagContainerAlreadyStopped struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -120,7 +121,7 @@ type swagContainerAlreadyStopped struct { type swagPodAlreadyStartedError struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -129,7 +130,7 @@ type swagPodAlreadyStartedError struct { type swagPodAlreadyStopped struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } 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/images/images.go b/pkg/bindings/images/images.go index ecdd1f553..ae6962c8c 100644 --- a/pkg/bindings/images/images.go +++ b/pkg/bindings/images/images.go @@ -113,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 } 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/images_test.go b/pkg/bindings/test/images_test.go index c6b9c20f9..81959e813 100644 --- a/pkg/bindings/test/images_test.go +++ b/pkg/bindings/test/images_test.go @@ -219,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) @@ -234,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)) @@ -251,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() { diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index a67ecebd5..96687b1de 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -178,6 +178,8 @@ type CheckpointOptions struct { Latest bool LeaveRunning bool TCPEstablished bool + PreCheckPoint bool + WithPrevious bool } type CheckpointReport struct { @@ -196,6 +198,7 @@ type RestoreOptions struct { Latest bool Name string TCPEstablished bool + ImportPrevious string } type RestoreReport struct { diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index d5f88502a..0805152c3 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -256,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 edb0af15a..32900d536 100644 --- a/pkg/domain/entities/pods.go +++ b/pkg/domain/entities/pods.go @@ -212,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/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/filters/containers.go b/pkg/domain/filters/containers.go index 09ef6201a..1de5aca91 100644 --- a/pkg/domain/filters/containers.go +++ b/pkg/domain/filters/containers.go @@ -35,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 } diff --git a/pkg/domain/filters/pods.go b/pkg/domain/filters/pods.go index 685c182ba..ce7028d2a 100644 --- a/pkg/domain/filters/pods.go +++ b/pkg/domain/filters/pods.go @@ -124,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 } 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 f7a538934..b5f5a0e91 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -113,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 } @@ -134,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 } @@ -489,6 +473,8 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [ IgnoreRootfs: options.IgnoreRootFS, IgnoreVolumes: options.IgnoreVolumes, KeepRunning: options.LeaveRunning, + PreCheckPoint: options.PreCheckPoint, + WithPrevious: options.WithPrevious, } if options.All { @@ -529,6 +515,7 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st IgnoreVolumes: options.IgnoreVolumes, IgnoreStaticIP: options.IgnoreStaticIP, IgnoreStaticMAC: options.IgnoreStaticMAC, + ImportPrevious: options.ImportPrevious, } filterFuncs := []libpod.ContainerFilter{ diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 19f081abb..1c233d9d5 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -22,7 +22,6 @@ 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" @@ -206,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 @@ -454,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_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 67c018122..97fa9d374 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -162,11 +162,6 @@ 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) diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 3366cb425..49bcdec98 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -211,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 } diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index fba60235e..7a4aa1fbc 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -8,16 +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" ) @@ -187,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 } @@ -215,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 10e8d7da8..f21336828 100644 --- a/pkg/domain/infra/tunnel/volumes.go +++ b/pkg/domain/infra/tunnel/volumes.go @@ -6,6 +6,7 @@ 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" ) @@ -55,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 } 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/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 c771e8bc8..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) 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 3cd5a3c9c..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 diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index ba68de6fd..7dc32a314 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 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 fb921cd72..8901298db 100644 --- a/pkg/systemd/generate/common.go +++ b/pkg/systemd/generate/common.go @@ -71,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 cfa02dc9d..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 @@ -185,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 { @@ -210,23 +222,27 @@ 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 // @@ -240,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 @@ -247,14 +270,21 @@ 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 diff --git a/pkg/systemd/generate/containers_test.go b/pkg/systemd/generate/containers_test.go index b8f3a90f9..c8e65bfe3 100644 --- a/pkg/systemd/generate/containers_test.go +++ b/pkg/systemd/generate/containers_test.go @@ -122,9 +122,9 @@ 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 10 -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 Type=forking @@ -228,6 +228,107 @@ Type=forking 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] +WantedBy=multi-user.target default.target +` + tests := []struct { name string info containerInfo @@ -321,7 +422,7 @@ WantedBy=multi-user.target default.target 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, @@ -337,7 +438,7 @@ WantedBy=multi-user.target default.target 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", @@ -356,7 +457,7 @@ WantedBy=multi-user.target default.target 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, @@ -372,13 +473,141 @@ WantedBy=multi-user.target default.target 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 diff --git a/pkg/systemd/generate/pods.go b/pkg/systemd/generate/pods.go index fc582e42a..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 @@ -264,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 @@ -277,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") } @@ -296,9 +310,9 @@ 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 diff --git a/pkg/systemd/generate/pods_test.go b/pkg/systemd/generate/pods_test.go index 93432ef96..1c6330160 100644 --- a/pkg/systemd/generate/pods_test.go +++ b/pkg/systemd/generate/pods_test.go @@ -89,6 +89,60 @@ Type=forking 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] +WantedBy=multi-user.target default.target +` + tests := []struct { name string info podInfo @@ -106,6 +160,23 @@ WantedBy=multi-user.target default.target 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, @@ -127,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 { diff --git a/test/e2e/checkpoint_test.go b/test/e2e/checkpoint_test.go index 4c6b2cf5c..abc37792a 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" @@ -747,4 +748,78 @@ var _ = Describe("Podman checkpoint", func() { // 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/generate_systemd_test.go b/test/e2e/generate_systemd_test.go index 3f059300b..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,12 +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()) - - found, _ = session.GrepString("TimeoutStopSec=1294") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("TimeoutStopSec=1294")) + Expect(session.OutputToString()).To(ContainSubstring(" stop -t 1234 ")) }) It("podman generate systemd", func() { @@ -87,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() { @@ -101,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() { @@ -114,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() { @@ -137,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() { @@ -185,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() { @@ -202,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() { @@ -222,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() { @@ -242,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() { @@ -256,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() { @@ -280,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() { @@ -294,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() { @@ -316,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() { @@ -347,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() { @@ -386,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/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/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/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/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/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/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/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/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 141f62bf9..b5c20e0ca 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 |