diff options
274 files changed, 3268 insertions, 3018 deletions
@@ -39,6 +39,7 @@ LIBPOD_INSTANCE := libpod_dev PREFIX ?= /usr/local BINDIR ?= ${PREFIX}/bin LIBEXECDIR ?= ${PREFIX}/libexec +LIBEXECPODMAN ?= ${LIBEXECDIR}/podman MANDIR ?= ${PREFIX}/share/man SHAREDIR_CONTAINERS ?= ${PREFIX}/share/containers ETCDIR ?= ${PREFIX}/etc @@ -175,6 +176,15 @@ define go-get $(GO) get -u ${1} endef +# Need to use CGO for mDNS resolution, but cross builds need CGO disabled +# See https://github.com/golang/go/issues/12524 for details +DARWIN_GCO := 0 +ifeq ($(NATIVE_GOOS),darwin) +ifdef HOMEBREW_PREFIX + DARWIN_GCO := 1 +endif +endif + ### ### Primary entry-point targets ### @@ -186,7 +196,7 @@ default: all all: binaries docs .PHONY: binaries -binaries: podman podman-remote ## Build podman and podman-remote binaries +binaries: podman podman-remote rootlessport ## Build podman, podman-remote and rootlessport binaries # Extract text following double-# for targets, as their description for # the `help` target. Otherwise These simple-substitutions are resolved @@ -350,11 +360,20 @@ podman-remote-windows: ## Build podman-remote for Windows .PHONY: podman-remote-darwin podman-remote-darwin: ## Build podman-remote for macOS $(MAKE) \ - CGO_ENABLED=0 \ + CGO_ENABLED=$(DARWIN_GCO) \ GOOS=darwin \ GOARCH=$(GOARCH) \ bin/darwin/podman +bin/rootlessport: .gopathok $(SOURCES) go.mod go.sum + CGO_ENABLED=$(CGO_ENABLED) \ + $(GO) build \ + $(BUILDFLAGS) \ + -o $@ ./cmd/rootlessport + +.PHONY: rootlessport +rootlessport: bin/rootlessport + ### ### Secondary binary-build targets ### @@ -718,11 +737,14 @@ install.bin-nobuild: install ${SELINUXOPT} -d -m 755 $(DESTDIR)$(BINDIR) install ${SELINUXOPT} -m 755 bin/podman $(DESTDIR)$(BINDIR)/podman test -z "${SELINUXOPT}" || chcon --verbose --reference=$(DESTDIR)$(BINDIR)/podman bin/podman + install ${SELINUXOPT} -d -m 755 $(DESTDIR)$(LIBEXECPODMAN) + install ${SELINUXOPT} -m 755 bin/rootlessport $(DESTDIR)$(LIBEXECPODMAN)/rootlessport + test -z "${SELINUXOPT}" || chcon --verbose --reference=$(DESTDIR)$(LIBEXECPODMAN)/rootlessport bin/rootlessport install ${SELINUXOPT} -m 755 -d ${DESTDIR}${TMPFILESDIR} install ${SELINUXOPT} -m 644 contrib/tmpfile/podman.conf ${DESTDIR}${TMPFILESDIR}/podman.conf .PHONY: install.bin -install.bin: podman install.bin-nobuild +install.bin: podman rootlessport install.bin-nobuild .PHONY: install.man-nobuild install.man-nobuild: @@ -770,20 +792,29 @@ install.docker-full: install.docker install.docker-docs .PHONY: install.systemd ifneq (,$(findstring systemd,$(BUILDTAGS))) -install.systemd: +PODMAN_UNIT_FILES = contrib/systemd/auto-update/podman-auto-update.service \ + contrib/systemd/system/podman.service \ + contrib/systemd/system/podman-restart.service + +%.service: %.service.in + sed -e 's;@@PODMAN@@;$(BINDIR)/podman;g' $< >$@.tmp.$$ \ + && mv -f $@.tmp.$$ $@ + +install.systemd: $(PODMAN_UNIT_FILES) install ${SELINUXOPT} -m 755 -d ${DESTDIR}${SYSTEMDDIR} ${DESTDIR}${USERSYSTEMDDIR} # User services install ${SELINUXOPT} -m 644 contrib/systemd/auto-update/podman-auto-update.service ${DESTDIR}${USERSYSTEMDDIR}/podman-auto-update.service install ${SELINUXOPT} -m 644 contrib/systemd/auto-update/podman-auto-update.timer ${DESTDIR}${USERSYSTEMDDIR}/podman-auto-update.timer - install ${SELINUXOPT} -m 644 contrib/systemd/user/podman.socket ${DESTDIR}${USERSYSTEMDDIR}/podman.socket - install ${SELINUXOPT} -m 644 contrib/systemd/user/podman.service ${DESTDIR}${USERSYSTEMDDIR}/podman.service - install ${SELINUXOPT} -m 644 contrib/systemd/user/podman-restart.service ${DESTDIR}${USERSYSTEMDDIR}/podman-restart.service + install ${SELINUXOPT} -m 644 contrib/systemd/system/podman.socket ${DESTDIR}${USERSYSTEMDDIR}/podman.socket + install ${SELINUXOPT} -m 644 contrib/systemd/system/podman.service ${DESTDIR}${USERSYSTEMDDIR}/podman.service + install ${SELINUXOPT} -m 644 contrib/systemd/system/podman-restart.service ${DESTDIR}${USERSYSTEMDDIR}/podman-restart.service # System services install ${SELINUXOPT} -m 644 contrib/systemd/auto-update/podman-auto-update.service ${DESTDIR}${SYSTEMDDIR}/podman-auto-update.service install ${SELINUXOPT} -m 644 contrib/systemd/auto-update/podman-auto-update.timer ${DESTDIR}${SYSTEMDDIR}/podman-auto-update.timer install ${SELINUXOPT} -m 644 contrib/systemd/system/podman.socket ${DESTDIR}${SYSTEMDDIR}/podman.socket install ${SELINUXOPT} -m 644 contrib/systemd/system/podman.service ${DESTDIR}${SYSTEMDDIR}/podman.service install ${SELINUXOPT} -m 644 contrib/systemd/system/podman-restart.service ${DESTDIR}${SYSTEMDDIR}/podman-restart.service + rm -f $(PODMAN_UNIT_FILES) else install.systemd: endif @@ -41,8 +41,8 @@ Podman presently only supports running containers on Linux. However, we are buil If you think you've identified a security issue in the project, please *DO NOT* report the issue publicly via the GitHub issue tracker, mailing list, or IRC. Instead, send an email with as many details as possible to `security@lists.podman.io`. This is a private mailing list for the core maintainers. -For general questions and discussion, please use the -IRC `#podman` channel on `irc.libera.chat`. +For general questions and discussion, please use Podman's +[channels](https://podman.io/community/#slack-irc-matrix-and-discord). For discussions around issues/bugs and features, you can use the GitHub [issues](https://github.com/containers/podman/issues) diff --git a/RELEASE_PROCESS.md b/RELEASE_PROCESS.md index 32d4c039e..8a6fea18c 100644 --- a/RELEASE_PROCESS.md +++ b/RELEASE_PROCESS.md @@ -251,7 +251,7 @@ spelled with complete minutiae. binaries under the "binary", then "bin" links. Tar these files as `podman-static.tar.gz`. 1. The `podman-vX.Y.Z.dmg` file is produced manually by someone in - posession of a developer signing key. + possession of a developer signing key. 1. In the directory where you downloaded the archives, run `sha256sum *.tar.gz *.zip *.msi > shasums` to generate SHA sums. 1. Go to `https://github.com/containers/podman/releases/tag/vX.Y.Z` and diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index 09ac61f2e..50d7c446d 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -104,15 +104,18 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, rtc *c addField(&builder, "target", m.Target) addField(&builder, "ro", strconv.FormatBool(m.ReadOnly)) addField(&builder, "consistency", string(m.Consistency)) - // Map any specialized mount options that intersect between *Options and cli options switch m.Type { case mount.TypeBind: - addField(&builder, "bind-propagation", string(m.BindOptions.Propagation)) - addField(&builder, "bind-nonrecursive", strconv.FormatBool(m.BindOptions.NonRecursive)) + if m.BindOptions != nil { + addField(&builder, "bind-propagation", string(m.BindOptions.Propagation)) + addField(&builder, "bind-nonrecursive", strconv.FormatBool(m.BindOptions.NonRecursive)) + } case mount.TypeTmpfs: - addField(&builder, "tmpfs-size", strconv.FormatInt(m.TmpfsOptions.SizeBytes, 10)) - addField(&builder, "tmpfs-mode", strconv.FormatUint(uint64(m.TmpfsOptions.Mode), 10)) + if m.TmpfsOptions != nil { + addField(&builder, "tmpfs-size", strconv.FormatInt(m.TmpfsOptions.SizeBytes, 10)) + addField(&builder, "tmpfs-mode", strconv.FormatUint(uint64(m.TmpfsOptions.Mode), 10)) + } case mount.TypeVolume: // All current VolumeOpts are handled above // See vendor/github.com/containers/common/pkg/parse/parse.go:ValidateVolumeOpts() diff --git a/cmd/podman/containers/rm.go b/cmd/podman/containers/rm.go index abab71a79..70cb76693 100644 --- a/cmd/podman/containers/rm.go +++ b/cmd/podman/containers/rm.go @@ -62,6 +62,9 @@ func rmFlags(cmd *cobra.Command) { flags.BoolVarP(&rmOptions.All, "all", "a", false, "Remove all containers") flags.BoolVarP(&rmOptions.Ignore, "ignore", "i", false, "Ignore errors when a specified container is missing") flags.BoolVarP(&rmOptions.Force, "force", "f", false, "Force removal of a running or unusable container. The default is false") + timeFlagName := "time" + flags.UintVarP(&stopTimeout, timeFlagName, "t", containerConfig.Engine.StopTimeout, "Seconds to wait for stop before killing the container") + _ = cmd.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone) flags.BoolVarP(&rmOptions.Volumes, "volumes", "v", false, "Remove anonymous volumes associated with the container") cidfileFlagName := "cidfile" @@ -91,6 +94,12 @@ func init() { } func rm(cmd *cobra.Command, args []string) error { + if cmd.Flag("time").Changed { + if !rmOptions.Force { + return errors.New("--force option must be specified to use the --time option") + } + rmOptions.Timeout = &stopTimeout + } for _, cidFile := range cidFiles { content, err := ioutil.ReadFile(string(cidFile)) if err != nil { diff --git a/cmd/podman/containers/stats.go b/cmd/podman/containers/stats.go index 11e8f6870..d21feaabc 100644 --- a/cmd/podman/containers/stats.go +++ b/cmd/podman/containers/stats.go @@ -11,9 +11,7 @@ import ( "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/libpod/define" - "github.com/containers/podman/v3/pkg/cgroups" "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/podman/v3/utils" "github.com/docker/go-units" "github.com/pkg/errors" @@ -113,16 +111,6 @@ func checkStatOptions(cmd *cobra.Command, args []string) error { } func stats(cmd *cobra.Command, args []string) error { - if rootless.IsRootless() { - unified, err := cgroups.IsCgroup2UnifiedMode() - if err != nil { - return err - } - if !unified { - return errors.New("stats is not supported in rootless mode without cgroups v2") - } - } - // Convert to the entities options. We should not leak CLI-only // options into the backend and separate concerns. opts := entities.ContainerStatsOptions{ diff --git a/cmd/podman/diff/diff.go b/cmd/podman/diff/diff.go index 81bbb6c43..fba4ea540 100644 --- a/cmd/podman/diff/diff.go +++ b/cmd/podman/diff/diff.go @@ -8,7 +8,7 @@ import ( "github.com/containers/common/pkg/report" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/docker/docker/pkg/archive" + "github.com/containers/storage/pkg/archive" "github.com/pkg/errors" "github.com/spf13/cobra" ) diff --git a/cmd/podman/images/buildx.go b/cmd/podman/images/buildx.go index 5c8e5aaa0..2577a3a74 100644 --- a/cmd/podman/images/buildx.go +++ b/cmd/podman/images/buildx.go @@ -14,11 +14,12 @@ var ( // If we are adding new buildx features, we will add them by default // to podman build. buildxCmd = &cobra.Command{ - Use: "buildx", - Short: "Build images", - Long: "Build images", - RunE: validate.SubCommandExists, - Hidden: true, + Use: "buildx", + Aliases: []string{"builder"}, + Short: "Build images", + Long: "Build images", + RunE: validate.SubCommandExists, + Hidden: true, } ) diff --git a/cmd/podman/images/prune.go b/cmd/podman/images/prune.go index 6c39e5c69..fc7451c41 100644 --- a/cmd/podman/images/prune.go +++ b/cmd/podman/images/prune.go @@ -36,6 +36,11 @@ var ( func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ Command: pruneCmd, + Parent: buildxCmd, + }) + + registry.Commands = append(registry.Commands, registry.CliCommand{ + Command: pruneCmd, Parent: imageCmd, }) diff --git a/cmd/podman/images/search.go b/cmd/podman/images/search.go index 11e54578a..c9a4793aa 100644 --- a/cmd/podman/images/search.go +++ b/cmd/podman/images/search.go @@ -3,6 +3,7 @@ package images import ( "fmt" "os" + "strings" "github.com/containers/common/pkg/auth" "github.com/containers/common/pkg/completion" @@ -19,6 +20,7 @@ import ( type searchOptionsWrapper struct { entities.ImageSearchOptions // CLI only flags + Compatible bool // Docker compat TLSVerifyCLI bool // Used to convert to an optional bool later Format string // For go templating } @@ -79,7 +81,7 @@ func searchFlags(cmd *cobra.Command) { filterFlagName := "filter" flags.StringSliceVarP(&searchOptions.Filters, filterFlagName, "f", []string{}, "Filter output based on conditions provided (default [])") - //TODO add custom filter function + // TODO add custom filter function _ = cmd.RegisterFlagCompletionFunc(filterFlagName, completion.AutocompleteNone) formatFlagName := "format" @@ -90,7 +92,8 @@ func searchFlags(cmd *cobra.Command) { flags.IntVar(&searchOptions.Limit, limitFlagName, 0, "Limit the number of results") _ = cmd.RegisterFlagCompletionFunc(limitFlagName, completion.AutocompleteNone) - flags.BoolVar(&searchOptions.NoTrunc, "no-trunc", false, "Do not truncate the output") + flags.Bool("no-trunc", true, "Do not truncate the output. Default: true") + flags.BoolVar(&searchOptions.Compatible, "compatible", false, "List stars, official and automated columns (Docker compatibility)") authfileFlagName := "authfile" flags.StringVar(&searchOptions.Authfile, authfileFlagName, auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") @@ -132,11 +135,20 @@ func imageSearch(cmd *cobra.Command, args []string) error { if err != nil { return err } - if len(searchReport) == 0 { return nil } + noTrunc, _ := cmd.Flags().GetBool("no-trunc") + isJSON := report.IsJSON(searchOptions.Format) + for i, element := range searchReport { + d := strings.ReplaceAll(element.Description, "\n", " ") + if len(d) > 44 && !(noTrunc || isJSON) { + d = strings.TrimSpace(d[:44]) + "..." + } + searchReport[i].Description = d + } + hdrs := report.Headers(entities.ImageSearchReport{}, nil) renderHeaders := true var row string @@ -145,18 +157,22 @@ 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) { + if isJSON { listTagsEntries := buildListTagsJSON(searchReport) return printArbitraryJSON(listTagsEntries) } row = "{{.Name}}\t{{.Tag}}\n" - case report.IsJSON(searchOptions.Format): + case isJSON: return printArbitraryJSON(searchReport) case cmd.Flags().Changed("format"): renderHeaders = report.HasTable(searchOptions.Format) row = report.NormalizeFormat(searchOptions.Format) default: - row = "{{.Index}}\t{{.Name}}\t{{.Description}}\t{{.Stars}}\t{{.Official}}\t{{.Automated}}\n" + row = "{{.Name}}\t{{.Description}}" + if searchOptions.Compatible { + row += "\t{{.Stars}}\t{{.Official}}\t{{.Automated}}" + } + row += "\n" } format := report.EnforceRange(row) @@ -190,7 +206,7 @@ func printArbitraryJSON(v interface{}) error { } func buildListTagsJSON(searchReport []entities.ImageSearchReport) []listEntryTag { - entries := []listEntryTag{} + entries := make([]listEntryTag, 0) ReportLoop: for _, report := range searchReport { diff --git a/cmd/podman/machine/list.go b/cmd/podman/machine/list.go index 95b7d860f..7e5459e08 100644 --- a/cmd/podman/machine/list.go +++ b/cmd/podman/machine/list.go @@ -188,11 +188,13 @@ func toHumanFormat(vms []*machine.ListResponse) ([]*machineReporter, error) { response := new(machineReporter) if vm.Name == cfg.Engine.ActiveService { response.Name = vm.Name + "*" + response.Default = true } else { response.Name = vm.Name } if vm.Running { response.LastUp = "Currently running" + response.Running = true } else { response.LastUp = units.HumanDuration(time.Since(vm.LastUp)) + " ago" } diff --git a/cmd/podman/networks/network.go b/cmd/podman/networks/network.go index ec045e3cf..1070e7e82 100644 --- a/cmd/podman/networks/network.go +++ b/cmd/podman/networks/network.go @@ -3,6 +3,7 @@ package network import ( "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" + "github.com/containers/podman/v3/pkg/util" "github.com/spf13/cobra" ) @@ -17,6 +18,7 @@ var ( Long: "Manage networks", RunE: validate.SubCommandExists, } + containerConfig = util.DefaultContainerConfig() ) func init() { diff --git a/cmd/podman/networks/rm.go b/cmd/podman/networks/rm.go index 14f9869e4..5efd02933 100644 --- a/cmd/podman/networks/rm.go +++ b/cmd/podman/networks/rm.go @@ -4,6 +4,7 @@ import ( "fmt" "strings" + "github.com/containers/common/pkg/completion" "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/utils" @@ -26,6 +27,7 @@ var ( Args: cobra.MinimumNArgs(1), ValidArgsFunction: common.AutocompleteNetworks, } + stopTimeout uint ) var ( @@ -34,6 +36,9 @@ var ( func networkRmFlags(flags *pflag.FlagSet) { flags.BoolVarP(&networkRmOptions.Force, "force", "f", false, "remove any containers using network") + timeFlagName := "time" + flags.UintVarP(&stopTimeout, timeFlagName, "t", containerConfig.Engine.StopTimeout, "Seconds to wait for running containers to stop before killing the container") + _ = networkrmCommand.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone) } func init() { @@ -50,6 +55,12 @@ func networkRm(cmd *cobra.Command, args []string) error { errs utils.OutputErrors ) + if cmd.Flag("time").Changed { + if !networkRmOptions.Force { + return errors.New("--force option must be specified to use the --time option") + } + networkRmOptions.Timeout = &stopTimeout + } responses, err := registry.ContainerEngine().NetworkRm(registry.Context(), args, networkRmOptions) if err != nil { setExitCode(err) diff --git a/cmd/podman/play/kube.go b/cmd/podman/play/kube.go index 85e0c279c..e6869efd3 100644 --- a/cmd/podman/play/kube.go +++ b/cmd/podman/play/kube.go @@ -11,7 +11,9 @@ import ( "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/utils" + "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/pkg/errorhandling" "github.com/containers/podman/v3/pkg/util" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -90,6 +92,9 @@ func init() { downFlagName := "down" flags.BoolVar(&kubeOptions.Down, downFlagName, false, "Stop pods defined in the YAML file") + replaceFlagName := "replace" + flags.BoolVar(&kubeOptions.Replace, replaceFlagName, false, "Delete and recreate pods defined in the YAML file") + if !registry.IsRemote() { certDirFlagName := "cert-dir" flags.StringVar(&kubeOptions.CertDir, certDirFlagName, "", "`Pathname` of a directory containing TLS certificates and keys") @@ -151,6 +156,11 @@ func kube(cmd *cobra.Command, args []string) error { if kubeOptions.Down { return teardown(yamlfile) } + if kubeOptions.Replace { + if err := teardown(yamlfile); err != nil && !errorhandling.Contains(err, define.ErrNoSuchPod) { + return err + } + } return playkube(yamlfile) } diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go index d5aaf09ce..7c2c72171 100644 --- a/cmd/podman/pods/create.go +++ b/cmd/podman/pods/create.go @@ -132,7 +132,7 @@ func create(cmd *cobra.Command, args []string) error { } createOptions.Share = nil } else { - // reassign certain optios for lbpod api, these need to be populated in spec + // reassign certain options for lbpod api, these need to be populated in spec flags := cmd.Flags() infraOptions.Net, err = common.NetFlagsToNetOptions(nil, *flags, false) if err != nil { diff --git a/cmd/podman/pods/rm.go b/cmd/podman/pods/rm.go index dc4c7eb83..d8a09d774 100644 --- a/cmd/podman/pods/rm.go +++ b/cmd/podman/pods/rm.go @@ -42,6 +42,7 @@ var ( podman pod rm -f 860a4b23 podman pod rm -f -a`, } + stopTimeout uint ) func init() { @@ -59,6 +60,10 @@ func init() { flags.StringArrayVarP(&rmOptions.PodIDFiles, podIDFileFlagName, "", nil, "Read the pod ID from the file") _ = rmCommand.RegisterFlagCompletionFunc(podIDFileFlagName, completion.AutocompleteDefault) + timeFlagName := "time" + flags.UintVarP(&stopTimeout, timeFlagName, "t", containerConfig.Engine.StopTimeout, "Seconds to wait for pod stop before killing the container") + _ = rmCommand.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone) + validate.AddLatestFlag(rmCommand, &rmOptions.Latest) if registry.IsRemote() { @@ -66,12 +71,18 @@ func init() { } } -func rm(_ *cobra.Command, args []string) error { +func rm(cmd *cobra.Command, args []string) error { ids, err := specgenutil.ReadPodIDFiles(rmOptions.PodIDFiles) if err != nil { return err } args = append(args, ids...) + if cmd.Flag("time").Changed { + if !rmOptions.Force { + return errors.New("--force option must be specified to use the --time option") + } + rmOptions.Timeout = &stopTimeout + } return removePods(args, rmOptions.PodRmOptions, true) } diff --git a/cmd/podman/registry/remote.go b/cmd/podman/registry/remote.go index b5da98bd4..c78930574 100644 --- a/cmd/podman/registry/remote.go +++ b/cmd/podman/registry/remote.go @@ -19,11 +19,17 @@ var remoteFromCLI = struct { // Use in init() functions as an initialization check func IsRemote() bool { remoteFromCLI.sync.Do(func() { + remote := false + if _, ok := os.LookupEnv("CONTAINER_HOST"); ok { + remote = true + } else if _, ok := os.LookupEnv("CONTAINER_CONNECTION"); ok { + remote = true + } fs := pflag.NewFlagSet("remote", pflag.ContinueOnError) fs.ParseErrorsWhitelist.UnknownFlags = true fs.Usage = func() {} fs.SetInterspersed(false) - fs.BoolVarP(&remoteFromCLI.Value, "remote", "r", false, "") + fs.BoolVarP(&remoteFromCLI.Value, "remote", "r", remote, "") // The shell completion logic will call a command called "__complete" or "__completeNoDesc" // This command will always be the second argument diff --git a/cmd/podman/root.go b/cmd/podman/root.go index eb30f1ef6..6da34050e 100644 --- a/cmd/podman/root.go +++ b/cmd/podman/root.go @@ -314,7 +314,7 @@ func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) { lFlags.StringVar(&opts.Identity, identityFlagName, ident, "path to SSH identity file, (CONTAINER_SSHKEY)") _ = cmd.RegisterFlagCompletionFunc(identityFlagName, completion.AutocompleteDefault) - lFlags.BoolVarP(&opts.Remote, "remote", "r", false, "Access remote Podman service (default false)") + lFlags.BoolVarP(&opts.Remote, "remote", "r", registry.IsRemote(), "Access remote Podman service") pFlags := cmd.PersistentFlags() if registry.IsRemote() { if err := lFlags.MarkHidden("remote"); err != nil { diff --git a/cmd/podman/system/connection/list.go b/cmd/podman/system/connection/list.go index de85ce3fa..a3290e3d6 100644 --- a/cmd/podman/system/connection/list.go +++ b/cmd/podman/system/connection/list.go @@ -44,6 +44,7 @@ func init() { type namedDestination struct { Name string config.Destination + Default bool } func list(cmd *cobra.Command, _ []string) error { @@ -60,12 +61,14 @@ func list(cmd *cobra.Command, _ []string) error { "Identity": "Identity", "Name": "Name", "URI": "URI", + "Default": "Default", }} rows := make([]namedDestination, 0) for k, v := range cfg.Engine.ServiceDestinations { + def := false if k == cfg.Engine.ActiveService { - k += "*" + def = true } r := namedDestination{ @@ -74,6 +77,7 @@ func list(cmd *cobra.Command, _ []string) error { Identity: v.Identity, URI: v.URI, }, + Default: def, } rows = append(rows, r) } @@ -82,7 +86,7 @@ func list(cmd *cobra.Command, _ []string) error { return rows[i].Name < rows[j].Name }) - format := "{{.Name}}\t{{.Identity}}\t{{.URI}}\n" + format := "{{.Name}}\t{{.URI}}\t{{.Identity}}\t{{.Default}}\n" switch { case report.IsJSON(cmd.Flag("format").Value.String()): buf, err := registry.JSONLibrary().MarshalIndent(rows, "", " ") diff --git a/cmd/podman/system/dial_stdio.go b/cmd/podman/system/dial_stdio.go new file mode 100644 index 000000000..eae89f38e --- /dev/null +++ b/cmd/podman/system/dial_stdio.go @@ -0,0 +1,145 @@ +package system + +import ( + "context" + "io" + "os" + + "github.com/containers/podman/v3/cmd/podman/registry" + "github.com/containers/podman/v3/cmd/podman/validate" + "github.com/containers/podman/v3/pkg/bindings" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +var ( + dialStdioCommand = &cobra.Command{ + Use: "dial-stdio", + Short: "Proxy the stdio stream to the daemon connection. Should not be invoked manually.", + Args: validate.NoArgs, + Hidden: true, + RunE: func(cmd *cobra.Command, args []string) error { + return runDialStdio() + }, + Example: "podman system dial-stdio", + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Command: dialStdioCommand, + Parent: systemCmd, + }) +} + +func runDialStdio() error { + ctx := registry.Context() + cfg := registry.PodmanConfig() + ctx, cancel := context.WithCancel(ctx) + defer cancel() + bindCtx, err := bindings.NewConnection(ctx, cfg.URI) + if err != nil { + return errors.Wrap(err, "failed to open connection to podman") + } + conn, err := bindings.GetClient(bindCtx) + if err != nil { + return errors.Wrap(err, "failed to get connection after initialization") + } + netConn, err := conn.GetDialer(bindCtx) + if err != nil { + return errors.Wrap(err, "failed to open the raw stream connection") + } + defer netConn.Close() + + var connHalfCloser halfCloser + switch t := netConn.(type) { + case halfCloser: + connHalfCloser = t + case halfReadWriteCloser: + connHalfCloser = &nopCloseReader{t} + default: + return errors.New("the raw stream connection does not implement halfCloser") + } + + stdin2conn := make(chan error, 1) + conn2stdout := make(chan error, 1) + go func() { + stdin2conn <- copier(connHalfCloser, &halfReadCloserWrapper{os.Stdin}, "stdin to stream") + }() + go func() { + conn2stdout <- copier(&halfWriteCloserWrapper{os.Stdout}, connHalfCloser, "stream to stdout") + }() + select { + case err = <-stdin2conn: + if err != nil { + return err + } + // wait for stdout + err = <-conn2stdout + case err = <-conn2stdout: + // return immediately + } + return err +} + +// Below portion taken from original docker CLI +// https://github.com/docker/cli/blob/v20.10.9/cli/command/system/dial_stdio.go +func copier(to halfWriteCloser, from halfReadCloser, debugDescription string) error { + defer func() { + if err := from.CloseRead(); err != nil { + logrus.Errorf("error while CloseRead (%s): %v", debugDescription, err) + } + if err := to.CloseWrite(); err != nil { + logrus.Errorf("error while CloseWrite (%s): %v", debugDescription, err) + } + }() + if _, err := io.Copy(to, from); err != nil { + return errors.Wrapf(err, "error while Copy (%s)", debugDescription) + } + return nil +} + +type halfReadCloser interface { + io.Reader + CloseRead() error +} + +type halfWriteCloser interface { + io.Writer + CloseWrite() error +} + +type halfCloser interface { + halfReadCloser + halfWriteCloser +} + +type halfReadWriteCloser interface { + io.Reader + halfWriteCloser +} + +type nopCloseReader struct { + halfReadWriteCloser +} + +func (x *nopCloseReader) CloseRead() error { + return nil +} + +type halfReadCloserWrapper struct { + io.ReadCloser +} + +func (x *halfReadCloserWrapper) CloseRead() error { + return x.Close() +} + +type halfWriteCloserWrapper struct { + io.WriteCloser +} + +func (x *halfWriteCloserWrapper) CloseWrite() error { + return x.Close() +} diff --git a/cmd/podman/system/service.go b/cmd/podman/system/service.go index 99a6b1e1e..41d20d9fd 100644 --- a/cmd/podman/system/service.go +++ b/cmd/podman/system/service.go @@ -35,12 +35,14 @@ Enable a listening service for API access to Podman commands. Long: srvDescription, RunE: service, ValidArgsFunction: common.AutocompleteDefaultOneArg, - Example: `podman system service --time=0 unix:///tmp/podman.sock`, + Example: `podman system service --time=0 unix:///tmp/podman.sock + podman system service --time=0 tcp://localhost:8888`, } srvArgs = struct { - Timeout int64 CorsHeaders string + PProfAddr string + Timeout uint }{} ) @@ -51,15 +53,20 @@ func init() { }) flags := srvCmd.Flags() - cfg := registry.PodmanConfig() + timeFlagName := "time" - flags.Int64VarP(&srvArgs.Timeout, timeFlagName, "t", int64(cfg.Engine.ServiceTimeout), "Time until the service session expires in seconds. Use 0 to disable the timeout") + flags.UintVarP(&srvArgs.Timeout, timeFlagName, "t", cfg.Engine.ServiceTimeout, + "Time until the service session expires in seconds. Use 0 to disable the timeout") _ = srvCmd.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone) + flags.SetNormalizeFunc(aliasTimeoutFlag) + flags.StringVarP(&srvArgs.CorsHeaders, "cors", "", "", "Set CORS Headers") _ = srvCmd.RegisterFlagCompletionFunc("cors", completion.AutocompleteNone) - flags.SetNormalizeFunc(aliasTimeoutFlag) + flags.StringVarP(&srvArgs.PProfAddr, "pprof-address", "", "", + "Binding network address for pprof profile endpoints, default: do not expose endpoints") + flags.MarkHidden("pprof-address") } func aliasTimeoutFlag(_ *pflag.FlagSet, name string) pflag.NormalizedName { @@ -74,7 +81,7 @@ func service(cmd *cobra.Command, args []string) error { if err != nil { return err } - logrus.Infof("Using API endpoint: '%s'", apiURI) + // Clean up any old existing unix domain socket if len(apiURI) > 0 { uri, err := url.Parse(apiURI) @@ -92,33 +99,31 @@ func service(cmd *cobra.Command, args []string) error { } } - opts := entities.ServiceOptions{ - URI: apiURI, - Command: cmd, + return restService(cmd.Flags(), registry.PodmanConfig(), entities.ServiceOptions{ CorsHeaders: srvArgs.CorsHeaders, - } - - opts.Timeout = time.Duration(srvArgs.Timeout) * time.Second - return restService(opts, cmd.Flags(), registry.PodmanConfig()) + PProfAddr: srvArgs.PProfAddr, + Timeout: time.Duration(srvArgs.Timeout) * time.Second, + URI: apiURI, + }) } -func resolveAPIURI(_url []string) (string, error) { +func resolveAPIURI(uri []string) (string, error) { // When determining _*THE*_ listening endpoint -- // 1) User input wins always // 2) systemd socket activation // 3) rootless honors XDG_RUNTIME_DIR // 4) lastly adapter.DefaultAPIAddress - if len(_url) == 0 { + if len(uri) == 0 { if v, found := os.LookupEnv("PODMAN_SOCKET"); found { - logrus.Debugf("PODMAN_SOCKET='%s' used to determine API endpoint", v) - _url = []string{v} + logrus.Debugf("PODMAN_SOCKET=%q used to determine API endpoint", v) + uri = []string{v} } } switch { - case len(_url) > 0 && _url[0] != "": - return _url[0], nil + case len(uri) > 0 && uri[0] != "": + return uri[0], nil case systemd.SocketActivated(): logrus.Info("Using systemd socket activation to determine API endpoint") return "", nil diff --git a/cmd/podman/system/service_abi.go b/cmd/podman/system/service_abi.go index e484db339..b9bd7538f 100644 --- a/cmd/podman/system/service_abi.go +++ b/cmd/podman/system/service_abi.go @@ -5,9 +5,9 @@ package system import ( "context" "net" + "net/url" "os" "path/filepath" - "strings" api "github.com/containers/podman/v3/pkg/api/server" "github.com/containers/podman/v3/pkg/domain/entities" @@ -20,41 +20,54 @@ import ( "golang.org/x/sys/unix" ) -func restService(opts entities.ServiceOptions, flags *pflag.FlagSet, cfg *entities.PodmanConfig) error { +func restService(flags *pflag.FlagSet, cfg *entities.PodmanConfig, opts entities.ServiceOptions) error { var ( listener *net.Listener err error ) if opts.URI != "" { - fields := strings.Split(opts.URI, ":") - if len(fields) == 1 { + uri, err := url.Parse(opts.URI) + if err != nil { return errors.Errorf("%s is an invalid socket destination", opts.URI) } - path := opts.URI - if fields[0] == "unix" { - if path, err = filepath.Abs(fields[1]); err != nil { - return err - } - } - util.SetSocketPath(path) - if os.Getenv("LISTEN_FDS") != "" { - // If it is activated by systemd, use the first LISTEN_FD (3) - // instead of opening the socket file. - f := os.NewFile(uintptr(3), "podman.sock") - l, err := net.FileListener(f) + + switch uri.Scheme { + case "unix": + path, err := filepath.Abs(uri.Path) if err != nil { return err } - listener = &l - } else { - network := fields[0] - address := strings.Join(fields[1:], ":") - l, err := net.Listen(network, address) + util.SetSocketPath(path) + if os.Getenv("LISTEN_FDS") != "" { + // If it is activated by systemd, use the first LISTEN_FD (3) + // instead of opening the socket file. + f := os.NewFile(uintptr(3), "podman.sock") + l, err := net.FileListener(f) + if err != nil { + return err + } + listener = &l + } else { + l, err := net.Listen(uri.Scheme, path) + if err != nil { + return errors.Wrapf(err, "unable to create socket") + } + listener = &l + } + case "tcp": + host := uri.Host + if host == "" { + // For backward compatibility, support "tcp:<host>:<port>" and "tcp://<host>:<port>" + host = uri.Opaque + } + l, err := net.Listen(uri.Scheme, host) if err != nil { - return errors.Wrapf(err, "unable to create socket") + return errors.Wrapf(err, "unable to create socket %v", host) } listener = &l + default: + logrus.Debugf("Attempting API Service endpoint scheme %q", uri.Scheme) } } @@ -75,12 +88,12 @@ func restService(opts entities.ServiceOptions, flags *pflag.FlagSet, cfg *entiti servicereaper.Start() infra.StartWatcher(rt) - server, err := api.NewServerWithSettings(rt, listener, api.Options{Timeout: opts.Timeout, CorsHeaders: opts.CorsHeaders}) + server, err := api.NewServerWithSettings(rt, listener, opts) if err != nil { return err } defer func() { - if err := server.Shutdown(); err != nil { + if err := server.Shutdown(true); err != nil { logrus.Warnf("Error when stopping API service: %s", err) } }() diff --git a/cmd/podman/volumes/rm.go b/cmd/podman/volumes/rm.go index 9ba4a30a1..fd5df20b7 100644 --- a/cmd/podman/volumes/rm.go +++ b/cmd/podman/volumes/rm.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + "github.com/containers/common/pkg/completion" "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/utils" @@ -32,7 +33,8 @@ var ( ) var ( - rmOptions = entities.VolumeRmOptions{} + rmOptions = entities.VolumeRmOptions{} + stopTimeout uint ) func init() { @@ -43,6 +45,9 @@ func init() { flags := rmCommand.Flags() flags.BoolVarP(&rmOptions.All, "all", "a", false, "Remove all volumes") flags.BoolVarP(&rmOptions.Force, "force", "f", false, "Remove a volume by force, even if it is being used by a container") + timeFlagName := "time" + flags.UintVarP(&stopTimeout, timeFlagName, "t", containerConfig.Engine.StopTimeout, "Seconds to wait for running containers to stop before killing the container") + _ = rmCommand.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone) } func rm(cmd *cobra.Command, args []string) error { @@ -52,6 +57,12 @@ func rm(cmd *cobra.Command, args []string) error { if (len(args) > 0 && rmOptions.All) || (len(args) < 1 && !rmOptions.All) { return errors.New("choose either one or more volumes or all") } + if cmd.Flag("time").Changed { + if !rmOptions.Force { + return errors.New("--force option must be specified to use the --time option") + } + rmOptions.Timeout = &stopTimeout + } responses, err := registry.ContainerEngine().VolumeRm(context.Background(), args, rmOptions) if err != nil { setExitCode(err) diff --git a/cmd/podman/volumes/volume.go b/cmd/podman/volumes/volume.go index f42a6d81a..2f06abd4e 100644 --- a/cmd/podman/volumes/volume.go +++ b/cmd/podman/volumes/volume.go @@ -3,6 +3,7 @@ package volumes import ( "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" + "github.com/containers/podman/v3/pkg/util" "github.com/spf13/cobra" ) @@ -17,6 +18,7 @@ var ( Long: "Volumes are created in and can be shared between containers", RunE: validate.SubCommandExists, } + containerConfig = util.DefaultContainerConfig() ) func init() { diff --git a/cmd/rootlessport/main.go b/cmd/rootlessport/main.go new file mode 100644 index 000000000..feb9f5c06 --- /dev/null +++ b/cmd/rootlessport/main.go @@ -0,0 +1,353 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net" + "os" + "os/exec" + "path/filepath" + + "github.com/containernetworking/plugins/pkg/ns" + "github.com/containers/podman/v3/libpod/network/types" + "github.com/containers/podman/v3/pkg/rootlessport" + "github.com/pkg/errors" + rkport "github.com/rootless-containers/rootlesskit/pkg/port" + rkbuiltin "github.com/rootless-containers/rootlesskit/pkg/port/builtin" + rkportutil "github.com/rootless-containers/rootlesskit/pkg/port/portutil" + "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" +) + +const ( + // ReexecChildKey is used internally for the second reexec + ReexecChildKey = "rootlessport-child" + reexecChildEnvOpaque = "_CONTAINERS_ROOTLESSPORT_CHILD_OPAQUE" +) + +func main() { + if len(os.Args) > 1 { + fmt.Fprintln(os.Stderr, `too many arguments, rootlessport expects a json config via STDIN`) + os.Exit(1) + } + var err error + if os.Args[0] == ReexecChildKey { + err = child() + } else { + err = parent() + } + if err != nil { + fmt.Println(err) + os.Exit(1) + } +} + +func loadConfig(r io.Reader) (*rootlessport.Config, io.ReadCloser, io.WriteCloser, error) { + stdin, err := ioutil.ReadAll(r) + if err != nil { + return nil, nil, nil, err + } + var cfg rootlessport.Config + if err := json.Unmarshal(stdin, &cfg); err != nil { + return nil, nil, nil, err + } + if cfg.NetNSPath == "" { + return nil, nil, nil, errors.New("missing NetNSPath") + } + if cfg.ExitFD <= 0 { + return nil, nil, nil, errors.New("missing ExitFD") + } + exitFile := os.NewFile(uintptr(cfg.ExitFD), "exitfile") + if exitFile == nil { + return nil, nil, nil, errors.New("invalid ExitFD") + } + if cfg.ReadyFD <= 0 { + return nil, nil, nil, errors.New("missing ReadyFD") + } + readyFile := os.NewFile(uintptr(cfg.ReadyFD), "readyfile") + if readyFile == nil { + return nil, nil, nil, errors.New("invalid ReadyFD") + } + return &cfg, exitFile, readyFile, nil +} + +func parent() error { + // load config from stdin + cfg, exitR, readyW, err := loadConfig(os.Stdin) + if err != nil { + return err + } + + socketDir := filepath.Join(cfg.TmpDir, "rp") + err = os.MkdirAll(socketDir, 0700) + if err != nil { + return err + } + + // create the parent driver + stateDir, err := ioutil.TempDir(cfg.TmpDir, "rootlessport") + if err != nil { + return err + } + defer os.RemoveAll(stateDir) + driver, err := rkbuiltin.NewParentDriver(&logrusWriter{prefix: "parent: "}, stateDir) + if err != nil { + return err + } + initComplete := make(chan struct{}) + quit := make(chan struct{}) + errCh := make(chan error) + // start the parent driver. initComplete will be closed when the child connected to the parent. + logrus.Infof("Starting parent driver") + go func() { + driverErr := driver.RunParentDriver(initComplete, quit, nil) + if driverErr != nil { + logrus.WithError(driverErr).Warn("Parent driver exited") + } + errCh <- driverErr + close(errCh) + }() + opaque := driver.OpaqueForChild() + logrus.Infof("opaque=%+v", opaque) + opaqueJSON, err := json.Marshal(opaque) + if err != nil { + return err + } + childQuitR, childQuitW, err := os.Pipe() + if err != nil { + return err + } + defer func() { + // stop the child + logrus.Info("Stopping child driver") + if err := childQuitW.Close(); err != nil { + logrus.WithError(err).Warn("Unable to close childQuitW") + } + }() + + // reexec the child process in the child netns + cmd := exec.Command("/proc/self/exe") + cmd.Args = []string{ReexecChildKey} + cmd.Stdin = childQuitR + cmd.Stdout = &logrusWriter{prefix: "child"} + cmd.Stderr = cmd.Stdout + cmd.Env = append(os.Environ(), reexecChildEnvOpaque+"="+string(opaqueJSON)) + childNS, err := ns.GetNS(cfg.NetNSPath) + if err != nil { + return err + } + if err := childNS.Do(func(_ ns.NetNS) error { + logrus.Infof("Starting child driver in child netns (%q %v)", cmd.Path, cmd.Args) + return cmd.Start() + }); err != nil { + return err + } + + childErrCh := make(chan error) + go func() { + err := cmd.Wait() + childErrCh <- err + close(childErrCh) + }() + + defer func() { + if err := unix.Kill(cmd.Process.Pid, unix.SIGTERM); err != nil { + logrus.WithError(err).Warn("Kill child process") + } + }() + + logrus.Info("Waiting for initComplete") + // wait for the child to connect to the parent +outer: + for { + select { + case <-initComplete: + logrus.Infof("initComplete is closed; parent and child established the communication channel") + break outer + case err := <-childErrCh: + if err != nil { + return err + } + case err := <-errCh: + if err != nil { + return err + } + } + } + + defer func() { + logrus.Info("Stopping parent driver") + quit <- struct{}{} + if err := <-errCh; err != nil { + logrus.WithError(err).Warn("Parent driver returned error on exit") + } + }() + + // let parent expose ports + logrus.Infof("Exposing ports %v", cfg.Mappings) + if err := exposePorts(driver, cfg.Mappings, cfg.ChildIP); err != nil { + return err + } + + // we only need to have a socket to reload ports when we run under rootless cni + if cfg.RootlessCNI { + socketfile := filepath.Join(socketDir, cfg.ContainerID) + // make sure to remove the file if it exists to prevent EADDRINUSE + _ = os.Remove(socketfile) + // workaround to bypass the 108 char socket path limit + // open the fd and use the path to the fd as bind argument + fd, err := unix.Open(socketDir, unix.O_PATH, 0) + if err != nil { + return err + } + socket, err := net.ListenUnix("unixpacket", &net.UnixAddr{Name: fmt.Sprintf("/proc/self/fd/%d/%s", fd, cfg.ContainerID), Net: "unixpacket"}) + if err != nil { + return err + } + err = unix.Close(fd) + // remove the socket file on exit + defer os.Remove(socketfile) + if err != nil { + logrus.Warnf("Failed to close the socketDir fd: %v", err) + } + defer socket.Close() + go serve(socket, driver) + } + + logrus.Info("Ready") + + // https://github.com/containers/podman/issues/11248 + // Copy /dev/null to stdout and stderr to prevent SIGPIPE errors + if f, err := os.OpenFile("/dev/null", os.O_WRONLY, 0755); err == nil { + unix.Dup2(int(f.Fd()), 1) // nolint:errcheck + unix.Dup2(int(f.Fd()), 2) // nolint:errcheck + f.Close() + } + // write and close ReadyFD (convention is same as slirp4netns --ready-fd) + if _, err := readyW.Write([]byte("1")); err != nil { + return err + } + if err := readyW.Close(); err != nil { + return err + } + + // wait for ExitFD to be closed + logrus.Info("Waiting for exitfd to be closed") + if _, err := ioutil.ReadAll(exitR); err != nil { + return err + } + return nil +} + +func serve(listener net.Listener, pm rkport.Manager) { + for { + conn, err := listener.Accept() + if err != nil { + // we cannot log this error, stderr is already closed + continue + } + ctx := context.TODO() + err = handler(ctx, conn, pm) + if err != nil { + conn.Write([]byte(err.Error())) + } else { + conn.Write([]byte("OK")) + } + conn.Close() + } +} + +func handler(ctx context.Context, conn io.Reader, pm rkport.Manager) error { + var childIP string + dec := json.NewDecoder(conn) + err := dec.Decode(&childIP) + if err != nil { + return errors.Wrap(err, "rootless port failed to decode ports") + } + portStatus, err := pm.ListPorts(ctx) + if err != nil { + return errors.Wrap(err, "rootless port failed to list ports") + } + for _, status := range portStatus { + err = pm.RemovePort(ctx, status.ID) + if err != nil { + return errors.Wrap(err, "rootless port failed to remove port") + } + } + // add the ports with the new child IP + for _, status := range portStatus { + // set the new child IP + status.Spec.ChildIP = childIP + _, err = pm.AddPort(ctx, status.Spec) + if err != nil { + return errors.Wrap(err, "rootless port failed to add port") + } + } + return nil +} + +func exposePorts(pm rkport.Manager, portMappings []types.OCICNIPortMapping, childIP string) error { + ctx := context.TODO() + for _, i := range portMappings { + hostIP := i.HostIP + if hostIP == "" { + hostIP = "0.0.0.0" + } + spec := rkport.Spec{ + Proto: i.Protocol, + ParentIP: hostIP, + ParentPort: int(i.HostPort), + ChildPort: int(i.ContainerPort), + ChildIP: childIP, + } + if err := rkportutil.ValidatePortSpec(spec, nil); err != nil { + return err + } + if _, err := pm.AddPort(ctx, spec); err != nil { + return err + } + } + return nil +} + +func child() error { + // load the config from the parent + var opaque map[string]string + if err := json.Unmarshal([]byte(os.Getenv(reexecChildEnvOpaque)), &opaque); err != nil { + return err + } + + // start the child driver + quit := make(chan struct{}) + errCh := make(chan error) + go func() { + d := rkbuiltin.NewChildDriver(os.Stderr) + dErr := d.RunChildDriver(opaque, quit) + errCh <- dErr + }() + defer func() { + logrus.Info("Stopping child driver") + quit <- struct{}{} + if err := <-errCh; err != nil { + logrus.WithError(err).Warn("Child driver returned error on exit") + } + }() + + // wait for stdin to be closed + if _, err := ioutil.ReadAll(os.Stdin); err != nil { + return err + } + return nil +} + +type logrusWriter struct { + prefix string +} + +func (w *logrusWriter) Write(p []byte) (int, error) { + logrus.Infof("%s%s", w.prefix, string(p)) + return len(p), nil +} diff --git a/contrib/cirrus/pr-should-include-tests b/contrib/cirrus/pr-should-include-tests index 09ab002cf..4b6329311 100755 --- a/contrib/cirrus/pr-should-include-tests +++ b/contrib/cirrus/pr-should-include-tests @@ -8,7 +8,10 @@ if [[ "${CIRRUS_CHANGE_TITLE}" =~ CI:DOCS ]]; then exit 0 fi -# So are PRs where 'NO TESTS NEEDED' appears in the Github message +# So are PRs where 'NO NEW TESTS NEEDED' appears in the Github message +if [[ "${CIRRUS_CHANGE_MESSAGE}" =~ NO.NEW.TESTS.NEEDED ]]; then + exit 0 +fi if [[ "${CIRRUS_CHANGE_MESSAGE}" =~ NO.TESTS.NEEDED ]]; then exit 0 fi @@ -49,8 +52,11 @@ if [[ -z "$filtered_changes" ]]; then exit 0 fi -# One last chance: perhaps the developer included the magic '[NO TESTS NEEDED]' +# One last chance: perhaps the developer included the magic '[NO (NEW) TESTS NEEDED]' # string in an amended commit. +if git log --format=%B ${base}..${head} | fgrep '[NO NEW TESTS NEEDED]'; then + exit 0 +fi if git log --format=%B ${base}..${head} | fgrep '[NO TESTS NEEDED]'; then exit 0 fi @@ -67,7 +73,7 @@ tests, possibly just adding a small step to a similar existing test. Every second counts in CI. If your commit really, truly does not need tests, you can proceed -by adding '[NO TESTS NEEDED]' to the body of your commit message. +by adding '[NO NEW TESTS NEEDED]' to the body of your commit message. Please think carefully before doing so. EOF diff --git a/contrib/cirrus/pr-should-include-tests.t b/contrib/cirrus/pr-should-include-tests.t index 965a3b574..e77608fa4 100755 --- a/contrib/cirrus/pr-should-include-tests.t +++ b/contrib/cirrus/pr-should-include-tests.t @@ -36,9 +36,9 @@ tests=" 0 a47515008 ecedda63a PR 8816, unit tests only 0 caa84cd35 e55320efd PR 8565, hack/podman-socat only 0 c342583da 12f835d12 PR 8523, version.go + podman.spec.in -0 c342583da db1d2ff11 version bump to v2.2.0 0 8f75ed958 7b3ad6d89 PR 8835, only a README.md change 0 b6db60e58 f06dd45e0 PR 9420, a test rename +0 c6a896b0c 4ea5d6971 PR 11833, includes magic string " # The script we're testing diff --git a/contrib/cirrus/setup_environment.sh b/contrib/cirrus/setup_environment.sh index f2afbfef5..3786054a7 100755 --- a/contrib/cirrus/setup_environment.sh +++ b/contrib/cirrus/setup_environment.sh @@ -236,9 +236,19 @@ case "$TEST_FLAVOR" in # Use existing host bits when testing is to happen inside a container # since this script will run again in that environment. # shellcheck disable=SC2154 - if ((CONTAINER==0)) && [[ "$TEST_ENVIRON" == "host" ]]; then + if [[ "$TEST_ENVIRON" == "host" ]]; then + if ((CONTAINER)); then + die "Refusing to config. host-test in container"; + fi remove_packaged_podman_files make install PREFIX=/usr ETCDIR=/etc + elif [[ "$TEST_ENVIRON" == "container" ]]; then + if ((CONTAINER)); then + remove_packaged_podman_files + make install PREFIX=/usr ETCDIR=/etc + fi + else + die "Invalid value for $$TEST_ENVIRON=$TEST_ENVIRON" fi install_test_configs diff --git a/contrib/podmanimage/upstream/Dockerfile b/contrib/podmanimage/upstream/Dockerfile index baad49e08..75de947ea 100644 --- a/contrib/podmanimage/upstream/Dockerfile +++ b/contrib/podmanimage/upstream/Dockerfile @@ -40,7 +40,8 @@ RUN yum -y update; rpm --restore shadow-utils 2>/dev/null; yum -y install --exc crun \ fuse-overlayfs \ fuse3 \ - containers-common; \ + containers-common \ + podman-plugins; \ mkdir /root/podman; \ git clone https://github.com/containers/podman /root/podman/src/github.com/containers/podman; \ cd /root/podman/src/github.com/containers/podman; \ diff --git a/contrib/spec/podman.spec.in b/contrib/spec/podman.spec.in index fa513932f..295a953ef 100644 --- a/contrib/spec/podman.spec.in +++ b/contrib/spec/podman.spec.in @@ -3,18 +3,8 @@ %global with_check 0 %global with_unit_test 0 %bcond_without doc -%bcond_without debug -%if %{with debug} -%global _find_debuginfo_dwz_opts %{nil} -%global _dwz_low_mem_die_limit 0 -%else %global debug_package %{nil} -%endif - -%if ! 0%{?gobuild:1} -%define gobuild(o:) go build -buildmode pie -compiler gc -tags="rpm_crashtraceback ${BUILDTAGS:-}" -ldflags "${LDFLAGS:-} -B 0x$(head -c20 /dev/urandom|od -An -tx1|tr -d ' \n') -extldflags '-Wl,-z,relro -Wl,--as-needed -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld '" -a -v -x %{?**}; -%endif # podman hack directory %define hackdir %{_builddir}/%{repo}-%{shortcommit0} @@ -435,7 +425,7 @@ BUILDTAGS=$BUILDTAGS make binaries %install install -dp %{buildroot}%{_unitdir} install -dp %{buildroot}%{_usr}/lib/systemd/user -PODMAN_VERSION=%{version} %{__make} PREFIX=%{buildroot}%{_prefix} ETCDIR=%{buildroot}%{_sysconfdir} \ +PODMAN_VERSION=%{version} %{__make} DESTDIR=%{buildroot} PREFIX=%{_prefix} ETCDIR=%{_sysconfdir} \ install.bin-nobuild \ install.remote-nobuild \ %if %{with doc} @@ -536,6 +526,8 @@ export GOPATH=%{buildroot}/%{gopath}:$(pwd)/vendor:%{gopath} %{_usr}/lib/systemd/user/podman-auto-update.timer %{_usr}/lib/systemd/user/podman-restart.service %{_usr}/lib/tmpfiles.d/podman.conf +%dir %{_libexecdir}/%{name} +%{_libexecdir}/%{name}/rootlessport %if 0%{?with_devel} %files -n libpod-devel -f devel.file-list diff --git a/contrib/systemd/auto-update/podman-auto-update.service b/contrib/systemd/auto-update/podman-auto-update.service.in index dc5fac8cf..de4460d60 100644 --- a/contrib/systemd/auto-update/podman-auto-update.service +++ b/contrib/systemd/auto-update/podman-auto-update.service.in @@ -6,8 +6,8 @@ After=network-online.target [Service] Type=oneshot -ExecStart=/usr/bin/podman auto-update -ExecStartPost=/usr/bin/podman image prune -f +ExecStart=@@PODMAN@@ auto-update +ExecStartPost=@@PODMAN@@ image prune -f [Install] WantedBy=multi-user.target default.target diff --git a/contrib/systemd/system/podman-restart.service b/contrib/systemd/system/podman-restart.service.in index baf12b3ae..46193e2c6 100644 --- a/contrib/systemd/system/podman-restart.service +++ b/contrib/systemd/system/podman-restart.service.in @@ -5,8 +5,9 @@ StartLimitIntervalSec=0 [Service] Type=oneshot +RemainAfterExit=true Environment=LOGGING="--log-level=info" -ExecStart=/usr/bin/podman $LOGGING start --all --filter restart-policy=always +ExecStart=@@PODMAN@@ $LOGGING start --all --filter restart-policy=always [Install] -WantedBy=multi-user.target +WantedBy=multi-user.target default.target diff --git a/contrib/systemd/system/podman.service b/contrib/systemd/system/podman.service.in index cefb13ae3..132671dff 100644 --- a/contrib/systemd/system/podman.service +++ b/contrib/systemd/system/podman.service.in @@ -9,7 +9,7 @@ StartLimitIntervalSec=0 Type=exec KillMode=process Environment=LOGGING="--log-level=info" -ExecStart=/usr/bin/podman $LOGGING system service +ExecStart=@@PODMAN@@ $LOGGING system service [Install] WantedBy=multi-user.target diff --git a/docs/dckrman.sh b/docs/dckrman.sh index 18fb364bf..48685a14b 100755 --- a/docs/dckrman.sh +++ b/docs/dckrman.sh @@ -5,3 +5,5 @@ for i in $@; do echo .so man1/$b > $filename done echo .so man5/containerfile.5 > $(dirname $1)/dockerfile.5 +echo .so man5/containerignore.5 > $(dirname $1)/.dockerignore.5 +echo .so man5/containerignore.5 > $(dirname $1)/dockerignore.5 diff --git a/docs/source/markdown/podman-build.1.md b/docs/source/markdown/podman-build.1.md index 15d936d17..fd36166b6 100644 --- a/docs/source/markdown/podman-build.1.md +++ b/docs/source/markdown/podman-build.1.md @@ -35,8 +35,8 @@ location. When a Git repository is set as the URL, the repository is cloned locally and then set as the context. -NOTE: `podman build` uses code sourced from the `buildah` project to build -container images. This `buildah` code creates `buildah` containers for the +NOTE: `podman build` uses code sourced from the `Buildah` project to build +container images. This `Buildah` code creates `Buildah` containers for the `RUN` options in container storage. In certain situations, when the `podman build` crashes or users kill the `podman build` process, these external containers can be left in container storage. Use the `podman ps --all --storage` @@ -256,7 +256,7 @@ specifying **--disable-compression=false**. #### **--disable-content-trust** -This is a Docker specific option to disable image verification to a Docker +This is a Docker specific option to disable image verification to a container registry and is not supported by Podman. This flag is a NOOP and provided solely for scripting compatibility. (This option is not available with the remote Podman client) @@ -328,7 +328,7 @@ than once, attempting to use this option will trigger an error. #### **--ignorefile** -Path to an alternative .dockerignore file. +Path to an alternative .containerignore file. #### **--ipc**=*how* @@ -845,15 +845,15 @@ $ podman build . $ podman build -f Containerfile.simple . -$ cat $HOME/Dockerfile | podman build -f - . +$ cat $HOME/Containerfile | podman build -f - . -$ podman build -f Dockerfile.simple -f Containerfile.notsosimple . +$ podman build -f Containerfile.simple -f Containerfile.notsosimple . -$ podman build -f Dockerfile.in $HOME +$ podman build -f Containerfile.in $HOME $ podman build -t imageName . -$ podman build --tls-verify=true -t imageName -f Dockerfile.simple . +$ podman build --tls-verify=true -t imageName -f Containerfile.simple . $ podman build --tls-verify=false -t imageName . @@ -861,7 +861,7 @@ $ podman build --runtime-flag log-format=json . $ podman build --runtime-flag debug . -$ podman build --authfile /tmp/auths/myauths.json --cert-dir $HOME/auth --tls-verify=true --creds=username:password -t imageName -f Dockerfile.simple . +$ podman build --authfile /tmp/auths/myauths.json --cert-dir $HOME/auth --tls-verify=true --creds=username:password -t imageName -f Containerfile.simple . $ podman build --memory 40m --cpu-period 10000 --cpu-quota 50000 --ulimit nofile=1024:1028 -t imageName . @@ -940,22 +940,26 @@ $ podman build -f dev/Containerfile https://10.10.10.1/podman/context.tar.gz ## Files -### `.dockerignore` +### .containerignore/.dockerignore -If the file .dockerignore exists in the context directory, `buildah copy` reads -its contents. Use the `--ignorefile` flag to override .dockerignore path location. +If the file *.containerignore* or *.dockerignore* exists in the context directory, +`podman build` reads its contents. Use the `--ignorefile` flag to override the +.containerignore path location. Podman uses the content to exclude files and directories from the context directory, when executing COPY and ADD directives in the Containerfile/Dockerfile -Users can specify a series of Unix shell globals in a .dockerignore file to +The .containerignore and .dockerignore files use the same syntax; if both +are in the context directory, podman build will only use .containerignore. + +Users can specify a series of Unix shell globs in a .containerignore file to identify files/directories to exclude. Podman supports a special wildcard string `**` which matches any number of directories (including zero). For example, **/*.go will exclude all files that end with .go that are found in all directories. -Example .dockerignore file: +Example .containerignore file: ``` # exclude this content for image @@ -975,7 +979,7 @@ Excludes files and directories starting with `output` from any directory. Excludes files named src and the directory src as well as any content in it. Lines starting with ! (exclamation mark) can be used to make exceptions to -exclusions. The following is an example .dockerignore file that uses this +exclusions. The following is an example .containerignore file that uses this mechanism: ``` *.doc @@ -984,10 +988,10 @@ mechanism: Exclude all doc files except Help.doc from the image. -This functionality is compatible with the handling of .dockerignore files +This functionality is compatible with the handling of .containerignore files described here: -https://docs.docker.com/engine/reference/builder/#dockerignore-file +https://github.com/containers/buildah/blob/main/docs/containerignore.5.md **registries.conf** (`/etc/containers/registries.conf`) @@ -1009,10 +1013,10 @@ If you are using `useradd` within your build script, you should pass the useradd to stop creating the lastlog file. ## SEE ALSO -podman(1), buildah(1), containers-certs.d(5), containers-registries.conf(5), crun(8), runc(8), useradd(8), podman-ps(1), podman-rm(1) +podman(1), buildah(1), containers-certs.d(5), containers-registries.conf(5), crun(8), runc(8), useradd(8), podman-ps(1), podman-rm(1), Containerfile(5), containerignore(5) ## HISTORY -Aug 2020, Additional options and .dockerignore added by Dan Walsh `<dwalsh@redhat.com>` +Aug 2020, Additional options and .containerignore added by Dan Walsh `<dwalsh@redhat.com>` May 2018, Minor revisions added by Joe Doss `<joe@solidadmin.com>` diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md index ee52bfd13..0d4366dbe 100644 --- a/docs/source/markdown/podman-create.1.md +++ b/docs/source/markdown/podman-create.1.md @@ -515,6 +515,11 @@ Not implemented Logging driver for the container. Currently available options are *k8s-file*, *journald*, *none* and *passthrough*, with *json-file* aliased to *k8s-file* for scripting compatibility. +The podman info command below will display the default log-driver for the system. +``` +$ podman info --format '{{ .Host.LogDriver }}' +journald +``` The *passthrough* driver passes down the standard streams (stdin, stdout, stderr) to the container. It is not allowed with the remote Podman client and on a tty, since it is vulnerable to attacks via TIOCSTI. diff --git a/docs/source/markdown/podman-manifest.1.md b/docs/source/markdown/podman-manifest.1.md index 6b82cc1ad..964f89afe 100644 --- a/docs/source/markdown/podman-manifest.1.md +++ b/docs/source/markdown/podman-manifest.1.md @@ -24,5 +24,49 @@ The `podman manifest` command provides subcommands which can be used to: | remove | [podman-manifest-remove(1)](podman-manifest-remove.1.md) | Remove an image from a manifest list or image index. | | rm | [podman-manifest-rme(1)](podman-manifest-rm.1.md) | Remove manifest list or image index from local storage. | +## EXAMPLES + +### Building a multi-arch manifest list from a Containerfile + +Assuming the `Containerfile` uses `RUN` instructions, the host needs +a way to execute non-native binaries. Configuring this is beyond +the scope of this example. Building a multi-arch manifest list +`shazam` in parallel across 4-threads can be done like this: + + $ platarch=linux/amd64,linux/ppc64le,linux/arm64,linux/s390x + $ podman build --jobs=4 --platform=$platarch --manifest shazam . + +**Note:** The `--jobs` argument is optional, and the `-t` or `--tag` +option should *not* be used. + +### Assembling a multi-arch manifest from separately built images + +Assuming `example.com/example/shazam:$arch` images are built separately +on other hosts and pushed to the `example.com` registry. They may +be combined into a manifest list, and pushed using a simple loop: + + $ REPO=example.com/example/shazam + $ podman manifest create $REPO:latest + $ for IMGTAG in amd64 s390x ppc64le arm64; do \ + podman manifest add $REPO:latest docker://$REPO:IMGTAG; \ + done + $ podman manifest push --all $REPO:latest + +**Note:** The `add` instruction argument order is `<manifest>` then `<image>`. +Also, the `--all` push option is required to ensure all contents are +pushed, not just the native platform/arch. + +### Removing and tagging a manifest list before pushing + +Special care is needed when removing and pushing manifest lists, as opposed +to the contents. You almost always want to use the `manifest rm` and +`manifest push --all` subcommands. For example, a rename and push could +be performed like this: + + $ podman tag localhost/shazam example.com/example/shazam + $ podman manifest rm localhost/shazam + $ podman manifest push --all example.com/example/shazam + + ## SEE ALSO podman(1), podman-manifest-add(1), podman-manifest-annotate(1), podman-manifest-create(1), podman-manifest-inspect(1), podman-manifest-push(1), podman-manifest-remove(1) diff --git a/docs/source/markdown/podman-network-rm.1.md b/docs/source/markdown/podman-network-rm.1.md index ad4bc11e2..b12e81810 100644 --- a/docs/source/markdown/podman-network-rm.1.md +++ b/docs/source/markdown/podman-network-rm.1.md @@ -15,6 +15,10 @@ Delete one or more Podman networks. The `force` option will remove all containers that use the named network. If the container is running, the container will be stopped and removed. +#### **--time**, **-t**=*seconds* + +Seconds to wait before forcibly stopping the running containers that are using the specified network. The --force option must be specified to use the --time option. + ## EXAMPLE Delete the `cni-podman9` network diff --git a/docs/source/markdown/podman-play-kube.1.md b/docs/source/markdown/podman-play-kube.1.md index a4b9722b8..d4770a538 100644 --- a/docs/source/markdown/podman-play-kube.1.md +++ b/docs/source/markdown/podman-play-kube.1.md @@ -9,6 +9,7 @@ podman-play-kube - Create containers, pods or volumes based on Kubernetes YAML ## DESCRIPTION **podman play kube** will read in a structured file of Kubernetes YAML. It will then recreate the containers, pods or volumes described in the YAML. Containers within a pod are then started and the ID of the new Pod or the name of the new Volume is output. If the yaml file is specified as "-" then `podman play kube` will read the YAML file from stdin. Using the `--down` command line option, it is also capable of tearing down the pods created by a previous run of `podman play kube`. +Using the `--replace` command line option, it will tear down the pods(if any) created by a previous run of `podman play kube` and recreate the pods with the Kubernetes YAML file. Ideally the input file would be one created by Podman (see podman-generate-kube(1)). This would guarantee a smooth import and expected results. Currently, the supported Kubernetes kinds are: @@ -146,6 +147,10 @@ Do not create /etc/hosts within the pod's containers, instead use the version fr Suppress output information when pulling images +#### **--replace** + +Tears down the pods created by a previous run of `play kube` and recreates the pods. This option is used to keep the existing pods up to date based upon the Kubernetes YAML. + #### **--seccomp-profile-root**=*path* Directory path for seccomp profiles (default: "/var/lib/kubelet/seccomp"). (This option is not available with the remote Podman client) diff --git a/docs/source/markdown/podman-pod-logs.1.md b/docs/source/markdown/podman-pod-logs.1.md index 8378f2eea..5ccc69bb9 100644 --- a/docs/source/markdown/podman-pod-logs.1.md +++ b/docs/source/markdown/podman-pod-logs.1.md @@ -15,7 +15,7 @@ Note: Long running command of `podman pod log` with a `-f` or `--follow` needs t #### **--container**, **-c** -By default `podman pod logs` retrives logs for all the containers available within the pod differentiate by field `container`. However there are use-cases where user would want to limit the log stream only to a particular container of a pod for such cases `-c` can be used like `podman pod logs -c ctrNameorID podname`. +By default `podman pod logs` retrieves logs for all the containers available within the pod differentiate by field `container`. However there are use-cases where user would want to limit the log stream only to a particular container of a pod for such cases `-c` can be used like `podman pod logs -c ctrNameorID podname`. #### **--follow**, **-f** diff --git a/docs/source/markdown/podman-pod-rm.1.md b/docs/source/markdown/podman-pod-rm.1.md index ce91dab5b..fc834a69c 100644 --- a/docs/source/markdown/podman-pod-rm.1.md +++ b/docs/source/markdown/podman-pod-rm.1.md @@ -33,6 +33,10 @@ Stop running containers and delete all stopped containers before removal of pod. Read pod ID from the specified file and remove the pod. Can be specified multiple times. +#### **--time**, **-t**=*seconds* + +Seconds to wait before forcibly stopping running containers within the pod. The --force option must be specified to use the --time option. + ## EXAMPLE podman pod rm mywebserverpod diff --git a/docs/source/markdown/podman-pod-stop.1.md b/docs/source/markdown/podman-pod-stop.1.md index 77f6af433..57abf608d 100644 --- a/docs/source/markdown/podman-pod-stop.1.md +++ b/docs/source/markdown/podman-pod-stop.1.md @@ -25,9 +25,9 @@ ExecStop directive of a systemd service referencing that pod. Instead of providing the pod name or ID, stop the last created pod. (This option is not available with the remote Podman client) -#### **--time**, **-t**=*time* +#### **--time**, **-t**=*seconds* -Timeout to wait before forcibly stopping the containers in the pod. +Seconds to wait before forcibly stopping the containers in the pod. #### **--pod-id-file** diff --git a/docs/source/markdown/podman-remote.1.md b/docs/source/markdown/podman-remote.1.md index 1a6c7d3cc..fb77f3300 100644 --- a/docs/source/markdown/podman-remote.1.md +++ b/docs/source/markdown/podman-remote.1.md @@ -29,6 +29,8 @@ The `containers.conf` file should be placed under `$HOME/.config/containers/cont Remote connection name +Overrides environment variable `CONTAINER_CONNECTION` if set. + #### **--help**, **-h** Print usage statement @@ -71,6 +73,26 @@ URL value resolution precedence: Print the version +## Environment Variables + +Podman can set up environment variables from env of [engine] table in containers.conf. These variables can be overridden by passing environment variables before the `podman` commands. + +#### **CONTAINERS_CONF** + +Set default locations of containers.conf file + +#### **CONTAINER_CONNECTION** + +Set default `--connection` value to access Podman service. + +#### **CONTAINER_HOST** + +Set default `--url` value to access Podman service. + +#### **CONTAINER_SSHKEY** + +Set default `--identity` path to ssh key file value used to access Podman service. + ## Exit Status The exit code from `podman` gives information about why the container diff --git a/docs/source/markdown/podman-restart.1.md b/docs/source/markdown/podman-restart.1.md index 2b37e2ea4..9ffca7b58 100644 --- a/docs/source/markdown/podman-restart.1.md +++ b/docs/source/markdown/podman-restart.1.md @@ -24,9 +24,9 @@ to run containers such as CRI-O, the last started container could be from either #### **--running** Restart all containers that are already in the *running* state. -#### **--time**=*time*, **-t** -Timeout to wait before forcibly stopping the container. +#### **--time**, **-t**=*seconds* +Seconds to wait before forcibly stopping the container. ## EXAMPLES diff --git a/docs/source/markdown/podman-rm.1.md b/docs/source/markdown/podman-rm.1.md index 0abf2768c..ad44fd894 100644 --- a/docs/source/markdown/podman-rm.1.md +++ b/docs/source/markdown/podman-rm.1.md @@ -41,6 +41,10 @@ during the ExecStop directive of a systemd service referencing that container. Instead of providing the container name or ID, use the last created container. If you use methods other than Podman to run containers such as CRI-O, the last started container could be from either of those methods. (This option is not available with the remote Podman client) +#### **--time**, **-t**=*seconds* + +Seconds to wait before forcibly stopping the container. The --force option must be specified to use the --time option. + #### **--volumes**, **-v** Remove anonymous volumes associated with the container. This does not include named volumes diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md index 5cc17f470..30a9fad9a 100644 --- a/docs/source/markdown/podman-run.1.md +++ b/docs/source/markdown/podman-run.1.md @@ -538,8 +538,13 @@ Not implemented. #### **--log-driver**="*driver*" -Logging driver for the container. Currently available options are **k8s-file**, **journald**, **none** and **passthrough**, with **json-file** aliased to **k8s-file** for scripting compatibility. +Logging driver for the container. Currently available options are **k8s-file**, **journald**, **none** and **passthrough**, with **json-file** aliased to **k8s-file** for scripting compatibility. (Default journald) +The podman info command below will display the default log-driver for the system. +``` +$ podman info --format '{{ .Host.LogDriver }}' +journald +``` The **passthrough** driver passes down the standard streams (stdin, stdout, stderr) to the container. It is not allowed with the remote Podman client and on a tty, since it is vulnerable to attacks via TIOCSTI. diff --git a/docs/source/markdown/podman-search.1.md b/docs/source/markdown/podman-search.1.md index d541e5c93..911bbcb4b 100644 --- a/docs/source/markdown/podman-search.1.md +++ b/docs/source/markdown/podman-search.1.md @@ -37,6 +37,11 @@ Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE environment variable. `export REGISTRY_AUTH_FILE=path` +#### **--compatible** + +After the name and the description, also show the stars, official and automated descriptors as Docker does. +Podman does not show these descriptors by default since they are not supported by most public container registries. + #### **--filter**, **-f**=*filter* Filter output based on conditions provided (default []) @@ -81,7 +86,7 @@ The result contains the Image name and its tag, one line for every tag associate #### **--no-trunc** -Do not truncate the output (default *false*). +Do not truncate the output (default *true*). #### **--tls-verify** @@ -97,72 +102,39 @@ Print usage statement ## EXAMPLES ``` -$ podman search --limit 3 rhel -INDEX NAME DESCRIPTION STARS OFFICIAL AUTOMATED -docker.io docker.io/richxsl/rhel7 RHEL 7 image with minimal installation 9 -docker.io docker.io/bluedata/rhel7 RHEL-7.x base container images 1 -docker.io docker.io/gidikern/rhel-oracle-jre RHEL7 with jre8u60 5 [OK] -redhat.com redhat.com/rhel This platform image provides a minimal runti... 0 -redhat.com redhat.com/rhel6 This platform image provides a minimal runti... 0 -redhat.com redhat.com/rhel6.5 This platform image provides a minimal runti... 0 +$ podman search --limit 3 fedora +NAME DESCRIPTION +registry.centos.org/centos +registry.centos.org/cdrage/mosh-centos7 +registry.centos.org/centos/bind +docker.io/library/centos The official build of CentOS. +docker.io/jdeathe/centos-ssh OpenSSH / Supervisor / EPEL/IUS/SCL Repos - ... +docker.io/ansible/centos7-ansible Ansible on Centos7 +quay.io/centos/centos The official CentOS base containers. +quay.io/ukhomeofficedigital/centos-base +quay.io/quarkus/centos-quarkus-maven Quarkus.io builder image for building Quarku... ``` +Note that the Stars, Official and Automated descriptors are only available on Docker Hub and are hence not displayed by default. ``` -$ podman search alpine -INDEX NAME DESCRIPTION STARS OFFICIAL AUTOMATED -docker.io docker.io/library/alpine A minimal Docker image based on Alpine Linux... 3009 [OK] -docker.io docker.io/mhart/alpine-node Minimal Node.js built on Alpine Linux 332 -docker.io docker.io/anapsix/alpine-java Oracle Java 8 (and 7) with GLIBC 2.23 over A... 272 [OK] -docker.io docker.io/tenstartups/alpine Alpine linux base docker image with useful p... 5 [OK] +$ podman search --format "{{.Name}}\t{{.Stars}}\t{{.Official}}" alpine --limit 3 +docker.io/library/alpine 7956 [OK] +docker.io/alpine/git 192 +docker.io/anapsix/alpine-java 474 +quay.io/libpod/alpine 0 +quay.io/vqcomms/alpine-tools 0 +quay.io/wire/alpine-deps 0 ``` ``` -$ podman search registry.fedoraproject.org/fedora -INDEX NAME DESCRIPTION STARS OFFICIAL AUTOMATED -fedoraproject.org fedoraproject.org/fedora 0 -fedoraproject.org fedoraproject.org/fedora-minimal 0 -``` +$ podman search --list-tags registry.access.redhat.com/ubi8 --limit 4 +NAME TAG +registry.access.redhat.com/ubi8 8.4-211 +registry.access.redhat.com/ubi8 8.4-206.1626828523-source +registry.access.redhat.com/ubi8 8.4-199 +registry.access.redhat.com/ubi8 8.4-211-source ``` -$ podman search --filter=is-official alpine -INDEX NAME DESCRIPTION STARS OFFICIAL AUTOMATED -docker.io docker.io/library/alpine A minimal Docker image based on Alpine Linux... 3009 [OK] -``` - -``` -$ podman search --format "table {{.Index}} {{.Name}}" registry.fedoraproject.org/fedora -INDEX NAME -fedoraproject.org fedoraproject.org/fedora -fedoraproject.org fedoraproject.org/fedora-minimal -``` - -``` -$ podman search registry.fedoraproject.org/ -INDEX NAME DESCRIPTION STARS OFFICIAL AUTOMATED -fedoraproject.org registry.fedoraproject.org/f25/cockpit 0 -fedoraproject.org registry.fedoraproject.org/f25/container-engine 0 -fedoraproject.org registry.fedoraproject.org/f25/docker 0 -fedoraproject.org registry.fedoraproject.org/f25/etcd 0 -fedoraproject.org registry.fedoraproject.org/f25/flannel 0 -fedoraproject.org registry.fedoraproject.org/f25/httpd 0 -fedoraproject.org registry.fedoraproject.org/f25/kubernetes-apiserver 0 -fedoraproject.org registry.fedoraproject.org/f25/kubernetes-controller-manager 0 -fedoraproject.org registry.fedoraproject.org/f25/kubernetes-kubelet 0 -fedoraproject.org registry.fedoraproject.org/f25/kubernetes-master 0 -fedoraproject.org registry.fedoraproject.org/f25/kubernetes-node 0 -fedoraproject.org registry.fedoraproject.org/f25/kubernetes-proxy 0 -fedoraproject.org registry.fedoraproject.org/f25/kubernetes-scheduler 0 -fedoraproject.org registry.fedoraproject.org/f25/mariadb 0 -``` - -``` -$ podman search --list-tags registry.redhat.io/rhel -NAME TAG -registry.redhat.io/rhel 7.3-74 -registry.redhat.io/rhel 7.6-301 -registry.redhat.io/rhel 7.1-9 -... -``` Note: This works only with registries that implement the v2 API. If tried with a v1 registry an error will be returned. ## FILES diff --git a/docs/source/markdown/podman-stop.1.md b/docs/source/markdown/podman-stop.1.md index a1e9675c1..2a3f60844 100644 --- a/docs/source/markdown/podman-stop.1.md +++ b/docs/source/markdown/podman-stop.1.md @@ -36,9 +36,9 @@ during the ExecStop directive of a systemd service referencing that container. Instead of providing the container name or ID, use the last created container. If you use methods other than Podman to run containers such as CRI-O, the last started container could be from either of those methods. (This option is not available with the remote Podman client) -#### **--time**, **-t**=*time* +#### **--time**, **-t**=*seconds* -Time to wait before forcibly stopping the container +Seconds to wait before forcibly stopping the container ## EXAMPLES diff --git a/docs/source/markdown/podman-system-connection-list.1.md b/docs/source/markdown/podman-system-connection-list.1.md index 6b25a045d..4dc85dd98 100644 --- a/docs/source/markdown/podman-system-connection-list.1.md +++ b/docs/source/markdown/podman-system-connection-list.1.md @@ -23,14 +23,14 @@ Valid placeholders for the Go template listed below: | *.Name* | Connection Name/Identifier | | *.Identity* | Path to file containing SSH identity | | *.URI* | URI to podman service. Valid schemes are ssh://[user@]*host*[:port]*Unix domain socket*[?secure=True], unix://*Unix domain socket*, and tcp://localhost[:*port*] | - -An asterisk is appended to the default connection. +| *.Default* | Indicates whether connection is the default | ## EXAMPLE ``` $ podman system connection list -Name URI Identity -devl ssh://root@example.com/run/podman/podman.sock ~/.ssh/id_rsa +Name URI Identity Default +devl ssh://root@example.com:/run/podman/podman.sock ~/.ssh/id_rsa True +devl ssh://user@example.com:/run/user/1000/podman/podman.sock ~/.ssh/id_rsa False ``` ## SEE ALSO podman-system(1) , containers.conf(5) diff --git a/docs/source/markdown/podman-system-connection.1.md b/docs/source/markdown/podman-system-connection.1.md index 6cd4a5fa8..b00a2aec3 100644 --- a/docs/source/markdown/podman-system-connection.1.md +++ b/docs/source/markdown/podman-system-connection.1.md @@ -24,8 +24,8 @@ The user will be prompted for the ssh login password or key file pass phrase as ## EXAMPLE ``` $ podman system connection list -Name URI Identity -devl ssh://root@example.com/run/podman/podman.sock ~/.ssh/id_rsa +Name URI Identity Default +devl ssh://root@example.com/run/podman/podman.sock ~/.ssh/id_rsa true ``` ## SEE ALSO podman-system(1) , containers.conf(5) diff --git a/docs/source/markdown/podman-volume-import.1.md b/docs/source/markdown/podman-volume-import.1.md index 6bb868774..88b7b1b5b 100644 --- a/docs/source/markdown/podman-volume-import.1.md +++ b/docs/source/markdown/podman-volume-import.1.md @@ -22,7 +22,7 @@ Print usage statement ## EXAMPLES ``` -$ gunzip -c hellow.tar.gz | podman volume import myvol - +$ gunzip -c hello.tar.gz | podman volume import myvol - ``` ``` $ podman volume import myvol test.tar diff --git a/docs/source/markdown/podman-volume-rm.1.md b/docs/source/markdown/podman-volume-rm.1.md index c066d1c6e..26cc605a2 100644 --- a/docs/source/markdown/podman-volume-rm.1.md +++ b/docs/source/markdown/podman-volume-rm.1.md @@ -28,6 +28,9 @@ If it is being used by containers, the containers will be removed first. Print usage statement +#### **--time**, **-t**=*seconds* + +Seconds to wait before forcibly stopping running containers that are using the specified volume. The --force option must be specified to use the --time option. ## EXAMPLES diff --git a/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md index 8b5f385cb..beb6e26d8 100644 --- a/docs/source/markdown/podman.1.md +++ b/docs/source/markdown/podman.1.md @@ -82,7 +82,7 @@ Remote connections use local containers.conf for default. #### **--log-level**=*level* -Log messages above specified level: debug, info, warn, error (default), fatal or panic (default: "error") +Log messages at and above specified level: debug, info, warn, error, fatal or panic (default: "warn") #### **--namespace**=*namespace* @@ -93,8 +93,9 @@ When namespace is set, created containers and pods will join the given namespace Path to the command binary to use for setting up a network. It is currently only used for setting up a slirp4netns network. If "" is used then the binary is looked up using the $PATH environment variable. #### **--remote**, **-r** -Access Podman service will be remote -Remote connections use local containers.conf for default. +When true, access to the Podman service will be remote. Defaults to false. +Settings can be modified in the containers.conf file. If the CONTAINER_HOST +environment variable is set, the remote option defaults to true. #### **--url**=*value* URL to access Podman service (default from `containers.conf`, rootless `unix://run/user/$UID/podman/podman.sock` or as root `unix://run/podman/podman.sock`). @@ -172,6 +173,58 @@ Print the version Podman can set up environment variables from env of [engine] table in containers.conf. These variables can be overridden by passing environment variables before the `podman` commands. +#### **CONTAINERS_CONF** + +Set default locations of containers.conf file + +#### **CONTAINERS_REGISTRIES_CONF** + +Set default location of the registries.conf file. + +#### **CONTAINERS_STORAGE_CONF** + +Set default location of the storage.conf file. + +#### **CONTAINER_CONNECTION** + +Override default `--connection` value to access Podman service. Also enabled --remote option. + +#### **CONTAINER_HOST** + +Set default `--url` value to access Podman service. Also enabled --remote option. + +#### **CONTAINER_SSHKEY** + +Set default `--identity` path to ssh key file value used to access Podman service. + +#### **STORAGE_DRIVER** + +Set default `--storage-driver` value. + +#### **STORAGE_OPTS** + +Set default `--storage-opts` value. + +#### **TMPDIR** + +Set the the temporary storage location of downloaded container images. Podman defaults to use `/var/tmp`. + +#### **XDG_CONFIG_HOME** + +In Rootless mode configuration files are read from `XDG_CONFIG_HOME` when +specified, otherwise in the home directory of the user under +`$HOME/.config/containers`. + +#### **XDG_DATA_HOME** + +In Rootless mode images are pulled under `XDG_DATA_HOME` when specified, +otherwise in the home directory of the user under +`$HOME/.local/share/containers/storage`. + +#### **XDG_RUNTIME_DIR** + +In Rootless mode temporary configuration data is stored in `${XDG_RUNTIME_DIR}/containers`. + ## Remote Access The Podman command can be used with remote services using the `--remote` flag. Connections can @@ -12,19 +12,19 @@ require ( github.com/containernetworking/cni v1.0.1 github.com/containernetworking/plugins v1.0.1 github.com/containers/buildah v1.23.1 - github.com/containers/common v0.46.1-0.20210928081721-32e20295f1c6 + github.com/containers/common v0.46.1-0.20211008123044-d846f5aaec0e github.com/containers/conmon v2.0.20+incompatible - github.com/containers/image/v5 v5.16.0 + github.com/containers/image/v5 v5.16.1 github.com/containers/ocicrypt v1.1.2 github.com/containers/psgo v1.7.1 - github.com/containers/storage v1.36.1-0.20210929132900-162a0bf730ce + github.com/containers/storage v1.37.0 github.com/coreos/go-systemd/v22 v22.3.2 github.com/coreos/stream-metadata-go v0.0.0-20210225230131-70edb9eb47b3 github.com/cyphar/filepath-securejoin v0.2.3 github.com/davecgh/go-spew v1.1.1 github.com/digitalocean/go-qemu v0.0.0-20210209191958-152a1535e49f github.com/docker/distribution v2.7.1+incompatible - github.com/docker/docker v20.10.8+incompatible + github.com/docker/docker v20.10.9+incompatible github.com/docker/go-connections v0.4.0 github.com/docker/go-plugins-helpers v0.0.0-20200102110956-c9a8a2d92ccc github.com/docker/go-units v0.4.0 @@ -44,14 +44,14 @@ require ( github.com/mattn/go-isatty v0.0.14 github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 github.com/mrunalp/fileutils v0.5.0 - github.com/onsi/ginkgo v1.16.4 + github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.16.0 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.0.2-0.20210819154149-5ad6f50d6283 github.com/opencontainers/runc v1.0.2 github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 github.com/opencontainers/runtime-tools v0.9.0 - github.com/opencontainers/selinux v1.8.5 + github.com/opencontainers/selinux v1.9.1 github.com/pkg/errors v0.9.1 github.com/pmezard/go-difflib v1.0.0 github.com/rootless-containers/rootlesskit v0.14.5 @@ -66,7 +66,7 @@ require ( go.etcd.io/bbolt v1.3.6 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34 + golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b k8s.io/api v0.22.2 k8s.io/apimachinery v0.22.2 @@ -110,7 +110,6 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= -github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA= github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= @@ -192,6 +191,7 @@ github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= +github.com/containerd/containerd v1.5.4/go.mod h1:sx18RgvW6ABJ4iYUw7Q5x7bgFOAB9B6G7+yO0XBc4zw= github.com/containerd/containerd v1.5.5 h1:q1gxsZsGZ8ddVe98yO6pR21b5xQSMiR61lD0W96pgQo= github.com/containerd/containerd v1.5.5/go.mod h1:oSTh0QpT1w6jYcGmbiSbxv9OSQYaa88mPyWIuU79zyo= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= @@ -222,8 +222,9 @@ github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJ github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/stargz-snapshotter/estargz v0.8.0 h1:oA1wx8kTFfImfsT5bScbrZd8gK+WtQnn15q82Djvm0Y= github.com/containerd/stargz-snapshotter/estargz v0.8.0/go.mod h1:mwIwuwb+D8FX2t45Trwi0hmWmZm5VW7zPP/rekwhWQU= +github.com/containerd/stargz-snapshotter/estargz v0.9.0 h1:PkB6BSTfOKX23erT2GkoUKkJEcXfNcyKskIViK770v8= +github.com/containerd/stargz-snapshotter/estargz v0.9.0/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= @@ -250,12 +251,13 @@ github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNB github.com/containers/buildah v1.23.1 h1:Tpc9DsRuU+0Oofewpxb6OJVNQjCu7yloN/obUqzfDTY= github.com/containers/buildah v1.23.1/go.mod h1:4WnrN0yrA7ab0ppgunixu2WM1rlD2rG8QLJAKbEkZlQ= github.com/containers/common v0.44.2/go.mod h1:7sdP4vmI5Bm6FPFxb3lvAh1Iktb6tiO1MzjUzhxdoGo= -github.com/containers/common v0.46.1-0.20210928081721-32e20295f1c6 h1:DojkCc4a9f3WB25Fk0GDap1/OkKU9UmDLvPJyqw3TBc= -github.com/containers/common v0.46.1-0.20210928081721-32e20295f1c6/go.mod h1:L4+sJlqi+R7frlbiWBW0baPra/cH8u5ZYwbxkukw3Lk= +github.com/containers/common v0.46.1-0.20211008123044-d846f5aaec0e h1:lYazDued7KBcMq5IJzRIbX47SSLRg/yYxvM/P9LaVhE= +github.com/containers/common v0.46.1-0.20211008123044-d846f5aaec0e/go.mod h1:ggZks97KCmjBcHvNTCyLc17SqdjSYoeexW7rnRt9H9Y= 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.16.0 h1:WQcNSzb7+ngS2cfynx0vUwhk+scpgiKlldVcsF8GPbI= github.com/containers/image/v5 v5.16.0/go.mod h1:XgTpfAPLRGOd1XYyCU5cISFr777bLmOerCSpt/v7+Q4= +github.com/containers/image/v5 v5.16.1 h1:4786k48/af3dOkVf9EM+xB880ArkXalICsGC4AXC6to= +github.com/containers/image/v5 v5.16.1/go.mod h1:mCvIFdzyyP1B0NBcZ80OIuaYqFn/OpFpaOMOMn1kU2M= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= @@ -268,8 +270,8 @@ github.com/containers/psgo v1.7.1/go.mod h1:mWGpFzW73qWFA+blhF6l7GuKzbrACkYgr/aj github.com/containers/storage v1.23.5/go.mod h1:ha26Q6ngehFNhf3AWoXldvAvwI4jFe3ETQAf/CeZPyM= github.com/containers/storage v1.35.0/go.mod h1:qzYhasQP2/V9D9XdO+vRwkHBhsBO0oznMLzzRDQ8s20= github.com/containers/storage v1.36.0/go.mod h1:vbd3SKVQNHdmU5qQI6hTEcKPxnZkGqydG4f6uwrI5a8= -github.com/containers/storage v1.36.1-0.20210929132900-162a0bf730ce h1:6YOfANEWtL7+Q4RmnAfloGLIJNtt17MEHjvlHXz0vVY= -github.com/containers/storage v1.36.1-0.20210929132900-162a0bf730ce/go.mod h1:b7OGxODIyB3XpvCSWR91lllT9fv9DXeC8yfnaUocWJU= +github.com/containers/storage v1.37.0 h1:HVhDsur6sx889ZIZ1d1kEiOzv3gsr5q0diX2VZmOdSg= +github.com/containers/storage v1.37.0/go.mod h1:kqeJeS0b7DO2ZT1nVWs0XufrmPFbgV3c+Q/45RlH6r4= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -323,8 +325,9 @@ github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TT github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v20.10.8+incompatible h1:RVqD337BgQicVCzYrrlhLDWhq6OAD2PJDUg2LsEUvKM= github.com/docker/docker v20.10.8+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.9+incompatible h1:JlsVnETOjM2RLQa0Cc1XCIspUdXW3Zenq9P54uXBm6k= +github.com/docker/docker v20.10.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o= github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= @@ -715,8 +718,9 @@ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -761,8 +765,9 @@ github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqi github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= github.com/opencontainers/selinux v1.8.4/go.mod h1:HTvjPFoGMbpQsG886e3lQwnsRWtE4TC1OF3OUvG9FAo= -github.com/opencontainers/selinux v1.8.5 h1:OkT6bMHOQ1JQQO4ihjQ49sj0+wciDcjziSVTRn8VeTA= github.com/opencontainers/selinux v1.8.5/go.mod h1:HTvjPFoGMbpQsG886e3lQwnsRWtE4TC1OF3OUvG9FAo= +github.com/opencontainers/selinux v1.9.1 h1:b4VPEF3O5JLZgdTDBmGepaaIbAo0GqoF6EBRq5f/g3Y= +github.com/opencontainers/selinux v1.9.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/openshift/imagebuilder v1.2.2-0.20210415181909-87f3e48c2656 h1:WaxyNFpmIDu4i6so9r6LVFIbSaXqsj8oitMitt86ae4= github.com/openshift/imagebuilder v1.2.2-0.20210415181909-87f3e48c2656/go.mod h1:9aJRczxCH0mvT6XQ+5STAQaPWz7OsWcU5/mRkt8IWeo= github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913 h1:TnbXhKzrTOyuvWrjI8W6pcoI9XPbLHFXCdN2dtUw7Rw= @@ -917,8 +922,8 @@ github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaW github.com/vbauerster/mpb/v6 v6.0.4 h1:h6J5zM/2wimP5Hj00unQuV8qbo5EPcj6wbkCqgj7KcY= github.com/vbauerster/mpb/v6 v6.0.4/go.mod h1:a/+JT57gqh6Du0Ay5jSR+uBMfXGdlR7VQlGP52fJxLM= github.com/vbauerster/mpb/v7 v7.1.3/go.mod h1:X5GlohZw2fIpypMXWaKart+HGSAjpz49skxkDk+ZL7c= -github.com/vbauerster/mpb/v7 v7.1.4 h1:XGWpWEB8aWnvqSlAMA7F7kdeUGqcTujuVFvYj9+59Ww= -github.com/vbauerster/mpb/v7 v7.1.4/go.mod h1:4zulrZfvshMOnd2APiHgWS9Yrw08AzZVRr9G11tkpcQ= +github.com/vbauerster/mpb/v7 v7.1.5 h1:vtUEUfQHmNeJETyF4AcRCOV6RC4wqFwNORy52UMXPbQ= +github.com/vbauerster/mpb/v7 v7.1.5/go.mod h1:4M8+qAoQqV60WDNktBM5k05i1iTrXE7rjKOHEVkVlec= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= @@ -1206,8 +1211,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34 h1:GkvMjFtXUmahfDtashnc1mnrCtuBVcwse5QV2lUk/tI= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 h1:xrCZDmdtoloIiooiA9q0OQb9r8HejIHYoHGhGCe1pGg= +golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/libpod/container_api.go b/libpod/container_api.go index 50be0eea4..38223316e 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -184,7 +184,7 @@ func (c *Container) StopWithTimeout(timeout uint) error { return define.ErrCtrStopped } - if !c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning) { + if !c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning, define.ContainerStateStopping) { return errors.Wrapf(define.ErrCtrStateInvalid, "can only stop created or running containers. %s is in state %s", c.ID(), c.state.State.String()) } @@ -690,7 +690,7 @@ func (c *Container) Sync() error { // If runtime knows about the container, update its status in runtime // And then save back to disk - if c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning, define.ContainerStatePaused, define.ContainerStateStopped) { + if c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning, define.ContainerStatePaused, define.ContainerStateStopped, define.ContainerStateStopping) { oldState := c.state.State if err := c.ociRuntime.UpdateContainerStatus(c); err != nil { return err diff --git a/libpod/container_copy_linux.go b/libpod/container_copy_linux.go index 7d4dd0d46..954d54a1d 100644 --- a/libpod/container_copy_linux.go +++ b/libpod/container_copy_linux.go @@ -15,8 +15,8 @@ import ( "github.com/containers/buildah/util" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/rootless" + "github.com/containers/storage/pkg/archive" "github.com/containers/storage/pkg/idtools" - "github.com/docker/docker/pkg/archive" "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 1c3de526c..43f5398a2 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -17,12 +17,14 @@ import ( "github.com/containers/buildah/copier" "github.com/containers/buildah/pkg/overlay" butil "github.com/containers/buildah/util" + "github.com/containers/common/pkg/chown" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/events" "github.com/containers/podman/v3/pkg/cgroups" "github.com/containers/podman/v3/pkg/ctime" "github.com/containers/podman/v3/pkg/hooks" "github.com/containers/podman/v3/pkg/hooks/exec" + "github.com/containers/podman/v3/pkg/lookup" "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/podman/v3/pkg/selinux" "github.com/containers/podman/v3/pkg/util" @@ -485,8 +487,12 @@ func (c *Container) setupStorage(ctx context.Context) error { return errors.Wrapf(err, "error creating container storage") } - c.config.IDMappings.UIDMap = containerInfo.UIDMap - c.config.IDMappings.GIDMap = containerInfo.GIDMap + // only reconfig IDMappings if layer was mounted from storage + // if its a external overlay do not reset IDmappings + if !c.config.RootfsOverlay { + c.config.IDMappings.UIDMap = containerInfo.UIDMap + c.config.IDMappings.GIDMap = containerInfo.GIDMap + } processLabel, err := c.processLabel(containerInfo.ProcessLabel) if err != nil { @@ -1515,6 +1521,19 @@ func (c *Container) mountStorage() (_ string, deferredErr error) { } mountPoint = overlayMount.Source + execUser, err := lookup.GetUserGroupInfo(mountPoint, c.config.User, nil) + if err != nil { + return "", err + } + hostUID, hostGID, err := butil.GetHostIDs(util.IDtoolsToRuntimeSpec(c.config.IDMappings.UIDMap), util.IDtoolsToRuntimeSpec(c.config.IDMappings.GIDMap), uint32(execUser.Uid), uint32(execUser.Gid)) + if err != nil { + return "", errors.Wrap(err, "unable to get host UID and host GID") + } + + //note: this should not be recursive, if using external rootfs users should be responsible on configuring ownership. + if err := chown.ChangeHostPathOwnership(mountPoint, false, int(hostUID), int(hostGID)); err != nil { + return "", err + } } if mountPoint == "" { @@ -1690,9 +1709,23 @@ func (c *Container) cleanupStorage() error { var cleanupErr error + markUnmounted := func() { + c.state.Mountpoint = "" + c.state.Mounted = false + + if c.valid { + if err := c.save(); err != nil { + if cleanupErr != nil { + logrus.Errorf("Unmounting container %s: %v", c.ID(), cleanupErr) + } + cleanupErr = err + } + } + } + // umount rootfs overlay if it was created if c.config.RootfsOverlay { - overlayBasePath := c.runtime.store.GraphRoot() + overlayBasePath := filepath.Dir(c.config.StaticDir) overlayBasePath = filepath.Join(overlayBasePath, "rootfs") if err := overlay.Unmount(overlayBasePath); err != nil { // If the container can't remove content report the error @@ -1717,6 +1750,7 @@ func (c *Container) cleanupStorage() error { } if c.config.Rootfs != "" { + markUnmounted() return cleanupErr } @@ -1761,17 +1795,7 @@ func (c *Container) cleanupStorage() error { } } - c.state.Mountpoint = "" - c.state.Mounted = false - - if c.valid { - if err := c.save(); err != nil { - if cleanupErr != nil { - logrus.Errorf("Unmounting container %s: %v", c.ID(), cleanupErr) - } - cleanupErr = err - } - } + markUnmounted() return cleanupErr } diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 867ecc2ad..27cc318b4 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -50,6 +50,7 @@ import ( runcuser "github.com/opencontainers/runc/libcontainer/user" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" + "github.com/opencontainers/selinux/go-selinux" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -390,11 +391,11 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { for _, o := range namedVol.Options { switch o { case "U": - if err := chown.ChangeHostPathOwnership(mountPoint, true, int(hostUID), int(hostGID)); err != nil { + if err := c.ChangeHostPathOwnership(mountPoint, true, int(hostUID), int(hostGID)); err != nil { return nil, err } - if err := chown.ChangeHostPathOwnership(contentDir, true, int(hostUID), int(hostGID)); err != nil { + if err := c.ChangeHostPathOwnership(contentDir, true, int(hostUID), int(hostGID)); err != nil { return nil, err } } @@ -423,14 +424,15 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { if m.Type == "tmpfs" { options = append(options, []string{fmt.Sprintf("uid=%d", execUser.Uid), fmt.Sprintf("gid=%d", execUser.Gid)}...) } else { - if err := chown.ChangeHostPathOwnership(m.Source, true, int(hostUID), int(hostGID)); err != nil { + // only chown on initial creation of container + if err := c.ChangeHostPathOwnership(m.Source, true, int(hostUID), int(hostGID)); err != nil { return nil, err } } case "z": fallthrough case "Z": - if err := label.Relabel(m.Source, c.MountLabel(), label.IsShared(o)); err != nil { + if err := c.relabel(m.Source, c.MountLabel(), label.IsShared(o)); err != nil { return nil, err } @@ -477,11 +479,11 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { for _, o := range overlayVol.Options { switch o { case "U": - if err := chown.ChangeHostPathOwnership(overlayVol.Source, true, int(hostUID), int(hostGID)); err != nil { + if err := c.ChangeHostPathOwnership(overlayVol.Source, true, int(hostUID), int(hostGID)); err != nil { return nil, err } - if err := chown.ChangeHostPathOwnership(contentDir, true, int(hostUID), int(hostGID)); err != nil { + if err := c.ChangeHostPathOwnership(contentDir, true, int(hostUID), int(hostGID)); err != nil { return nil, err } } @@ -1008,12 +1010,15 @@ func (c *Container) exportCheckpoint(options ContainerCheckpointOptions) error { includeFiles := []string{ "artifacts", - "ctr.log", metadata.ConfigDumpFile, metadata.SpecDumpFile, metadata.NetworkStatusFile, } + if c.LogDriver() == define.KubernetesLogging || + c.LogDriver() == define.JSONLogging { + includeFiles = append(includeFiles, "ctr.log") + } if options.PreCheckPoint { includeFiles = append(includeFiles, preCheckpointDir) } else { @@ -1706,13 +1711,13 @@ func (c *Container) makeBindMounts() error { } if c.state.BindMounts["/etc/hosts"] != "" { - if err := label.Relabel(c.state.BindMounts["/etc/hosts"], c.config.MountLabel, true); err != nil { + if err := c.relabel(c.state.BindMounts["/etc/hosts"], c.config.MountLabel, true); err != nil { return err } } if c.state.BindMounts["/etc/resolv.conf"] != "" { - if err := label.Relabel(c.state.BindMounts["/etc/resolv.conf"], c.config.MountLabel, true); err != nil { + if err := c.relabel(c.state.BindMounts["/etc/resolv.conf"], c.config.MountLabel, true); err != nil { return err } } @@ -1994,7 +1999,7 @@ func (c *Container) generateResolvConf() (string, error) { } // Relabel resolv.conf for the container - if err := label.Relabel(destPath, c.config.MountLabel, true); err != nil { + if err := c.relabel(destPath, c.config.MountLabel, true); err != nil { return "", err } @@ -2016,7 +2021,7 @@ func (c *Container) generateHosts(path string) (string, error) { } // based on networking mode we may want to append the localhost -// if there isn't any record for it and also this shoud happen +// if there isn't any record for it and also this should happen // in slirp4netns and similar network modes. func (c *Container) appendLocalhost(hosts string) string { if !strings.Contains(hosts, "localhost") && @@ -2611,7 +2616,7 @@ func (c *Container) copyTimezoneFile(zonePath string) (string, error) { if err != nil { return "", err } - if err := label.Relabel(localtimeCopy, c.config.MountLabel, false); err != nil { + if err := c.relabel(localtimeCopy, c.config.MountLabel, false); err != nil { return "", err } if err := dest.Chown(c.RootUID(), c.RootGID()); err != nil { @@ -2746,3 +2751,37 @@ func (c *Container) fixVolumePermissions(v *ContainerNamedVolume) error { } return nil } + +func (c *Container) relabel(src, mountLabel string, recurse bool) error { + if !selinux.GetEnabled() || mountLabel == "" { + return nil + } + // only relabel on initial creation of container + if !c.ensureState(define.ContainerStateConfigured, define.ContainerStateUnknown) { + label, err := label.FileLabel(src) + if err != nil { + return err + } + // If labels are different, might be on a tmpfs + if label == mountLabel { + return nil + } + } + return label.Relabel(src, mountLabel, recurse) +} + +func (c *Container) ChangeHostPathOwnership(src string, recurse bool, uid, gid int) error { + // only chown on initial creation of container + if !c.ensureState(define.ContainerStateConfigured, define.ContainerStateUnknown) { + st, err := os.Stat(src) + if err != nil { + return err + } + + // If labels are different, might be on a tmpfs + if int(st.Sys().(*syscall.Stat_t).Uid) == uid && int(st.Sys().(*syscall.Stat_t).Gid) == gid { + return nil + } + } + return chown.ChangeHostPathOwnership(src, recurse, uid, gid) +} diff --git a/libpod/container_log_linux.go b/libpod/container_log_linux.go index ca1e11ef5..562169ce2 100644 --- a/libpod/container_log_linux.go +++ b/libpod/container_log_linux.go @@ -91,8 +91,12 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption var cursorError error for i := 1; i <= 3; i++ { cursor, cursorError = journal.GetCursor() + hundreds := 1 + for j := 1; j < i; j++ { + hundreds *= 2 + } if cursorError != nil { - time.Sleep(time.Duration(i*100) * time.Millisecond) + time.Sleep(time.Duration(hundreds*100) * time.Millisecond) continue } break diff --git a/libpod/kube.go b/libpod/kube.go index bf86a9d16..d47f47f1c 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -25,6 +25,7 @@ import ( v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" v12 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" ) // GenerateForKube takes a slice of libpod containers and generates @@ -196,10 +197,11 @@ func containerPortsToServicePorts(containerPorts []v1.ContainerPort) []v1.Servic for _, cp := range containerPorts { nodePort := 30000 + rand.Intn(32767-30000+1) servicePort := v1.ServicePort{ - Protocol: cp.Protocol, - Port: cp.ContainerPort, - NodePort: int32(nodePort), - Name: strconv.Itoa(int(cp.ContainerPort)), + Protocol: cp.Protocol, + Port: cp.ContainerPort, + NodePort: int32(nodePort), + Name: strconv.Itoa(int(cp.ContainerPort)), + TargetPort: intstr.Parse(strconv.Itoa(int(cp.ContainerPort))), } sps = append(sps, servicePort) } @@ -246,7 +248,7 @@ func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, po return nil, err } for k, v := range annotations { - podAnnotations[define.BindMountPrefix+k] = v + podAnnotations[define.BindMountPrefix+k] = strings.TrimSpace(v) } // Since port bindings for the pod are handled by the // infra container, wipe them here. @@ -366,7 +368,7 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container) (*v1.Pod, return nil, err } for k, v := range annotations { - kubeAnnotations[define.BindMountPrefix+k] = v + kubeAnnotations[define.BindMountPrefix+k] = strings.TrimSpace(v) } if isInit { kubeInitCtrs = append(kubeInitCtrs, kubeCtr) @@ -477,14 +479,20 @@ func containerToV1Container(ctx context.Context, c *Container) (v1.Container, [] if err != nil { return kubeContainer, kubeVolumes, nil, annotations, err } - imgData, err := img.Inspect(ctx, false) + imgData, err := img.Inspect(ctx, nil) if err != nil { return kubeContainer, kubeVolumes, nil, annotations, err } - if reflect.DeepEqual(imgData.Config.Cmd, kubeContainer.Command) { + // If the user doesn't set a command/entrypoint when creating the container with podman and + // is using the image command or entrypoint from the image, don't add it to the generated kube yaml + if reflect.DeepEqual(imgData.Config.Cmd, kubeContainer.Command) || reflect.DeepEqual(imgData.Config.Entrypoint, kubeContainer.Command) { kubeContainer.Command = nil } + if imgData.User == c.User() { + kubeSec.RunAsGroup, kubeSec.RunAsUser = nil, nil + } + kubeContainer.WorkingDir = c.WorkingDir() kubeContainer.Ports = ports // This should not be applicable @@ -572,7 +580,8 @@ func ocicniPortMappingToContainerPort(portMappings []types.OCICNIPortMapping) ([ var protocol v1.Protocol switch strings.ToUpper(p.Protocol) { case "TCP": - protocol = v1.ProtocolTCP + // do nothing as it is the default protocol in k8s, there is no need to explicitly + // add it to the generated yaml case "UDP": protocol = v1.ProtocolUDP default: diff --git a/libpod/network/cni/cni_conversion.go b/libpod/network/cni/cni_conversion.go index 93d871767..01e149114 100644 --- a/libpod/network/cni/cni_conversion.go +++ b/libpod/network/cni/cni_conversion.go @@ -103,7 +103,7 @@ func createNetworkFromCNIConfigList(conf *libcni.NetworkConfigList, confPath str } default: - // A warning would be good but users would get this warning everytime so keep this at info level. + // A warning would be good but users would get this warning every time so keep this at info level. logrus.Infof("Unsupported CNI config type %s in %s, this network can still be used but inspect or list cannot show all information", firstPlugin.Network.Type, confPath) } diff --git a/libpod/network/cni/cni_types.go b/libpod/network/cni/cni_types.go index fbf917c2d..87beceff3 100644 --- a/libpod/network/cni/cni_types.go +++ b/libpod/network/cni/cni_types.go @@ -182,7 +182,7 @@ func newIPAMLocalHostRange(subnet types.IPNet, leaseRange *types.LeaseRange, gw hostRange.RangeStart = leaseRange.StartIP.String() } if leaseRange.EndIP != nil { - hostRange.RangeStart = leaseRange.EndIP.String() + hostRange.RangeEnd = leaseRange.EndIP.String() } } diff --git a/libpod/network/cni/config_test.go b/libpod/network/cni/config_test.go index 288cf4626..0dfc6173c 100644 --- a/libpod/network/cni/config_test.go +++ b/libpod/network/cni/config_test.go @@ -621,7 +621,7 @@ var _ = Describe("Config", func() { err = libpodNet.NetworkRemove(network1.Name) Expect(err).To(BeNil()) - endIP := "10.0.0.10" + endIP := "10.0.0.30" network = types.Network{ Driver: "bridge", Subnets: []types.Subnet{ @@ -665,6 +665,22 @@ var _ = Describe("Config", func() { Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.0.0.1")) Expect(network1.Subnets[0].LeaseRange.StartIP.String()).To(Equal(startIP)) Expect(network1.Subnets[0].LeaseRange.EndIP.String()).To(Equal(endIP)) + + // create a new interface to force a config load from disk + libpodNet, err = getNetworkInterface(cniConfDir, false) + Expect(err).To(BeNil()) + + network1, err = libpodNet.NetworkInspect(network1.Name) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(BeEmpty()) + Expect(network1.ID).ToNot(BeEmpty()) + Expect(network1.NetworkInterface).ToNot(BeEmpty()) + Expect(network1.Driver).To(Equal("bridge")) + Expect(network1.Subnets).To(HaveLen(1)) + Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet)) + Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.0.0.1")) + Expect(network1.Subnets[0].LeaseRange.StartIP.String()).To(Equal(startIP)) + Expect(network1.Subnets[0].LeaseRange.EndIP.String()).To(Equal(endIP)) }) It("create bridge with subnet and invalid lease range", func() { @@ -1020,28 +1036,6 @@ var _ = Describe("Config", func() { Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("subnet 10.10.0.0/24 is already used on the host or by another config")) }) - - It("remove network should not error when config file does not exists on disk", func() { - name := "mynet" - network := types.Network{Name: name} - _, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - - path := filepath.Join(cniConfDir, name+".conflist") - Expect(path).To(BeARegularFile()) - - err = os.Remove(path) - Expect(err).To(BeNil()) - Expect(path).ToNot(BeARegularFile()) - - err = libpodNet.NetworkRemove(name) - Expect(err).To(BeNil()) - - nets, err := libpodNet.NetworkList() - Expect(err).To(BeNil()) - Expect(nets).To(HaveLen(1)) - Expect(nets).ToNot(ContainElement(HaveNetworkName(name))) - }) }) Context("network load valid existing ones", func() { @@ -1335,7 +1329,7 @@ var _ = Describe("Config", func() { Expect(networks).To(HaveLen(0)) }) - It("crate bridge network with used interface name", func() { + It("create bridge network with used interface name", func() { network := types.Network{ NetworkInterface: "cni-podman9", } diff --git a/libpod/network/cni/network.go b/libpod/network/cni/network.go index 02801641e..a37a84373 100644 --- a/libpod/network/cni/network.go +++ b/libpod/network/cni/network.go @@ -10,6 +10,7 @@ import ( "net" "os" "strings" + "time" "github.com/containernetworking/cni/libcni" "github.com/containers/podman/v3/libpod/define" @@ -40,6 +41,9 @@ type cniNetwork struct { // lock is a internal lock for critical operations lock lockfile.Locker + // modTime is the timestamp when the config dir was modified + modTime time.Time + // networks is a map with loaded networks, the key is the network name networks map[string]*network } @@ -113,10 +117,22 @@ func (n *cniNetwork) Drivers() []string { } func (n *cniNetwork) loadNetworks() error { - // skip loading networks if they are already loaded - if n.networks != nil { + // check the mod time of the config dir + f, err := os.Stat(n.cniConfigDir) + if err != nil { + return err + } + modTime := f.ModTime() + + // skip loading networks if they are already loaded and + // if the config dir was not modified since the last call + if n.networks != nil && modTime.Equal(n.modTime) { return nil } + // make sure the remove all networks before we reload them + n.networks = nil + n.modTime = modTime + // FIXME: do we have to support other file types as well, e.g. .conf? files, err := libcni.ConfFiles(n.cniConfigDir, []string{".conflist"}) if err != nil { @@ -153,7 +169,7 @@ func (n *cniNetwork) loadNetworks() error { logrus.Errorf("CNI config list %s could not be converted to a libpod config, skipping: %v", file, err) continue } - logrus.Tracef("Successfully loaded network %s: %v", net.Name, net) + logrus.Debugf("Successfully loaded network %s: %v", net.Name, net) networkInfo := network{ filename: file, cniNet: conf, diff --git a/libpod/network/types/network.go b/libpod/network/types/network.go index 2fe4f3da2..657c1ca6a 100644 --- a/libpod/network/types/network.go +++ b/libpod/network/types/network.go @@ -137,7 +137,7 @@ type NetInterface struct { MacAddress net.HardwareAddr `json:"mac_address"` } -// NetAddress contains the subnet and gatway. +// NetAddress contains the subnet and gateway. type NetAddress struct { // Subnet of this NetAddress. Note that the subnet contains the // actual ip of the net interface and not the network address. diff --git a/libpod/networking_slirp4netns.go b/libpod/networking_slirp4netns.go index 46cda89a9..ffd53ec2b 100644 --- a/libpod/networking_slirp4netns.go +++ b/libpod/networking_slirp4netns.go @@ -484,10 +484,14 @@ func (r *Runtime) setupRootlessPortMappingViaRLK(ctr *Container, netnsPath strin } cfgR := bytes.NewReader(cfgJSON) var stdout bytes.Buffer - cmd := exec.Command(fmt.Sprintf("/proc/%d/exe", os.Getpid())) - cmd.Args = []string{rootlessport.ReexecKey} - // Leak one end of the pipe in rootlessport process, the other will be sent to conmon + path, err := r.config.FindHelperBinary(rootlessport.BinaryName, false) + if err != nil { + return err + } + cmd := exec.Command(path) + cmd.Args = []string{rootlessport.BinaryName} + // Leak one end of the pipe in rootlessport process, the other will be sent to conmon if ctr.rootlessPortSyncR != nil { defer errorhandling.CloseQuiet(ctr.rootlessPortSyncR) } diff --git a/libpod/oci_attach_linux.go b/libpod/oci_attach_linux.go index d4d4a1076..1f2a28ead 100644 --- a/libpod/oci_attach_linux.go +++ b/libpod/oci_attach_linux.go @@ -93,7 +93,7 @@ func (c *Container) attach(streams *define.AttachStreams, keys string, resize <- if attachRdy != nil { attachRdy <- true } - return readStdio(streams, receiveStdoutError, stdinDone) + return readStdio(conn, streams, receiveStdoutError, stdinDone) } // Attach to the given container's exec session @@ -174,7 +174,7 @@ func (c *Container) attachToExec(streams *define.AttachStreams, keys *string, se return err } - return readStdio(streams, receiveStdoutError, stdinDone) + return readStdio(conn, streams, receiveStdoutError, stdinDone) } func processDetachKeys(keys string) ([]byte, error) { @@ -217,11 +217,6 @@ func setupStdioChannels(streams *define.AttachStreams, conn *net.UnixConn, detac var err error if streams.AttachInput { _, err = utils.CopyDetachable(conn, streams.InputStream, detachKeys) - if err == nil { - if connErr := conn.CloseWrite(); connErr != nil { - logrus.Errorf("Unable to close conn: %q", connErr) - } - } } stdinDone <- err }() @@ -274,7 +269,7 @@ func redirectResponseToOutputStreams(outputStream, errorStream io.Writer, writeO return err } -func readStdio(streams *define.AttachStreams, receiveStdoutError, stdinDone chan error) error { +func readStdio(conn *net.UnixConn, streams *define.AttachStreams, receiveStdoutError, stdinDone chan error) error { var err error select { case err = <-receiveStdoutError: @@ -283,6 +278,12 @@ func readStdio(streams *define.AttachStreams, receiveStdoutError, stdinDone chan if err == define.ErrDetach { return err } + if err == nil { + // copy stdin is done, close it + if connErr := conn.CloseWrite(); connErr != nil { + logrus.Errorf("Unable to close conn: %v", connErr) + } + } if streams.AttachOutput || streams.AttachError { return <-receiveStdoutError } diff --git a/libpod/oci_conmon_exec_linux.go b/libpod/oci_conmon_exec_linux.go index 822377bfe..654306f92 100644 --- a/libpod/oci_conmon_exec_linux.go +++ b/libpod/oci_conmon_exec_linux.go @@ -609,9 +609,6 @@ func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.Resp _, err := utils.CopyDetachable(conn, httpBuf, detachKeys) logrus.Debugf("STDIN copy completed") stdinChan <- err - if connErr := conn.CloseWrite(); connErr != nil { - logrus.Errorf("Unable to close conn: %v", connErr) - } }() } @@ -654,6 +651,10 @@ func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.Resp if err != nil { return err } + // copy stdin is done, close it + if connErr := conn.CloseWrite(); connErr != nil { + logrus.Errorf("Unable to close conn: %v", connErr) + } case <-cancel: return nil } diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go index 71a7b29fa..1719b2dfa 100644 --- a/libpod/oci_conmon_linux.go +++ b/libpod/oci_conmon_linux.go @@ -351,6 +351,12 @@ func (r *ConmonOCIRuntime) UpdateContainerStatus(ctr *Container) error { return ctr.handleExitFile(exitFile, fi) } + // Handle ContainerStateStopping - keep it unless the container + // transitioned to no longer running. + if oldState == define.ContainerStateStopping && (ctr.state.State == define.ContainerStatePaused || ctr.state.State == define.ContainerStateRunning) { + ctr.state.State = define.ContainerStateStopping + } + return nil } @@ -435,7 +441,8 @@ func (r *ConmonOCIRuntime) StopContainer(ctr *Container, timeout uint, all bool) } if err := waitContainerStop(ctr, time.Duration(timeout)*time.Second); err != nil { - logrus.Infof("Timed out stopping container %s, resorting to SIGKILL: %v", ctr.ID(), err) + logrus.Debugf("Timed out stopping container %s with %s, resorting to SIGKILL: %v", ctr.ID(), unix.SignalName(syscall.Signal(stopSignal)), err) + logrus.Warnf("StopSignal %s failed to stop container %s in %d seconds, resorting to SIGKILL", unix.SignalName(syscall.Signal(stopSignal)), ctr.Name(), timeout) } else { // No error, the container is dead return nil @@ -701,6 +708,10 @@ func (r *ConmonOCIRuntime) HTTPAttach(ctr *Container, req *http.Request, w http. if err != nil { return err } + // copy stdin is done, close it + if connErr := conn.CloseWrite(); connErr != nil { + logrus.Errorf("Unable to close conn: %v", connErr) + } case <-cancel: return nil } diff --git a/libpod/pod.go b/libpod/pod.go index 068a835f6..0e5ac4906 100644 --- a/libpod/pod.go +++ b/libpod/pod.go @@ -390,7 +390,7 @@ func (p *Pod) InfraContainerID() (string, error) { return p.infraContainerID() } -// infraContainer is the unlocked versio of InfraContainer which returns the infra container +// infraContainer is the unlocked version of InfraContainer which returns the infra container func (p *Pod) infraContainer() (*Container, error) { id, err := p.infraContainerID() if err != nil { diff --git a/libpod/pod_api.go b/libpod/pod_api.go index 4ae02fb40..feb8ff250 100644 --- a/libpod/pod_api.go +++ b/libpod/pod_api.go @@ -37,7 +37,8 @@ func (p *Pod) startInitContainers(ctx context.Context) error { if initCon.config.InitContainerType == define.OneShotInitContainer { icLock := initCon.lock icLock.Lock() - if err := p.runtime.removeContainer(ctx, initCon, false, false, true); err != nil { + var time *uint + if err := p.runtime.removeContainer(ctx, initCon, false, false, true, time); err != nil { icLock.Unlock() return errors.Wrapf(err, "failed to remove once init container %s", initCon.ID()) } diff --git a/libpod/reset.go b/libpod/reset.go index 7b25ed680..5d9bb0e90 100644 --- a/libpod/reset.go +++ b/libpod/reset.go @@ -18,12 +18,13 @@ import ( // Reset removes all storage func (r *Runtime) Reset(ctx context.Context) error { + var timeout *uint pods, err := r.GetAllPods() if err != nil { return err } for _, p := range pods { - if err := r.RemovePod(ctx, p, true, true); err != nil { + if err := r.RemovePod(ctx, p, true, true, timeout); err != nil { if errors.Cause(err) == define.ErrNoSuchPod { continue } @@ -37,7 +38,7 @@ func (r *Runtime) Reset(ctx context.Context) error { } for _, c := range ctrs { - if err := r.RemoveContainer(ctx, c, true, true); err != nil { + if err := r.RemoveContainer(ctx, c, true, true, timeout); err != nil { if err := r.RemoveStorageContainer(c.ID(), true); err != nil { if errors.Cause(err) == define.ErrNoSuchCtr { continue @@ -61,7 +62,7 @@ func (r *Runtime) Reset(ctx context.Context) error { return err } for _, v := range volumes { - if err := r.RemoveVolume(ctx, v, true); err != nil { + if err := r.RemoveVolume(ctx, v, true, timeout); err != nil { if errors.Cause(err) == define.ErrNoSuchVolume { continue } diff --git a/libpod/runtime.go b/libpod/runtime.go index 27885bf5c..855f3a9f9 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -489,8 +489,7 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) { DefaultNetwork: runtime.config.Network.DefaultNetwork, DefaultSubnet: runtime.config.Network.DefaultSubnet, IsMachine: runtime.config.Engine.MachineEnabled, - // TODO use cni.lock - LockFile: filepath.Join(runtime.config.Network.NetworkConfigDir, "cni1.lock"), + LockFile: filepath.Join(runtime.config.Network.NetworkConfigDir, "cni.lock"), }) if err != nil { return errors.Wrapf(err, "could not create network interface") diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index 00979a500..2256ba57c 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -535,10 +535,10 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai // If removeVolume is specified, named volumes used by the container will // be removed also if and only if the container is the sole user // Otherwise, RemoveContainer will return an error if the container is running -func (r *Runtime) RemoveContainer(ctx context.Context, c *Container, force bool, removeVolume bool) error { +func (r *Runtime) RemoveContainer(ctx context.Context, c *Container, force bool, removeVolume bool, timeout *uint) error { r.lock.Lock() defer r.lock.Unlock() - return r.removeContainer(ctx, c, force, removeVolume, false) + return r.removeContainer(ctx, c, force, removeVolume, false, timeout) } // Internal function to remove a container. @@ -546,7 +546,7 @@ func (r *Runtime) RemoveContainer(ctx context.Context, c *Container, force bool, // removePod is used only when removing pods. It instructs Podman to ignore // infra container protections, and *not* remove from the database (as pod // remove will handle that). -func (r *Runtime) removeContainer(ctx context.Context, c *Container, force, removeVolume, removePod bool) error { +func (r *Runtime) removeContainer(ctx context.Context, c *Container, force, removeVolume, removePod bool, timeout *uint) error { if !c.valid { if ok, _ := r.state.HasContainer(c.ID()); !ok { // Container probably already removed @@ -642,9 +642,13 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force, remo // Check that the container's in a good state to be removed. if c.state.State == define.ContainerStateRunning { + time := c.StopTimeout() + if timeout != nil { + time = *timeout + } // Ignore ErrConmonDead - we couldn't retrieve the container's // exit code properly, but it's still stopped. - if err := c.stop(c.StopTimeout()); err != nil && errors.Cause(err) != define.ErrConmonDead { + if err := c.stop(time); err != nil && errors.Cause(err) != define.ErrConmonDead { return errors.Wrapf(err, "cannot remove container %s as it could not be stopped", c.ID()) } @@ -751,7 +755,7 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force, remo if !volume.Anonymous() { continue } - if err := runtime.removeVolume(ctx, volume, false); err != nil && errors.Cause(err) != define.ErrNoSuchVolume { + if err := runtime.removeVolume(ctx, volume, false, timeout); err != nil && errors.Cause(err) != define.ErrNoSuchVolume { logrus.Errorf("Cleanup volume (%s): %v", v, err) } } @@ -782,6 +786,7 @@ func (r *Runtime) EvictContainer(ctx context.Context, idOrName string, removeVol // remove will handle that). func (r *Runtime) evictContainer(ctx context.Context, idOrName string, removeVolume bool) (string, error) { var err error + var timeout *uint if !r.valid { return "", define.ErrRuntimeStopped @@ -797,7 +802,7 @@ func (r *Runtime) evictContainer(ctx context.Context, idOrName string, removeVol if err == nil { logrus.Infof("Container %s successfully retrieved from state, attempting normal removal", id) // Assume force = true for the evict case - err = r.removeContainer(ctx, tmpCtr, true, removeVolume, false) + err = r.removeContainer(ctx, tmpCtr, true, removeVolume, false, timeout) if !tmpCtr.valid { // If the container is marked invalid, remove succeeded // in kicking it out of the state - no need to continue. @@ -892,7 +897,7 @@ func (r *Runtime) evictContainer(ctx context.Context, idOrName string, removeVol if !volume.Anonymous() { continue } - if err := r.removeVolume(ctx, volume, false); err != nil && err != define.ErrNoSuchVolume && err != define.ErrVolumeBeingUsed { + if err := r.removeVolume(ctx, volume, false, timeout); err != nil && err != define.ErrNoSuchVolume && err != define.ErrVolumeBeingUsed { logrus.Errorf("Cleanup volume (%s): %v", v, err) } } @@ -1089,7 +1094,8 @@ func (r *Runtime) PruneContainers(filterFuncs []ContainerFilter) ([]*reports.Pru preports = append(preports, report) continue } - err = r.RemoveContainer(context.Background(), c, false, false) + var time *uint + err = r.RemoveContainer(context.Background(), c, false, false, time) if err != nil { report.Err = err } else { diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go index 1915a5c4d..52ac0d4d7 100644 --- a/libpod/runtime_img.go +++ b/libpod/runtime_img.go @@ -37,7 +37,8 @@ func (r *Runtime) RemoveContainersForImageCallback(ctx context.Context) libimage } for _, ctr := range ctrs { if ctr.config.RootfsImageID == imageID { - if err := r.removeContainer(ctx, ctr, true, false, false); err != nil { + var timeout *uint + if err := r.removeContainer(ctx, ctr, true, false, false, timeout); err != nil { return errors.Wrapf(err, "error removing image %s: container %s using image could not be removed", imageID, ctr.ID()) } } diff --git a/libpod/runtime_pod.go b/libpod/runtime_pod.go index b142472e8..2389ee6d9 100644 --- a/libpod/runtime_pod.go +++ b/libpod/runtime_pod.go @@ -26,7 +26,7 @@ type PodFilter func(*Pod) bool // If force is specified with removeCtrs, all containers will be stopped before // being removed // Otherwise, the pod will not be removed if any containers are running -func (r *Runtime) RemovePod(ctx context.Context, p *Pod, removeCtrs, force bool) error { +func (r *Runtime) RemovePod(ctx context.Context, p *Pod, removeCtrs, force bool, timeout *uint) error { r.lock.Lock() defer r.lock.Unlock() @@ -45,7 +45,7 @@ func (r *Runtime) RemovePod(ctx context.Context, p *Pod, removeCtrs, force bool) p.lock.Lock() defer p.lock.Unlock() - return r.removePod(ctx, p, removeCtrs, force) + return r.removePod(ctx, p, removeCtrs, force, timeout) } // GetPod retrieves a pod by its ID @@ -196,7 +196,8 @@ func (r *Runtime) PrunePods(ctx context.Context) (map[string]error, error) { return response, nil } for _, pod := range pods { - err := r.removePod(context.TODO(), pod, true, false) + var timeout *uint + err := r.removePod(context.TODO(), pod, true, false, timeout) response[pod.ID()] = err } return response, nil diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go index b0279c453..9bd6d5e78 100644 --- a/libpod/runtime_pod_linux.go +++ b/libpod/runtime_pod_linux.go @@ -168,7 +168,7 @@ func (r *Runtime) SavePod(pod *Pod) error { return nil } -func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) error { +func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool, timeout *uint) error { if err := p.updatePod(); err != nil { return err } @@ -254,7 +254,7 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) ctrNamedVolumes[vol.Name] = vol } - if err := r.removeContainer(ctx, ctr, force, false, true); err != nil { + if err := r.removeContainer(ctx, ctr, force, false, true, timeout); err != nil { if removalErr == nil { removalErr = err } else { @@ -280,7 +280,7 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) if !volume.Anonymous() { continue } - if err := r.removeVolume(ctx, volume, false); err != nil { + if err := r.removeVolume(ctx, volume, false, timeout); err != nil { if errors.Cause(err) == define.ErrNoSuchVolume || errors.Cause(err) == define.ErrVolumeRemoved { continue } diff --git a/libpod/runtime_volume.go b/libpod/runtime_volume.go index 5f8f9ca1e..2b3ad10b4 100644 --- a/libpod/runtime_volume.go +++ b/libpod/runtime_volume.go @@ -21,7 +21,7 @@ type VolumeCreateOption func(*Volume) error type VolumeFilter func(*Volume) bool // RemoveVolume removes a volumes -func (r *Runtime) RemoveVolume(ctx context.Context, v *Volume, force bool) error { +func (r *Runtime) RemoveVolume(ctx context.Context, v *Volume, force bool, timeout *uint) error { r.lock.Lock() defer r.lock.Unlock() @@ -36,7 +36,7 @@ func (r *Runtime) RemoveVolume(ctx context.Context, v *Volume, force bool) error return nil } } - return r.removeVolume(ctx, v, force) + return r.removeVolume(ctx, v, force, timeout) } // GetVolume retrieves a volume given its full name. @@ -149,7 +149,8 @@ func (r *Runtime) PruneVolumes(ctx context.Context, filterFuncs []VolumeFilter) } report.Size = volSize report.Id = vol.Name() - if err := r.RemoveVolume(ctx, vol, false); err != nil { + var timeout *uint + if err := r.RemoveVolume(ctx, vol, false, timeout); err != nil { if errors.Cause(err) != define.ErrVolumeBeingUsed && errors.Cause(err) != define.ErrVolumeRemoved { report.Err = err } else { diff --git a/libpod/runtime_volume_linux.go b/libpod/runtime_volume_linux.go index def6ca411..ed3cc971c 100644 --- a/libpod/runtime_volume_linux.go +++ b/libpod/runtime_volume_linux.go @@ -189,7 +189,7 @@ func makeVolumeInPluginIfNotExist(name string, options map[string]string, plugin } // removeVolume removes the specified volume from state as well tears down its mountpoint and storage -func (r *Runtime) removeVolume(ctx context.Context, v *Volume, force bool) error { +func (r *Runtime) removeVolume(ctx context.Context, v *Volume, force bool, timeout *uint) error { if !v.valid { if ok, _ := r.state.HasVolume(v.Name()); !ok { return nil @@ -230,11 +230,7 @@ func (r *Runtime) removeVolume(ctx context.Context, v *Volume, force bool) error logrus.Debugf("Removing container %s (depends on volume %q)", ctr.ID(), v.Name()) - // TODO: do we want to set force here when removing - // containers? - // I'm inclined to say no, in case someone accidentally - // wipes a container they're using... - if err := r.removeContainer(ctx, ctr, false, false, false); err != nil { + if err := r.removeContainer(ctx, ctr, force, false, false, timeout); err != nil { return errors.Wrapf(err, "error removing container %s that depends on volume %s", ctr.ID(), v.Name()) } } diff --git a/libpod/shutdown/handler.go b/libpod/shutdown/handler.go index cca74c3c4..9add05c9c 100644 --- a/libpod/shutdown/handler.go +++ b/libpod/shutdown/handler.go @@ -5,9 +5,10 @@ import ( "os/signal" "sync" "syscall" + "time" "github.com/pkg/errors" - "github.com/sirupsen/logrus" + logrusImport "github.com/sirupsen/logrus" ) var ( @@ -25,6 +26,7 @@ var ( // Ordering that on-shutdown handlers will be invoked. handlerOrder []string shutdownInhibit sync.RWMutex + logrus = logrusImport.WithField("PID", os.Getpid()) ) // Start begins handling SIGTERM and SIGINT and will run the given on-signal @@ -44,25 +46,31 @@ func Start() error { go func() { select { case <-cancelChan: + logrus.Infof("Received shutdown.Stop(), terminating!") signal.Stop(sigChan) close(sigChan) close(cancelChan) stopped = true return case sig := <-sigChan: - logrus.Infof("Received shutdown signal %v, terminating!", sig) + logrus.Infof("Received shutdown signal %q, terminating!", sig.String()) shutdownInhibit.Lock() handlerLock.Lock() + for _, name := range handlerOrder { handler, ok := handlers[name] if !ok { - logrus.Errorf("Shutdown handler %s definition not found!", name) + logrus.Errorf("Shutdown handler %q definition not found!", name) continue } - logrus.Infof("Invoking shutdown handler %s", name) + + logrus.Infof("Invoking shutdown handler %q", name) + start := time.Now() if err := handler(sig); err != nil { - logrus.Errorf("Running shutdown handler %s: %v", name, err) + logrus.Errorf("Running shutdown handler %q: %v", name, err) } + logrus.Debugf("Completed shutdown handler %q, duration %v", name, + time.Since(start).Round(time.Second)) } handlerLock.Unlock() shutdownInhibit.Unlock() @@ -87,12 +95,12 @@ func Stop() error { return nil } -// Temporarily inhibit signals from shutting down Libpod. +// Inhibit temporarily inhibit signals from shutting down Libpod. func Inhibit() { shutdownInhibit.RLock() } -// Stop inhibiting signals from shutting down Libpod. +// Uninhibit stop inhibiting signals from shutting down Libpod. func Uninhibit() { shutdownInhibit.RUnlock() } diff --git a/nix/default-arm64.nix b/nix/default-arm64.nix index 8868788ae..bb958a193 100644 --- a/nix/default-arm64.nix +++ b/nix/default-arm64.nix @@ -77,10 +77,12 @@ let patchShebangs . make bin/podman make bin/podman-remote + make bin/rootlessport ''; installPhase = '' install -Dm755 bin/podman $out/bin/podman install -Dm755 bin/podman-remote $out/bin/podman-remote + install -Dm755 bin/rootlessport $out/libexec/podman/rootlessport ''; }; in diff --git a/nix/default.nix b/nix/default.nix index 4d15532c2..1dc6f92b6 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -75,10 +75,12 @@ let patchShebangs . make bin/podman make bin/podman-remote + make bin/rootlessport ''; installPhase = '' install -Dm755 bin/podman $out/bin/podman install -Dm755 bin/podman-remote $out/bin/podman-remote + install -Dm755 bin/rootlessport $out/libexec/podman/rootlessport ''; }; in diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index 18005e24a..4f101ce84 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -34,11 +34,12 @@ import ( func RemoveContainer(w http.ResponseWriter, r *http.Request) { decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { - Force bool `schema:"force"` - Ignore bool `schema:"ignore"` - Link bool `schema:"link"` - DockerVolumes bool `schema:"v"` - LibpodVolumes bool `schema:"volumes"` + Force bool `schema:"force"` + Ignore bool `schema:"ignore"` + Link bool `schema:"link"` + Timeout *uint `schema:"timeout"` + DockerVolumes bool `schema:"v"` + LibpodVolumes bool `schema:"volumes"` }{ // override any golang type defaults } @@ -55,6 +56,7 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request) { } if utils.IsLibpodRequest(r) { options.Volumes = query.LibpodVolumes + options.Timeout = query.Timeout } else { if query.Link { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, diff --git a/pkg/api/handlers/compat/images_search.go b/pkg/api/handlers/compat/images_search.go index 01282513e..e9cc3e2b6 100644 --- a/pkg/api/handlers/compat/images_search.go +++ b/pkg/api/handlers/compat/images_search.go @@ -22,7 +22,6 @@ func SearchImages(w http.ResponseWriter, r *http.Request) { query := struct { Term string `json:"term"` Limit int `json:"limit"` - NoTrunc bool `json:"noTrunc"` Filters map[string][]string `json:"filters"` TLSVerify bool `json:"tlsVerify"` ListTags bool `json:"listTags"` @@ -50,7 +49,6 @@ func SearchImages(w http.ResponseWriter, r *http.Request) { options := entities.ImageSearchOptions{ Authfile: authfile, Limit: query.Limit, - NoTrunc: query.NoTrunc, ListTags: query.ListTags, Filters: filters, } diff --git a/pkg/api/handlers/compat/networks.go b/pkg/api/handlers/compat/networks.go index b1456ed9e..dd28f6deb 100644 --- a/pkg/api/handlers/compat/networks.go +++ b/pkg/api/handlers/compat/networks.go @@ -245,7 +245,8 @@ func RemoveNetwork(w http.ResponseWriter, r *http.Request) { ic := abi.ContainerEngine{Libpod: runtime} query := struct { - Force bool `schema:"force"` + Force bool `schema:"force"` + Timeout *uint `schema:"timeout"` }{ // This is where you can override the golang default value for one of fields } @@ -257,7 +258,8 @@ func RemoveNetwork(w http.ResponseWriter, r *http.Request) { } options := entities.NetworkRmOptions{ - Force: query.Force, + Force: query.Force, + Timeout: query.Timeout, } name := utils.GetName(r) diff --git a/pkg/api/handlers/compat/volumes.go b/pkg/api/handlers/compat/volumes.go index 0f9b66888..e779aa185 100644 --- a/pkg/api/handlers/compat/volumes.go +++ b/pkg/api/handlers/compat/volumes.go @@ -213,7 +213,8 @@ func RemoveVolume(w http.ResponseWriter, r *http.Request) { decoder = r.Context().Value(api.DecoderKey).(*schema.Decoder) ) query := struct { - Force bool `schema:"force"` + Force bool `schema:"force"` + Timeout *uint `schema:"timeout"` }{ // override any golang type defaults } @@ -239,7 +240,7 @@ func RemoveVolume(w http.ResponseWriter, r *http.Request) { vol, err := runtime.LookupVolume(name) if err == nil { // As above, we do not pass `force` from the query parameters here - if err := runtime.RemoveVolume(r.Context(), vol, false); err != nil { + if err := runtime.RemoveVolume(r.Context(), vol, false, query.Timeout); err != nil { if errors.Cause(err) == define.ErrVolumeBeingUsed { utils.Error(w, "volumes being used", http.StatusConflict, err) } else { diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index 1c6cc917c..f2f93434a 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -11,6 +11,7 @@ import ( "strings" "github.com/containers/buildah" + "github.com/containers/common/libimage" "github.com/containers/common/pkg/filters" "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/types" @@ -93,7 +94,8 @@ func GetImage(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusNotFound, errors.Wrapf(err, "failed to find image %s", name)) return } - inspect, err := newImage.Inspect(r.Context(), true) + options := &libimage.InspectOptions{WithParent: true, WithSize: true} + inspect, err := newImage.Inspect(r.Context(), options) if err != nil { utils.Error(w, "Server error", http.StatusInternalServerError, errors.Wrapf(err, "failed in inspect image %s", inspect.ID)) return diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go index 7bd6d3dbf..77d026550 100644 --- a/pkg/api/handlers/libpod/pods.go +++ b/pkg/api/handlers/libpod/pods.go @@ -246,7 +246,8 @@ func PodDelete(w http.ResponseWriter, r *http.Request) { decoder = r.Context().Value(api.DecoderKey).(*schema.Decoder) ) query := struct { - Force bool `schema:"force"` + Force bool `schema:"force"` + Timeout *uint `schema:"timeout"` }{ // override any golang type defaults } @@ -262,7 +263,7 @@ func PodDelete(w http.ResponseWriter, r *http.Request) { utils.PodNotFound(w, name, err) return } - if err := runtime.RemovePod(r.Context(), pod, true, query.Force); err != nil { + if err := runtime.RemovePod(r.Context(), pod, true, query.Force, query.Timeout); err != nil { utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) return } diff --git a/pkg/api/handlers/libpod/volumes.go b/pkg/api/handlers/libpod/volumes.go index 318758868..3ba39b860 100644 --- a/pkg/api/handlers/libpod/volumes.go +++ b/pkg/api/handlers/libpod/volumes.go @@ -169,7 +169,8 @@ func RemoveVolume(w http.ResponseWriter, r *http.Request) { decoder = r.Context().Value(api.DecoderKey).(*schema.Decoder) ) query := struct { - Force bool `schema:"force"` + Force bool `schema:"force"` + Timeout *uint `schema:"timeout"` }{ // override any golang type defaults } @@ -185,7 +186,7 @@ func RemoveVolume(w http.ResponseWriter, r *http.Request) { utils.VolumeNotFound(w, name, err) return } - if err := runtime.RemoveVolume(r.Context(), vol, query.Force); err != nil { + if err := runtime.RemoveVolume(r.Context(), vol, query.Force, query.Timeout); err != nil { if errors.Cause(err) == define.ErrVolumeBeingUsed { utils.Error(w, "volumes being used", http.StatusConflict, err) return diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go index fedab3bb3..b90154e30 100644 --- a/pkg/api/handlers/types.go +++ b/pkg/api/handlers/types.go @@ -166,7 +166,8 @@ type ExecStartConfig struct { } func ImageToImageSummary(l *libimage.Image) (*entities.ImageSummary, error) { - imageData, err := l.Inspect(context.TODO(), true) + options := &libimage.InspectOptions{WithParent: true, WithSize: true} + imageData, err := l.Inspect(context.TODO(), options) if err != nil { return nil, errors.Wrapf(err, "failed to obtain summary for image %s", l.ID()) } @@ -205,7 +206,8 @@ func ImageToImageSummary(l *libimage.Image) (*entities.ImageSummary, error) { } func ImageDataToImageInspect(ctx context.Context, l *libimage.Image) (*ImageInspect, error) { - info, err := l.Inspect(context.Background(), true) + options := &libimage.InspectOptions{WithParent: true, WithSize: true} + info, err := l.Inspect(context.Background(), options) if err != nil { return nil, err } diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index aa573eaa6..95a8b4939 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -1090,10 +1090,6 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // default: 25 // description: maximum number of results // - in: query - // name: noTrunc - // type: boolean - // description: do not truncate any of the result strings - // - in: query // name: filters // type: string // description: | diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go index c7174775e..8c5c7aeeb 100644 --- a/pkg/api/server/server.go +++ b/pkg/api/server/server.go @@ -6,6 +6,7 @@ import ( "log" "net" "net/http" + "net/http/pprof" "os" "runtime" "strings" @@ -18,6 +19,7 @@ import ( "github.com/containers/podman/v3/pkg/api/handlers" "github.com/containers/podman/v3/pkg/api/server/idle" "github.com/containers/podman/v3/pkg/api/types" + "github.com/containers/podman/v3/pkg/domain/entities" "github.com/coreos/go-systemd/v22/activation" "github.com/coreos/go-systemd/v22/daemon" "github.com/gorilla/mux" @@ -27,14 +29,14 @@ import ( type APIServer struct { http.Server // The HTTP work happens here - *schema.Decoder // Decoder for Query parameters to structs - context.Context // Context to carry objects to handlers - *libpod.Runtime // Where the real work happens net.Listener // mux for routing HTTP API calls to libpod routines + *libpod.Runtime // Where the real work happens + *schema.Decoder // Decoder for Query parameters to structs context.CancelFunc // Stop APIServer + context.Context // Context to carry objects to handlers + CorsHeaders string // Inject Cross-Origin Resource Sharing (CORS) headers + PProfAddr string // Binding network address for pprof profiles idleTracker *idle.Tracker // Track connections to support idle shutdown - pprof *http.Server // Sidecar http server for providing performance data - CorsHeaders string // Inject CORS headers to each request } // Number of seconds to wait for next request, if exceeded shutdown server @@ -49,22 +51,20 @@ var ( shutdownOnce sync.Once ) -type Options struct { - Timeout time.Duration - CorsHeaders string -} - // NewServer will create and configure a new API server with all defaults func NewServer(runtime *libpod.Runtime) (*APIServer, error) { - return newServer(runtime, DefaultServiceDuration, nil, DefaultCorsHeaders) + return newServer(runtime, nil, entities.ServiceOptions{ + CorsHeaders: DefaultCorsHeaders, + Timeout: DefaultServiceDuration, + }) } // NewServerWithSettings will create and configure a new API server using provided settings -func NewServerWithSettings(runtime *libpod.Runtime, listener *net.Listener, opts Options) (*APIServer, error) { - return newServer(runtime, opts.Timeout, listener, opts.CorsHeaders) +func NewServerWithSettings(runtime *libpod.Runtime, listener *net.Listener, opts entities.ServiceOptions) (*APIServer, error) { + return newServer(runtime, listener, opts) } -func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Listener, corsHeaders string) (*APIServer, error) { +func newServer(runtime *libpod.Runtime, listener *net.Listener, opts entities.ServiceOptions) (*APIServer, error) { // If listener not provided try socket activation protocol if listener == nil { if _, found := os.LookupEnv("LISTEN_PID"); !found { @@ -80,15 +80,15 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li } listener = &listeners[0] } - if corsHeaders == "" { + if opts.CorsHeaders == "" { logrus.Debug("CORS Headers were not set") } else { - logrus.Debugf("CORS Headers were set to %s", corsHeaders) + logrus.Debugf("CORS Headers were set to %q", opts.CorsHeaders) } logrus.Infof("API service listening on %q", (*listener).Addr()) router := mux.NewRouter().UseEncodedPath() - tracker := idle.NewTracker(duration) + tracker := idle.NewTracker(opts.Timeout) server := APIServer{ Server: http.Server{ @@ -98,10 +98,11 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li ConnState: tracker.ConnState, ErrorLog: log.New(logrus.StandardLogger().Out, "", 0), Handler: router, - IdleTimeout: duration * 2, + IdleTimeout: opts.Timeout * 2, }, - CorsHeaders: corsHeaders, + CorsHeaders: opts.CorsHeaders, Listener: *listener, + PProfAddr: opts.PProfAddr, idleTracker: tracker, } @@ -181,18 +182,18 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li return &server, nil } -// If the NOTIFY_SOCKET is set, communicate the PID and readiness, and -// further unset NOTIFY_SOCKET to prevent containers from sending -// messages and unset INVOCATION_ID so conmon and containers are in -// the correct cgroup. -func setupSystemd() { - if len(os.Getenv("NOTIFY_SOCKET")) == 0 { +// setupSystemd notifies systemd API service is ready +// If the NOTIFY_SOCKET is set, communicate the PID and readiness, and unset INVOCATION_ID +// so conmon and containers are in the correct cgroup. +func (s *APIServer) setupSystemd() { + if _, found := os.LookupEnv("NOTIFY_SOCKET"); !found { return } + payload := fmt.Sprintf("MAINPID=%d\n", os.Getpid()) payload += daemon.SdNotifyReady if sent, err := daemon.SdNotify(true, payload); err != nil { - logrus.Error("API service error notifying systemd of Conmon PID: " + err.Error()) + logrus.Error("API service failed to notify systemd of Conmon PID: " + err.Error()) } else if !sent { logrus.Warn("API service unable to successfully send SDNotify") } @@ -204,10 +205,10 @@ func setupSystemd() { // Serve starts responding to HTTP requests. func (s *APIServer) Serve() error { - setupSystemd() + s.setupPprof() - if err := shutdown.Register("server", func(sig os.Signal) error { - return s.Shutdown() + if err := shutdown.Register("service", func(sig os.Signal) error { + return s.Shutdown(true) }); err != nil { return err } @@ -216,32 +217,17 @@ func (s *APIServer) Serve() error { return err } - errChan := make(chan error, 1) - go func() { <-s.idleTracker.Done() - logrus.Debug("API service shutting down, idle for " + s.idleTracker.Duration.Round(time.Second).String()) - _ = s.Shutdown() + logrus.Debugf("API service(s) shutting down, idle for %ds", int(s.idleTracker.Duration.Seconds())) + _ = s.Shutdown(false) }() - if logrus.IsLevelEnabled(logrus.DebugLevel) { - go func() { - pprofMux := mux.NewRouter() - pprofMux.PathPrefix("/debug/pprof").Handler(http.DefaultServeMux) - runtime.SetMutexProfileFraction(1) - runtime.SetBlockProfileRate(1) - s.pprof = &http.Server{Addr: "localhost:8888", Handler: pprofMux} - err := s.pprof.ListenAndServe() - if err != nil && err != http.ErrServerClosed { - logrus.Warnf("API profiler service failed: %v", err) - } - }() - } - - // Before we start serving, ensure umask is properly set for container - // creation. + // Before we start serving, ensure umask is properly set for container creation. _ = syscall.Umask(0022) + errChan := make(chan error, 1) + s.setupSystemd() go func() { err := s.Server.Serve(s.Listener) if err != nil && err != http.ErrServerClosed { @@ -254,33 +240,56 @@ func (s *APIServer) Serve() error { return <-errChan } +// setupPprof enables pprof default endpoints +// Note: These endpoints and the podman flag --cpu-profile are mutually exclusive +// +// Examples: +// #1 go tool pprof -http localhost:8889 localhost:8888/debug/pprof/heap?seconds=120 +// Note: web page will only render after a sample has been recorded +// #2 curl http://localhost:8888/debug/pprof/heap > heap.pprof && go tool pprof heap.pprof +func (s *APIServer) setupPprof() { + if s.PProfAddr == "" { + return + } + + logrus.Infof("pprof service listening on %q", s.PProfAddr) + go func() { + old := runtime.SetMutexProfileFraction(1) + defer runtime.SetMutexProfileFraction(old) + + runtime.SetBlockProfileRate(1) + defer runtime.SetBlockProfileRate(0) + + router := mux.NewRouter() + router.PathPrefix("/debug/pprof/").HandlerFunc(pprof.Index) + + err := http.ListenAndServe(s.PProfAddr, router) + if err != nil && err != http.ErrServerClosed { + logrus.Warnf("pprof service failed: %v", err) + } + }() +} + // Shutdown is a clean shutdown waiting on existing clients -func (s *APIServer) Shutdown() error { - if s.idleTracker.Duration == UnlimitedServiceDuration { - logrus.Debug("API service shutdown ignored as Duration is UnlimitedService") +func (s *APIServer) Shutdown(halt bool) error { + switch { + case halt: + logrus.Debug("API service forced shutdown, ignoring timeout Duration") + case s.idleTracker.Duration == UnlimitedServiceDuration: + logrus.Debug("API service shutdown request ignored as timeout Duration is UnlimitedService") return nil } shutdownOnce.Do(func() { - if logrus.IsLevelEnabled(logrus.DebugLevel) { - _, file, line, _ := runtime.Caller(1) - logrus.Debugf("API service shutdown by %s:%d, %d/%d connection(s)", - file, line, s.idleTracker.ActiveConnections(), s.idleTracker.TotalConnections()) - - go func() { - ctx, cancel := context.WithTimeout(context.Background(), s.idleTracker.Duration) - go func() { - defer cancel() - if err := s.pprof.Shutdown(ctx); err != nil { - logrus.Warnf("Failed to cleanly shutdown API pprof service: %v", err) - } - }() - <-ctx.Done() - }() - } + logrus.Debugf("API service shutdown, %d/%d connection(s)", + s.idleTracker.ActiveConnections(), s.idleTracker.TotalConnections()) // Gracefully shutdown server(s), duration of wait same as idle window - ctx, cancel := context.WithTimeout(context.Background(), s.idleTracker.Duration) + deadline := 1 * time.Second + if s.idleTracker.Duration > 0 { + deadline = s.idleTracker.Duration + } + ctx, cancel := context.WithTimeout(context.Background(), deadline) go func() { defer cancel() @@ -291,7 +300,6 @@ func (s *APIServer) Shutdown() error { }() <-ctx.Done() }) - return nil } diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go index e2c46e481..dc75dac5a 100644 --- a/pkg/bindings/connection.go +++ b/pkg/bindings/connection.go @@ -349,6 +349,17 @@ func (c *Connection) DoRequest(httpBody io.Reader, httpMethod, endpoint string, return &APIResponse{response, req}, err } +// Get raw Transport.DialContext from client +func (c *Connection) GetDialer(ctx context.Context) (net.Conn, error) { + client := c.Client + transport := client.Transport.(*http.Transport) + if transport.DialContext != nil && transport.TLSClientConfig == nil { + return transport.DialContext(ctx, c.URI.Scheme, c.URI.String()) + } + + return nil, errors.New("Unable to get dial context") +} + // FiltersToString converts our typical filter format of a // map[string][]string to a query/html safe string. func FiltersToString(filters map[string][]string) (string, error) { diff --git a/pkg/bindings/containers/attach.go b/pkg/bindings/containers/attach.go index abf58aaf9..c5f54c1af 100644 --- a/pkg/bindings/containers/attach.go +++ b/pkg/bindings/containers/attach.go @@ -157,24 +157,24 @@ func Attach(ctx context.Context, nameOrID string, stdin io.Reader, stdout io.Wri } stdoutChan := make(chan error) - stdinChan := make(chan error) + stdinChan := make(chan error, 1) //stdin channel should not block if isSet.stdin { go func() { logrus.Debugf("Copying STDIN to socket") _, err := utils.CopyDetachable(socket, stdin, detachKeysInBytes) - if err != nil && err != define.ErrDetach { logrus.Errorf("Failed to write input to service: %v", err) } - stdinChan <- err - - if closeWrite, ok := socket.(CloseWriter); ok { - if err := closeWrite.CloseWrite(); err != nil { - logrus.Warnf("Failed to close STDIN for writing: %v", err) + if err == nil { + if closeWrite, ok := socket.(CloseWriter); ok { + if err := closeWrite.CloseWrite(); err != nil { + logrus.Warnf("Failed to close STDIN for writing: %v", err) + } } } + stdinChan <- err }() } diff --git a/pkg/bindings/containers/types.go b/pkg/bindings/containers/types.go index 3fe3442bb..3a7d5a4c7 100644 --- a/pkg/bindings/containers/types.go +++ b/pkg/bindings/containers/types.go @@ -132,6 +132,7 @@ type RemoveOptions struct { Ignore *bool Force *bool Volumes *bool + Timeout *uint } //go:generate go run ../generator/generator.go InspectOptions diff --git a/pkg/bindings/containers/types_remove_options.go b/pkg/bindings/containers/types_remove_options.go index 8eaa13c2a..1e52e819d 100644 --- a/pkg/bindings/containers/types_remove_options.go +++ b/pkg/bindings/containers/types_remove_options.go @@ -61,3 +61,18 @@ func (o *RemoveOptions) GetVolumes() bool { } return *o.Volumes } + +// WithTimeout set field Timeout to given value +func (o *RemoveOptions) WithTimeout(value uint) *RemoveOptions { + o.Timeout = &value + return o +} + +// GetTimeout returns value of field Timeout +func (o *RemoveOptions) GetTimeout() uint { + if o.Timeout == nil { + var z uint + return z + } + return *o.Timeout +} diff --git a/pkg/bindings/images/types.go b/pkg/bindings/images/types.go index dc6bd91c3..a44a3527f 100644 --- a/pkg/bindings/images/types.go +++ b/pkg/bindings/images/types.go @@ -133,8 +133,6 @@ type SearchOptions struct { Filters map[string][]string // Limit the number of results. Limit *int - // NoTrunc will not truncate the output. - NoTrunc *bool // SkipTLSVerify to skip HTTPS and certificate verification. SkipTLSVerify *bool // ListTags search the available tags of the repository diff --git a/pkg/bindings/images/types_search_options.go b/pkg/bindings/images/types_search_options.go index e38ef9fb1..4424f1504 100644 --- a/pkg/bindings/images/types_search_options.go +++ b/pkg/bindings/images/types_search_options.go @@ -62,21 +62,6 @@ func (o *SearchOptions) GetLimit() int { return *o.Limit } -// WithNoTrunc set field NoTrunc to given value -func (o *SearchOptions) WithNoTrunc(value bool) *SearchOptions { - o.NoTrunc = &value - return o -} - -// GetNoTrunc returns value of field NoTrunc -func (o *SearchOptions) GetNoTrunc() bool { - if o.NoTrunc == nil { - var z bool - return z - } - return *o.NoTrunc -} - // WithSkipTLSVerify set field SkipTLSVerify to given value func (o *SearchOptions) WithSkipTLSVerify(value bool) *SearchOptions { o.SkipTLSVerify = &value diff --git a/pkg/bindings/network/types.go b/pkg/bindings/network/types.go index e62ae8f52..8088de061 100644 --- a/pkg/bindings/network/types.go +++ b/pkg/bindings/network/types.go @@ -40,7 +40,8 @@ type InspectOptions struct { // RemoveOptions are optional options for inspecting networks type RemoveOptions struct { // Force removes the network even if it is being used - Force *bool + Force *bool + Timeout *uint } //go:generate go run ../generator/generator.go ListOptions diff --git a/pkg/bindings/network/types_remove_options.go b/pkg/bindings/network/types_remove_options.go index 57fc4fa3a..2f7fea77e 100644 --- a/pkg/bindings/network/types_remove_options.go +++ b/pkg/bindings/network/types_remove_options.go @@ -31,3 +31,18 @@ func (o *RemoveOptions) GetForce() bool { } return *o.Force } + +// WithTimeout set field Timeout to given value +func (o *RemoveOptions) WithTimeout(value uint) *RemoveOptions { + o.Timeout = &value + return o +} + +// GetTimeout returns value of field Timeout +func (o *RemoveOptions) GetTimeout() uint { + if o.Timeout == nil { + var z uint + return z + } + return *o.Timeout +} diff --git a/pkg/bindings/pods/types.go b/pkg/bindings/pods/types.go index cb41cf623..71fada4eb 100644 --- a/pkg/bindings/pods/types.go +++ b/pkg/bindings/pods/types.go @@ -68,7 +68,8 @@ type StatsOptions struct { //go:generate go run ../generator/generator.go RemoveOptions // RemoveOptions are optional options for removing pods type RemoveOptions struct { - Force *bool + Force *bool + Timeout *uint } //go:generate go run ../generator/generator.go ExistsOptions diff --git a/pkg/bindings/pods/types_remove_options.go b/pkg/bindings/pods/types_remove_options.go index ce142ee74..bbcc4d769 100644 --- a/pkg/bindings/pods/types_remove_options.go +++ b/pkg/bindings/pods/types_remove_options.go @@ -31,3 +31,18 @@ func (o *RemoveOptions) GetForce() bool { } return *o.Force } + +// WithTimeout set field Timeout to given value +func (o *RemoveOptions) WithTimeout(value uint) *RemoveOptions { + o.Timeout = &value + return o +} + +// GetTimeout returns value of field Timeout +func (o *RemoveOptions) GetTimeout() uint { + if o.Timeout == nil { + var z uint + return z + } + return *o.Timeout +} diff --git a/pkg/bindings/volumes/types.go b/pkg/bindings/volumes/types.go index 3fda77ddd..d2f19c0c8 100644 --- a/pkg/bindings/volumes/types.go +++ b/pkg/bindings/volumes/types.go @@ -28,7 +28,8 @@ type PruneOptions struct { // RemoveOptions are optional options for removing volumes type RemoveOptions struct { // Force removes the volume even if it is being used - Force *bool + Force *bool + Timeout *uint } //go:generate go run ../generator/generator.go ExistsOptions diff --git a/pkg/bindings/volumes/types_remove_options.go b/pkg/bindings/volumes/types_remove_options.go index 0e0a3c804..fe079cddf 100644 --- a/pkg/bindings/volumes/types_remove_options.go +++ b/pkg/bindings/volumes/types_remove_options.go @@ -31,3 +31,18 @@ func (o *RemoveOptions) GetForce() bool { } return *o.Force } + +// WithTimeout set field Timeout to given value +func (o *RemoveOptions) WithTimeout(value uint) *RemoveOptions { + o.Timeout = &value + return o +} + +// GetTimeout returns value of field Timeout +func (o *RemoveOptions) GetTimeout() uint { + if o.Timeout == nil { + var z uint + return z + } + return *o.Timeout +} diff --git a/pkg/cgroups/cgroups.go b/pkg/cgroups/cgroups.go index 4bb8de69b..f1ef538e4 100644 --- a/pkg/cgroups/cgroups.go +++ b/pkg/cgroups/cgroups.go @@ -129,8 +129,8 @@ func init() { func getAvailableControllers(exclude map[string]controllerHandler, cgroup2 bool) ([]controller, error) { if cgroup2 { controllers := []controller{} - subtreeControl := cgroupRoot + "/cgroup.subtree_control" - // rootless cgroupv2: check available controllers for current user ,systemd or servicescope will inherit + controllersFile := cgroupRoot + "/cgroup.controllers" + // rootless cgroupv2: check available controllers for current user, systemd or servicescope will inherit if rootless.IsRootless() { userSlice, err := getCgroupPathForCurrentProcess() if err != nil { @@ -138,13 +138,13 @@ func getAvailableControllers(exclude map[string]controllerHandler, cgroup2 bool) } //userSlice already contains '/' so not adding here basePath := cgroupRoot + userSlice - subtreeControl = fmt.Sprintf("%s/cgroup.subtree_control", basePath) + controllersFile = fmt.Sprintf("%s/cgroup.controllers", basePath) } - subtreeControlBytes, err := ioutil.ReadFile(subtreeControl) + controllersFileBytes, err := ioutil.ReadFile(controllersFile) if err != nil { - return nil, errors.Wrapf(err, "failed while reading controllers for cgroup v2 from %q", subtreeControl) + return nil, errors.Wrapf(err, "failed while reading controllers for cgroup v2 from %q", controllersFile) } - for _, controllerName := range strings.Fields(string(subtreeControlBytes)) { + for _, controllerName := range strings.Fields(string(controllersFileBytes)) { c := controller{ name: controllerName, symlink: false, diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index a302cdb7d..deae85fe1 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -132,6 +132,7 @@ type RmOptions struct { Force bool Ignore bool Latest bool + Timeout *uint Volumes bool } diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index 2822b1ad7..ac5e6f410 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -218,8 +218,6 @@ type ImageSearchOptions struct { Filters []string // Limit the number of results. Limit int - // NoTrunc will not truncate the output. - NoTrunc bool // SkipTLSVerify to skip HTTPS and certificate verification. SkipTLSVerify types.OptionalBool // ListTags search the available tags of the repository diff --git a/pkg/domain/entities/network.go b/pkg/domain/entities/network.go index b61297d41..d7389a699 100644 --- a/pkg/domain/entities/network.go +++ b/pkg/domain/entities/network.go @@ -27,7 +27,8 @@ type NetworkReloadReport struct { // NetworkRmOptions describes options for removing networks type NetworkRmOptions struct { - Force bool + Force bool + Timeout *uint } //NetworkRmReport describes the results of network removal diff --git a/pkg/domain/entities/play.go b/pkg/domain/entities/play.go index af4b0fc35..715d8acaf 100644 --- a/pkg/domain/entities/play.go +++ b/pkg/domain/entities/play.go @@ -17,6 +17,8 @@ type PlayKubeOptions struct { // Down indicates whether to bring contents of a yaml file "down" // as in stop Down bool + // Replace indicates whether to delete and recreate a yaml file + Replace bool // Do not create /etc/hosts within the pod's containers, // instead use the version from the image NoHosts bool diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go index 653f64b42..309677396 100644 --- a/pkg/domain/entities/pods.go +++ b/pkg/domain/entities/pods.go @@ -95,10 +95,11 @@ type PodStartReport struct { } type PodRmOptions struct { - All bool - Force bool - Ignore bool - Latest bool + All bool + Force bool + Ignore bool + Latest bool + Timeout *uint } type PodRmReport struct { diff --git a/pkg/domain/entities/system.go b/pkg/domain/entities/system.go index cca4bf44e..fe041dec8 100644 --- a/pkg/domain/entities/system.go +++ b/pkg/domain/entities/system.go @@ -6,15 +6,14 @@ import ( "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities/reports" "github.com/containers/podman/v3/pkg/domain/entities/types" - "github.com/spf13/cobra" ) -// ServiceOptions provides the input for starting an API Service +// ServiceOptions provides the input for starting an API and sidecar pprof services type ServiceOptions struct { - URI string // Path to unix domain socket service should listen on - Timeout time.Duration // duration of inactivity the service should wait before shutting down - Command *cobra.Command // CLI command provided. Used in V1 code - CorsHeaders string // CORS headers + CorsHeaders string // Cross-Origin Resource Sharing (CORS) headers + PProfAddr string // Network address to bind pprof profiles service + Timeout time.Duration // Duration of inactivity the service should wait before shutting down + URI string // Path to unix domain socket service should listen on } // SystemPruneOptions provides options to prune system. diff --git a/pkg/domain/entities/volumes.go b/pkg/domain/entities/volumes.go index 62f5401cc..2ecfb4446 100644 --- a/pkg/domain/entities/volumes.go +++ b/pkg/domain/entities/volumes.go @@ -94,8 +94,9 @@ type VolumeConfigResponse struct { } type VolumeRmOptions struct { - All bool - Force bool + All bool + Force bool + Timeout *uint } type VolumeRmReport struct { diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 8e7e2d411..c30129001 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -283,7 +283,7 @@ func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []st } func (ic *ContainerEngine) removeContainer(ctx context.Context, ctr *libpod.Container, options entities.RmOptions) error { - err := ic.Libpod.RemoveContainer(ctx, ctr, options.Force, options.Volumes) + err := ic.Libpod.RemoveContainer(ctx, ctr, options.Force, options.Volumes, options.Timeout) if err == nil { return nil } @@ -963,7 +963,8 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta return &report, nil } if opts.Rm { - if deleteError := ic.Libpod.RemoveContainer(ctx, ctr, true, false); deleteError != nil { + var timeout *uint + if deleteError := ic.Libpod.RemoveContainer(ctx, ctr, true, false, timeout); deleteError != nil { logrus.Debugf("unable to remove container %s after failing to start and attach to it", ctr.ID()) } } @@ -977,7 +978,8 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta } report.ExitCode = ic.GetContainerExitCode(ctx, ctr) if opts.Rm && !ctr.ShouldRestart(ctx) { - if err := ic.Libpod.RemoveContainer(ctx, ctr, false, true); err != nil { + var timeout *uint + if err := ic.Libpod.RemoveContainer(ctx, ctr, false, true, timeout); err != nil { if errors.Cause(err) == define.ErrNoSuchCtr || errors.Cause(err) == define.ErrCtrRemoved { logrus.Infof("Container %s was already removed, skipping --rm", ctr.ID()) @@ -1082,7 +1084,8 @@ func (ic *ContainerEngine) ContainerCleanup(ctx context.Context, namesOrIds []st } if options.Remove && !ctr.ShouldRestart(ctx) { - err = ic.Libpod.RemoveContainer(ctx, ctr, false, true) + var timeout *uint + err = ic.Libpod.RemoveContainer(ctx, ctr, false, true, timeout) if err != nil { report.RmErr = errors.Wrapf(err, "failed to cleanup and remove container %v", ctr.ID()) } @@ -1316,6 +1319,15 @@ func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []stri if options.Interval < 1 { return nil, errors.New("Invalid interval, must be a positive number greater zero") } + if rootless.IsRootless() { + unified, err := cgroups.IsCgroup2UnifiedMode() + if err != nil { + return nil, err + } + if !unified { + return nil, errors.New("stats is not supported in rootless mode without cgroups v2") + } + } statsChan = make(chan entities.ContainerStatsReport, 1) containerFunc := ic.Libpod.GetRunningContainers diff --git a/pkg/domain/infra/abi/containers_runlabel.go b/pkg/domain/infra/abi/containers_runlabel.go index add82f0fb..b0aae4b76 100644 --- a/pkg/domain/infra/abi/containers_runlabel.go +++ b/pkg/domain/infra/abi/containers_runlabel.go @@ -92,7 +92,8 @@ func (ic *ContainerEngine) ContainerRunlabel(ctx context.Context, label string, } } else { logrus.Debugf("Runlabel --replace option given. Container %s will be deleted. The new container will be named %s", ctr.ID(), name) - if err := ic.Libpod.RemoveContainer(ctx, ctr, true, false); err != nil { + var timeout *uint + if err := ic.Libpod.RemoveContainer(ctx, ctr, true, false, timeout); err != nil { return err } } diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index c06059205..d2222c017 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -252,6 +252,8 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entiti func (ir *ImageEngine) Inspect(ctx context.Context, namesOrIDs []string, opts entities.InspectOptions) ([]*entities.ImageInspectReport, []error, error) { reports := []*entities.ImageInspectReport{} errs := []error{} + + inspectOptions := &libimage.InspectOptions{WithParent: true, WithSize: true} for _, i := range namesOrIDs { img, _, err := ir.Libpod.LibimageRuntime().LookupImage(i, nil) if err != nil { @@ -259,7 +261,7 @@ func (ir *ImageEngine) Inspect(ctx context.Context, namesOrIDs []string, opts en errs = append(errs, err) continue } - result, err := img.Inspect(ctx, true) + result, err := img.Inspect(ctx, inspectOptions) if err != nil { // This is more likely to be fatal. return nil, nil, err @@ -415,6 +417,7 @@ func (ir *ImageEngine) Import(ctx context.Context, options entities.ImageImportO return &entities.ImageImportReport{Id: imageID}, nil } +// Search for images using term and filters func (ir *ImageEngine) Search(ctx context.Context, term string, opts entities.ImageSearchOptions) ([]entities.ImageSearchReport, error) { filter, err := libimage.ParseSearchFilter(opts.Filters) if err != nil { @@ -425,7 +428,7 @@ func (ir *ImageEngine) Search(ctx context.Context, term string, opts entities.Im Authfile: opts.Authfile, Filter: *filter, Limit: opts.Limit, - NoTrunc: opts.NoTrunc, + NoTrunc: true, InsecureSkipTLSVerify: opts.SkipTLSVerify, ListTags: opts.ListTags, } @@ -452,7 +455,7 @@ func (ir *ImageEngine) Search(ctx context.Context, term string, opts entities.Im return reports, nil } -// GetConfig returns a copy of the configuration used by the runtime +// Config returns a copy of the configuration used by the runtime func (ir *ImageEngine) Config(_ context.Context) (*config.Config, error) { return ir.Libpod.GetConfig() } diff --git a/pkg/domain/infra/abi/network.go b/pkg/domain/infra/abi/network.go index d792226a8..ee7403ed5 100644 --- a/pkg/domain/infra/abi/network.go +++ b/pkg/domain/infra/abi/network.go @@ -91,10 +91,10 @@ func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, o if err != nil { return reports, err } - if err := ic.Libpod.RemovePod(ctx, pod, true, true); err != nil { + if err := ic.Libpod.RemovePod(ctx, pod, true, true, options.Timeout); err != nil { return reports, err } - } else if err := ic.Libpod.RemoveContainer(ctx, c, true, true); err != nil && errors.Cause(err) != define.ErrNoSuchCtr { + } else if err := ic.Libpod.RemoveContainer(ctx, c, true, true, options.Timeout); err != nil && errors.Cause(err) != define.ErrNoSuchCtr { return reports, err } } diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go index 6b432c214..028de9e81 100644 --- a/pkg/domain/infra/abi/pods.go +++ b/pkg/domain/infra/abi/pods.go @@ -259,7 +259,7 @@ func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, optio reports := make([]*entities.PodRmReport, 0, len(pods)) for _, p := range pods { report := entities.PodRmReport{Id: p.ID()} - err := ic.Libpod.RemovePod(ctx, p, true, options.Force) + err := ic.Libpod.RemovePod(ctx, p, true, options.Force, options.Timeout) if err != nil { report.Err = err } diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go index 1610c0b48..e514631db 100644 --- a/pkg/domain/infra/abi/volumes.go +++ b/pkg/domain/infra/abi/volumes.go @@ -66,7 +66,7 @@ func (ic *ContainerEngine) VolumeRm(ctx context.Context, namesOrIds []string, op } for _, vol := range vols { reports = append(reports, &entities.VolumeRmReport{ - Err: ic.Libpod.RemoveVolume(ctx, vol, opts.Force), + Err: ic.Libpod.RemoveVolume(ctx, vol, opts.Force, opts.Timeout), Id: vol.Name(), }) } diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 9fe2d163c..3f78ba7bc 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -185,7 +185,9 @@ func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []st func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, opts entities.RmOptions) ([]*entities.RmReport, error) { // TODO there is no endpoint for container eviction. Need to discuss options := new(containers.RemoveOptions).WithForce(opts.Force).WithVolumes(opts.Volumes).WithIgnore(opts.Ignore) - + if opts.Timeout != nil { + options = options.WithTimeout(*opts.Timeout) + } if opts.All { ctrs, err := getContainersByContext(ic.ClientCtx, opts.All, opts.Ignore, namesOrIds) if err != nil { diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index d41a20348..b8af2de68 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -12,7 +12,7 @@ import ( "github.com/containers/common/pkg/config" "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/types" - images "github.com/containers/podman/v3/pkg/bindings/images" + "github.com/containers/podman/v3/pkg/bindings/images" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/entities/reports" "github.com/containers/podman/v3/pkg/domain/utils" @@ -323,7 +323,7 @@ func (ir *ImageEngine) Search(ctx context.Context, term string, opts entities.Im options := new(images.SearchOptions) options.WithAuthfile(opts.Authfile).WithFilters(mappedFilters).WithLimit(opts.Limit) - options.WithListTags(opts.ListTags).WithNoTrunc(opts.NoTrunc) + options.WithListTags(opts.ListTags) if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined { if s == types.OptionalBoolTrue { options.WithSkipTLSVerify(true) diff --git a/pkg/domain/infra/tunnel/network.go b/pkg/domain/infra/tunnel/network.go index 6f227f565..79fba1943 100644 --- a/pkg/domain/infra/tunnel/network.go +++ b/pkg/domain/infra/tunnel/network.go @@ -47,6 +47,9 @@ func (ic *ContainerEngine) NetworkReload(ctx context.Context, names []string, op func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, opts entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) { reports := make([]*entities.NetworkRmReport, 0, len(namesOrIds)) options := new(network.RemoveOptions).WithForce(opts.Force) + if opts.Timeout != nil { + options = options.WithTimeout(*opts.Timeout) + } for _, name := range namesOrIds { response, err := network.Remove(ic.ClientCtx, name, options) if err != nil { diff --git a/pkg/domain/infra/tunnel/pods.go b/pkg/domain/infra/tunnel/pods.go index 8139216b3..bd3df1aa7 100644 --- a/pkg/domain/infra/tunnel/pods.go +++ b/pkg/domain/infra/tunnel/pods.go @@ -169,6 +169,9 @@ func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, opts } reports := make([]*entities.PodRmReport, 0, len(foundPods)) options := new(pods.RemoveOptions).WithForce(opts.Force) + if opts.Timeout != nil { + options = options.WithTimeout(*opts.Timeout) + } for _, p := range foundPods { response, err := pods.Remove(ic.ClientCtx, p.Id, options) if err != nil { diff --git a/pkg/domain/infra/tunnel/volumes.go b/pkg/domain/infra/tunnel/volumes.go index 2b2b2c2a1..cfd1574c3 100644 --- a/pkg/domain/infra/tunnel/volumes.go +++ b/pkg/domain/infra/tunnel/volumes.go @@ -31,6 +31,9 @@ func (ic *ContainerEngine) VolumeRm(ctx context.Context, namesOrIds []string, op reports := make([]*entities.VolumeRmReport, 0, len(namesOrIds)) for _, id := range namesOrIds { options := new(volumes.RemoveOptions).WithForce(opts.Force) + if opts.Timeout != nil { + options = options.WithTimeout(*opts.Timeout) + } reports = append(reports, &entities.VolumeRmReport{ Err: volumes.Remove(ic.ClientCtx, id, options), Id: id, diff --git a/pkg/hooks/docs/oci-hooks.5.md b/pkg/hooks/docs/oci-hooks.5.md index d6b866231..9a1a35682 100644 --- a/pkg/hooks/docs/oci-hooks.5.md +++ b/pkg/hooks/docs/oci-hooks.5.md @@ -1,4 +1,4 @@ -% oci-hooks(5) OCI Hooks Configuration +% oci-hooks 5 OCI Hooks Configuration % W. Trevor King % MAY 2018 diff --git a/pkg/machine/pull.go b/pkg/machine/pull.go index f79ac6ec4..3c8422a30 100644 --- a/pkg/machine/pull.go +++ b/pkg/machine/pull.go @@ -15,7 +15,7 @@ import ( "time" "github.com/containers/image/v5/pkg/compression" - "github.com/docker/docker/pkg/archive" + "github.com/containers/storage/pkg/archive" "github.com/sirupsen/logrus" "github.com/vbauerster/mpb/v6" "github.com/vbauerster/mpb/v6/decor" diff --git a/pkg/ps/ps.go b/pkg/ps/ps.go index 69ac9c215..90ad23f49 100644 --- a/pkg/ps/ps.go +++ b/pkg/ps/ps.go @@ -136,6 +136,12 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities ) batchErr := ctr.Batch(func(c *libpod.Container) error { + if opts.Sync { + if err := c.Sync(); err != nil { + return errors.Wrapf(err, "unable to update container state from OCI runtime") + } + } + conConfig = c.Config() conState, err = c.State() if err != nil { diff --git a/pkg/rootlessport/rootlessport_linux.go b/pkg/rootlessport/rootlessport_linux.go index 37fb7ce79..7b9e5bbfa 100644 --- a/pkg/rootlessport/rootlessport_linux.go +++ b/pkg/rootlessport/rootlessport_linux.go @@ -12,33 +12,12 @@ package rootlessport import ( - "context" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "net" - "os" - "os/exec" - "path/filepath" - - "github.com/containernetworking/plugins/pkg/ns" "github.com/containers/podman/v3/libpod/network/types" - "github.com/containers/storage/pkg/reexec" - "github.com/pkg/errors" - rkport "github.com/rootless-containers/rootlesskit/pkg/port" - rkbuiltin "github.com/rootless-containers/rootlesskit/pkg/port/builtin" - rkportutil "github.com/rootless-containers/rootlesskit/pkg/port/portutil" - "github.com/sirupsen/logrus" - "golang.org/x/sys/unix" ) const ( - // ReexecKey is the reexec key for the parent process. - ReexecKey = "containers-rootlessport" - // reexecChildKey is used internally for the second reexec - reexecChildKey = "containers-rootlessport-child" - reexecChildEnvOpaque = "_CONTAINERS_ROOTLESSPORT_CHILD_OPAQUE" + // BinaryName is the binary name for the parent process. + BinaryName = "rootlessport" ) // Config needs to be provided to the process via stdin as a JSON string. @@ -53,325 +32,3 @@ type Config struct { ContainerID string RootlessCNI bool } - -func init() { - reexec.Register(ReexecKey, func() { - if err := parent(); err != nil { - fmt.Println(err) - os.Exit(1) - } - }) - reexec.Register(reexecChildKey, func() { - if err := child(); err != nil { - fmt.Println(err) - os.Exit(1) - } - }) -} - -func loadConfig(r io.Reader) (*Config, io.ReadCloser, io.WriteCloser, error) { - stdin, err := ioutil.ReadAll(r) - if err != nil { - return nil, nil, nil, err - } - var cfg Config - if err := json.Unmarshal(stdin, &cfg); err != nil { - return nil, nil, nil, err - } - if cfg.NetNSPath == "" { - return nil, nil, nil, errors.New("missing NetNSPath") - } - if cfg.ExitFD <= 0 { - return nil, nil, nil, errors.New("missing ExitFD") - } - exitFile := os.NewFile(uintptr(cfg.ExitFD), "exitfile") - if exitFile == nil { - return nil, nil, nil, errors.New("invalid ExitFD") - } - if cfg.ReadyFD <= 0 { - return nil, nil, nil, errors.New("missing ReadyFD") - } - readyFile := os.NewFile(uintptr(cfg.ReadyFD), "readyfile") - if readyFile == nil { - return nil, nil, nil, errors.New("invalid ReadyFD") - } - return &cfg, exitFile, readyFile, nil -} - -func parent() error { - // load config from stdin - cfg, exitR, readyW, err := loadConfig(os.Stdin) - if err != nil { - return err - } - - socketDir := filepath.Join(cfg.TmpDir, "rp") - err = os.MkdirAll(socketDir, 0700) - if err != nil { - return err - } - - // create the parent driver - stateDir, err := ioutil.TempDir(cfg.TmpDir, "rootlessport") - if err != nil { - return err - } - defer os.RemoveAll(stateDir) - driver, err := rkbuiltin.NewParentDriver(&logrusWriter{prefix: "parent: "}, stateDir) - if err != nil { - return err - } - initComplete := make(chan struct{}) - quit := make(chan struct{}) - errCh := make(chan error) - // start the parent driver. initComplete will be closed when the child connected to the parent. - logrus.Infof("Starting parent driver") - go func() { - driverErr := driver.RunParentDriver(initComplete, quit, nil) - if driverErr != nil { - logrus.WithError(driverErr).Warn("Parent driver exited") - } - errCh <- driverErr - close(errCh) - }() - opaque := driver.OpaqueForChild() - logrus.Infof("Opaque=%+v", opaque) - opaqueJSON, err := json.Marshal(opaque) - if err != nil { - return err - } - childQuitR, childQuitW, err := os.Pipe() - if err != nil { - return err - } - defer func() { - // stop the child - logrus.Info("Stopping child driver") - if err := childQuitW.Close(); err != nil { - logrus.WithError(err).Warn("Unable to close childQuitW") - } - }() - - // reexec the child process in the child netns - cmd := exec.Command("/proc/self/exe") - cmd.Args = []string{reexecChildKey} - cmd.Stdin = childQuitR - cmd.Stdout = &logrusWriter{prefix: "child"} - cmd.Stderr = cmd.Stdout - cmd.Env = append(os.Environ(), reexecChildEnvOpaque+"="+string(opaqueJSON)) - childNS, err := ns.GetNS(cfg.NetNSPath) - if err != nil { - return err - } - if err := childNS.Do(func(_ ns.NetNS) error { - logrus.Infof("Starting child driver in child netns (%q %v)", cmd.Path, cmd.Args) - return cmd.Start() - }); err != nil { - return err - } - - childErrCh := make(chan error) - go func() { - err := cmd.Wait() - childErrCh <- err - close(childErrCh) - }() - - defer func() { - if err := unix.Kill(cmd.Process.Pid, unix.SIGTERM); err != nil { - logrus.WithError(err).Warn("Kill child process") - } - }() - - logrus.Info("Waiting for initComplete") - // wait for the child to connect to the parent -outer: - for { - select { - case <-initComplete: - logrus.Infof("initComplete is closed; parent and child established the communication channel") - break outer - case err := <-childErrCh: - if err != nil { - return err - } - case err := <-errCh: - if err != nil { - return err - } - } - } - - defer func() { - logrus.Info("Stopping parent driver") - quit <- struct{}{} - if err := <-errCh; err != nil { - logrus.WithError(err).Warn("Parent driver returned error on exit") - } - }() - - // let parent expose ports - logrus.Infof("Exposing ports %v", cfg.Mappings) - if err := exposePorts(driver, cfg.Mappings, cfg.ChildIP); err != nil { - return err - } - - // we only need to have a socket to reload ports when we run under rootless cni - if cfg.RootlessCNI { - socketfile := filepath.Join(socketDir, cfg.ContainerID) - // make sure to remove the file if it exists to prevent EADDRINUSE - _ = os.Remove(socketfile) - // workaround to bypass the 108 char socket path limit - // open the fd and use the path to the fd as bind argument - fd, err := unix.Open(socketDir, unix.O_PATH, 0) - if err != nil { - return err - } - socket, err := net.ListenUnix("unixpacket", &net.UnixAddr{Name: fmt.Sprintf("/proc/self/fd/%d/%s", fd, cfg.ContainerID), Net: "unixpacket"}) - if err != nil { - return err - } - err = unix.Close(fd) - // remove the socket file on exit - defer os.Remove(socketfile) - if err != nil { - logrus.Warnf("Failed to close the socketDir fd: %v", err) - } - defer socket.Close() - go serve(socket, driver) - } - - logrus.Info("Ready") - - // https://github.com/containers/podman/issues/11248 - // Copy /dev/null to stdout and stderr to prevent SIGPIPE errors - if f, err := os.OpenFile("/dev/null", os.O_WRONLY, 0755); err == nil { - unix.Dup2(int(f.Fd()), 1) // nolint:errcheck - unix.Dup2(int(f.Fd()), 2) // nolint:errcheck - f.Close() - } - // write and close ReadyFD (convention is same as slirp4netns --ready-fd) - if _, err := readyW.Write([]byte("1")); err != nil { - return err - } - if err := readyW.Close(); err != nil { - return err - } - - // wait for ExitFD to be closed - logrus.Info("Waiting for exitfd to be closed") - if _, err := ioutil.ReadAll(exitR); err != nil { - return err - } - return nil -} - -func serve(listener net.Listener, pm rkport.Manager) { - for { - conn, err := listener.Accept() - if err != nil { - // we cannot log this error, stderr is already closed - continue - } - ctx := context.TODO() - err = handler(ctx, conn, pm) - if err != nil { - conn.Write([]byte(err.Error())) - } else { - conn.Write([]byte("OK")) - } - conn.Close() - } -} - -func handler(ctx context.Context, conn io.Reader, pm rkport.Manager) error { - var childIP string - dec := json.NewDecoder(conn) - err := dec.Decode(&childIP) - if err != nil { - return errors.Wrap(err, "rootless port failed to decode ports") - } - portStatus, err := pm.ListPorts(ctx) - if err != nil { - return errors.Wrap(err, "rootless port failed to list ports") - } - for _, status := range portStatus { - err = pm.RemovePort(ctx, status.ID) - if err != nil { - return errors.Wrap(err, "rootless port failed to remove port") - } - } - // add the ports with the new child IP - for _, status := range portStatus { - // set the new child IP - status.Spec.ChildIP = childIP - _, err = pm.AddPort(ctx, status.Spec) - if err != nil { - return errors.Wrap(err, "rootless port failed to add port") - } - } - return nil -} - -func exposePorts(pm rkport.Manager, portMappings []types.OCICNIPortMapping, childIP string) error { - ctx := context.TODO() - for _, i := range portMappings { - hostIP := i.HostIP - if hostIP == "" { - hostIP = "0.0.0.0" - } - spec := rkport.Spec{ - Proto: i.Protocol, - ParentIP: hostIP, - ParentPort: int(i.HostPort), - ChildPort: int(i.ContainerPort), - ChildIP: childIP, - } - if err := rkportutil.ValidatePortSpec(spec, nil); err != nil { - return err - } - if _, err := pm.AddPort(ctx, spec); err != nil { - return err - } - } - return nil -} - -func child() error { - // load the config from the parent - var opaque map[string]string - if err := json.Unmarshal([]byte(os.Getenv(reexecChildEnvOpaque)), &opaque); err != nil { - return err - } - - // start the child driver - quit := make(chan struct{}) - errCh := make(chan error) - go func() { - d := rkbuiltin.NewChildDriver(os.Stderr) - dErr := d.RunChildDriver(opaque, quit) - errCh <- dErr - }() - defer func() { - logrus.Info("Stopping child driver") - quit <- struct{}{} - if err := <-errCh; err != nil { - logrus.WithError(err).Warn("Child driver returned error on exit") - } - }() - - // wait for stdin to be closed - if _, err := ioutil.ReadAll(os.Stdin); err != nil { - return err - } - return nil -} - -type logrusWriter struct { - prefix string -} - -func (w *logrusWriter) Write(p []byte) (int, error) { - logrus.Infof("%s%s", w.prefix, string(p)) - return len(p), nil -} diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go index f126aa018..002b4ace3 100644 --- a/pkg/specgen/generate/container.go +++ b/pkg/specgen/generate/container.go @@ -26,7 +26,7 @@ func getImageFromSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGen // Image may already have been set in the generator. image, resolvedName := s.GetImage() if image != nil { - inspectData, err := image.Inspect(ctx, false) + inspectData, err := image.Inspect(ctx, nil) if err != nil { return nil, "", nil, err } @@ -39,7 +39,7 @@ func getImageFromSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGen return nil, "", nil, err } s.SetImage(image, resolvedName) - inspectData, err := image.Inspect(ctx, false) + inspectData, err := image.Inspect(ctx, nil) if err != nil { return nil, "", nil, err } @@ -55,7 +55,7 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat return nil, err } if inspectData != nil { - inspectData, err = newImage.Inspect(ctx, false) + inspectData, err = newImage.Inspect(ctx, nil) if err != nil { return nil, err } diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index 194c8dce5..6eebc6376 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -194,7 +194,7 @@ 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. - imageData, err := opts.Image.Inspect(ctx, false) + imageData, err := opts.Image.Inspect(ctx, nil) if err != nil { return nil, err } @@ -257,7 +257,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener // Environment Variables envs := map[string]string{} for _, env := range imageData.Config.Env { - keyval := strings.Split(env, "=") + keyval := strings.SplitN(env, "=", 2) envs[keyval[0]] = keyval[1] } diff --git a/pkg/specgen/generate/storage.go b/pkg/specgen/generate/storage.go index de655ad7d..3fde1a1b4 100644 --- a/pkg/specgen/generate/storage.go +++ b/pkg/specgen/generate/storage.go @@ -208,7 +208,7 @@ func getImageVolumes(ctx context.Context, img *libimage.Image, s *specgen.SpecGe return mounts, volumes, nil } - inspect, err := img.Inspect(ctx, false) + inspect, err := img.Inspect(ctx, nil) if err != nil { return nil, nil, errors.Wrapf(err, "error inspecting image to get image volumes") } diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index dbb669291..79185db04 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -551,11 +551,11 @@ func NewSpecGenerator(arg string, rootfs bool) *SpecGenerator { csc := ContainerStorageConfig{} if rootfs { csc.Rootfs = arg - // check if rootfs is actually overlayed - parts := strings.SplitN(csc.Rootfs, ":", 2) - if len(parts) > 1 && parts[1] == "O" { + // check if rootfs should use overlay + lastColonIndex := strings.LastIndex(csc.Rootfs, ":") + if lastColonIndex != -1 && lastColonIndex+1 < len(csc.Rootfs) && csc.Rootfs[lastColonIndex+1:] == "O" { csc.RootfsOverlay = true - csc.Rootfs = parts[0] + csc.Rootfs = csc.Rootfs[:lastColonIndex] } } else { csc.Image = arg diff --git a/pkg/specgen/specgen_test.go b/pkg/specgen/specgen_test.go new file mode 100644 index 000000000..b838d9d30 --- /dev/null +++ b/pkg/specgen/specgen_test.go @@ -0,0 +1,25 @@ +package specgen + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewSpecGeneratorWithRootfs(t *testing.T) { + tests := []struct { + rootfs string + expectedRootfsOverlay bool + expectedRootfs string + }{ + {"/root/a:b:O", true, "/root/a:b"}, + {"/root/a:b/c:O", true, "/root/a:b/c"}, + {"/root/a:b/c:", false, "/root/a:b/c:"}, + {"/root/a/b", false, "/root/a/b"}, + } + for _, args := range tests { + val := NewSpecGenerator(args.rootfs, true) + assert.Equal(t, val.RootfsOverlay, args.expectedRootfsOverlay) + assert.Equal(t, val.Rootfs, args.expectedRootfs) + } +} diff --git a/pkg/specgenutil/specgen.go b/pkg/specgenutil/specgen.go index 6a6397257..8007e5d8e 100644 --- a/pkg/specgenutil/specgen.go +++ b/pkg/specgenutil/specgen.go @@ -133,12 +133,14 @@ func getMemoryLimits(s *specgen.SpecGenerator, c *entities.ContainerCreateOption if err != nil { return nil, errors.Wrapf(err, "invalid value for memory") } - memory.Limit = &ml - if c.MemorySwap == "" { - limit := 2 * ml - memory.Swap = &(limit) + if ml > 0 { + memory.Limit = &ml + if c.MemorySwap == "" { + limit := 2 * ml + memory.Swap = &(limit) + } + hasLimits = true } - hasLimits = true } if m := c.MemoryReservation; len(m) > 0 { mr, err := units.RAMInBytes(m) diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at index afff68c22..748a0750f 100644 --- a/test/apiv2/20-containers.at +++ b/test/apiv2/20-containers.at @@ -379,3 +379,21 @@ t GET containers/$cid/json 200 \ .HostConfig.Tmpfs['"/mnt/scratch"']~.*mode=755.* t DELETE containers/$cid?v=true 204 + +# compat api: tmpfs without mount options +payload='{"Mounts":[{"Type":"tmpfs","Target":"/mnt/scratch"}]}' +t POST containers/create Image=$IMAGE HostConfig="$payload" 201 .Id~[0-9a-f]\\{64\\} +cid=$(jq -r '.Id' <<<"$output") +t GET containers/$cid/json 200 \ + .HostConfig.Tmpfs['"/mnt/scratch"']~.*tmpcopyup.* \ + +t DELETE containers/$cid?v=true 204 + +# compat api: bind mount without mount options +payload='{"Mounts":[{"Type":"bind","Source":"/tmp","Target":"/mnt"}]}' +t POST containers/create Image=$IMAGE HostConfig="$payload" 201 .Id~[0-9a-f]\\{64\\} +cid=$(jq -r '.Id' <<<"$output") +t GET containers/$cid/json 200 \ + .HostConfig.Binds[0]~/tmp:/mnt:.* \ + +t DELETE containers/$cid?v=true 204 diff --git a/test/apiv2/35-networks.at b/test/apiv2/35-networks.at index a4cb5a480..fd8dfd32b 100644 --- a/test/apiv2/35-networks.at +++ b/test/apiv2/35-networks.at @@ -131,8 +131,8 @@ t DELETE libpod/networks/network2 200 \ .[0].Err=null # test until filter - libpod api -t POST libpod/networks/create name='"network5"' labels='{"xyz":""}' 200 \ - .name=network5 +# create network via cli to test that the server can use it +podman network create --label xyz network5 # with date way back in the past, network should not be deleted t POST libpod/networks/prune?filters='{"until":["500000"]}' 200 diff --git a/test/e2e/checkpoint_test.go b/test/e2e/checkpoint_test.go index 403d739f0..770a7c7bd 100644 --- a/test/e2e/checkpoint_test.go +++ b/test/e2e/checkpoint_test.go @@ -6,10 +6,12 @@ import ( "os" "os/exec" "strings" + "time" "github.com/containers/podman/v3/pkg/checkpoint/crutils" "github.com/containers/podman/v3/pkg/criu" . "github.com/containers/podman/v3/test/utils" + "github.com/containers/podman/v3/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" . "github.com/onsi/gomega/gexec" @@ -159,7 +161,7 @@ var _ = Describe("Podman checkpoint", func() { Expect(result).Should(Exit(2)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) - result = podmanTest.Podman([]string{"rm", "-f", cid}) + result = podmanTest.Podman([]string{"rm", "-t", "1", "-f", cid}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) @@ -197,7 +199,7 @@ var _ = Describe("Podman checkpoint", func() { Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) Expect(podmanTest.GetContainerStatus()).To(Not(ContainSubstring("Exited"))) - result = podmanTest.Podman([]string{"rm", "-fa"}) + result = podmanTest.Podman([]string{"rm", "-t", "0", "-fa"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) @@ -234,7 +236,7 @@ var _ = Describe("Podman checkpoint", func() { Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) Expect(podmanTest.GetContainerStatus()).To(Not(ContainSubstring("Exited"))) - result = podmanTest.Podman([]string{"rm", "-fa"}) + result = podmanTest.Podman([]string{"rm", "-t", "0", "-fa"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) @@ -247,16 +249,19 @@ var _ = Describe("Podman checkpoint", func() { session := podmanTest.Podman(localRunString) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) + cid := session.OutputToString() + if !WaitContainerReady(podmanTest, cid, "Ready to accept connections", 20, 1) { + Fail("Container failed to get ready") + } IP := podmanTest.Podman([]string{"inspect", "-l", "--format={{.NetworkSettings.IPAddress}}"}) IP.WaitWithDefaultTimeout() Expect(IP).Should(Exit(0)) // Open a network connection to the redis server - conn, err := net.Dial("tcp", IP.OutputToString()+":6379") - if err != nil { - os.Exit(1) - } + conn, err := net.DialTimeout("tcp4", IP.OutputToString()+":6379", time.Duration(3)*time.Second) + Expect(err).To(BeNil()) + // This should fail as the container has established TCP connections result := podmanTest.Podman([]string{"container", "checkpoint", "-l"}) result.WaitWithDefaultTimeout() @@ -289,7 +294,7 @@ var _ = Describe("Podman checkpoint", func() { Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) - result = podmanTest.Podman([]string{"rm", "-fa"}) + result = podmanTest.Podman([]string{"rm", "-t", "0", "-fa"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) @@ -329,7 +334,7 @@ var _ = Describe("Podman checkpoint", func() { Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) - result = podmanTest.Podman([]string{"rm", "-fa"}) + result = podmanTest.Podman([]string{"rm", "-t", "0", "-fa"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) @@ -377,7 +382,7 @@ var _ = Describe("Podman checkpoint", func() { Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) - result = podmanTest.Podman([]string{"rm", "-fa"}) + result = podmanTest.Podman([]string{"rm", "-t", "0", "-fa"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) @@ -425,7 +430,7 @@ var _ = Describe("Podman checkpoint", func() { Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) - result = podmanTest.Podman([]string{"rm", "-fa"}) + result = podmanTest.Podman([]string{"rm", "-t", "0", "-fa"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) @@ -525,7 +530,7 @@ var _ = Describe("Podman checkpoint", func() { Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.NumberOfContainers()).To(Equal(1)) - result = podmanTest.Podman([]string{"rm", "-fa"}) + result = podmanTest.Podman([]string{"rm", "--time", "0", "-fa"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) @@ -753,7 +758,7 @@ var _ = Describe("Podman checkpoint", func() { Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) - result = podmanTest.Podman([]string{"rm", "-fa"}) + result = podmanTest.Podman([]string{"rm", "-t", "0", "-fa"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) @@ -916,7 +921,7 @@ var _ = Describe("Podman checkpoint", func() { Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited")) - result = podmanTest.Podman([]string{"rm", "-f", cid}) + result = podmanTest.Podman([]string{"rm", "-t", "0", "-f", cid}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) @@ -933,18 +938,23 @@ var _ = Describe("Podman checkpoint", func() { }) It("podman checkpoint and restore container with different port mappings", func() { - localRunString := getRunString([]string{"-p", "1234:6379", "--rm", redis}) + randomPort, err := utils.GetRandomPort() + Expect(err).ShouldNot(HaveOccurred()) + localRunString := getRunString([]string{"-p", fmt.Sprintf("%d:6379", randomPort), "--rm", redis}) session := podmanTest.Podman(localRunString) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) cid := session.OutputToString() fileName := "/tmp/checkpoint-" + cid + ".tar.gz" - // Open a network connection to the redis server via initial port mapping - conn, err := net.Dial("tcp", "localhost:1234") - if err != nil { - os.Exit(1) + if !WaitContainerReady(podmanTest, cid, "Ready to accept connections", 20, 1) { + Fail("Container failed to get ready") } + + fmt.Fprintf(os.Stderr, "Trying to connect to redis server at localhost:%d", randomPort) + // Open a network connection to the redis server via initial port mapping + conn, err := net.DialTimeout("tcp4", fmt.Sprintf("localhost:%d", randomPort), time.Duration(3)*time.Second) + Expect(err).ShouldNot(HaveOccurred()) conn.Close() // Checkpoint the container @@ -958,7 +968,9 @@ var _ = Describe("Podman checkpoint", func() { Expect(podmanTest.NumberOfContainers()).To(Equal(0)) // Restore container with different port mapping - result = podmanTest.Podman([]string{"container", "restore", "-p", "1235:6379", "-i", fileName}) + newRandomPort, err := utils.GetRandomPort() + Expect(err).ShouldNot(HaveOccurred()) + result = podmanTest.Podman([]string{"container", "restore", "-p", fmt.Sprintf("%d:6379", newRandomPort), "-i", fileName}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) @@ -967,16 +979,15 @@ var _ = Describe("Podman checkpoint", func() { // Open a network connection to the redis server via initial port mapping // This should fail - conn, err = net.Dial("tcp", "localhost:1234") + conn, err = net.DialTimeout("tcp4", fmt.Sprintf("localhost:%d", randomPort), time.Duration(3)*time.Second) Expect(err.Error()).To(ContainSubstring("connection refused")) // Open a network connection to the redis server via new port mapping - conn, err = net.Dial("tcp", "localhost:1235") - if err != nil { - os.Exit(1) - } + fmt.Fprintf(os.Stderr, "Trying to reconnect to redis server at localhost:%d", newRandomPort) + conn, err = net.DialTimeout("tcp4", fmt.Sprintf("localhost:%d", newRandomPort), time.Duration(3)*time.Second) + Expect(err).ShouldNot(HaveOccurred()) conn.Close() - result = podmanTest.Podman([]string{"rm", "-fa"}) + result = podmanTest.Podman([]string{"rm", "-t", "0", "-fa"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) diff --git a/test/e2e/containers_conf_test.go b/test/e2e/containers_conf_test.go index fac200c3c..2faad8d91 100644 --- a/test/e2e/containers_conf_test.go +++ b/test/e2e/containers_conf_test.go @@ -445,7 +445,7 @@ var _ = Describe("Podman run", func() { Expect(session.ErrorToString()).To(ContainSubstring("invalid image_copy_tmp_dir")) }) - It("podman system sevice --help shows (default 20)", func() { + It("podman system service --help shows (default 20)", func() { SkipIfRemote("this test is only for local") result := podmanTest.Podman([]string{"system", "service", "--help"}) result.WaitWithDefaultTimeout() diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go index cb987e139..69941494b 100644 --- a/test/e2e/generate_kube_test.go +++ b/test/e2e/generate_kube_test.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" "strconv" + "strings" "github.com/containers/podman/v3/libpod/define" @@ -119,20 +120,28 @@ var _ = Describe("Podman generate kube", func() { Expect(kube.OutputToString()).To(ContainSubstring("type: foo_bar_t")) }) - It("podman generate service kube on container", func() { - session := podmanTest.RunTopContainer("top") + It("podman generate service kube on container - targetPort should match port name", func() { + session := podmanTest.Podman([]string{"create", "--name", "test-ctr", "-p", "3890:3890", ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - kube := podmanTest.Podman([]string{"generate", "kube", "-s", "top"}) + kube := podmanTest.Podman([]string{"generate", "kube", "-s", "test-ctr"}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) - // TODO - test generated YAML - service produces multiple - // structs. - // pod := new(v1.Pod) - // err := yaml.Unmarshal([]byte(kube.OutputToString()), pod) - // Expect(err).To(BeNil()) + // Separate out the Service and Pod yaml + arr := strings.Split(string(kube.Out.Contents()), "---") + Expect(len(arr)).To(Equal(2)) + + svc := new(v1.Service) + err := yaml.Unmarshal([]byte(arr[0]), svc) + Expect(err).To(BeNil()) + Expect(len(svc.Spec.Ports)).To(Equal(1)) + Expect(svc.Spec.Ports[0].TargetPort.IntValue()).To(Equal(3890)) + + pod := new(v1.Pod) + err = yaml.Unmarshal([]byte(arr[1]), pod) + Expect(err).To(BeNil()) }) It("podman generate kube on pod", func() { @@ -315,21 +324,28 @@ var _ = Describe("Podman generate kube", func() { }) It("podman generate service kube on pod", func() { - _, rc, _ := podmanTest.CreatePod(map[string][]string{"--name": {"toppod"}}) - Expect(rc).To(Equal(0)) - - session := podmanTest.RunTopContainerInPod("topcontainer", "toppod") + session := podmanTest.Podman([]string{"create", "--pod", "new:test-pod", "-p", "4000:4000/udp", ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - kube := podmanTest.Podman([]string{"generate", "kube", "-s", "toppod"}) + kube := podmanTest.Podman([]string{"generate", "kube", "-s", "test-pod"}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) - // TODO: How do we test unmarshal with a service? We have two - // structs that need to be unmarshalled... - // _, err := yaml.Marshal(kube.OutputToString()) - // Expect(err).To(BeNil()) + // Separate out the Service and Pod yaml + arr := strings.Split(string(kube.Out.Contents()), "---") + Expect(len(arr)).To(Equal(2)) + + svc := new(v1.Service) + err := yaml.Unmarshal([]byte(arr[0]), svc) + Expect(err).To(BeNil()) + Expect(len(svc.Spec.Ports)).To(Equal(1)) + Expect(svc.Spec.Ports[0].TargetPort.IntValue()).To(Equal(4000)) + Expect(svc.Spec.Ports[0].Protocol).To(Equal(v1.ProtocolUDP)) + + pod := new(v1.Pod) + err = yaml.Unmarshal([]byte(arr[1]), pod) + Expect(err).To(BeNil()) }) It("podman generate kube on pod with restartPolicy", func() { @@ -451,6 +467,10 @@ var _ = Describe("Podman generate kube", func() { foundOtherPort := 0 for _, ctr := range pod.Spec.Containers { for _, port := range ctr.Ports { + // Since we are using tcp here, the generated kube yaml shouldn't + // have anything for protocol under the ports as tcp is the default + // for k8s + Expect(port.Protocol).To(BeEmpty()) if port.HostPort == 4000 { foundPort4000 = foundPort4000 + 1 } else if port.HostPort == 5000 { @@ -463,6 +483,24 @@ var _ = Describe("Podman generate kube", func() { Expect(foundPort4000).To(Equal(1)) Expect(foundPort5000).To(Equal(1)) Expect(foundOtherPort).To(Equal(0)) + + // Create container with UDP port and check the generated kube yaml + ctrWithUDP := podmanTest.Podman([]string{"create", "--pod", "new:test-pod", "-p", "6666:66/udp", ALPINE, "top"}) + ctrWithUDP.WaitWithDefaultTimeout() + Expect(ctrWithUDP).Should(Exit(0)) + + kube = podmanTest.Podman([]string{"generate", "kube", "test-pod"}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + pod = new(v1.Pod) + err = yaml.Unmarshal(kube.Out.Contents(), pod) + Expect(err).To(BeNil()) + + containers := pod.Spec.Containers + Expect(len(containers)).To(Equal(1)) + Expect(len(containers[0].Ports)).To(Equal(1)) + Expect(containers[0].Ports[0].Protocol).To(Equal(v1.ProtocolUDP)) }) It("podman generate and reimport kube on pod", func() { @@ -566,7 +604,7 @@ var _ = Describe("Podman generate kube", func() { Expect(found).To(BeTrue()) Expect(val).To(HaveSuffix("z")) - rm := podmanTest.Podman([]string{"pod", "rm", "-f", "test1"}) + rm := podmanTest.Podman([]string{"pod", "rm", "-t", "0", "-f", "test1"}) rm.WaitWithDefaultTimeout() Expect(rm).Should(Exit(0)) @@ -619,7 +657,7 @@ var _ = Describe("Podman generate kube", func() { kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) - rm := podmanTest.Podman([]string{"pod", "rm", "-f", "test1"}) + rm := podmanTest.Podman([]string{"pod", "rm", "-t", "0", "-f", "test1"}) rm.WaitWithDefaultTimeout() Expect(rm).Should(Exit(0)) @@ -648,7 +686,7 @@ var _ = Describe("Podman generate kube", func() { kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) - rm := podmanTest.Podman([]string{"pod", "rm", "-f", podName}) + rm := podmanTest.Podman([]string{"pod", "rm", "-t", "0", "-f", podName}) rm.WaitWithDefaultTimeout() Expect(rm).Should(Exit(0)) @@ -803,7 +841,7 @@ var _ = Describe("Podman generate kube", func() { Expect(containers[0].Args).To(Equal([]string{"10s"})) }) - It("podman generate kube - no command", func() { + It("podman generate kube - use command from image unless explicitly set in the podman command", func() { session := podmanTest.Podman([]string{"create", "--name", "test", ALPINE}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) @@ -812,8 +850,8 @@ var _ = Describe("Podman generate kube", func() { kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) - // Now make sure that the container's command is not set to the - // entrypoint and it's arguments to "10s". + // Now make sure that the container's command in the kube yaml is not set to the + // image command. pod := new(v1.Pod) err := yaml.Unmarshal(kube.Out.Contents(), pod) Expect(err).To(BeNil()) @@ -831,8 +869,8 @@ var _ = Describe("Podman generate kube", func() { kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) - // Now make sure that the container's command is not set to the - // entrypoint and it's arguments to "10s". + // Now make sure that the container's command in the kube yaml is set to the + // command passed via the cli to podman create. pod = new(v1.Pod) err = yaml.Unmarshal(kube.Out.Contents(), pod) Expect(err).To(BeNil()) @@ -842,10 +880,10 @@ var _ = Describe("Podman generate kube", func() { Expect(containers[0].Command).To(Equal(cmd)) }) - It("podman generate kube - use entrypoint from image", func() { + It("podman generate kube - use entrypoint from image unless --entrypoint is set", func() { // Build an image with an entrypoint. containerfile := `FROM quay.io/libpod/alpine:latest -ENTRYPOINT /bin/sleep` +ENTRYPOINT ["sleep"]` targetPath, err := CreateTempDirInTempDir() Expect(err).To(BeNil()) @@ -866,17 +904,34 @@ ENTRYPOINT /bin/sleep` kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) - // Now make sure that the container's command is set to the - // entrypoint and it's arguments to "10s". + // Now make sure that the container's command in the kube yaml is NOT set to the + // entrypoint but the arguments should be set to "10s". pod := new(v1.Pod) err = yaml.Unmarshal(kube.Out.Contents(), pod) Expect(err).To(BeNil()) containers := pod.Spec.Containers Expect(len(containers)).To(Equal(1)) - - Expect(containers[0].Command).To(Equal([]string{"/bin/sh", "-c", "/bin/sleep"})) Expect(containers[0].Args).To(Equal([]string{"10s"})) + + session = podmanTest.Podman([]string{"create", "--pod", "new:testpod-2", "--entrypoint", "echo", image, "hello"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + kube = podmanTest.Podman([]string{"generate", "kube", "testpod-2"}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + // Now make sure that the container's command in the kube yaml is set to the + // entrypoint defined by the --entrypoint flag and the arguments should be set to "hello". + pod = new(v1.Pod) + err = yaml.Unmarshal(kube.Out.Contents(), pod) + Expect(err).To(BeNil()) + + containers = pod.Spec.Containers + Expect(len(containers)).To(Equal(1)) + Expect(containers[0].Command).To(Equal([]string{"echo"})) + Expect(containers[0].Args).To(Equal([]string{"hello"})) }) It("podman generate kube - --privileged container", func() { @@ -905,7 +960,7 @@ ENTRYPOINT /bin/sleep` Expect(kube).Should(Exit(0)) // Remove the pod so play can recreate it. - kube = podmanTest.Podman([]string{"pod", "rm", "-f", "testpod"}) + kube = podmanTest.Podman([]string{"pod", "rm", "-t", "0", "-f", "testpod"}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -942,7 +997,7 @@ USER test1` pod := new(v1.Pod) err = yaml.Unmarshal(kube.Out.Contents(), pod) Expect(err).To(BeNil()) - Expect(*pod.Spec.Containers[0].SecurityContext.RunAsUser).To(Equal(int64(10001))) + Expect(pod.Spec.Containers[0].SecurityContext.RunAsUser).To(BeNil()) }) It("podman generate kube on named volume", func() { diff --git a/test/e2e/images_test.go b/test/e2e/images_test.go index b4ec7447e..56af64f04 100644 --- a/test/e2e/images_test.go +++ b/test/e2e/images_test.go @@ -446,4 +446,25 @@ RUN > file2 }) + It("podman builder prune", func() { + dockerfile := `FROM quay.io/libpod/alpine:latest +RUN > file +` + dockerfile2 := `FROM quay.io/libpod/alpine:latest +RUN > file2 +` + podmanTest.BuildImageWithLabel(dockerfile, "foobar.com/workdir:latest", "false", "abc") + podmanTest.BuildImageWithLabel(dockerfile2, "foobar.com/workdir:latest", "false", "xyz") + // --force used to to avoid y/n question + result := podmanTest.Podman([]string{"builder", "prune", "--filter", "label=abc", "--force"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + Expect(len(result.OutputToStringArray())).To(Equal(1)) + + //check if really abc is removed + result = podmanTest.Podman([]string{"image", "list", "--filter", "label=abc"}) + Expect(len(result.OutputToStringArray())).To(Equal(0)) + + }) + }) diff --git a/test/e2e/logs_test.go b/test/e2e/logs_test.go index 71d30f063..314e09b9a 100644 --- a/test/e2e/logs_test.go +++ b/test/e2e/logs_test.go @@ -227,7 +227,7 @@ var _ = Describe("Podman logs", func() { Expect(inspect.ErrorToString()).To(ContainSubstring("no such container")) } - results = podmanTest.Podman([]string{"rm", "-f", containerName}) + results = podmanTest.Podman([]string{"rm", "--time", "0", "-f", containerName}) results.WaitWithDefaultTimeout() Expect(results).To(Exit(0)) }) diff --git a/test/e2e/network_connect_disconnect_test.go b/test/e2e/network_connect_disconnect_test.go index 5f7c55d3f..6cddf9285 100644 --- a/test/e2e/network_connect_disconnect_test.go +++ b/test/e2e/network_connect_disconnect_test.go @@ -200,7 +200,7 @@ var _ = Describe("Podman network connect and disconnect", func() { Expect(exec).Should(Exit(0)) // make sure no logrus errors are shown https://github.com/containers/podman/issues/9602 - rm := podmanTest.Podman([]string{"rm", "-f", "test"}) + rm := podmanTest.Podman([]string{"rm", "--time=0", "-f", "test"}) rm.WaitWithDefaultTimeout() Expect(rm).Should(Exit(0)) Expect(rm.ErrorToString()).To(Equal("")) diff --git a/test/e2e/network_create_test.go b/test/e2e/network_create_test.go index ae9f112b5..c9e13e7d2 100644 --- a/test/e2e/network_create_test.go +++ b/test/e2e/network_create_test.go @@ -43,7 +43,7 @@ var _ = Describe("Podman network create", func() { It("podman network create with name and subnet", func() { netName := "subnet-" + stringid.GenerateNonCryptoID() - nc := podmanTest.Podman([]string{"network", "create", "--subnet", "10.11.12.0/24", netName}) + nc := podmanTest.Podman([]string{"network", "create", "--subnet", "10.11.12.0/24", "--ip-range", "10.11.12.0/26", netName}) nc.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(netName) Expect(nc).Should(Exit(0)) @@ -61,7 +61,11 @@ var _ = Describe("Podman network create", func() { result := results[0] Expect(result.Name).To(Equal(netName)) Expect(result.Subnets).To(HaveLen(1)) + Expect(result.Subnets[0].Subnet.String()).To(Equal("10.11.12.0/24")) Expect(result.Subnets[0].Gateway.String()).To(Equal("10.11.12.1")) + Expect(result.Subnets[0].LeaseRange).ToNot(BeNil()) + Expect(result.Subnets[0].LeaseRange.StartIP.String()).To(Equal("10.11.12.1")) + Expect(result.Subnets[0].LeaseRange.EndIP.String()).To(Equal("10.11.12.63")) // Once a container executes a new network, the nic will be created. We should clean those up // best we can diff --git a/test/e2e/network_test.go b/test/e2e/network_test.go index 8e47fac75..d64b28063 100644 --- a/test/e2e/network_test.go +++ b/test/e2e/network_test.go @@ -272,7 +272,7 @@ var _ = Describe("Podman network", func() { Expect(strings.HasPrefix(net.IPAddress, "10.50.50.")).To(BeTrue()) // Necessary to ensure the CNI network is removed cleanly - rmAll := podmanTest.Podman([]string{"rm", "-f", ctrName}) + rmAll := podmanTest.Podman([]string{"rm", "-t", "0", "-f", ctrName}) rmAll.WaitWithDefaultTimeout() Expect(rmAll).Should(Exit(0)) }) @@ -309,7 +309,7 @@ var _ = Describe("Podman network", func() { Expect(net2.NetworkID).To(Equal(netName2)) // Necessary to ensure the CNI network is removed cleanly - rmAll := podmanTest.Podman([]string{"rm", "-f", ctrName}) + rmAll := podmanTest.Podman([]string{"rm", "-t", "0", "-f", ctrName}) rmAll.WaitWithDefaultTimeout() Expect(rmAll).Should(Exit(0)) }) @@ -350,7 +350,7 @@ var _ = Describe("Podman network", func() { Expect(strings.HasPrefix(net2.IPAddress, "10.50.51.")).To(BeTrue()) // Necessary to ensure the CNI network is removed cleanly - rmAll := podmanTest.Podman([]string{"rm", "-f", ctrName}) + rmAll := podmanTest.Podman([]string{"rm", "-t", "0", "-f", ctrName}) rmAll.WaitWithDefaultTimeout() Expect(rmAll).Should(Exit(0)) }) @@ -404,7 +404,7 @@ var _ = Describe("Podman network", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(2)) - session = podmanTest.Podman([]string{"network", "rm", "--force", netName}) + session = podmanTest.Podman([]string{"network", "rm", "-t", "0", "--force", netName}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) diff --git a/test/e2e/pause_test.go b/test/e2e/pause_test.go index 2e5e07de9..99488a507 100644 --- a/test/e2e/pause_test.go +++ b/test/e2e/pause_test.go @@ -168,7 +168,7 @@ var _ = Describe("Podman pause", func() { Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(strings.ToLower(podmanTest.GetContainerStatus())).To(ContainSubstring(pausedState)) - result = podmanTest.Podman([]string{"rm", "--force", cid}) + result = podmanTest.Podman([]string{"rm", "-t", "0", "--force", cid}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) @@ -205,7 +205,7 @@ var _ = Describe("Podman pause", func() { Expect(result).Should(Exit(2)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) - result = podmanTest.Podman([]string{"rm", "-f", cid}) + result = podmanTest.Podman([]string{"rm", "-t", "0", "-f", cid}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index a29d0ad46..8aeba9d75 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -18,6 +18,7 @@ import ( "github.com/containers/storage/pkg/stringid" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/onsi/gomega/format" . "github.com/onsi/gomega/gexec" "github.com/opencontainers/selinux/go-selinux" ) @@ -2798,4 +2799,99 @@ invalid kube kind exists.WaitWithDefaultTimeout() Expect(exists).To(Exit(0)) }) + + It("podman play kube replace", func() { + pod := getPod() + err := generateKubeYaml("pod", pod, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + ls := podmanTest.Podman([]string{"pod", "ps", "--format", "'{{.ID}}'"}) + ls.WaitWithDefaultTimeout() + Expect(ls).Should(Exit(0)) + Expect(len(ls.OutputToStringArray())).To(Equal(1)) + + containerLen := podmanTest.Podman([]string{"pod", "inspect", pod.Name, "--format", "'{{len .Containers}}'"}) + + ctr01Name := "ctr01" + ctr02Name := "ctr02" + + ctr01 := getCtr(withName(ctr01Name)) + ctr02 := getCtr(withName(ctr02Name)) + + newPod := getPod( + withCtr(ctr01), + withCtr(ctr02), + ) + err = generateKubeYaml("pod", newPod, kubeYaml) + Expect(err).To(BeNil()) + + replace := podmanTest.Podman([]string{"play", "kube", "--replace", kubeYaml}) + replace.WaitWithDefaultTimeout() + Expect(replace).Should(Exit(0)) + + newContainerLen := podmanTest.Podman([]string{"pod", "inspect", newPod.Name, "--format", "'{{len .Containers}}'"}) + newContainerLen.WaitWithDefaultTimeout() + Expect(newContainerLen).Should(Exit(0)) + Expect(newContainerLen.OutputToString()).NotTo(Equal(containerLen.OutputToString())) + }) + + It("podman play kube replace non-existing pod", func() { + pod := getPod() + err := generateKubeYaml("pod", pod, kubeYaml) + Expect(err).To(BeNil()) + + replace := podmanTest.Podman([]string{"play", "kube", "--replace", kubeYaml}) + replace.WaitWithDefaultTimeout() + Expect(replace).Should(Exit(0)) + + ls := podmanTest.Podman([]string{"pod", "ps", "--format", "'{{.ID}}'"}) + ls.WaitWithDefaultTimeout() + Expect(ls).Should(Exit(0)) + Expect(len(ls.OutputToStringArray())).To(Equal(1)) + }) + + Describe("verify environment variables", func() { + var maxLength int + BeforeEach(func() { + maxLength = format.MaxLength + format.MaxLength = 0 + }) + AfterEach(func() { + format.MaxLength = maxLength + }) + + It("values containing equal sign", func() { + javaToolOptions := `-XX:+IgnoreUnrecognizedVMOptions -XX:+IdleTuningGcOnIdle -Xshareclasses:name=openj9_system_scc,cacheDir=/opt/java/.scc,readonly,nonFatal` + openj9JavaOptions := `-XX:+IgnoreUnrecognizedVMOptions -XX:+IdleTuningGcOnIdle -Xshareclasses:name=openj9_system_scc,cacheDir=/opt/java/.scc,readonly,nonFatal -Dosgi.checkConfiguration=false` + + containerfile := fmt.Sprintf(`FROM %s +ENV JAVA_TOOL_OPTIONS=%q +ENV OPENJ9_JAVA_OPTIONS=%q +`, + ALPINE, javaToolOptions, openj9JavaOptions) + + image := "podman-kube-test:env" + podmanTest.BuildImage(containerfile, image, "false") + ctnr := getCtr(withImage(image)) + pod := getPod(withCtr(ctnr)) + Expect(generateKubeYaml("pod", pod, kubeYaml)).Should(Succeed()) + + play := podmanTest.Podman([]string{"play", "kube", "--start", kubeYaml}) + play.WaitWithDefaultTimeout() + Expect(play).Should(Exit(0)) + + inspect := podmanTest.Podman([]string{"container", "inspect", "--format=json", getCtrNameInPod(pod)}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + + contents := string(inspect.Out.Contents()) + Expect(contents).To(ContainSubstring(javaToolOptions)) + Expect(contents).To(ContainSubstring(openj9JavaOptions)) + }) + }) + }) diff --git a/test/e2e/pod_rm_test.go b/test/e2e/pod_rm_test.go index ac1f322ef..7dc3dfa7f 100644 --- a/test/e2e/pod_rm_test.go +++ b/test/e2e/pod_rm_test.go @@ -107,7 +107,7 @@ var _ = Describe("Podman pod rm", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - result := podmanTest.Podman([]string{"pod", "rm", "-f", podid}) + result := podmanTest.Podman([]string{"pod", "rm", "-t", "0", "-f", podid}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) @@ -179,7 +179,7 @@ var _ = Describe("Podman pod rm", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - result := podmanTest.Podman([]string{"pod", "rm", "-fa"}) + result := podmanTest.Podman([]string{"pod", "rm", "-t", "0", "-fa"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) @@ -225,7 +225,7 @@ var _ = Describe("Podman pod rm", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - session = podmanTest.Podman([]string{"pod", "rm", "--force", "--ignore", "bogus", "test1"}) + session = podmanTest.Podman([]string{"pod", "rm", "-t", "0", "--force", "--ignore", "bogus", "test1"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) @@ -257,7 +257,7 @@ var _ = Describe("Podman pod rm", func() { Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) // infra+top - session = podmanTest.Podman([]string{"pod", "rm", "--pod-id-file", tmpFile, "--force"}) + session = podmanTest.Podman([]string{"pod", "rm", "-t", "0", "--pod-id-file", tmpFile, "--force"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) @@ -294,7 +294,7 @@ var _ = Describe("Podman pod rm", func() { Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(20)) // 10*(infra+top) - cmd = []string{"pod", "rm", "--force"} + cmd = []string{"pod", "rm", "--time=0", "--force"} cmd = append(cmd, podIDFiles...) session = podmanTest.Podman(cmd) session.WaitWithDefaultTimeout() diff --git a/test/e2e/rm_test.go b/test/e2e/rm_test.go index ec4dce752..e355de42f 100644 --- a/test/e2e/rm_test.go +++ b/test/e2e/rm_test.go @@ -82,7 +82,7 @@ var _ = Describe("Podman rm", func() { Expect(session).Should(Exit(0)) cid := session.OutputToString() - result := podmanTest.Podman([]string{"rm", "-f", cid}) + result := podmanTest.Podman([]string{"rm", "-t", "0", "-f", cid}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) }) @@ -275,7 +275,7 @@ var _ = Describe("Podman rm", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - session = podmanTest.Podman([]string{"rm", "--force", "--ignore", "bogus", "test1"}) + session = podmanTest.Podman([]string{"rm", "-t", "0", "--force", "--ignore", "bogus", "test1"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index 84707732b..ca242a17c 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -680,7 +680,7 @@ var _ = Describe("Podman run networking", func() { Expect(run).Should(Exit(0)) Expect(run.OutputToString()).To(ContainSubstring(ipAddr)) - podrm := podmanTest.Podman([]string{"pod", "rm", "-f", podname}) + podrm := podmanTest.Podman([]string{"pod", "rm", "-t", "0", "-f", podname}) podrm.WaitWithDefaultTimeout() Expect(podrm).Should(Exit(0)) }) diff --git a/test/e2e/run_selinux_test.go b/test/e2e/run_selinux_test.go index 1a5ef4d5d..3cb0663e0 100644 --- a/test/e2e/run_selinux_test.go +++ b/test/e2e/run_selinux_test.go @@ -201,7 +201,7 @@ var _ = Describe("Podman run", func() { Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal(label1)) - session = podmanTest.Podman([]string{"pod", "rm", podID, "--force"}) + session = podmanTest.Podman([]string{"pod", "rm", "-t", "0", podID, "--force"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) }) @@ -222,7 +222,7 @@ var _ = Describe("Podman run", func() { Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Not(Equal(label1))) - session = podmanTest.Podman([]string{"pod", "rm", podID, "--force"}) + session = podmanTest.Podman([]string{"pod", "rm", "-t", "0", podID, "--force"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) }) diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index b6743f4b7..8502879ff 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -5,6 +5,7 @@ import ( "io/ioutil" "net" "os" + "os/exec" "path/filepath" "strconv" "strings" @@ -12,6 +13,7 @@ import ( "time" "github.com/containers/podman/v3/pkg/cgroups" + "github.com/containers/podman/v3/pkg/rootless" . "github.com/containers/podman/v3/test/utils" "github.com/containers/storage/pkg/stringid" "github.com/mrunalp/fileutils" @@ -226,6 +228,49 @@ var _ = Describe("Podman run", func() { stdoutLines := session.OutputToStringArray() Expect(stdoutLines).Should(HaveLen(1)) Expect(stdoutLines[0]).Should(Equal(uniqueString)) + + SkipIfRemote("External overlay only work locally") + if os.Getenv("container") != "" { + Skip("Overlay mounts not supported when running in a container") + } + if rootless.IsRootless() { + if _, err := exec.LookPath("fuse-overlayfs"); err != nil { + Skip("Fuse-Overlayfs required for rootless overlay mount test") + } + } + // Test --rootfs with an external overlay + // use --rm to remove container and confirm if we did not leak anything + osession := podmanTest.Podman([]string{"run", "-i", "--rm", "--security-opt", "label=disable", + "--rootfs", rootfs + ":O", "cat", testFilePath}) + osession.WaitWithDefaultTimeout() + Expect(osession).Should(Exit(0)) + + // Test podman start stop with overlay + osession = podmanTest.Podman([]string{"run", "--name", "overlay-foo", "--security-opt", "label=disable", + "--rootfs", rootfs + ":O", "echo", "hello"}) + osession.WaitWithDefaultTimeout() + Expect(osession).Should(Exit(0)) + + osession = podmanTest.Podman([]string{"stop", "overlay-foo"}) + osession.WaitWithDefaultTimeout() + Expect(osession).Should(Exit(0)) + + startsession := podmanTest.Podman([]string{"start", "--attach", "overlay-foo"}) + startsession.WaitWithDefaultTimeout() + Expect(startsession).Should(Exit(0)) + Expect(startsession.OutputToString()).To(Equal("hello")) + + // remove container for above test overlay-foo + osession = podmanTest.Podman([]string{"rm", "overlay-foo"}) + osession.WaitWithDefaultTimeout() + Expect(osession).Should(Exit(0)) + + // Test --rootfs with an external overlay with --uidmap + osession = podmanTest.Podman([]string{"run", "--uidmap", "0:1000:1000", "--rm", "--security-opt", "label=disable", + "--rootfs", rootfs + ":O", "echo", "hello"}) + osession.WaitWithDefaultTimeout() + Expect(osession).Should(Exit(0)) + Expect(osession.OutputToString()).To(Equal("hello")) }) It("podman run a container with --init", func() { diff --git a/test/e2e/run_volume_test.go b/test/e2e/run_volume_test.go index f1baa7780..634a498b9 100644 --- a/test/e2e/run_volume_test.go +++ b/test/e2e/run_volume_test.go @@ -222,7 +222,7 @@ var _ = Describe("Podman run with volumes", func() { Expect(matches[0]).To(Not(ContainSubstring("nosuid"))) }) - // Container should start when workdir is overlayed volume + // Container should start when workdir is overlay volume It("podman run with volume mounted as overlay and used as workdir", func() { SkipIfRemote("Overlay volumes only work locally") if os.Getenv("container") != "" { @@ -236,7 +236,7 @@ var _ = Describe("Podman run with volumes", func() { mountPath := filepath.Join(podmanTest.TempDir, "secrets") os.Mkdir(mountPath, 0755) - //Container should be able to start with custom overlayed volume + //Container should be able to start with custom overlay volume session := podmanTest.Podman([]string{"run", "--rm", "-v", mountPath + ":/data:O", "--workdir=/data", ALPINE, "echo", "hello"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) @@ -262,15 +262,15 @@ var _ = Describe("Podman run with volumes", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - // create file on overlayed volume - session = podmanTest.Podman([]string{"run", "--volume", volName + ":/data:O", ALPINE, "sh", "-c", "echo hello >> " + "/data/overlayed"}) + // create file on overlay volume + session = podmanTest.Podman([]string{"run", "--volume", volName + ":/data:O", ALPINE, "sh", "-c", "echo hello >> " + "/data/overlay"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - // volume should contain only `test` not `overlayed` + // volume should contain only `test` not `overlay` session = podmanTest.Podman([]string{"run", "--volume", volName + ":/data", ALPINE, "sh", "-c", "ls /data"}) session.WaitWithDefaultTimeout() - Expect(session.OutputToString()).To(Not(ContainSubstring("overlayed"))) + Expect(session.OutputToString()).To(Not(ContainSubstring("overlay"))) Expect(session.OutputToString()).To(ContainSubstring("test")) }) diff --git a/test/e2e/search_test.go b/test/e2e/search_test.go index f82c3d9d1..10e991d9f 100644 --- a/test/e2e/search_test.go +++ b/test/e2e/search_test.go @@ -2,6 +2,7 @@ package integration import ( "bytes" + "encoding/json" "fmt" "io/ioutil" "os" @@ -9,6 +10,7 @@ import ( "strconv" "text/template" + "github.com/containers/podman/v3/pkg/domain/entities" . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -105,7 +107,18 @@ registries = ['{{.Host}}:{{.Port}}']` search.WaitWithDefaultTimeout() Expect(search).Should(Exit(0)) output := string(search.Out.Contents()) - match, _ := regexp.MatchString(`(?m)^quay.io\s+quay.io/libpod/whalesay\s+Static image used for automated testing.+$`, output) + match, _ := regexp.MatchString(`(?m)NAME\s+DESCRIPTION$`, output) + Expect(match).To(BeTrue()) + match, _ = regexp.MatchString(`(?m)quay.io/libpod/whalesay\s+Static image used for automated testing.+$`, output) + Expect(match).To(BeTrue()) + }) + + It("podman search image with --compatible", func() { + search := podmanTest.Podman([]string{"search", "--compatible", "quay.io/libpod/whalesay"}) + search.WaitWithDefaultTimeout() + Expect(search).Should(Exit(0)) + output := string(search.Out.Contents()) + match, _ := regexp.MatchString(`(?m)NAME\s+DESCRIPTION\s+STARS\s+OFFICIAL\s+AUTOMATED$`, output) Expect(match).To(BeTrue()) }) @@ -123,6 +136,15 @@ registries = ['{{.Host}}:{{.Port}}']` Expect(search).Should(Exit(0)) Expect(search.IsJSONOutputValid()).To(BeTrue()) Expect(search.OutputToString()).To(ContainSubstring("docker.io/library/alpine")) + + // Test for https://github.com/containers/podman/issues/11894 + contents := make([]entities.ImageSearchReport, 0) + err := json.Unmarshal(search.Out.Contents(), &contents) + Expect(err).ToNot(HaveOccurred()) + Expect(len(contents)).To(BeNumerically(">", 0), "No results from image search") + for _, element := range contents { + Expect(element.Description).ToNot(HaveSuffix("...")) + } }) It("podman search format json list tags", func() { @@ -135,13 +157,17 @@ registries = ['{{.Host}}:{{.Port}}']` Expect(search.OutputToString()).To(ContainSubstring("2.7")) }) - It("podman search no-trunc flag", func() { - search := podmanTest.Podman([]string{"search", "--no-trunc", "alpine"}) + // Test for https://github.com/containers/podman/issues/11894 + It("podman search no-trunc=false flag", func() { + search := podmanTest.Podman([]string{"search", "--no-trunc=false", "alpine", "--format={{.Description}}"}) search.WaitWithDefaultTimeout() Expect(search).Should(Exit(0)) - Expect(len(search.OutputToStringArray())).To(BeNumerically(">", 1)) - Expect(search.LineInOutputContains("docker.io/library/alpine")).To(BeTrue()) - Expect(search.LineInOutputContains("...")).To(BeFalse()) + + for _, line := range search.OutputToStringArray() { + if len(line) > 44 { + Expect(line).To(HaveSuffix("..."), line+" should have been truncated") + } + } }) It("podman search limit flag", func() { diff --git a/test/e2e/stop_test.go b/test/e2e/stop_test.go index 7f178d719..fb8f92e0f 100644 --- a/test/e2e/stop_test.go +++ b/test/e2e/stop_test.go @@ -181,6 +181,18 @@ var _ = Describe("Podman stop", func() { Expect(strings.TrimSpace(finalCtrs.OutputToString())).To(Equal("")) }) + It("podman stop container --timeout Warning", func() { + SkipIfRemote("warning will happen only on server side") + session := podmanTest.Podman([]string{"run", "-d", "--name", "test5", ALPINE, "sleep", "100"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + session = podmanTest.Podman([]string{"stop", "--timeout", "1", "test5"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + warning := session.ErrorToString() + Expect(warning).To(ContainSubstring("StopSignal SIGTERM failed to stop container test5 in 1 seconds, resorting to SIGKILL")) + }) + It("podman stop latest containers", func() { SkipIfRemote("--latest flag n/a") session := podmanTest.RunTopContainer("test1") diff --git a/test/e2e/system_connection_test.go b/test/e2e/system_connection_test.go index 6cdb78c5e..842ae8df6 100644 --- a/test/e2e/system_connection_test.go +++ b/test/e2e/system_connection_test.go @@ -208,13 +208,13 @@ var _ = Describe("podman system connection", func() { session = podmanTest.Podman(cmd) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.Out).Should(Say("Name *Identity *URI")) + Expect(session.Out).Should(Say("Name *URI *Identity *Default")) cmd = []string{"system", "connection", "list", "--format", "{{.Name}}"} session = podmanTest.Podman(cmd) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.OutputToString()).Should(Equal("devl* qe")) + Expect(session.OutputToString()).Should(Equal("devl qe")) }) It("failed default", func() { diff --git a/test/e2e/system_dial_stdio_test.go b/test/e2e/system_dial_stdio_test.go new file mode 100644 index 000000000..afe3d5acd --- /dev/null +++ b/test/e2e/system_dial_stdio_test.go @@ -0,0 +1,53 @@ +package integration + +import ( + "fmt" + "os" + + . "github.com/containers/podman/v3/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" +) + +var _ = Describe("podman system dial-stdio", func() { + var ( + tempdir string + err error + podmanTest *PodmanTestIntegration + ) + + BeforeEach(func() { + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanTestCreate(tempdir) + podmanTest.Setup() + podmanTest.SeedImages() + }) + + AfterEach(func() { + podmanTest.Cleanup() + f := CurrentGinkgoTestDescription() + timedResult := fmt.Sprintf("Test: %s completed in %f seconds", f.TestText, f.Duration.Seconds()) + GinkgoWriter.Write([]byte(timedResult)) + }) + + It("podman system dial-stdio help", func() { + session := podmanTest.Podman([]string{"system", "dial-stdio", "--help"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring("Examples: podman system dial-stdio")) + }) + + It("podman system dial-stdio while service is not running", func() { + if IsRemote() { + Skip("this test is only for non-remote") + } + session := podmanTest.Podman([]string{"system", "dial-stdio"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(125)) + Expect(session.ErrorToString()).To(ContainSubstring("Error: failed to open connection to podman")) + }) +}) diff --git a/test/e2e/system_service_test.go b/test/e2e/system_service_test.go new file mode 100644 index 000000000..9a4d687c3 --- /dev/null +++ b/test/e2e/system_service_test.go @@ -0,0 +1,142 @@ +package integration + +import ( + "io/ioutil" + "net" + "net/http" + "net/url" + "strconv" + "time" + + . "github.com/containers/podman/v3/test/utils" + "github.com/containers/podman/v3/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" +) + +var _ = Describe("podman system service", func() { + var podmanTest *PodmanTestIntegration + + BeforeEach(func() { + tempdir, err := CreateTempDirInTempDir() + Expect(err).ShouldNot(HaveOccurred()) + + podmanTest = PodmanTestCreate(tempdir) + podmanTest.Setup() + }) + + AfterEach(func() { + podmanTest.Cleanup() + processTestResult(CurrentGinkgoTestDescription()) + }) + + Describe("verify timeout", func() { + It("of 2 seconds", func() { + SkipIfRemote("service subcommand not supported remotely") + + address := url.URL{ + Scheme: "tcp", + Host: net.JoinHostPort("localhost", randomPort()), + } + session := podmanTest.Podman([]string{ + "system", "service", "--time=2", address.String(), + }) + defer session.Kill() + + WaitForService(address) + + session.Wait(5 * time.Second) + Eventually(session, 5).Should(Exit(0)) + }) + }) + + Describe("verify pprof endpoints", func() { + // Depends on pkg/api/server/server.go:255 + const magicComment = "pprof service listening on" + + It("are available", func() { + SkipIfRemote("service subcommand not supported remotely") + + address := url.URL{ + Scheme: "tcp", + Host: net.JoinHostPort("localhost", randomPort()), + } + + pprofPort := randomPort() + session := podmanTest.Podman([]string{ + "system", "service", "--log-level=debug", "--time=0", + "--pprof-address=localhost:" + pprofPort, address.String(), + }) + defer session.Kill() + + WaitForService(address) + + // Combined with test below we have positive/negative test for pprof + Expect(session.Err.Contents()).Should(ContainSubstring(magicComment)) + + heap := url.URL{ + Scheme: "http", + Host: net.JoinHostPort("localhost", pprofPort), + Path: "/debug/pprof/heap", + RawQuery: "seconds=2", + } + resp, err := http.Get(heap.String()) + Expect(err).ShouldNot(HaveOccurred()) + defer resp.Body.Close() + Expect(resp).To(HaveHTTPStatus(http.StatusOK)) + + body, err := ioutil.ReadAll(resp.Body) + Expect(err).ShouldNot(HaveOccurred()) + Expect(body).ShouldNot(BeEmpty()) + + session.Interrupt().Wait(2 * time.Second) + Eventually(session).Should(Exit(1)) + }) + + It("are not available", func() { + SkipIfRemote("service subcommand not supported remotely") + + address := url.URL{ + Scheme: "tcp", + Host: net.JoinHostPort("localhost", randomPort()), + } + + session := podmanTest.Podman([]string{ + "system", "service", "--log-level=debug", "--time=0", address.String(), + }) + defer session.Kill() + + WaitForService(address) + + // Combined with test above we have positive/negative test for pprof + Expect(session.Err.Contents()).ShouldNot(ContainSubstring(magicComment)) + + session.Interrupt().Wait(2 * time.Second) + Eventually(session).Should(Exit(1)) + }) + }) +}) + +// WaitForService blocks, waiting for some service listening on given host:port +func WaitForService(address url.URL) { + // Wait for podman to be ready + var conn net.Conn + var err error + for i := 1; i <= 5; i++ { + conn, err = net.Dial("tcp", address.Host) + if err != nil { + // Podman not available yet... + time.Sleep(time.Duration(i) * time.Second) + } + } + Expect(err).ShouldNot(HaveOccurred()) + conn.Close() +} + +// randomPort leans on the go net library to find an available port... +func randomPort() string { + port, err := utils.GetRandomPort() + Expect(err).ShouldNot(HaveOccurred()) + return strconv.Itoa(port) +} diff --git a/test/e2e/volume_rm_test.go b/test/e2e/volume_rm_test.go index b979444bc..0119e0f7a 100644 --- a/test/e2e/volume_rm_test.go +++ b/test/e2e/volume_rm_test.go @@ -59,7 +59,7 @@ var _ = Describe("Podman volume rm", func() { Expect(session).Should(Exit(2)) Expect(session.ErrorToString()).To(ContainSubstring(cid)) - session = podmanTest.Podman([]string{"volume", "rm", "-f", "myvol"}) + session = podmanTest.Podman([]string{"volume", "rm", "-t", "0", "-f", "myvol"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) diff --git a/test/system/001-basic.bats b/test/system/001-basic.bats index 2e5ebe4a3..78b8ecdfd 100644 --- a/test/system/001-basic.bats +++ b/test/system/001-basic.bats @@ -93,6 +93,25 @@ function setup() { is "$output" "Error: unknown flag: --remote" "podman version --remote" } +@test "podman-remote: defaults" { + skip_if_remote "only applicable on a local run" + + # By default, podman should include '--remote' in its help output + run_podman --help + is "$output" ".* --remote " "podman --help includes the --remote option" + + # When it detects CONTAINER_HOST or _CONNECTION, --remote is not an option + CONTAINER_HOST=foobar run_podman --help + if grep -- " --remote " <<<"$output"; then + die "podman --help, with CONTAINER_HOST set, is showing --remote" + fi + + CONTAINER_CONNECTION=foobar run_podman --help + if grep -- " --remote " <<<"$output"; then + die "podman --help, with CONTAINER_CONNECTION set, is showing --remote" + fi +} + # Check that just calling "podman-remote" prints the usage message even # without a running endpoint. Use "podman --remote" for this as this works the same. @test "podman-remote: check for command usage message without a running endpoint" { diff --git a/test/system/030-run.bats b/test/system/030-run.bats index 4080f08b4..2c8d08b99 100644 --- a/test/system/030-run.bats +++ b/test/system/030-run.bats @@ -67,6 +67,11 @@ echo $rand | 0 | $rand is "$output" ".*invalidflag" "failed when passing undefined flags to the runtime" } +@test "podman run --memory=0 runtime option" { + run_podman run --memory=0 --rm $IMAGE echo hello + is "$output" "hello" "failed to run when --memory is set to 0" +} + # 'run --preserve-fds' passes a number of additional file descriptors into the container @test "podman run --preserve-fds" { skip_if_remote "preserve-fds is meaningless over remote" @@ -725,4 +730,10 @@ EOF is "$output" "Error: strconv.ParseInt: parsing \"a\": invalid syntax" } +@test "podman run closes stdin" { + random_1=$(random_string 25) + run_podman run -i --rm $IMAGE cat <<<"$random_1" + is "$output" "$random_1" "output matches STDIN" +} + # vim: filetype=sh diff --git a/test/system/035-logs.bats b/test/system/035-logs.bats index 76ce12b81..44b66676e 100644 --- a/test/system/035-logs.bats +++ b/test/system/035-logs.bats @@ -114,7 +114,7 @@ $s_after" run_podman logs --since $after test is "$output" "$s_after" - run_podman rm -f test + run_podman rm -t 1 -f test } @test "podman logs - since k8s-file" { @@ -167,7 +167,7 @@ $s_after" run_podman logs --until $after test is "$output" "$s_both" "podman logs --until after" - run_podman rm -f test + run_podman rm -t 0 -f test } @test "podman logs - until k8s-file" { @@ -195,7 +195,7 @@ function _log_test_follow() { $contentB $contentC" "logs -f on exitted container works" - run_podman rm -f $cname + run_podman rm -t 0 -f $cname } @test "podman logs - --follow k8s-file" { diff --git a/test/system/040-ps.bats b/test/system/040-ps.bats index 63f57efdc..09a0f8de1 100644 --- a/test/system/040-ps.bats +++ b/test/system/040-ps.bats @@ -138,7 +138,7 @@ EOF is "$output" "Error: container .* is mounted and cannot be removed without using force: container state improper" "podman rm <buildah container> without -f" # With -f, we can remove it. - run_podman rm -f "$cid" + run_podman rm -t 0 -f "$cid" run_podman ps --external -a is "${#lines[@]}" "1" "storage container has been removed" diff --git a/test/system/050-stop.bats b/test/system/050-stop.bats index d809507a5..e049da518 100644 --- a/test/system/050-stop.bats +++ b/test/system/050-stop.bats @@ -166,4 +166,11 @@ load helpers is "$output" "137" "Exit code of killed container" } +@test "podman stop -t 1 Generate warning" { + skip_if_remote "warning only happens on server side" + run_podman run --rm --name stopme -d $IMAGE sleep 100 + run_podman stop -t 1 stopme + is "$output" ".*StopSignal SIGTERM failed to stop container stopme in 1 seconds, resorting to SIGKILL" "stopping container should print warning" +} + # vim: filetype=sh diff --git a/test/system/055-rm.bats b/test/system/055-rm.bats index a5770f20f..7fe81c084 100644 --- a/test/system/055-rm.bats +++ b/test/system/055-rm.bats @@ -30,7 +30,7 @@ load helpers is "$output" "Error: cannot remove container $cid as it is running - running or paused containers cannot be removed without force: container state improper" "error message" # rm -f should succeed - run_podman rm -f $cid + run_podman rm -t 0 -f $cid } @test "podman rm container from storage" { @@ -70,7 +70,7 @@ load helpers # See https://github.com/containers/podman/issues/3795 @test "podman rm -f" { rand=$(random_string 30) - ( sleep 3; run_podman rm -f $rand ) & + ( sleep 3; run_podman rm -t 0 -f $rand ) & run_podman 137 run --name $rand $IMAGE sleep 30 } diff --git a/test/system/060-mount.bats b/test/system/060-mount.bats index ba37ea5e1..7addbd88e 100644 --- a/test/system/060-mount.bats +++ b/test/system/060-mount.bats @@ -125,8 +125,7 @@ load helpers run_podman exec $cid find /image-mount/etc/ # Clean up - run_podman stop -t 0 $cid - run_podman rm -f $cid + run_podman rm -t 0 -f $cid } @test "podman run --mount image inspection" { @@ -148,8 +147,7 @@ load helpers run_podman inspect --format "{{(index .Mounts 0).RW}}" $cid is "$output" "true" "inspect data includes image mount source" - run_podman stop -t 0 $cid - run_podman rm -f $cid + run_podman rm -t 0 -f $cid } @test "podman mount external container - basic test" { diff --git a/test/system/065-cp.bats b/test/system/065-cp.bats index 38c38d671..780fc6737 100644 --- a/test/system/065-cp.bats +++ b/test/system/065-cp.bats @@ -70,7 +70,7 @@ load helpers "copy into nonexistent path in container" run_podman kill cpcontainer - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer # CREATED container while read id dest dest_fullname description; do @@ -80,7 +80,7 @@ load helpers run_podman exec cpcontainer cat $dest_fullname is "$output" "${randomcontent[$id]}" "$description (cp -> ctr:$dest)" run_podman kill cpcontainer - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer done < <(parse_table "$tests") run_podman rmi -f $cpimage @@ -99,7 +99,7 @@ load helpers run_podman exec cpcontainer cat /tmp/file is "$output" "${content}" "cp to running container's tmpfs" run_podman kill cpcontainer - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer # CREATED container (with copy up) run_podman create --mount type=tmpfs,dst=/tmp --name cpcontainer $IMAGE sleep infinity @@ -108,7 +108,7 @@ load helpers run_podman exec cpcontainer cat /tmp/file is "$output" "${content}" "cp to created container's tmpfs" run_podman kill cpcontainer - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer } @@ -124,7 +124,7 @@ load helpers run_podman exec cpcontainer stat -c "%u" /tmp/hostfile is "$output" "$userid" "copied file is chowned to the container user" run_podman kill cpcontainer - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer } @test "podman cp (-a=false) file from host to container and check ownership" { @@ -143,7 +143,7 @@ load helpers run_podman exec cpcontainer stat -c "%u:%g" /tmp/a.txt is "$output" "1042:1043" "copied file retains uid/gid from the tar" run_podman kill cpcontainer - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer } @@ -160,7 +160,7 @@ load helpers run_podman cp $srcdir/hostfile cpcontainer:/tmp/hostfile run_podman cp cpcontainer:/tmp/hostfile $srcdir/hostfile1 run_podman kill cpcontainer - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer } @test "podman cp file from container to host" { @@ -206,7 +206,7 @@ load helpers rm $srcdir$dest_fullname done < <(parse_table "$tests") run_podman kill cpcontainer - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer # Created container run_podman create --name cpcontainer --workdir=/srv $cpimage @@ -219,7 +219,7 @@ load helpers is "$(< $srcdir$dest_fullname)" "${randomcontent[$id]}" "$description (cp ctr:$src to \$srcdir$dest)" rm $srcdir$dest_fullname done < <(parse_table "$tests") - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer run_podman rmi -f $cpimage } @@ -281,7 +281,7 @@ load helpers is "$output" "${randomcontent[$id]}" "$description (cp ctr:$src to /$dest)" done < <(parse_table "$tests") run_podman kill cpcontainer ${destcontainers[@]} - run_podman rm -f cpcontainer ${destcontainers[@]} + run_podman rm -t 0 -f cpcontainer ${destcontainers[@]} # From CREATED container destcontainers=() @@ -309,8 +309,7 @@ load helpers is "$output" "${randomcontent[$id]}" "$description (cp ctr:$src to /$dest)" done < <(parse_table "$tests") run_podman kill ${destcontainers[@]} - run_podman rm -f cpcontainer ${destcontainers[@]} - + run_podman rm -t 0 -f cpcontainer ${destcontainers[@]} run_podman rmi -f $cpimage } @@ -361,7 +360,7 @@ load helpers is "${lines[1]}" "${randomcontent[1]}" "$description (cp -> ctr:$dest)" done < <(parse_table "$tests") run_podman kill cpcontainer - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer # CREATED container while read src dest dest_fullname description; do @@ -376,13 +375,13 @@ load helpers is "${lines[0]}" "${randomcontent[0]}" "$description (cp -> ctr:$dest)" is "${lines[1]}" "${randomcontent[1]}" "$description (cp -> ctr:$dest)" run_podman kill cpcontainer - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer done < <(parse_table "$tests") run_podman create --name cpcontainer --workdir=/srv $cpimage sleep infinity run_podman 125 cp $srcdir cpcontainer:/etc/os-release is "$output" "Error: destination must be a directory when copying a directory" "cannot copy directory to file" - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer run_podman rmi -f $cpimage } @@ -436,7 +435,7 @@ load helpers rm -rf $destdir/* done < <(parse_table "$tests") run_podman kill cpcontainer - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer # CREATED container run_podman create --name cpcontainer --workdir=/srv $cpimage @@ -459,7 +458,7 @@ load helpers touch $destdir/testfile run_podman 125 cp cpcontainer:/etc/ $destdir/testfile is "$output" "Error: destination must be a directory when copying a directory" "cannot copy directory to file" - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer run_podman rmi -f $cpimage } @@ -526,7 +525,7 @@ ${randomcontent[1]}" "$description" ${randomcontent[1]}" "$description" done < <(parse_table "$tests") run_podman kill cpcontainer ${destcontainers[@]} - run_podman rm -f cpcontainer ${destcontainers[@]} + run_podman rm -t 0 -f cpcontainer ${destcontainers[@]} # From CREATED container destcontainers=() @@ -563,7 +562,7 @@ ${randomcontent[1]}" "$description" done < <(parse_table "$tests") run_podman kill ${destcontainers[@]} - run_podman rm -f cpcontainer ${destcontainers[@]} + run_podman rm -t 0 -f cpcontainer ${destcontainers[@]} run_podman rmi -f $cpimage } @@ -595,7 +594,7 @@ ${randomcontent[1]}" "$description" is "${lines[1]}" "${randomcontent[1]}" "eval symlink - running container" run_podman kill cpcontainer - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer run rm -rf $srcdir/dest # CREATED container @@ -604,7 +603,7 @@ ${randomcontent[1]}" "$description" run cat $destdir/dest/containerfile0 $destdir/dest/containerfile1 is "${lines[0]}" "${randomcontent[0]}" "eval symlink - created container" is "${lines[1]}" "${randomcontent[1]}" "eval symlink - created container" - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer run_podman rmi $cpimage } @@ -638,7 +637,7 @@ ${randomcontent[1]}" "$description" run ls $volume1_mount is "$output" "" - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer run_podman volume rm $volume1 $volume2 } @@ -658,7 +657,7 @@ ${randomcontent[1]}" "$description" run_podman cp $srcdir/hostfile cpcontainer:/tmp/volume/mount is "$(< $mountdir/hostfile)" "This file should be in the mount" - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer run_podman volume rm $volume } @@ -684,7 +683,7 @@ ${randomcontent[1]}" "$description" # cp no longer supports wildcarding run_podman 125 cp 'cpcontainer:/tmp/*' $dstdir - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer } @@ -708,7 +707,7 @@ ${randomcontent[1]}" "$description" # make sure there are no files in dstdir is "$(/bin/ls -1 $dstdir)" "" "incorrectly copied symlink from host" - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer } @@ -732,7 +731,7 @@ ${randomcontent[1]}" "$description" # make sure there are no files in dstdir is "$(/bin/ls -1 $dstdir)" "" "incorrectly copied symlink from host" - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer } @@ -752,7 +751,7 @@ ${randomcontent[1]}" "$description" # dstdir must be empty is "$(/bin/ls -1 $dstdir)" "" "incorrectly copied symlink from host" - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer } @@ -810,7 +809,7 @@ ${randomcontent[1]}" "$description" is "$output" "$rand_content3" "cp creates file named x" run_podman kill cpcontainer - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer } @@ -848,7 +847,7 @@ ${randomcontent[1]}" "$description" is "$output" "$rand_content" "Contents of file copied into container" run_podman kill cpcontainer - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer } @@ -897,7 +896,7 @@ ${randomcontent[1]}" "$description" is "$output" 'Error: destination must be a directory when copying from stdin' run_podman kill cpcontainer - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer } @@ -945,12 +944,12 @@ ${randomcontent[1]}" "$description" is "$(< $srcdir/tmp/empty.txt)" "" run_podman kill cpcontainer - run_podman rm -f cpcontainer + run_podman rm -t 0 -f cpcontainer } function teardown() { # In case any test fails, clean up the container we left behind - run_podman rm -f cpcontainer + run_podman rm -t 0 f cpcontainer basic_teardown } diff --git a/test/system/070-build.bats b/test/system/070-build.bats index 4e89e299a..d3dc14d81 100644 --- a/test/system/070-build.bats +++ b/test/system/070-build.bats @@ -969,7 +969,7 @@ function teardown() { # A timeout or other error in 'build' can leave behind stale images # that podman can't even see and which will cascade into subsequent # test failures. Try a last-ditch force-rm in cleanup, ignoring errors. - run_podman '?' rm -a -f + run_podman '?' rm -t 0 -a -f run_podman '?' rmi -f build_test # Many of the tests above leave interim layers behind. Clean them up. diff --git a/test/system/075-exec.bats b/test/system/075-exec.bats index de767a1e8..42954e5ec 100644 --- a/test/system/075-exec.bats +++ b/test/system/075-exec.bats @@ -53,7 +53,7 @@ load helpers is "$(check_exec_pid)" "" "there isn't any exec pid hash file leak" run_podman stop --time 1 $cid - run_podman rm -f $cid + run_podman rm -t 0 -f $cid } # Issue #4785 - piping to exec statement - fixed in #4818 @@ -126,7 +126,7 @@ load helpers is "$output" "" "exec output is identical with the file" # Clean up - run_podman rm -f $cid + run_podman rm -t 0 -f $cid } # vim: filetype=sh diff --git a/test/system/080-pause.bats b/test/system/080-pause.bats index 2314324a9..857c8bbf4 100644 --- a/test/system/080-pause.bats +++ b/test/system/080-pause.bats @@ -48,8 +48,7 @@ load helpers # would imply that the container never paused. is "$max_delta" "[3456]" "delta t between paused and restarted" - run_podman stop -t 0 $cname - run_podman rm -f $cname + run_podman rm -t 0 -f $cname # Pause/unpause on nonexistent name or id - these should all fail run_podman 125 pause $cid @@ -75,7 +74,7 @@ load helpers run_podman ps --format '{{.ID}} {{.Names}} {{.Status}}' is "$output" "${cid:0:12} $cname Up.*" "podman ps on resumed container" run_podman stop -t 0 $cname - run_podman rm -f $cname - run_podman rm -f notrunning + run_podman rm -t 0 -f $cname + run_podman rm -t 0 -f notrunning } # vim: filetype=sh diff --git a/test/system/125-import.bats b/test/system/125-import.bats index 5b8d84a2f..743da85b2 100644 --- a/test/system/125-import.bats +++ b/test/system/125-import.bats @@ -15,7 +15,7 @@ load helpers run_podman run --name import $IMAGE sh -c "echo ${random_content} > /random.txt" run_podman export import -o $archive - run_podman rm -f import + run_podman rm -t 0 -f import # Simple import run_podman import -q $archive @@ -71,7 +71,7 @@ EOF # Export built container as tarball run_podman export -o $PODMAN_TMPDIR/$b_cnt.tar $b_cnt - run_podman rm -f $b_cnt + run_podman rm -t 0 -f $b_cnt # Modify tarball contents tar --delete -f $PODMAN_TMPDIR/$b_cnt.tar tmp/testfile1 @@ -102,7 +102,7 @@ EOF run_podman ps -a --filter name=$a_cnt --format '{{.Status}}' is "$output" "Exited (33) .*" "Exit by non-TERM/KILL" - run_podman rm -f $a_cnt + run_podman rm -t 0 -f $a_cnt run_podman rmi $b_img $a_img } diff --git a/test/system/150-login.bats b/test/system/150-login.bats index c003a0409..33b8438bf 100644 --- a/test/system/150-login.bats +++ b/test/system/150-login.bats @@ -83,7 +83,7 @@ function setup() { fi # Run the registry container. - run_podman '?' ${PODMAN_LOGIN_ARGS} rm -f registry + run_podman '?' ${PODMAN_LOGIN_ARGS} rm -t 0 -f registry run_podman ${PODMAN_LOGIN_ARGS} run -d \ -p ${PODMAN_LOGIN_REGISTRY_PORT}:5000 \ --name registry \ diff --git a/test/system/160-volumes.bats b/test/system/160-volumes.bats index c02525e0d..43462de36 100644 --- a/test/system/160-volumes.bats +++ b/test/system/160-volumes.bats @@ -13,7 +13,7 @@ function setup() { function teardown() { run_podman '?' rm -a --volumes - run_podman '?' volume rm -a -f + run_podman '?' volume rm -t 0 -a -f basic_teardown } @@ -97,6 +97,14 @@ Labels.l | $mylabel run_podman volume rm $myvolume } +# Removing volumes with --force +@test "podman volume rm --force" { + run_podman run -d --volume myvol:/myvol $IMAGE top + cid=$output + run_podman 2 volume rm myvol + is "$output" "Error: volume myvol is being used by the following container(s): $cid: volume is being used" "should error since container is running" + run_podman volume rm myvol --force +} # Running scripts (executables) from a volume @test "podman volume: exec/noexec" { @@ -202,6 +210,36 @@ EOF run_podman volume rm my_vol2 } +# Podman volume user test +@test "podman volume user test" { + is_rootless || skip "only meaningful when run rootless" + user="1000:2000" + newuser="100:200" + tmpdir=${PODMAN_TMPDIR}/volume_$(random_string) + mkdir $tmpdir + touch $tmpdir/test1 + + run_podman run --name user --user $user -v $tmpdir:/data:U $IMAGE stat -c "%u:%g" /data + is "$output" "$user" "user should be changed" + + # Now chown the source directory and make sure recursive chown happens + run_podman unshare chown -R $newuser $tmpdir + run_podman start --attach user + is "$output" "$user" "user should be the same" + + # Now chown the file in source directory and make sure recursive chown + # doesn't happen + run_podman unshare chown -R $newuser $tmpdir/test1 + run_podman start --attach user + is "$output" "$user" "user should be the same" + # test1 should still be chowned to $newuser + run_podman unshare stat -c "%u:%g" $tmpdir/test1 + is "$output" "$newuser" "user should not be changed" + + run_podman unshare rm $tmpdir/test1 + run_podman rm user +} + # Confirm that container sees the correct id @test "podman volume with --userns=keep-id" { diff --git a/test/system/200-pod.bats b/test/system/200-pod.bats index 027abf9dc..86f3610ab 100644 --- a/test/system/200-pod.bats +++ b/test/system/200-pod.bats @@ -4,8 +4,8 @@ load helpers # This is a long ugly way to clean up pods and remove the pause image function teardown() { - run_podman pod rm -f -a - run_podman rm -f -a + run_podman pod rm -f -t 0 -a + run_podman rm -f -t 0 -a run_podman image list --format '{{.ID}} {{.Repository}}' while read id name; do if [[ "$name" =~ /pause ]]; then @@ -57,7 +57,7 @@ function teardown() { fi # Clean up - run_podman pod rm -f $podid + run_podman pod rm -f -t 0 $podid } @@ -301,7 +301,7 @@ EOF # Clean up run_podman rm $cid - run_podman pod rm -f mypod + run_podman pod rm -t 0 -f mypod run_podman rmi $infra_image } diff --git a/test/system/220-healthcheck.bats b/test/system/220-healthcheck.bats index 28fe8eb92..1d4a2ea7e 100644 --- a/test/system/220-healthcheck.bats +++ b/test/system/220-healthcheck.bats @@ -108,8 +108,7 @@ Log[-1].Output | is "$output" "unhealthy" "output from 'podman healthcheck run'" # Clean up - run_podman stop -t 0 healthcheck_c - run_podman rm -f healthcheck_c + run_podman rm -t 0 -f healthcheck_c run_podman rmi healthcheck_i } diff --git a/test/system/250-systemd.bats b/test/system/250-systemd.bats index 4578d9e60..98241c309 100644 --- a/test/system/250-systemd.bats +++ b/test/system/250-systemd.bats @@ -153,10 +153,10 @@ function service_cleanup() { cname3=$(random_string) run_podman create --restart=on-failure:42 --name $cname3 $IMAGE run_podman generate systemd --new $cname3 - is "$output" ".*Restart=on-failure.*" "on-failure:xx is parsed correclty" + is "$output" ".*Restart=on-failure.*" "on-failure:xx is parsed correctly" is "$output" ".*StartLimitBurst=42.*" "on-failure:xx is parsed correctly" - run_podman rm -f $cname $cname2 $cname3 + run_podman rm -t 0 -f $cname $cname2 $cname3 } function set_listen_env() { diff --git a/test/system/255-auto-update.bats b/test/system/255-auto-update.bats index bb4b5c13f..99211f304 100644 --- a/test/system/255-auto-update.bats +++ b/test/system/255-auto-update.bats @@ -78,7 +78,7 @@ function generate_service() { (cd $UNIT_DIR; run_podman generate systemd --new --files --name $cname) echo "container-$cname" >> $SNAME_FILE - run_podman rm -f $cname + run_podman rm -t 0 -f $cname systemctl daemon-reload systemctl start container-$cname diff --git a/test/system/270-socket-activation.bats b/test/system/270-socket-activation.bats index dd439d3ae..6d582be18 100644 --- a/test/system/270-socket-activation.bats +++ b/test/system/270-socket-activation.bats @@ -8,14 +8,16 @@ load helpers.systemd SERVICE_NAME="podman_test_$(random_string)" -SERVICE_SOCK_ADDR="/run/podman/podman.sock" +SERVICE_SOCK_ADDR="/run/podman/$SERVICE_NAME.sock" if is_rootless; then - SERVICE_SOCK_ADDR="$XDG_RUNTIME_DIR/podman/podman.sock" + SERVICE_SOCK_ADDR="$XDG_RUNTIME_DIR/podman/$SERVICE_NAME.sock" fi SERVICE_FILE="$UNIT_DIR/$SERVICE_NAME.service" SOCKET_FILE="$UNIT_DIR/$SERVICE_NAME.socket" +# URL to use for ping +_PING=http://placeholder-hostname/libpod/_ping function setup() { skip_if_remote "systemd tests are meaningless over remote" @@ -25,8 +27,8 @@ function setup() { cat > $SERVICE_FILE <<EOF [Unit] Description=Podman API Service -Requires=podman.socket -After=podman.socket +Requires=$SERVICE_NAME.socket +After=$SERVICE_NAME.socket Documentation=man:podman-system-service(1) StartLimitIntervalSec=0 @@ -42,7 +44,7 @@ Description=Podman API Socket Documentation=man:podman-system-service(1) [Socket] -ListenStream=%t/podman/podman.sock +ListenStream=%t/podman/$SERVICE_NAME.sock SocketMode=0660 [Install] @@ -51,10 +53,10 @@ EOF # ensure pause die before each test runs if is_rootless; then - local pause_pid="$XDG_RUNTIME_DIR/libpod/tmp/pause.pid" - if [ -f $pause_pid ]; then - kill -9 $(cat $pause_pid) 2> /dev/null - rm -f $pause_pid + local pause_pid_file="$XDG_RUNTIME_DIR/libpod/tmp/pause.pid" + if [ -f $pause_pid_file ]; then + kill -9 $(< $pause_pid_file) 2> /dev/null + rm -f $pause_pid_file fi fi systemctl start "$SERVICE_NAME.socket" @@ -68,7 +70,9 @@ function teardown() { } @test "podman system service - socket activation - no container" { - run curl -s --max-time 3 --unix-socket $SERVICE_SOCK_ADDR http://podman/libpod/_ping + run curl -s --max-time 3 --unix-socket $SERVICE_SOCK_ADDR $_PING + echo "curl output: $output" + is "$status" "0" "curl exit status" is "$output" "OK" "podman service responds normally" } @@ -76,29 +80,36 @@ function teardown() { run_podman run -d $IMAGE sleep 90 cid="$output" - run curl -s --max-time 3 --unix-socket $SERVICE_SOCK_ADDR http://podman/libpod/_ping + run curl -s --max-time 3 --unix-socket $SERVICE_SOCK_ADDR $_PING + echo "curl output: $output" + is "$status" "0" "curl exit status" is "$output" "OK" "podman service responds normally" - run_podman stop -t 0 $cid - run_podman rm -f $cid + run_podman rm -f -t 0 $cid } @test "podman system service - socket activation - kill rootless pause" { if ! is_rootless; then - skip "root podman no need pause process" + skip "there is no pause process when running rootful" fi run_podman run -d $IMAGE sleep 90 cid="$output" - local pause_pid="$XDG_RUNTIME_DIR/libpod/tmp/pause.pid" - if [ -f $pause_pid ]; then - kill -9 $(cat $pause_pid) 2> /dev/null + local pause_pid_file="$XDG_RUNTIME_DIR/libpod/tmp/pause.pid" + if [ ! -f $pause_pid_file ]; then + # This seems unlikely, but not impossible + die "Pause pid file does not exist: $pause_pid_file" fi - run curl -s --max-time 3 --unix-socket $SERVICE_SOCK_ADDR http://podman/libpod/_ping + + echo "kill -9 $(< pause_pid_file)" + kill -9 $(< $pause_pid_file) + + run curl -s --max-time 3 --unix-socket $SERVICE_SOCK_ADDR $_PING + echo "curl output: $output" + is "$status" "0" "curl exit status" is "$output" "OK" "podman service responds normally" - run_podman stop -t 0 $cid - run_podman rm -f $cid + run_podman rm -f -t 0 $cid } # vim: filetype=sh diff --git a/test/system/271-tcp-cors-server.bats b/test/system/271-tcp-cors-server.bats index d8e4eb3df..be77afd40 100644 --- a/test/system/271-tcp-cors-server.bats +++ b/test/system/271-tcp-cors-server.bats @@ -38,7 +38,7 @@ SOCKET_FILE="$UNIT_DIR/$SERVICE_NAME.socket" @test "podman system service - CORS enabled in logs" { skip_if_remote "system service tests are meaningless over remote" run_podman system service --log-level="debug" --cors="*" -t 1 - is "$output" ".*CORS Headers were set to \*.*" "debug log confirms CORS headers set" + is "$output" ".*CORS Headers were set to ..\*...*" "debug log confirms CORS headers set" } # vim: filetype=sh diff --git a/test/system/410-selinux.bats b/test/system/410-selinux.bats index 7f7f23000..dbdfd4b9d 100644 --- a/test/system/410-selinux.bats +++ b/test/system/410-selinux.bats @@ -27,9 +27,9 @@ function check_label() { is "$type" "$1" "SELinux type" if [ -n "$2" ]; then - # e.g. from the above example -> "s0:c45,c745" - range=$(cut -d: -f4,5 <<<"$context") - is "$range" "$2^@" "SELinux range" + # e.g. from the above example -> "s0:c45,c745" + range=$(cut -d: -f4,5 <<<"$context") + is "$range" "$2^@" "SELinux range" fi } @@ -66,9 +66,9 @@ function check_label() { # FIXME this test fails when run rootless with runc: # Error: container_linux.go:367: starting container process caused: process_linux.go:495: container init caused: readonly path /proc/asound: operation not permitted: OCI permission denied if is_rootless; then - runtime=$(podman_runtime) - test "$runtime" == "crun" \ - || skip "runtime is $runtime; this test requires crun" + runtime=$(podman_runtime) + test "$runtime" == "crun" \ + || skip "runtime is $runtime; this test requires crun" fi check_label "--pid=host" "spc_t" @@ -96,16 +96,16 @@ function check_label() { skip_if_no_selinux run_podman run -d --name myc \ - --security-opt seccomp=unconfined \ - --security-opt label=type:spc_t \ - --security-opt label=level:s0 \ - $IMAGE sh -c 'while test ! -e /stop; do sleep 0.1; done' + --security-opt seccomp=unconfined \ + --security-opt label=type:spc_t \ + --security-opt label=level:s0 \ + $IMAGE sh -c 'while test ! -e /stop; do sleep 0.1; done' run_podman inspect --format='{{ .HostConfig.SecurityOpt }}' myc is "$output" "[label=type:spc_t,label=level:s0 seccomp=unconfined]" \ "'podman inspect' preserves all --security-opts" run_podman exec myc touch /stop - run_podman rm -f myc + run_podman rm -t 0 -f myc } # Sharing context between two containers not in a pod @@ -118,7 +118,7 @@ function check_label() { skip_if_rootless_cgroupsv1 if [[ $(podman_runtime) == "runc" ]]; then - skip "some sort of runc bug, not worth fixing (#11784)" + skip "some sort of runc bug, not worth fixing (#11784)" fi run_podman run -d --name myctr $IMAGE top @@ -136,7 +136,7 @@ function check_label() { # net NS: do not share context run_podman run --rm --net container:myctr $IMAGE cat -v /proc/self/attr/current if [[ "$output" = "$context_c1" ]]; then - die "run --net : context ($output) is same as running container (it should not be)" + die "run --net : context ($output) is same as running container (it should not be)" fi # The 'myctr2' above was not run with --rm, so it still exists, and @@ -158,8 +158,8 @@ function check_label() { # We don't need a fullblown pause container; avoid pulling the k8s one run_podman pod create --name myselinuxpod \ - --infra-image $IMAGE \ - --infra-command /home/podman/pause + --infra-image $IMAGE \ + --infra-command /home/podman/pause # Get baseline run_podman run --rm --pod myselinuxpod $IMAGE cat -v /proc/self/attr/current @@ -190,7 +190,7 @@ function check_label() { # Even after #7902, labels (':c123,c456') should be different run_podman run --rm --pod myselinuxpod $IMAGE cat -v /proc/self/attr/current if [[ "$output" = "$context_c1" ]]; then - die "context ($output) is the same on two separate containers, it should have been different" + die "context ($output) is the same on two separate containers, it should have been different" fi run_podman pod rm myselinuxpod @@ -203,12 +203,12 @@ function check_label() { # runc and crun emit different diagnostics runtime=$(podman_runtime) case "$runtime" in - # crun 0.20.1 changes the error message - # from /proc/thread-self/attr/exec`: .* unable to assign - # to /proc/self/attr/keycreate`: .* unable to process - crun) expect="\`/proc/.*\`: OCI runtime error: unable to \(assign\|process\) security attribute" ;; - runc) expect="OCI runtime error: .*: failed to set /proc/self/attr/keycreate on procfs" ;; - *) skip "Unknown runtime '$runtime'";; + # crun 0.20.1 changes the error message + # from /proc/thread-self/attr/exec`: .* unable to assign + # to /proc/self/attr/keycreate`: .* unable to process + crun) expect="\`/proc/.*\`: OCI runtime error: unable to \(assign\|process\) security attribute" ;; + runc) expect="OCI runtime error: .*: failed to set /proc/self/attr/keycreate on procfs" ;; + *) skip "Unknown runtime '$runtime'";; esac # The '.*' in the error below is for dealing with podman-remote, which @@ -223,7 +223,7 @@ function check_label() { LABEL="system_u:object_r:tmp_t:s0" RELABEL="system_u:object_r:container_file_t:s0" tmpdir=$PODMAN_TMPDIR/vol - touch $tmpdir + mkdir -p $tmpdir chcon -vR ${LABEL} $tmpdir ls -Z $tmpdir @@ -239,12 +239,36 @@ function check_label() { run ls -dZ $tmpdir is "$output" "${RELABEL} $tmpdir" "Privileged Relabel Correctly" - run_podman run -v $tmpdir:/test:Z $IMAGE cat /proc/self/attr/current + run_podman run --name label -v $tmpdir:/test:Z $IMAGE cat /proc/self/attr/current level=$(secon -l $output) run ls -dZ $tmpdir is "$output" "system_u:object_r:container_file_t:$level $tmpdir" \ "Confined Relabel Correctly" + if is_rootless; then + run_podman unshare touch $tmpdir/test1 + # Relabel entire directory + run_podman unshare chcon system_u:object_r:usr_t:s0 $tmpdir + run_podman start --attach label + newlevel=$(secon -l $output) + is "$level" "$newlevel" "start should relabel with same SELinux labels" + run ls -dZ $tmpdir + is "$output" "system_u:object_r:container_file_t:$level $tmpdir" \ + "Confined Relabel Correctly" + run ls -dZ $tmpdir/test1 + is "$output" "system_u:object_r:container_file_t:$level $tmpdir/test1" \ + "Start did not Relabel" + + # Relabel only file in subdir + run_podman unshare chcon system_u:object_r:usr_t:s0 $tmpdir/test1 + run_podman start --attach label + newlevel=$(secon -l $output) + is "$level" "$newlevel" "start should use same SELinux labels" + + run ls -dZ $tmpdir/test1 + is "$output" "system_u:object_r:usr_t:s0 $tmpdir/test1" \ + "Start did not Relabel" + fi run_podman run -v $tmpdir:/test:z $IMAGE cat /proc/self/attr/current run ls -dZ $tmpdir is "$output" "${RELABEL} $tmpdir" "Shared Relabel Correctly" diff --git a/test/system/450-interactive.bats b/test/system/450-interactive.bats index 55c2afcd4..b817fe1a7 100644 --- a/test/system/450-interactive.bats +++ b/test/system/450-interactive.bats @@ -61,7 +61,7 @@ function teardown() { run_podman run -it --name mystty $IMAGE stty size <$PODMAN_TEST_PTY is "$output" "$rows $cols$CR" "stty under podman run reads the correct dimensions" - run_podman rm -f mystty + run_podman rm -t 0 -f mystty # FIXME: the checks below are flaking a lot (see #10710). @@ -70,7 +70,7 @@ function teardown() { # run_podman exec -it mystty stty size <$PODMAN_TEST_PTY # is "$output" "$rows $cols" "stty under podman exec reads the correct dimensions" # -# run_podman rm -f mystty +# run_podman rm -t 0 -f mystty } diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats index 29fa309f3..cb73cf24d 100644 --- a/test/system/500-networking.bats +++ b/test/system/500-networking.bats @@ -192,9 +192,9 @@ load helpers is "$output" "Error: network name $mynetname already used: network already exists" \ "Trying to create an already-existing network" - run_podman rm $cid + run_podman rm -t 0 -f $cid run_podman network rm $mynetname - run_podman 1 network rm $mynetname + run_podman 1 network rm -f $mynetname } @test "podman network reload" { @@ -293,13 +293,13 @@ load helpers is "$output" "$random_1" "curl 127.0.0.1:/index.txt" # cleanup the container - run_podman rm -f $cid + run_podman rm -t 0 -f $cid # test that we cannot remove the default network - run_podman 125 network rm -f $netname + run_podman 125 network rm -t 0 -f $netname is "$output" "Error: default network $netname cannot be removed" "Remove default network" - run_podman network rm -f $netname2 + run_podman network rm -t 0 -f $netname2 } @test "podman rootless cni adds /usr/sbin to PATH" { @@ -314,7 +314,7 @@ load helpers PATH=/usr/local/bin:/usr/bin run_podman run --rm --network $mynetname $IMAGE ip addr is "$output" ".*eth0.*" "Interface eth0 not found in ip addr output" - run_podman network rm -f $mynetname + run_podman network rm -t 0 -f $mynetname } @test "podman ipv6 in /etc/resolv.conf" { @@ -357,7 +357,7 @@ load helpers die "resolv.conf contains a ipv6 nameserver" fi - run_podman network rm -f $netname + run_podman network rm -t 0 -f $netname # ipv6 cni mysubnet=fd00:4:4:4:4::/64 @@ -372,7 +372,7 @@ load helpers die "resolv.conf does not contain a ipv6 nameserver" fi - run_podman network rm -f $netname + run_podman network rm -t 0 -f $netname } # Test for https://github.com/containers/podman/issues/10052 @@ -463,9 +463,8 @@ load helpers is "$output" "$random_1" "curl 127.0.0.1:/index.txt should still work" # cleanup - run_podman stop -t 0 $cid $background_cid - run_podman rm -f $cid $background_cid - run_podman network rm -f $netname $netname2 + run_podman rm -t 0 -f $cid $background_cid + run_podman network rm -t 0 -f $netname $netname2 } @test "podman network after restart" { @@ -538,12 +537,11 @@ load helpers run curl --retry 2 -s $SERVER/index.txt is "$output" "$random_1" "curl 127.0.0.1:/index.txt after podman restart" - run_podman stop -t 0 $cid - run_podman rm -f $cid + run_podman rm -t 0 -f $cid done # Cleanup network - run_podman network rm $netname + run_podman network rm -t 0 -f $netname } # vim: filetype=sh diff --git a/test/system/600-completion.bats b/test/system/600-completion.bats index 5f4610e9e..ac934732e 100644 --- a/test/system/600-completion.bats +++ b/test/system/600-completion.bats @@ -299,7 +299,7 @@ function _check_completion_end() { run_podman image untag $IMAGE $random_image_name:$random_image_tag for state in created running degraded exited; do - run_podman pod rm --force $state-$random_pod_name + run_podman pod rm -t 0 --force $state-$random_pod_name done for state in created running pause exited; do diff --git a/test/system/700-play.bats b/test/system/700-play.bats index f41c50e4c..8cf279ada 100644 --- a/test/system/700-play.bats +++ b/test/system/700-play.bats @@ -7,8 +7,8 @@ load helpers # This is a long ugly way to clean up pods and remove the pause image function teardown() { - run_podman pod rm -f -a - run_podman rm -f -a + run_podman pod rm -t 0 -f -a + run_podman rm -t 0 -f -a run_podman image list --format '{{.ID}} {{.Repository}}' while read id name; do if [[ "$name" =~ /pause ]]; then @@ -77,8 +77,7 @@ RELABEL="system_u:object_r:container_file_t:s0" fi run_podman stop -a -t 0 - run_podman pod stop test_pod - run_podman pod rm -f test_pod + run_podman pod rm -t 0 -f test_pod } @test "podman play" { @@ -92,8 +91,7 @@ RELABEL="system_u:object_r:container_file_t:s0" fi run_podman stop -a -t 0 - run_podman pod stop test_pod - run_podman pod rm -f test_pod + run_podman pod rm -t 0 -f test_pod } @test "podman play --network" { @@ -111,8 +109,7 @@ RELABEL="system_u:object_r:container_file_t:s0" is "$output" "slirp4netns" "network mode slirp4netns is set for the container" run_podman stop -a -t 0 - run_podman pod stop test_pod - run_podman pod rm -f test_pod + run_podman pod rm -t 0 -f test_pod run_podman play kube --network none $PODMAN_TMPDIR/test.yaml run_podman pod inspect --format {{.InfraContainerID}} "${lines[1]}" @@ -121,8 +118,7 @@ RELABEL="system_u:object_r:container_file_t:s0" is "$output" "none" "network mode none is set for the container" run_podman stop -a -t 0 - run_podman pod stop test_pod - run_podman pod rm -f test_pod + run_podman pod rm -t 0 -f test_pod } @test "podman play with user from image" { @@ -165,7 +161,6 @@ _EOF is "$output" bin "expect container within pod to run as the bin user" run_podman stop -a -t 0 - run_podman pod stop test_pod - run_podman pod rm -f test_pod + run_podman pod rm -t 0 -f test_pod run_podman rmi -f userimage:latest } diff --git a/test/system/helpers.bash b/test/system/helpers.bash index 03e1ab82b..97b6db05c 100644 --- a/test/system/helpers.bash +++ b/test/system/helpers.bash @@ -56,7 +56,7 @@ fi # Setup helper: establish a test environment with exactly the images needed function basic_setup() { # Clean up all containers - run_podman rm --all --force + run_podman rm -t 0 --all --force # ...including external (buildah) ones run_podman ps --all --external --format '{{.ID}} {{.Names}}' @@ -109,8 +109,8 @@ function basic_setup() { # Basic teardown: remove all pods and containers function basic_teardown() { echo "# [teardown]" >&2 - run_podman '?' pod rm --all --force - run_podman '?' rm --all --force + run_podman '?' pod rm -t 0 --all --force + run_podman '?' rm -t 0 --all --force command rm -rf $PODMAN_TMPDIR } diff --git a/test/test_podman_baseline.sh b/test/test_podman_baseline.sh index 3624d24c2..5a420fe60 100755 --- a/test/test_podman_baseline.sh +++ b/test/test_podman_baseline.sh @@ -553,7 +553,7 @@ podman build -f Dockerfile -t build-priv ######## # Cleanup ######## -podman rm -a -f +podman rm -a -f -t 0 podman rmi -a -f rm ./Dockerfile diff --git a/test/test_podman_pods.sh b/test/test_podman_pods.sh index c19f4fcab..cd72fce7c 100755 --- a/test/test_podman_pods.sh +++ b/test/test_podman_pods.sh @@ -114,4 +114,4 @@ podman pod kill foobar ######## # Remove all pods and their containers ######## -podman pod rm -fa +podman pod rm -t 0 -fa diff --git a/troubleshooting.md b/troubleshooting.md index a6c014625..b335eaaa8 100644 --- a/troubleshooting.md +++ b/troubleshooting.md @@ -875,7 +875,7 @@ def signal_listener(): sys.exit(0) except Exception as e: loop.quit() - sys.stderr.write(f"Error occured {e}") + sys.stderr.write(f"Error occurred {e}") sys.exit(1) if __name__ == "__main__": diff --git a/vendor/github.com/bits-and-blooms/bitset/.gitignore b/vendor/github.com/bits-and-blooms/bitset/.gitignore deleted file mode 100644 index 5c204d28b..000000000 --- a/vendor/github.com/bits-and-blooms/bitset/.gitignore +++ /dev/null @@ -1,26 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.prof - -target diff --git a/vendor/github.com/bits-and-blooms/bitset/.travis.yml b/vendor/github.com/bits-and-blooms/bitset/.travis.yml deleted file mode 100644 index 094aa5ce0..000000000 --- a/vendor/github.com/bits-and-blooms/bitset/.travis.yml +++ /dev/null @@ -1,37 +0,0 @@ -language: go - -sudo: false - -branches: - except: - - release - -branches: - only: - - master - - travis - -go: - - "1.11.x" - - tip - -matrix: - allow_failures: - - go: tip - -before_install: - - if [ -n "$GH_USER" ]; then git config --global github.user ${GH_USER}; fi; - - if [ -n "$GH_TOKEN" ]; then git config --global github.token ${GH_TOKEN}; fi; - - go get github.com/mattn/goveralls - -before_script: - - make deps - -script: - - make qa - -after_failure: - - cat ./target/test/report.xml - -after_success: - - if [ "$TRAVIS_GO_VERSION" = "1.11.1" ]; then $HOME/gopath/bin/goveralls -covermode=count -coverprofile=target/report/coverage.out -service=travis-ci; fi; diff --git a/vendor/github.com/bits-and-blooms/bitset/LICENSE b/vendor/github.com/bits-and-blooms/bitset/LICENSE deleted file mode 100644 index 59cab8a93..000000000 --- a/vendor/github.com/bits-and-blooms/bitset/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2014 Will Fitzgerald. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/bits-and-blooms/bitset/README.md b/vendor/github.com/bits-and-blooms/bitset/README.md deleted file mode 100644 index 97e83071e..000000000 --- a/vendor/github.com/bits-and-blooms/bitset/README.md +++ /dev/null @@ -1,93 +0,0 @@ -# bitset - -*Go language library to map between non-negative integers and boolean values* - -[![Test](https://github.com/bits-and-blooms/bitset/workflows/Test/badge.svg)](https://github.com/willf/bitset/actions?query=workflow%3ATest) -[![Go Report Card](https://goreportcard.com/badge/github.com/willf/bitset)](https://goreportcard.com/report/github.com/willf/bitset) -[![PkgGoDev](https://pkg.go.dev/badge/github.com/bits-and-blooms/bitset?tab=doc)](https://pkg.go.dev/github.com/bits-and-blooms/bitset?tab=doc) - - -## Description - -Package bitset implements bitsets, a mapping between non-negative integers and boolean values. -It should be more efficient than map[uint] bool. - -It provides methods for setting, clearing, flipping, and testing individual integers. - -But it also provides set intersection, union, difference, complement, and symmetric operations, as well as tests to check whether any, all, or no bits are set, and querying a bitset's current length and number of positive bits. - -BitSets are expanded to the size of the largest set bit; the memory allocation is approximately Max bits, where Max is the largest set bit. BitSets are never shrunk. On creation, a hint can be given for the number of bits that will be used. - -Many of the methods, including Set, Clear, and Flip, return a BitSet pointer, which allows for chaining. - -### Example use: - -```go -package main - -import ( - "fmt" - "math/rand" - - "github.com/bits-and-blooms/bitset" -) - -func main() { - fmt.Printf("Hello from BitSet!\n") - var b bitset.BitSet - // play some Go Fish - for i := 0; i < 100; i++ { - card1 := uint(rand.Intn(52)) - card2 := uint(rand.Intn(52)) - b.Set(card1) - if b.Test(card2) { - fmt.Println("Go Fish!") - } - b.Clear(card1) - } - - // Chaining - b.Set(10).Set(11) - - for i, e := b.NextSet(0); e; i, e = b.NextSet(i + 1) { - fmt.Println("The following bit is set:", i) - } - if b.Intersection(bitset.New(100).Set(10)).Count() == 1 { - fmt.Println("Intersection works.") - } else { - fmt.Println("Intersection doesn't work???") - } -} -``` - -As an alternative to BitSets, one should check out the 'big' package, which provides a (less set-theoretical) view of bitsets. - -Package documentation is at: https://pkg.go.dev/github.com/bits-and-blooms/bitset?tab=doc - -## Memory Usage - -The memory usage of a bitset using N bits is at least N/8 bytes. The number of bits in a bitset is at least as large as one plus the greatest bit index you have accessed. Thus it is possible to run out of memory while using a bitset. If you have lots of bits, you might prefer compressed bitsets, like the [Roaring bitmaps](http://roaringbitmap.org) and its [Go implementation](https://github.com/RoaringBitmap/roaring). - -## Implementation Note - -Go 1.9 introduced a native `math/bits` library. We provide backward compatibility to Go 1.7, which might be removed. - -It is possible that a later version will match the `math/bits` return signature for counts (which is `int`, rather than our library's `unit64`). If so, the version will be bumped. - -## Installation - -```bash -go get github.com/bits-and-blooms/bitset -``` - -## Contributing - -If you wish to contribute to this project, please branch and issue a pull request against master ("[GitHub Flow](https://guides.github.com/introduction/flow/)") - -## Running all tests - -Before committing the code, please check if it passes tests, has adequate coverage, etc. -```bash -go test -go test -cover -``` diff --git a/vendor/github.com/bits-and-blooms/bitset/azure-pipelines.yml b/vendor/github.com/bits-and-blooms/bitset/azure-pipelines.yml deleted file mode 100644 index f9b295918..000000000 --- a/vendor/github.com/bits-and-blooms/bitset/azure-pipelines.yml +++ /dev/null @@ -1,39 +0,0 @@ -# Go -# Build your Go project. -# Add steps that test, save build artifacts, deploy, and more: -# https://docs.microsoft.com/azure/devops/pipelines/languages/go - -trigger: -- master - -pool: - vmImage: 'Ubuntu-16.04' - -variables: - GOBIN: '$(GOPATH)/bin' # Go binaries path - GOROOT: '/usr/local/go1.11' # Go installation path - GOPATH: '$(system.defaultWorkingDirectory)/gopath' # Go workspace path - modulePath: '$(GOPATH)/src/github.com/$(build.repository.name)' # Path to the module's code - -steps: -- script: | - mkdir -p '$(GOBIN)' - mkdir -p '$(GOPATH)/pkg' - mkdir -p '$(modulePath)' - shopt -s extglob - shopt -s dotglob - mv !(gopath) '$(modulePath)' - echo '##vso[task.prependpath]$(GOBIN)' - echo '##vso[task.prependpath]$(GOROOT)/bin' - displayName: 'Set up the Go workspace' - -- script: | - go version - go get -v -t -d ./... - if [ -f Gopkg.toml ]; then - curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh - dep ensure - fi - go build -v . - workingDirectory: '$(modulePath)' - displayName: 'Get dependencies, then build' diff --git a/vendor/github.com/bits-and-blooms/bitset/bitset.go b/vendor/github.com/bits-and-blooms/bitset/bitset.go deleted file mode 100644 index d688806a5..000000000 --- a/vendor/github.com/bits-and-blooms/bitset/bitset.go +++ /dev/null @@ -1,952 +0,0 @@ -/* -Package bitset implements bitsets, a mapping -between non-negative integers and boolean values. It should be more -efficient than map[uint] bool. - -It provides methods for setting, clearing, flipping, and testing -individual integers. - -But it also provides set intersection, union, difference, -complement, and symmetric operations, as well as tests to -check whether any, all, or no bits are set, and querying a -bitset's current length and number of positive bits. - -BitSets are expanded to the size of the largest set bit; the -memory allocation is approximately Max bits, where Max is -the largest set bit. BitSets are never shrunk. On creation, -a hint can be given for the number of bits that will be used. - -Many of the methods, including Set,Clear, and Flip, return -a BitSet pointer, which allows for chaining. - -Example use: - - import "bitset" - var b BitSet - b.Set(10).Set(11) - if b.Test(1000) { - b.Clear(1000) - } - if B.Intersection(bitset.New(100).Set(10)).Count() > 1 { - fmt.Println("Intersection works.") - } - -As an alternative to BitSets, one should check out the 'big' package, -which provides a (less set-theoretical) view of bitsets. - -*/ -package bitset - -import ( - "bufio" - "bytes" - "encoding/base64" - "encoding/binary" - "encoding/json" - "errors" - "fmt" - "io" - "strconv" -) - -// the wordSize of a bit set -const wordSize = uint(64) - -// log2WordSize is lg(wordSize) -const log2WordSize = uint(6) - -// allBits has every bit set -const allBits uint64 = 0xffffffffffffffff - -// default binary BigEndian -var binaryOrder binary.ByteOrder = binary.BigEndian - -// default json encoding base64.URLEncoding -var base64Encoding = base64.URLEncoding - -// Base64StdEncoding Marshal/Unmarshal BitSet with base64.StdEncoding(Default: base64.URLEncoding) -func Base64StdEncoding() { base64Encoding = base64.StdEncoding } - -// LittleEndian Marshal/Unmarshal Binary as Little Endian(Default: binary.BigEndian) -func LittleEndian() { binaryOrder = binary.LittleEndian } - -// A BitSet is a set of bits. The zero value of a BitSet is an empty set of length 0. -type BitSet struct { - length uint - set []uint64 -} - -// Error is used to distinguish errors (panics) generated in this package. -type Error string - -// safeSet will fixup b.set to be non-nil and return the field value -func (b *BitSet) safeSet() []uint64 { - if b.set == nil { - b.set = make([]uint64, wordsNeeded(0)) - } - return b.set -} - -// From is a constructor used to create a BitSet from an array of integers -func From(buf []uint64) *BitSet { - return &BitSet{uint(len(buf)) * 64, buf} -} - -// Bytes returns the bitset as array of integers -func (b *BitSet) Bytes() []uint64 { - return b.set -} - -// wordsNeeded calculates the number of words needed for i bits -func wordsNeeded(i uint) int { - if i > (Cap() - wordSize + 1) { - return int(Cap() >> log2WordSize) - } - return int((i + (wordSize - 1)) >> log2WordSize) -} - -// New creates a new BitSet with a hint that length bits will be required -func New(length uint) (bset *BitSet) { - defer func() { - if r := recover(); r != nil { - bset = &BitSet{ - 0, - make([]uint64, 0), - } - } - }() - - bset = &BitSet{ - length, - make([]uint64, wordsNeeded(length)), - } - - return bset -} - -// Cap returns the total possible capacity, or number of bits -func Cap() uint { - return ^uint(0) -} - -// Len returns the number of bits in the BitSet. -// Note the difference to method Count, see example. -func (b *BitSet) Len() uint { - return b.length -} - -// extendSetMaybe adds additional words to incorporate new bits if needed -func (b *BitSet) extendSetMaybe(i uint) { - if i >= b.length { // if we need more bits, make 'em - if i >= Cap() { - panic("You are exceeding the capacity") - } - nsize := wordsNeeded(i + 1) - if b.set == nil { - b.set = make([]uint64, nsize) - } else if cap(b.set) >= nsize { - b.set = b.set[:nsize] // fast resize - } else if len(b.set) < nsize { - newset := make([]uint64, nsize, 2*nsize) // increase capacity 2x - copy(newset, b.set) - b.set = newset - } - b.length = i + 1 - } -} - -// Test whether bit i is set. -func (b *BitSet) Test(i uint) bool { - if i >= b.length { - return false - } - return b.set[i>>log2WordSize]&(1<<(i&(wordSize-1))) != 0 -} - -// Set bit i to 1, the capacity of the bitset is automatically -// increased accordingly. -// If i>= Cap(), this function will panic. -// Warning: using a very large value for 'i' -// may lead to a memory shortage and a panic: the caller is responsible -// for providing sensible parameters in line with their memory capacity. -func (b *BitSet) Set(i uint) *BitSet { - b.extendSetMaybe(i) - b.set[i>>log2WordSize] |= 1 << (i & (wordSize - 1)) - return b -} - -// Clear bit i to 0 -func (b *BitSet) Clear(i uint) *BitSet { - if i >= b.length { - return b - } - b.set[i>>log2WordSize] &^= 1 << (i & (wordSize - 1)) - return b -} - -// SetTo sets bit i to value. -// If i>= Cap(), this function will panic. -// Warning: using a very large value for 'i' -// may lead to a memory shortage and a panic: the caller is responsible -// for providing sensible parameters in line with their memory capacity. -func (b *BitSet) SetTo(i uint, value bool) *BitSet { - if value { - return b.Set(i) - } - return b.Clear(i) -} - -// Flip bit at i. -// If i>= Cap(), this function will panic. -// Warning: using a very large value for 'i' -// may lead to a memory shortage and a panic: the caller is responsible -// for providing sensible parameters in line with their memory capacity. -func (b *BitSet) Flip(i uint) *BitSet { - if i >= b.length { - return b.Set(i) - } - b.set[i>>log2WordSize] ^= 1 << (i & (wordSize - 1)) - return b -} - -// FlipRange bit in [start, end). -// If end>= Cap(), this function will panic. -// Warning: using a very large value for 'end' -// may lead to a memory shortage and a panic: the caller is responsible -// for providing sensible parameters in line with their memory capacity. -func (b *BitSet) FlipRange(start, end uint) *BitSet { - if start >= end { - return b - } - - b.extendSetMaybe(end - 1) - var startWord uint = start >> log2WordSize - var endWord uint = end >> log2WordSize - b.set[startWord] ^= ^(^uint64(0) << (start & (wordSize - 1))) - for i := startWord; i < endWord; i++ { - b.set[i] = ^b.set[i] - } - b.set[endWord] ^= ^uint64(0) >> (-end & (wordSize - 1)) - return b -} - -// Shrink shrinks BitSet so that the provided value is the last possible -// set value. It clears all bits > the provided index and reduces the size -// and length of the set. -// -// Note that the parameter value is not the new length in bits: it is the -// maximal value that can be stored in the bitset after the function call. -// The new length in bits is the parameter value + 1. Thus it is not possible -// to use this function to set the length to 0, the minimal value of the length -// after this function call is 1. -// -// A new slice is allocated to store the new bits, so you may see an increase in -// memory usage until the GC runs. Normally this should not be a problem, but if you -// have an extremely large BitSet its important to understand that the old BitSet will -// remain in memory until the GC frees it. -func (b *BitSet) Shrink(lastbitindex uint) *BitSet { - length := lastbitindex + 1 - idx := wordsNeeded(length) - if idx > len(b.set) { - return b - } - shrunk := make([]uint64, idx) - copy(shrunk, b.set[:idx]) - b.set = shrunk - b.length = length - b.set[idx-1] &= (allBits >> (uint64(64) - uint64(length&(wordSize-1)))) - return b -} - -// Compact shrinks BitSet to so that we preserve all set bits, while minimizing -// memory usage. Compact calls Shrink. -func (b *BitSet) Compact() *BitSet { - idx := len(b.set) - 1 - for ; idx >= 0 && b.set[idx] == 0; idx-- { - } - newlength := uint((idx + 1) << log2WordSize) - if newlength >= b.length { - return b // nothing to do - } - if newlength > 0 { - return b.Shrink(newlength - 1) - } - // We preserve one word - return b.Shrink(63) -} - -// InsertAt takes an index which indicates where a bit should be -// inserted. Then it shifts all the bits in the set to the left by 1, starting -// from the given index position, and sets the index position to 0. -// -// Depending on the size of your BitSet, and where you are inserting the new entry, -// this method could be extremely slow and in some cases might cause the entire BitSet -// to be recopied. -func (b *BitSet) InsertAt(idx uint) *BitSet { - insertAtElement := (idx >> log2WordSize) - - // if length of set is a multiple of wordSize we need to allocate more space first - if b.isLenExactMultiple() { - b.set = append(b.set, uint64(0)) - } - - var i uint - for i = uint(len(b.set) - 1); i > insertAtElement; i-- { - // all elements above the position where we want to insert can simply by shifted - b.set[i] <<= 1 - - // we take the most significant bit of the previous element and set it as - // the least significant bit of the current element - b.set[i] |= (b.set[i-1] & 0x8000000000000000) >> 63 - } - - // generate a mask to extract the data that we need to shift left - // within the element where we insert a bit - dataMask := ^(uint64(1)<<uint64(idx&(wordSize-1)) - 1) - - // extract that data that we'll shift - data := b.set[i] & dataMask - - // set the positions of the data mask to 0 in the element where we insert - b.set[i] &= ^dataMask - - // shift data mask to the left and insert its data to the slice element - b.set[i] |= data << 1 - - // add 1 to length of BitSet - b.length++ - - return b -} - -// String creates a string representation of the Bitmap -func (b *BitSet) String() string { - // follows code from https://github.com/RoaringBitmap/roaring - var buffer bytes.Buffer - start := []byte("{") - buffer.Write(start) - counter := 0 - i, e := b.NextSet(0) - for e { - counter = counter + 1 - // to avoid exhausting the memory - if counter > 0x40000 { - buffer.WriteString("...") - break - } - buffer.WriteString(strconv.FormatInt(int64(i), 10)) - i, e = b.NextSet(i + 1) - if e { - buffer.WriteString(",") - } - } - buffer.WriteString("}") - return buffer.String() -} - -// DeleteAt deletes the bit at the given index position from -// within the bitset -// All the bits residing on the left of the deleted bit get -// shifted right by 1 -// The running time of this operation may potentially be -// relatively slow, O(length) -func (b *BitSet) DeleteAt(i uint) *BitSet { - // the index of the slice element where we'll delete a bit - deleteAtElement := i >> log2WordSize - - // generate a mask for the data that needs to be shifted right - // within that slice element that gets modified - dataMask := ^((uint64(1) << (i & (wordSize - 1))) - 1) - - // extract the data that we'll shift right from the slice element - data := b.set[deleteAtElement] & dataMask - - // set the masked area to 0 while leaving the rest as it is - b.set[deleteAtElement] &= ^dataMask - - // shift the previously extracted data to the right and then - // set it in the previously masked area - b.set[deleteAtElement] |= (data >> 1) & dataMask - - // loop over all the consecutive slice elements to copy each - // lowest bit into the highest position of the previous element, - // then shift the entire content to the right by 1 - for i := int(deleteAtElement) + 1; i < len(b.set); i++ { - b.set[i-1] |= (b.set[i] & 1) << 63 - b.set[i] >>= 1 - } - - b.length = b.length - 1 - - return b -} - -// NextSet returns the next bit set from the specified index, -// including possibly the current index -// along with an error code (true = valid, false = no set bit found) -// for i,e := v.NextSet(0); e; i,e = v.NextSet(i + 1) {...} -// -// Users concerned with performance may want to use NextSetMany to -// retrieve several values at once. -func (b *BitSet) NextSet(i uint) (uint, bool) { - x := int(i >> log2WordSize) - if x >= len(b.set) { - return 0, false - } - w := b.set[x] - w = w >> (i & (wordSize - 1)) - if w != 0 { - return i + trailingZeroes64(w), true - } - x = x + 1 - for x < len(b.set) { - if b.set[x] != 0 { - return uint(x)*wordSize + trailingZeroes64(b.set[x]), true - } - x = x + 1 - - } - return 0, false -} - -// NextSetMany returns many next bit sets from the specified index, -// including possibly the current index and up to cap(buffer). -// If the returned slice has len zero, then no more set bits were found -// -// buffer := make([]uint, 256) // this should be reused -// j := uint(0) -// j, buffer = bitmap.NextSetMany(j, buffer) -// for ; len(buffer) > 0; j, buffer = bitmap.NextSetMany(j,buffer) { -// for k := range buffer { -// do something with buffer[k] -// } -// j += 1 -// } -// -// -// It is possible to retrieve all set bits as follow: -// -// indices := make([]uint, bitmap.Count()) -// bitmap.NextSetMany(0, indices) -// -// However if bitmap.Count() is large, it might be preferable to -// use several calls to NextSetMany, for performance reasons. -func (b *BitSet) NextSetMany(i uint, buffer []uint) (uint, []uint) { - myanswer := buffer - capacity := cap(buffer) - x := int(i >> log2WordSize) - if x >= len(b.set) || capacity == 0 { - return 0, myanswer[:0] - } - skip := i & (wordSize - 1) - word := b.set[x] >> skip - myanswer = myanswer[:capacity] - size := int(0) - for word != 0 { - r := trailingZeroes64(word) - t := word & ((^word) + 1) - myanswer[size] = r + i - size++ - if size == capacity { - goto End - } - word = word ^ t - } - x++ - for idx, word := range b.set[x:] { - for word != 0 { - r := trailingZeroes64(word) - t := word & ((^word) + 1) - myanswer[size] = r + (uint(x+idx) << 6) - size++ - if size == capacity { - goto End - } - word = word ^ t - } - } -End: - if size > 0 { - return myanswer[size-1], myanswer[:size] - } - return 0, myanswer[:0] -} - -// NextClear returns the next clear bit from the specified index, -// including possibly the current index -// along with an error code (true = valid, false = no bit found i.e. all bits are set) -func (b *BitSet) NextClear(i uint) (uint, bool) { - x := int(i >> log2WordSize) - if x >= len(b.set) { - return 0, false - } - w := b.set[x] - w = w >> (i & (wordSize - 1)) - wA := allBits >> (i & (wordSize - 1)) - index := i + trailingZeroes64(^w) - if w != wA && index < b.length { - return index, true - } - x++ - for x < len(b.set) { - index = uint(x)*wordSize + trailingZeroes64(^b.set[x]) - if b.set[x] != allBits && index < b.length { - return index, true - } - x++ - } - return 0, false -} - -// ClearAll clears the entire BitSet -func (b *BitSet) ClearAll() *BitSet { - if b != nil && b.set != nil { - for i := range b.set { - b.set[i] = 0 - } - } - return b -} - -// wordCount returns the number of words used in a bit set -func (b *BitSet) wordCount() int { - return len(b.set) -} - -// Clone this BitSet -func (b *BitSet) Clone() *BitSet { - c := New(b.length) - if b.set != nil { // Clone should not modify current object - copy(c.set, b.set) - } - return c -} - -// Copy into a destination BitSet -// Returning the size of the destination BitSet -// like array copy -func (b *BitSet) Copy(c *BitSet) (count uint) { - if c == nil { - return - } - if b.set != nil { // Copy should not modify current object - copy(c.set, b.set) - } - count = c.length - if b.length < c.length { - count = b.length - } - return -} - -// Count (number of set bits). -// Also known as "popcount" or "population count". -func (b *BitSet) Count() uint { - if b != nil && b.set != nil { - return uint(popcntSlice(b.set)) - } - return 0 -} - -// Equal tests the equivalence of two BitSets. -// False if they are of different sizes, otherwise true -// only if all the same bits are set -func (b *BitSet) Equal(c *BitSet) bool { - if c == nil || b == nil { - return c == b - } - if b.length != c.length { - return false - } - if b.length == 0 { // if they have both length == 0, then could have nil set - return true - } - // testing for equality shoud not transform the bitset (no call to safeSet) - - for p, v := range b.set { - if c.set[p] != v { - return false - } - } - return true -} - -func panicIfNull(b *BitSet) { - if b == nil { - panic(Error("BitSet must not be null")) - } -} - -// Difference of base set and other set -// This is the BitSet equivalent of &^ (and not) -func (b *BitSet) Difference(compare *BitSet) (result *BitSet) { - panicIfNull(b) - panicIfNull(compare) - result = b.Clone() // clone b (in case b is bigger than compare) - l := int(compare.wordCount()) - if l > int(b.wordCount()) { - l = int(b.wordCount()) - } - for i := 0; i < l; i++ { - result.set[i] = b.set[i] &^ compare.set[i] - } - return -} - -// DifferenceCardinality computes the cardinality of the differnce -func (b *BitSet) DifferenceCardinality(compare *BitSet) uint { - panicIfNull(b) - panicIfNull(compare) - l := int(compare.wordCount()) - if l > int(b.wordCount()) { - l = int(b.wordCount()) - } - cnt := uint64(0) - cnt += popcntMaskSlice(b.set[:l], compare.set[:l]) - cnt += popcntSlice(b.set[l:]) - return uint(cnt) -} - -// InPlaceDifference computes the difference of base set and other set -// This is the BitSet equivalent of &^ (and not) -func (b *BitSet) InPlaceDifference(compare *BitSet) { - panicIfNull(b) - panicIfNull(compare) - l := int(compare.wordCount()) - if l > int(b.wordCount()) { - l = int(b.wordCount()) - } - for i := 0; i < l; i++ { - b.set[i] &^= compare.set[i] - } -} - -// Convenience function: return two bitsets ordered by -// increasing length. Note: neither can be nil -func sortByLength(a *BitSet, b *BitSet) (ap *BitSet, bp *BitSet) { - if a.length <= b.length { - ap, bp = a, b - } else { - ap, bp = b, a - } - return -} - -// Intersection of base set and other set -// This is the BitSet equivalent of & (and) -func (b *BitSet) Intersection(compare *BitSet) (result *BitSet) { - panicIfNull(b) - panicIfNull(compare) - b, compare = sortByLength(b, compare) - result = New(b.length) - for i, word := range b.set { - result.set[i] = word & compare.set[i] - } - return -} - -// IntersectionCardinality computes the cardinality of the union -func (b *BitSet) IntersectionCardinality(compare *BitSet) uint { - panicIfNull(b) - panicIfNull(compare) - b, compare = sortByLength(b, compare) - cnt := popcntAndSlice(b.set, compare.set) - return uint(cnt) -} - -// InPlaceIntersection destructively computes the intersection of -// base set and the compare set. -// This is the BitSet equivalent of & (and) -func (b *BitSet) InPlaceIntersection(compare *BitSet) { - panicIfNull(b) - panicIfNull(compare) - l := int(compare.wordCount()) - if l > int(b.wordCount()) { - l = int(b.wordCount()) - } - for i := 0; i < l; i++ { - b.set[i] &= compare.set[i] - } - for i := l; i < len(b.set); i++ { - b.set[i] = 0 - } - if compare.length > 0 { - b.extendSetMaybe(compare.length - 1) - } -} - -// Union of base set and other set -// This is the BitSet equivalent of | (or) -func (b *BitSet) Union(compare *BitSet) (result *BitSet) { - panicIfNull(b) - panicIfNull(compare) - b, compare = sortByLength(b, compare) - result = compare.Clone() - for i, word := range b.set { - result.set[i] = word | compare.set[i] - } - return -} - -// UnionCardinality computes the cardinality of the uniton of the base set -// and the compare set. -func (b *BitSet) UnionCardinality(compare *BitSet) uint { - panicIfNull(b) - panicIfNull(compare) - b, compare = sortByLength(b, compare) - cnt := popcntOrSlice(b.set, compare.set) - if len(compare.set) > len(b.set) { - cnt += popcntSlice(compare.set[len(b.set):]) - } - return uint(cnt) -} - -// InPlaceUnion creates the destructive union of base set and compare set. -// This is the BitSet equivalent of | (or). -func (b *BitSet) InPlaceUnion(compare *BitSet) { - panicIfNull(b) - panicIfNull(compare) - l := int(compare.wordCount()) - if l > int(b.wordCount()) { - l = int(b.wordCount()) - } - if compare.length > 0 { - b.extendSetMaybe(compare.length - 1) - } - for i := 0; i < l; i++ { - b.set[i] |= compare.set[i] - } - if len(compare.set) > l { - for i := l; i < len(compare.set); i++ { - b.set[i] = compare.set[i] - } - } -} - -// SymmetricDifference of base set and other set -// This is the BitSet equivalent of ^ (xor) -func (b *BitSet) SymmetricDifference(compare *BitSet) (result *BitSet) { - panicIfNull(b) - panicIfNull(compare) - b, compare = sortByLength(b, compare) - // compare is bigger, so clone it - result = compare.Clone() - for i, word := range b.set { - result.set[i] = word ^ compare.set[i] - } - return -} - -// SymmetricDifferenceCardinality computes the cardinality of the symmetric difference -func (b *BitSet) SymmetricDifferenceCardinality(compare *BitSet) uint { - panicIfNull(b) - panicIfNull(compare) - b, compare = sortByLength(b, compare) - cnt := popcntXorSlice(b.set, compare.set) - if len(compare.set) > len(b.set) { - cnt += popcntSlice(compare.set[len(b.set):]) - } - return uint(cnt) -} - -// InPlaceSymmetricDifference creates the destructive SymmetricDifference of base set and other set -// This is the BitSet equivalent of ^ (xor) -func (b *BitSet) InPlaceSymmetricDifference(compare *BitSet) { - panicIfNull(b) - panicIfNull(compare) - l := int(compare.wordCount()) - if l > int(b.wordCount()) { - l = int(b.wordCount()) - } - if compare.length > 0 { - b.extendSetMaybe(compare.length - 1) - } - for i := 0; i < l; i++ { - b.set[i] ^= compare.set[i] - } - if len(compare.set) > l { - for i := l; i < len(compare.set); i++ { - b.set[i] = compare.set[i] - } - } -} - -// Is the length an exact multiple of word sizes? -func (b *BitSet) isLenExactMultiple() bool { - return b.length%wordSize == 0 -} - -// Clean last word by setting unused bits to 0 -func (b *BitSet) cleanLastWord() { - if !b.isLenExactMultiple() { - b.set[len(b.set)-1] &= allBits >> (wordSize - b.length%wordSize) - } -} - -// Complement computes the (local) complement of a biset (up to length bits) -func (b *BitSet) Complement() (result *BitSet) { - panicIfNull(b) - result = New(b.length) - for i, word := range b.set { - result.set[i] = ^word - } - result.cleanLastWord() - return -} - -// All returns true if all bits are set, false otherwise. Returns true for -// empty sets. -func (b *BitSet) All() bool { - panicIfNull(b) - return b.Count() == b.length -} - -// None returns true if no bit is set, false otherwise. Returns true for -// empty sets. -func (b *BitSet) None() bool { - panicIfNull(b) - if b != nil && b.set != nil { - for _, word := range b.set { - if word > 0 { - return false - } - } - return true - } - return true -} - -// Any returns true if any bit is set, false otherwise -func (b *BitSet) Any() bool { - panicIfNull(b) - return !b.None() -} - -// IsSuperSet returns true if this is a superset of the other set -func (b *BitSet) IsSuperSet(other *BitSet) bool { - for i, e := other.NextSet(0); e; i, e = other.NextSet(i + 1) { - if !b.Test(i) { - return false - } - } - return true -} - -// IsStrictSuperSet returns true if this is a strict superset of the other set -func (b *BitSet) IsStrictSuperSet(other *BitSet) bool { - return b.Count() > other.Count() && b.IsSuperSet(other) -} - -// DumpAsBits dumps a bit set as a string of bits -func (b *BitSet) DumpAsBits() string { - if b.set == nil { - return "." - } - buffer := bytes.NewBufferString("") - i := len(b.set) - 1 - for ; i >= 0; i-- { - fmt.Fprintf(buffer, "%064b.", b.set[i]) - } - return buffer.String() -} - -// BinaryStorageSize returns the binary storage requirements -func (b *BitSet) BinaryStorageSize() int { - return binary.Size(uint64(0)) + binary.Size(b.set) -} - -// WriteTo writes a BitSet to a stream -func (b *BitSet) WriteTo(stream io.Writer) (int64, error) { - length := uint64(b.length) - - // Write length - err := binary.Write(stream, binaryOrder, length) - if err != nil { - return 0, err - } - - // Write set - err = binary.Write(stream, binaryOrder, b.set) - return int64(b.BinaryStorageSize()), err -} - -// ReadFrom reads a BitSet from a stream written using WriteTo -func (b *BitSet) ReadFrom(stream io.Reader) (int64, error) { - var length uint64 - - // Read length first - err := binary.Read(stream, binaryOrder, &length) - if err != nil { - return 0, err - } - newset := New(uint(length)) - - if uint64(newset.length) != length { - return 0, errors.New("unmarshalling error: type mismatch") - } - - // Read remaining bytes as set - err = binary.Read(stream, binaryOrder, newset.set) - if err != nil { - return 0, err - } - - *b = *newset - return int64(b.BinaryStorageSize()), nil -} - -// MarshalBinary encodes a BitSet into a binary form and returns the result. -func (b *BitSet) MarshalBinary() ([]byte, error) { - var buf bytes.Buffer - writer := bufio.NewWriter(&buf) - - _, err := b.WriteTo(writer) - if err != nil { - return []byte{}, err - } - - err = writer.Flush() - - return buf.Bytes(), err -} - -// UnmarshalBinary decodes the binary form generated by MarshalBinary. -func (b *BitSet) UnmarshalBinary(data []byte) error { - buf := bytes.NewReader(data) - reader := bufio.NewReader(buf) - - _, err := b.ReadFrom(reader) - - return err -} - -// MarshalJSON marshals a BitSet as a JSON structure -func (b *BitSet) MarshalJSON() ([]byte, error) { - buffer := bytes.NewBuffer(make([]byte, 0, b.BinaryStorageSize())) - _, err := b.WriteTo(buffer) - if err != nil { - return nil, err - } - - // URLEncode all bytes - return json.Marshal(base64Encoding.EncodeToString(buffer.Bytes())) -} - -// UnmarshalJSON unmarshals a BitSet from JSON created using MarshalJSON -func (b *BitSet) UnmarshalJSON(data []byte) error { - // Unmarshal as string - var s string - err := json.Unmarshal(data, &s) - if err != nil { - return err - } - - // URLDecode string - buf, err := base64Encoding.DecodeString(s) - if err != nil { - return err - } - - _, err = b.ReadFrom(bytes.NewReader(buf)) - return err -} diff --git a/vendor/github.com/bits-and-blooms/bitset/go.mod b/vendor/github.com/bits-and-blooms/bitset/go.mod deleted file mode 100644 index c43e4522b..000000000 --- a/vendor/github.com/bits-and-blooms/bitset/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/bits-and-blooms/bitset - -go 1.14 diff --git a/vendor/github.com/bits-and-blooms/bitset/go.sum b/vendor/github.com/bits-and-blooms/bitset/go.sum deleted file mode 100644 index e69de29bb..000000000 --- a/vendor/github.com/bits-and-blooms/bitset/go.sum +++ /dev/null diff --git a/vendor/github.com/bits-and-blooms/bitset/popcnt.go b/vendor/github.com/bits-and-blooms/bitset/popcnt.go deleted file mode 100644 index 76577a838..000000000 --- a/vendor/github.com/bits-and-blooms/bitset/popcnt.go +++ /dev/null @@ -1,53 +0,0 @@ -package bitset - -// bit population count, take from -// https://code.google.com/p/go/issues/detail?id=4988#c11 -// credit: https://code.google.com/u/arnehormann/ -func popcount(x uint64) (n uint64) { - x -= (x >> 1) & 0x5555555555555555 - x = (x>>2)&0x3333333333333333 + x&0x3333333333333333 - x += x >> 4 - x &= 0x0f0f0f0f0f0f0f0f - x *= 0x0101010101010101 - return x >> 56 -} - -func popcntSliceGo(s []uint64) uint64 { - cnt := uint64(0) - for _, x := range s { - cnt += popcount(x) - } - return cnt -} - -func popcntMaskSliceGo(s, m []uint64) uint64 { - cnt := uint64(0) - for i := range s { - cnt += popcount(s[i] &^ m[i]) - } - return cnt -} - -func popcntAndSliceGo(s, m []uint64) uint64 { - cnt := uint64(0) - for i := range s { - cnt += popcount(s[i] & m[i]) - } - return cnt -} - -func popcntOrSliceGo(s, m []uint64) uint64 { - cnt := uint64(0) - for i := range s { - cnt += popcount(s[i] | m[i]) - } - return cnt -} - -func popcntXorSliceGo(s, m []uint64) uint64 { - cnt := uint64(0) - for i := range s { - cnt += popcount(s[i] ^ m[i]) - } - return cnt -} diff --git a/vendor/github.com/bits-and-blooms/bitset/popcnt_19.go b/vendor/github.com/bits-and-blooms/bitset/popcnt_19.go deleted file mode 100644 index fc8ff4f36..000000000 --- a/vendor/github.com/bits-and-blooms/bitset/popcnt_19.go +++ /dev/null @@ -1,45 +0,0 @@ -// +build go1.9 - -package bitset - -import "math/bits" - -func popcntSlice(s []uint64) uint64 { - var cnt int - for _, x := range s { - cnt += bits.OnesCount64(x) - } - return uint64(cnt) -} - -func popcntMaskSlice(s, m []uint64) uint64 { - var cnt int - for i := range s { - cnt += bits.OnesCount64(s[i] &^ m[i]) - } - return uint64(cnt) -} - -func popcntAndSlice(s, m []uint64) uint64 { - var cnt int - for i := range s { - cnt += bits.OnesCount64(s[i] & m[i]) - } - return uint64(cnt) -} - -func popcntOrSlice(s, m []uint64) uint64 { - var cnt int - for i := range s { - cnt += bits.OnesCount64(s[i] | m[i]) - } - return uint64(cnt) -} - -func popcntXorSlice(s, m []uint64) uint64 { - var cnt int - for i := range s { - cnt += bits.OnesCount64(s[i] ^ m[i]) - } - return uint64(cnt) -} diff --git a/vendor/github.com/bits-and-blooms/bitset/popcnt_amd64.go b/vendor/github.com/bits-and-blooms/bitset/popcnt_amd64.go deleted file mode 100644 index 4cf64f24a..000000000 --- a/vendor/github.com/bits-and-blooms/bitset/popcnt_amd64.go +++ /dev/null @@ -1,68 +0,0 @@ -// +build !go1.9 -// +build amd64,!appengine - -package bitset - -// *** the following functions are defined in popcnt_amd64.s - -//go:noescape - -func hasAsm() bool - -// useAsm is a flag used to select the GO or ASM implementation of the popcnt function -var useAsm = hasAsm() - -//go:noescape - -func popcntSliceAsm(s []uint64) uint64 - -//go:noescape - -func popcntMaskSliceAsm(s, m []uint64) uint64 - -//go:noescape - -func popcntAndSliceAsm(s, m []uint64) uint64 - -//go:noescape - -func popcntOrSliceAsm(s, m []uint64) uint64 - -//go:noescape - -func popcntXorSliceAsm(s, m []uint64) uint64 - -func popcntSlice(s []uint64) uint64 { - if useAsm { - return popcntSliceAsm(s) - } - return popcntSliceGo(s) -} - -func popcntMaskSlice(s, m []uint64) uint64 { - if useAsm { - return popcntMaskSliceAsm(s, m) - } - return popcntMaskSliceGo(s, m) -} - -func popcntAndSlice(s, m []uint64) uint64 { - if useAsm { - return popcntAndSliceAsm(s, m) - } - return popcntAndSliceGo(s, m) -} - -func popcntOrSlice(s, m []uint64) uint64 { - if useAsm { - return popcntOrSliceAsm(s, m) - } - return popcntOrSliceGo(s, m) -} - -func popcntXorSlice(s, m []uint64) uint64 { - if useAsm { - return popcntXorSliceAsm(s, m) - } - return popcntXorSliceGo(s, m) -} diff --git a/vendor/github.com/bits-and-blooms/bitset/popcnt_amd64.s b/vendor/github.com/bits-and-blooms/bitset/popcnt_amd64.s deleted file mode 100644 index 666c0dcc1..000000000 --- a/vendor/github.com/bits-and-blooms/bitset/popcnt_amd64.s +++ /dev/null @@ -1,104 +0,0 @@ -// +build !go1.9 -// +build amd64,!appengine - -TEXT ·hasAsm(SB),4,$0-1 -MOVQ $1, AX -CPUID -SHRQ $23, CX -ANDQ $1, CX -MOVB CX, ret+0(FP) -RET - -#define POPCNTQ_DX_DX BYTE $0xf3; BYTE $0x48; BYTE $0x0f; BYTE $0xb8; BYTE $0xd2 - -TEXT ·popcntSliceAsm(SB),4,$0-32 -XORQ AX, AX -MOVQ s+0(FP), SI -MOVQ s_len+8(FP), CX -TESTQ CX, CX -JZ popcntSliceEnd -popcntSliceLoop: -BYTE $0xf3; BYTE $0x48; BYTE $0x0f; BYTE $0xb8; BYTE $0x16 // POPCNTQ (SI), DX -ADDQ DX, AX -ADDQ $8, SI -LOOP popcntSliceLoop -popcntSliceEnd: -MOVQ AX, ret+24(FP) -RET - -TEXT ·popcntMaskSliceAsm(SB),4,$0-56 -XORQ AX, AX -MOVQ s+0(FP), SI -MOVQ s_len+8(FP), CX -TESTQ CX, CX -JZ popcntMaskSliceEnd -MOVQ m+24(FP), DI -popcntMaskSliceLoop: -MOVQ (DI), DX -NOTQ DX -ANDQ (SI), DX -POPCNTQ_DX_DX -ADDQ DX, AX -ADDQ $8, SI -ADDQ $8, DI -LOOP popcntMaskSliceLoop -popcntMaskSliceEnd: -MOVQ AX, ret+48(FP) -RET - -TEXT ·popcntAndSliceAsm(SB),4,$0-56 -XORQ AX, AX -MOVQ s+0(FP), SI -MOVQ s_len+8(FP), CX -TESTQ CX, CX -JZ popcntAndSliceEnd -MOVQ m+24(FP), DI -popcntAndSliceLoop: -MOVQ (DI), DX -ANDQ (SI), DX -POPCNTQ_DX_DX -ADDQ DX, AX -ADDQ $8, SI -ADDQ $8, DI -LOOP popcntAndSliceLoop -popcntAndSliceEnd: -MOVQ AX, ret+48(FP) -RET - -TEXT ·popcntOrSliceAsm(SB),4,$0-56 -XORQ AX, AX -MOVQ s+0(FP), SI -MOVQ s_len+8(FP), CX -TESTQ CX, CX -JZ popcntOrSliceEnd -MOVQ m+24(FP), DI -popcntOrSliceLoop: -MOVQ (DI), DX -ORQ (SI), DX -POPCNTQ_DX_DX -ADDQ DX, AX -ADDQ $8, SI -ADDQ $8, DI -LOOP popcntOrSliceLoop -popcntOrSliceEnd: -MOVQ AX, ret+48(FP) -RET - -TEXT ·popcntXorSliceAsm(SB),4,$0-56 -XORQ AX, AX -MOVQ s+0(FP), SI -MOVQ s_len+8(FP), CX -TESTQ CX, CX -JZ popcntXorSliceEnd -MOVQ m+24(FP), DI -popcntXorSliceLoop: -MOVQ (DI), DX -XORQ (SI), DX -POPCNTQ_DX_DX -ADDQ DX, AX -ADDQ $8, SI -ADDQ $8, DI -LOOP popcntXorSliceLoop -popcntXorSliceEnd: -MOVQ AX, ret+48(FP) -RET diff --git a/vendor/github.com/bits-and-blooms/bitset/popcnt_generic.go b/vendor/github.com/bits-and-blooms/bitset/popcnt_generic.go deleted file mode 100644 index 21e0ff7b4..000000000 --- a/vendor/github.com/bits-and-blooms/bitset/popcnt_generic.go +++ /dev/null @@ -1,24 +0,0 @@ -// +build !go1.9 -// +build !amd64 appengine - -package bitset - -func popcntSlice(s []uint64) uint64 { - return popcntSliceGo(s) -} - -func popcntMaskSlice(s, m []uint64) uint64 { - return popcntMaskSliceGo(s, m) -} - -func popcntAndSlice(s, m []uint64) uint64 { - return popcntAndSliceGo(s, m) -} - -func popcntOrSlice(s, m []uint64) uint64 { - return popcntOrSliceGo(s, m) -} - -func popcntXorSlice(s, m []uint64) uint64 { - return popcntXorSliceGo(s, m) -} diff --git a/vendor/github.com/bits-and-blooms/bitset/trailing_zeros_18.go b/vendor/github.com/bits-and-blooms/bitset/trailing_zeros_18.go deleted file mode 100644 index c52b61be9..000000000 --- a/vendor/github.com/bits-and-blooms/bitset/trailing_zeros_18.go +++ /dev/null @@ -1,14 +0,0 @@ -// +build !go1.9 - -package bitset - -var deBruijn = [...]byte{ - 0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4, - 62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5, - 63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11, - 54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6, -} - -func trailingZeroes64(v uint64) uint { - return uint(deBruijn[((v&-v)*0x03f79d71b4ca8b09)>>58]) -} diff --git a/vendor/github.com/bits-and-blooms/bitset/trailing_zeros_19.go b/vendor/github.com/bits-and-blooms/bitset/trailing_zeros_19.go deleted file mode 100644 index 36a988e71..000000000 --- a/vendor/github.com/bits-and-blooms/bitset/trailing_zeros_19.go +++ /dev/null @@ -1,9 +0,0 @@ -// +build go1.9 - -package bitset - -import "math/bits" - -func trailingZeroes64(v uint64) uint { - return uint(bits.TrailingZeros64(v)) -} diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/estargz.go b/vendor/github.com/containerd/stargz-snapshotter/estargz/estargz.go index e997d9cce..3ef029116 100644 --- a/vendor/github.com/containerd/stargz-snapshotter/estargz/estargz.go +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/estargz.go @@ -23,7 +23,6 @@ package estargz import ( - "archive/tar" "bufio" "bytes" "compress/gzip" @@ -42,6 +41,7 @@ import ( "github.com/containerd/stargz-snapshotter/estargz/errorutil" digest "github.com/opencontainers/go-digest" "github.com/pkg/errors" + "github.com/vbatts/tar-split/archive/tar" ) // A Reader permits random access reads from a stargz file. @@ -95,10 +95,10 @@ func WithTelemetry(telemetry *Telemetry) OpenOption { } } -// A func which takes start time and records the diff +// MeasureLatencyHook is a func which takes start time and records the diff type MeasureLatencyHook func(time.Time) -// A struct which defines telemetry hooks. By implementing these hooks you should be able to record +// Telemetry is a struct which defines telemetry hooks. By implementing these hooks you should be able to record // the latency metrics of the respective steps of estargz open operation. To be used with estargz.OpenWithTelemetry(...) type Telemetry struct { GetFooterLatency MeasureLatencyHook // measure time to get stargz footer (in milliseconds) @@ -146,7 +146,7 @@ func Open(sr *io.SectionReader, opt ...OpenOption) (*Reader, error) { fSize := d.FooterSize() fOffset := positive(int64(len(footer)) - fSize) maybeTocBytes := footer[:fOffset] - tocOffset, tocSize, err := d.ParseFooter(footer[fOffset:]) + _, tocOffset, tocSize, err := d.ParseFooter(footer[fOffset:]) if err != nil { allErr = append(allErr, err) continue @@ -187,7 +187,7 @@ func OpenFooter(sr *io.SectionReader) (tocOffset int64, footerSize int64, rErr e for _, d := range []Decompressor{new(GzipDecompressor), new(legacyGzipDecompressor)} { fSize := d.FooterSize() fOffset := positive(int64(len(footer)) - fSize) - tocOffset, _, err := d.ParseFooter(footer[fOffset:]) + _, tocOffset, _, err := d.ParseFooter(footer[fOffset:]) if err == nil { return tocOffset, fSize, err } @@ -326,6 +326,10 @@ func (r *Reader) getOrCreateDir(d string) *TOCEntry { return e } +func (r *Reader) TOCDigest() digest.Digest { + return r.tocDigest +} + // VerifyTOC checks that the TOC JSON in the passed blob matches the // passed digests and that the TOC JSON contains digests for all chunks // contained in the blob. If the verification succceeds, this function @@ -335,7 +339,12 @@ func (r *Reader) VerifyTOC(tocDigest digest.Digest) (TOCEntryVerifier, error) { if r.tocDigest != tocDigest { return nil, fmt.Errorf("invalid TOC JSON %q; want %q", r.tocDigest, tocDigest) } + return r.Verifiers() +} +// Verifiers returns TOCEntryVerifier of this chunk. Use VerifyTOC instead in most cases +// because this doesn't verify TOC. +func (r *Reader) Verifiers() (TOCEntryVerifier, error) { chunkDigestMap := make(map[int64]digest.Digest) // map from chunk offset to the chunk digest regDigestMap := make(map[int64]digest.Digest) // map from chunk offset to the reg file digest var chunkDigestMapIncomplete bool @@ -591,6 +600,11 @@ type currentCompressionWriter struct{ w *Writer } func (ccw currentCompressionWriter) Write(p []byte) (int, error) { ccw.w.diffHash.Write(p) + if ccw.w.gz == nil { + if err := ccw.w.condOpenGz(); err != nil { + return 0, err + } + } return ccw.w.gz.Write(p) } @@ -601,6 +615,25 @@ func (w *Writer) chunkSize() int { return w.ChunkSize } +// Unpack decompresses the given estargz blob and returns a ReadCloser of the tar blob. +// TOC JSON and footer are removed. +func Unpack(sr *io.SectionReader, c Decompressor) (io.ReadCloser, error) { + footerSize := c.FooterSize() + if sr.Size() < footerSize { + return nil, fmt.Errorf("blob is too small; %d < %d", sr.Size(), footerSize) + } + footerOffset := sr.Size() - footerSize + footer := make([]byte, footerSize) + if _, err := sr.ReadAt(footer, footerOffset); err != nil { + return nil, err + } + blobPayloadSize, _, _, err := c.ParseFooter(footer) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse footer") + } + return c.Reader(io.LimitReader(sr, blobPayloadSize)) +} + // NewWriter returns a new stargz writer (gzip-based) writing to w. // // The writer must be closed to write its trailing table of contents. @@ -616,7 +649,7 @@ func NewWriterLevel(w io.Writer, compressionLevel int) *Writer { return NewWriterWithCompressor(w, NewGzipCompressorWithLevel(compressionLevel)) } -// NewWriterLevel returns a new stargz writer writing to w. +// NewWriterWithCompressor returns a new stargz writer writing to w. // The compression method is configurable. // // The writer must be closed to write its trailing table of contents. @@ -696,29 +729,71 @@ func (w *Writer) condOpenGz() (err error) { // each of its contents to w. // // The input r can optionally be gzip compressed but the output will -// always be gzip compressed. +// always be compressed by the specified compressor. func (w *Writer) AppendTar(r io.Reader) error { + return w.appendTar(r, false) +} + +// AppendTarLossLess reads the tar or tar.gz file from r and appends +// each of its contents to w. +// +// The input r can optionally be gzip compressed but the output will +// always be compressed by the specified compressor. +// +// The difference of this func with AppendTar is that this writes +// the input tar stream into w without any modification (e.g. to header bytes). +// +// Note that if the input tar stream already contains TOC JSON, this returns +// error because w cannot overwrite the TOC JSON to the one generated by w without +// lossy modification. To avoid this error, if the input stream is known to be stargz/estargz, +// you shoud decompress it and remove TOC JSON in advance. +func (w *Writer) AppendTarLossLess(r io.Reader) error { + return w.appendTar(r, true) +} + +func (w *Writer) appendTar(r io.Reader, lossless bool) error { + var src io.Reader br := bufio.NewReader(r) - var tr *tar.Reader if isGzip(br) { - // NewReader can't fail if isGzip returned true. zr, _ := gzip.NewReader(br) - tr = tar.NewReader(zr) + src = zr } else { - tr = tar.NewReader(br) + src = io.Reader(br) + } + dst := currentCompressionWriter{w} + var tw *tar.Writer + if !lossless { + tw = tar.NewWriter(dst) // use tar writer only when this isn't lossless mode. + } + tr := tar.NewReader(src) + if lossless { + tr.RawAccounting = true } for { h, err := tr.Next() if err == io.EOF { + if lossless { + if remain := tr.RawBytes(); len(remain) > 0 { + // Collect the remaining null bytes. + // https://github.com/vbatts/tar-split/blob/80a436fd6164c557b131f7c59ed69bd81af69761/concept/main.go#L49-L53 + if _, err := dst.Write(remain); err != nil { + return err + } + } + } break } if err != nil { return fmt.Errorf("error reading from source tar: tar.Reader.Next: %v", err) } - if h.Name == TOCTarName { + if cleanEntryName(h.Name) == TOCTarName { // It is possible for a layer to be "stargzified" twice during the // distribution lifecycle. So we reserve "TOCTarName" here to avoid // duplicated entries in the resulting layer. + if lossless { + // We cannot handle this in lossless way. + return fmt.Errorf("existing TOC JSON is not allowed; decompress layer before append") + } continue } @@ -744,9 +819,14 @@ func (w *Writer) AppendTar(r io.Reader) error { if err := w.condOpenGz(); err != nil { return err } - tw := tar.NewWriter(currentCompressionWriter{w}) - if err := tw.WriteHeader(h); err != nil { - return err + if tw != nil { + if err := tw.WriteHeader(h); err != nil { + return err + } + } else { + if _, err := dst.Write(tr.RawBytes()); err != nil { + return err + } } switch h.Typeflag { case tar.TypeLink: @@ -808,7 +888,13 @@ func (w *Writer) AppendTar(r io.Reader) error { } teeChunk := io.TeeReader(tee, chunkDigest.Hash()) - if _, err := io.CopyN(tw, teeChunk, chunkSize); err != nil { + var out io.Writer + if tw != nil { + out = tw + } else { + out = dst + } + if _, err := io.CopyN(out, teeChunk, chunkSize); err != nil { return fmt.Errorf("error copying %q: %v", h.Name, err) } ent.ChunkDigest = chunkDigest.Digest().String() @@ -825,11 +911,18 @@ func (w *Writer) AppendTar(r io.Reader) error { if payloadDigest != nil { regFileEntry.Digest = payloadDigest.Digest().String() } - if err := tw.Flush(); err != nil { - return err + if tw != nil { + if err := tw.Flush(); err != nil { + return err + } } } - return nil + remainDest := ioutil.Discard + if lossless { + remainDest = dst // Preserve the remaining bytes in lossless mode + } + _, err := io.Copy(remainDest, src) + return err } // DiffID returns the SHA-256 of the uncompressed tar bytes. diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/go.mod b/vendor/github.com/containerd/stargz-snapshotter/estargz/go.mod index ee6b2e17f..144d022ba 100644 --- a/vendor/github.com/containerd/stargz-snapshotter/estargz/go.mod +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/go.mod @@ -3,8 +3,9 @@ module github.com/containerd/stargz-snapshotter/estargz go 1.16 require ( - github.com/klauspost/compress v1.13.5 + github.com/klauspost/compress v1.13.6 github.com/opencontainers/go-digest v1.0.0 github.com/pkg/errors v0.9.1 + github.com/vbatts/tar-split v0.11.2 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a ) diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/go.sum b/vendor/github.com/containerd/stargz-snapshotter/estargz/go.sum index 66cd2d69c..d3c934ff8 100644 --- a/vendor/github.com/containerd/stargz-snapshotter/estargz/go.sum +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/go.sum @@ -1,8 +1,22 @@ -github.com/klauspost/compress v1.13.5 h1:9O69jUPDcsT9fEm74W92rZL9FQY7rCdaXVneq+yyzl4= -github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= +github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/gzip.go b/vendor/github.com/containerd/stargz-snapshotter/estargz/gzip.go index efc435e09..88e1283d8 100644 --- a/vendor/github.com/containerd/stargz-snapshotter/estargz/gzip.go +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/gzip.go @@ -124,31 +124,31 @@ func (gz *GzipDecompressor) ParseTOC(r io.Reader) (toc *JTOC, tocDgst digest.Dig return parseTOCEStargz(r) } -func (gz *GzipDecompressor) ParseFooter(p []byte) (tocOffset, tocSize int64, err error) { +func (gz *GzipDecompressor) ParseFooter(p []byte) (blobPayloadSize, tocOffset, tocSize int64, err error) { if len(p) != FooterSize { - return 0, 0, fmt.Errorf("invalid length %d cannot be parsed", len(p)) + return 0, 0, 0, fmt.Errorf("invalid length %d cannot be parsed", len(p)) } zr, err := gzip.NewReader(bytes.NewReader(p)) if err != nil { - return 0, 0, err + return 0, 0, 0, err } defer zr.Close() extra := zr.Header.Extra si1, si2, subfieldlen, subfield := extra[0], extra[1], extra[2:4], extra[4:] if si1 != 'S' || si2 != 'G' { - return 0, 0, fmt.Errorf("invalid subfield IDs: %q, %q; want E, S", si1, si2) + return 0, 0, 0, fmt.Errorf("invalid subfield IDs: %q, %q; want E, S", si1, si2) } if slen := binary.LittleEndian.Uint16(subfieldlen); slen != uint16(16+len("STARGZ")) { - return 0, 0, fmt.Errorf("invalid length of subfield %d; want %d", slen, 16+len("STARGZ")) + return 0, 0, 0, fmt.Errorf("invalid length of subfield %d; want %d", slen, 16+len("STARGZ")) } if string(subfield[16:]) != "STARGZ" { - return 0, 0, fmt.Errorf("STARGZ magic string must be included in the footer subfield") + return 0, 0, 0, fmt.Errorf("STARGZ magic string must be included in the footer subfield") } tocOffset, err = strconv.ParseInt(string(subfield[:16]), 16, 64) if err != nil { - return 0, 0, errors.Wrapf(err, "legacy: failed to parse toc offset") + return 0, 0, 0, errors.Wrapf(err, "legacy: failed to parse toc offset") } - return tocOffset, 0, nil + return tocOffset, tocOffset, 0, nil } func (gz *GzipDecompressor) FooterSize() int64 { @@ -165,27 +165,27 @@ func (gz *legacyGzipDecompressor) ParseTOC(r io.Reader) (toc *JTOC, tocDgst dige return parseTOCEStargz(r) } -func (gz *legacyGzipDecompressor) ParseFooter(p []byte) (tocOffset, tocSize int64, err error) { +func (gz *legacyGzipDecompressor) ParseFooter(p []byte) (blobPayloadSize, tocOffset, tocSize int64, err error) { if len(p) != legacyFooterSize { - return 0, 0, fmt.Errorf("legacy: invalid length %d cannot be parsed", len(p)) + return 0, 0, 0, fmt.Errorf("legacy: invalid length %d cannot be parsed", len(p)) } zr, err := gzip.NewReader(bytes.NewReader(p)) if err != nil { - return 0, 0, errors.Wrapf(err, "legacy: failed to get footer gzip reader") + return 0, 0, 0, errors.Wrapf(err, "legacy: failed to get footer gzip reader") } defer zr.Close() extra := zr.Header.Extra if len(extra) != 16+len("STARGZ") { - return 0, 0, fmt.Errorf("legacy: invalid stargz's extra field size") + return 0, 0, 0, fmt.Errorf("legacy: invalid stargz's extra field size") } if string(extra[16:]) != "STARGZ" { - return 0, 0, fmt.Errorf("legacy: magic string STARGZ not found") + return 0, 0, 0, fmt.Errorf("legacy: magic string STARGZ not found") } tocOffset, err = strconv.ParseInt(string(extra[:16]), 16, 64) if err != nil { - return 0, 0, errors.Wrapf(err, "legacy: failed to parse toc offset") + return 0, 0, 0, errors.Wrapf(err, "legacy: failed to parse toc offset") } - return tocOffset, 0, nil + return tocOffset, tocOffset, 0, nil } func (gz *legacyGzipDecompressor) FooterSize() int64 { diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/testutil.go b/vendor/github.com/containerd/stargz-snapshotter/estargz/testutil.go index 020729b7e..9224e456d 100644 --- a/vendor/github.com/containerd/stargz-snapshotter/estargz/testutil.go +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/testutil.go @@ -148,93 +148,96 @@ func testBuild(t *testing.T, controllers ...TestingController) { srcCompression := srcCompression for _, cl := range controllers { cl := cl - for _, prefix := range allowedPrefix { - prefix := prefix - t.Run(tt.name+"-"+fmt.Sprintf("compression=%v-prefix=%q-src=%d", cl, prefix, srcCompression), func(t *testing.T) { - tarBlob := buildTarStatic(t, tt.in, prefix) - // Test divideEntries() - entries, err := sortEntries(tarBlob, nil, nil) // identical order - if err != nil { - t.Fatalf("faield to parse tar: %v", err) - } - var merged []*entry - for _, part := range divideEntries(entries, 4) { - merged = append(merged, part...) - } - if !reflect.DeepEqual(entries, merged) { - for _, e := range entries { - t.Logf("Original: %v", e.header) + for _, srcTarFormat := range []tar.Format{tar.FormatUSTAR, tar.FormatPAX, tar.FormatGNU} { + srcTarFormat := srcTarFormat + for _, prefix := range allowedPrefix { + prefix := prefix + t.Run(tt.name+"-"+fmt.Sprintf("compression=%v,prefix=%q,src=%d,format=%s", cl, prefix, srcCompression, srcTarFormat), func(t *testing.T) { + tarBlob := buildTar(t, tt.in, prefix, srcTarFormat) + // Test divideEntries() + entries, err := sortEntries(tarBlob, nil, nil) // identical order + if err != nil { + t.Fatalf("failed to parse tar: %v", err) } - for _, e := range merged { - t.Logf("Merged: %v", e.header) + var merged []*entry + for _, part := range divideEntries(entries, 4) { + merged = append(merged, part...) + } + if !reflect.DeepEqual(entries, merged) { + for _, e := range entries { + t.Logf("Original: %v", e.header) + } + for _, e := range merged { + t.Logf("Merged: %v", e.header) + } + t.Errorf("divided entries couldn't be merged") + return } - t.Errorf("divided entries couldn't be merged") - return - } - // Prepare sample data - wantBuf := new(bytes.Buffer) - sw := NewWriterWithCompressor(wantBuf, cl) - sw.ChunkSize = tt.chunkSize - if err := sw.AppendTar(tarBlob); err != nil { - t.Fatalf("faield to append tar to want stargz: %v", err) - } - if _, err := sw.Close(); err != nil { - t.Fatalf("faield to prepare want stargz: %v", err) - } - wantData := wantBuf.Bytes() - want, err := Open(io.NewSectionReader( - bytes.NewReader(wantData), 0, int64(len(wantData))), - WithDecompressors(cl), - ) - if err != nil { - t.Fatalf("failed to parse the want stargz: %v", err) - } + // Prepare sample data + wantBuf := new(bytes.Buffer) + sw := NewWriterWithCompressor(wantBuf, cl) + sw.ChunkSize = tt.chunkSize + if err := sw.AppendTar(tarBlob); err != nil { + t.Fatalf("failed to append tar to want stargz: %v", err) + } + if _, err := sw.Close(); err != nil { + t.Fatalf("failed to prepare want stargz: %v", err) + } + wantData := wantBuf.Bytes() + want, err := Open(io.NewSectionReader( + bytes.NewReader(wantData), 0, int64(len(wantData))), + WithDecompressors(cl), + ) + if err != nil { + t.Fatalf("failed to parse the want stargz: %v", err) + } - // Prepare testing data - rc, err := Build(compressBlob(t, tarBlob, srcCompression), - WithChunkSize(tt.chunkSize), WithCompression(cl)) - if err != nil { - t.Fatalf("faield to build stargz: %v", err) - } - defer rc.Close() - gotBuf := new(bytes.Buffer) - if _, err := io.Copy(gotBuf, rc); err != nil { - t.Fatalf("failed to copy built stargz blob: %v", err) - } - gotData := gotBuf.Bytes() - got, err := Open(io.NewSectionReader( - bytes.NewReader(gotBuf.Bytes()), 0, int64(len(gotData))), - WithDecompressors(cl), - ) - if err != nil { - t.Fatalf("failed to parse the got stargz: %v", err) - } + // Prepare testing data + rc, err := Build(compressBlob(t, tarBlob, srcCompression), + WithChunkSize(tt.chunkSize), WithCompression(cl)) + if err != nil { + t.Fatalf("failed to build stargz: %v", err) + } + defer rc.Close() + gotBuf := new(bytes.Buffer) + if _, err := io.Copy(gotBuf, rc); err != nil { + t.Fatalf("failed to copy built stargz blob: %v", err) + } + gotData := gotBuf.Bytes() + got, err := Open(io.NewSectionReader( + bytes.NewReader(gotBuf.Bytes()), 0, int64(len(gotData))), + WithDecompressors(cl), + ) + if err != nil { + t.Fatalf("failed to parse the got stargz: %v", err) + } - // Check DiffID is properly calculated - rc.Close() - diffID := rc.DiffID() - wantDiffID := cl.DiffIDOf(t, gotData) - if diffID.String() != wantDiffID { - t.Errorf("DiffID = %q; want %q", diffID, wantDiffID) - } + // Check DiffID is properly calculated + rc.Close() + diffID := rc.DiffID() + wantDiffID := cl.DiffIDOf(t, gotData) + if diffID.String() != wantDiffID { + t.Errorf("DiffID = %q; want %q", diffID, wantDiffID) + } - // Compare as stargz - if !isSameVersion(t, cl, wantData, gotData) { - t.Errorf("built stargz hasn't same json") - return - } - if !isSameEntries(t, want, got) { - t.Errorf("built stargz isn't same as the original") - return - } + // Compare as stargz + if !isSameVersion(t, cl, wantData, gotData) { + t.Errorf("built stargz hasn't same json") + return + } + if !isSameEntries(t, want, got) { + t.Errorf("built stargz isn't same as the original") + return + } - // Compare as tar.gz - if !isSameTarGz(t, cl, wantData, gotData) { - t.Errorf("built stargz isn't same tar.gz") - return - } - }) + // Compare as tar.gz + if !isSameTarGz(t, cl, wantData, gotData) { + t.Errorf("built stargz isn't same tar.gz") + return + } + }) + } } } } @@ -526,7 +529,7 @@ func testDigestAndVerify(t *testing.T, controllers ...TestingController) { checks: []check{ checkStargzTOC, checkVerifyTOC, - checkVerifyInvalidStargzFail(buildTarStatic(t, tarOf( + checkVerifyInvalidStargzFail(buildTar(t, tarOf( dir("test2/"), // modified ), allowedPrefix[0])), }, @@ -544,7 +547,7 @@ func testDigestAndVerify(t *testing.T, controllers ...TestingController) { checks: []check{ checkStargzTOC, checkVerifyTOC, - checkVerifyInvalidStargzFail(buildTarStatic(t, tarOf( + checkVerifyInvalidStargzFail(buildTar(t, tarOf( file("baz.txt", ""), file("foo.txt", "M"), // modified dir("test/"), @@ -567,7 +570,7 @@ func testDigestAndVerify(t *testing.T, controllers ...TestingController) { checks: []check{ checkStargzTOC, checkVerifyTOC, - checkVerifyInvalidStargzFail(buildTarStatic(t, tarOf( + checkVerifyInvalidStargzFail(buildTar(t, tarOf( file("baz.txt", "bazbazbazMMMbazbazbaz"), // modified file("foo.txt", "a"), dir("test/"), @@ -593,7 +596,7 @@ func testDigestAndVerify(t *testing.T, controllers ...TestingController) { checks: []check{ checkStargzTOC, checkVerifyTOC, - checkVerifyInvalidStargzFail(buildTarStatic(t, tarOf( + checkVerifyInvalidStargzFail(buildTar(t, tarOf( file("baz.txt", "bazbazbazbazbazbazbaz"), file("foo.txt", "a"), symlink("barlink", "test/bar.txt"), @@ -615,30 +618,33 @@ func testDigestAndVerify(t *testing.T, controllers ...TestingController) { cl := cl for _, prefix := range allowedPrefix { prefix := prefix - t.Run(tt.name+"-"+fmt.Sprintf("compression=%v-prefix=%q", cl, prefix), func(t *testing.T) { - // Get original tar file and chunk digests - dgstMap := make(map[string]digest.Digest) - tarBlob := buildTarStatic(t, tt.tarInit(t, dgstMap), prefix) - - rc, err := Build(compressBlob(t, tarBlob, srcCompression), - WithChunkSize(chunkSize), WithCompression(cl)) - if err != nil { - t.Fatalf("failed to convert stargz: %v", err) - } - tocDigest := rc.TOCDigest() - defer rc.Close() - buf := new(bytes.Buffer) - if _, err := io.Copy(buf, rc); err != nil { - t.Fatalf("failed to copy built stargz blob: %v", err) - } - newStargz := buf.Bytes() - // NoPrefetchLandmark is added during `Bulid`, which is expected behaviour. - dgstMap[chunkID(NoPrefetchLandmark, 0, int64(len([]byte{landmarkContents})))] = digest.FromBytes([]byte{landmarkContents}) + for _, srcTarFormat := range []tar.Format{tar.FormatUSTAR, tar.FormatPAX, tar.FormatGNU} { + srcTarFormat := srcTarFormat + t.Run(tt.name+"-"+fmt.Sprintf("compression=%v,prefix=%q,format=%s", cl, prefix, srcTarFormat), func(t *testing.T) { + // Get original tar file and chunk digests + dgstMap := make(map[string]digest.Digest) + tarBlob := buildTar(t, tt.tarInit(t, dgstMap), prefix, srcTarFormat) + + rc, err := Build(compressBlob(t, tarBlob, srcCompression), + WithChunkSize(chunkSize), WithCompression(cl)) + if err != nil { + t.Fatalf("failed to convert stargz: %v", err) + } + tocDigest := rc.TOCDigest() + defer rc.Close() + buf := new(bytes.Buffer) + if _, err := io.Copy(buf, rc); err != nil { + t.Fatalf("failed to copy built stargz blob: %v", err) + } + newStargz := buf.Bytes() + // NoPrefetchLandmark is added during `Bulid`, which is expected behaviour. + dgstMap[chunkID(NoPrefetchLandmark, 0, int64(len([]byte{landmarkContents})))] = digest.FromBytes([]byte{landmarkContents}) - for _, check := range tt.checks { - check(t, newStargz, tocDigest, dgstMap, cl) - } - }) + for _, check := range tt.checks { + check(t, newStargz, tocDigest, dgstMap, cl) + } + }) + } } } } @@ -1058,7 +1064,7 @@ func parseStargz(sgz *io.SectionReader, controller TestingController) (decodedJT if _, err := sgz.ReadAt(footer, sgz.Size()-fSize); err != nil { return nil, 0, errors.Wrap(err, "error reading footer") } - tocOffset, _, err := controller.ParseFooter(footer[positive(int64(len(footer))-fSize):]) + _, tocOffset, _, err := controller.ParseFooter(footer[positive(int64(len(footer))-fSize):]) if err != nil { return nil, 0, errors.Wrapf(err, "failed to parse footer") } @@ -1085,11 +1091,15 @@ func testWriteAndOpen(t *testing.T, controllers ...TestingController) { in []tarEntry want []stargzCheck wantNumGz int // expected number of streams + + wantNumGzLossLess int // expected number of streams (> 0) in lossless mode if it's different from wantNumGz + wantFailOnLossLess bool }{ { - name: "empty", - in: tarOf(), - wantNumGz: 2, // TOC + footer + name: "empty", + in: tarOf(), + wantNumGz: 2, // empty tar + TOC + footer + wantNumGzLossLess: 3, // empty tar + TOC + footer want: checks( numTOCEntries(0), ), @@ -1224,26 +1234,29 @@ func testWriteAndOpen(t *testing.T, controllers ...TestingController) { { name: "block_char_fifo", in: tarOf( - tarEntryFunc(func(w *tar.Writer, prefix string) error { + tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { return w.WriteHeader(&tar.Header{ Name: prefix + "b", Typeflag: tar.TypeBlock, Devmajor: 123, Devminor: 456, + Format: format, }) }), - tarEntryFunc(func(w *tar.Writer, prefix string) error { + tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { return w.WriteHeader(&tar.Header{ Name: prefix + "c", Typeflag: tar.TypeChar, Devmajor: 111, Devminor: 222, + Format: format, }) }), - tarEntryFunc(func(w *tar.Writer, prefix string) error { + tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { return w.WriteHeader(&tar.Header{ Name: prefix + "f", Typeflag: tar.TypeFifo, + Format: format, }) }), ), @@ -1278,6 +1291,29 @@ func testWriteAndOpen(t *testing.T, controllers ...TestingController) { hasMode("foo3/bar5", os.FileMode(0755)), ), }, + { + name: "lossy", + in: tarOf( + dir("bar/", sampleOwner), + dir("foo/", sampleOwner), + file("foo/bar.txt", content, sampleOwner), + file(TOCTarName, "dummy"), // ignored by the writer. (lossless write returns error) + ), + wantNumGz: 4, // both dirs, foo.txt alone, TOC, footer + want: checks( + numTOCEntries(3), + hasDir("bar/"), + hasDir("foo/"), + hasFileLen("foo/bar.txt", len(content)), + entryHasChildren("", "bar", "foo"), + entryHasChildren("foo", "bar.txt"), + hasChunkEntries("foo/bar.txt", 1), + hasEntryOwner("bar/", sampleOwner), + hasEntryOwner("foo/", sampleOwner), + hasEntryOwner("foo/bar.txt", sampleOwner), + ), + wantFailOnLossLess: true, + }, } for _, tt := range tests { @@ -1285,47 +1321,90 @@ func testWriteAndOpen(t *testing.T, controllers ...TestingController) { cl := cl for _, prefix := range allowedPrefix { prefix := prefix - t.Run(tt.name+"-"+fmt.Sprintf("compression=%v-prefix=%q", cl, prefix), func(t *testing.T) { - tr, cancel := buildTar(t, tt.in, prefix) - defer cancel() - var stargzBuf bytes.Buffer - w := NewWriterWithCompressor(&stargzBuf, cl) - w.ChunkSize = tt.chunkSize - if err := w.AppendTar(tr); err != nil { - t.Fatalf("Append: %v", err) - } - if _, err := w.Close(); err != nil { - t.Fatalf("Writer.Close: %v", err) - } - b := stargzBuf.Bytes() + for _, srcTarFormat := range []tar.Format{tar.FormatUSTAR, tar.FormatPAX, tar.FormatGNU} { + srcTarFormat := srcTarFormat + for _, lossless := range []bool{true, false} { + t.Run(tt.name+"-"+fmt.Sprintf("compression=%v,prefix=%q,lossless=%v,format=%s", cl, prefix, lossless, srcTarFormat), func(t *testing.T) { + var tr io.Reader = buildTar(t, tt.in, prefix, srcTarFormat) + origTarDgstr := digest.Canonical.Digester() + tr = io.TeeReader(tr, origTarDgstr.Hash()) + var stargzBuf bytes.Buffer + w := NewWriterWithCompressor(&stargzBuf, cl) + w.ChunkSize = tt.chunkSize + if lossless { + err := w.AppendTarLossLess(tr) + if tt.wantFailOnLossLess { + if err != nil { + return // expected to fail + } + t.Fatalf("Append wanted to fail on lossless") + } + if err != nil { + t.Fatalf("Append(lossless): %v", err) + } + } else { + if err := w.AppendTar(tr); err != nil { + t.Fatalf("Append: %v", err) + } + } + if _, err := w.Close(); err != nil { + t.Fatalf("Writer.Close: %v", err) + } + b := stargzBuf.Bytes() + + if lossless { + // Check if the result blob reserves original tar metadata + rc, err := Unpack(io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b))), cl) + if err != nil { + t.Errorf("failed to decompress blob: %v", err) + return + } + defer rc.Close() + resultDgstr := digest.Canonical.Digester() + if _, err := io.Copy(resultDgstr.Hash(), rc); err != nil { + t.Errorf("failed to read result decompressed blob: %v", err) + return + } + if resultDgstr.Digest() != origTarDgstr.Digest() { + t.Errorf("lossy compression occurred: digest=%v; want %v", + resultDgstr.Digest(), origTarDgstr.Digest()) + return + } + } - diffID := w.DiffID() - wantDiffID := cl.DiffIDOf(t, b) - if diffID != wantDiffID { - t.Errorf("DiffID = %q; want %q", diffID, wantDiffID) - } + diffID := w.DiffID() + wantDiffID := cl.DiffIDOf(t, b) + if diffID != wantDiffID { + t.Errorf("DiffID = %q; want %q", diffID, wantDiffID) + } - got := cl.CountStreams(t, b) - if got != tt.wantNumGz { - t.Errorf("number of streams = %d; want %d", got, tt.wantNumGz) - } + got := cl.CountStreams(t, b) + wantNumGz := tt.wantNumGz + if lossless && tt.wantNumGzLossLess > 0 { + wantNumGz = tt.wantNumGzLossLess + } + if got != wantNumGz { + t.Errorf("number of streams = %d; want %d", got, wantNumGz) + } - telemetry, checkCalled := newCalledTelemetry() - r, err := Open( - io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b))), - WithDecompressors(cl), - WithTelemetry(telemetry), - ) - if err != nil { - t.Fatalf("stargz.Open: %v", err) - } - if err := checkCalled(); err != nil { - t.Errorf("telemetry failure: %v", err) - } - for _, want := range tt.want { - want.check(t, r) + telemetry, checkCalled := newCalledTelemetry() + r, err := Open( + io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b))), + WithDecompressors(cl), + WithTelemetry(telemetry), + ) + if err != nil { + t.Fatalf("stargz.Open: %v", err) + } + if err := checkCalled(); err != nil { + t.Errorf("telemetry failure: %v", err) + } + for _, want := range tt.want { + want.check(t, r) + } + }) } - }) + } } } } @@ -1655,49 +1734,41 @@ func hasEntryOwner(entry string, owner owner) stargzCheck { func tarOf(s ...tarEntry) []tarEntry { return s } type tarEntry interface { - appendTar(tw *tar.Writer, prefix string) error + appendTar(tw *tar.Writer, prefix string, format tar.Format) error } -type tarEntryFunc func(*tar.Writer, string) error - -func (f tarEntryFunc) appendTar(tw *tar.Writer, prefix string) error { return f(tw, prefix) } +type tarEntryFunc func(*tar.Writer, string, tar.Format) error -func buildTar(t *testing.T, ents []tarEntry, prefix string) (r io.Reader, cancel func()) { - pr, pw := io.Pipe() - go func() { - tw := tar.NewWriter(pw) - for _, ent := range ents { - if err := ent.appendTar(tw, prefix); err != nil { - t.Errorf("building input tar: %v", err) - pw.Close() - return - } - } - if err := tw.Close(); err != nil { - t.Errorf("closing write of input tar: %v", err) - } - pw.Close() - }() - return pr, func() { go pr.Close(); go pw.Close() } +func (f tarEntryFunc) appendTar(tw *tar.Writer, prefix string, format tar.Format) error { + return f(tw, prefix, format) } -func buildTarStatic(t *testing.T, ents []tarEntry, prefix string) *io.SectionReader { +func buildTar(t *testing.T, ents []tarEntry, prefix string, opts ...interface{}) *io.SectionReader { + format := tar.FormatUnknown + for _, opt := range opts { + switch v := opt.(type) { + case tar.Format: + format = v + default: + panic(fmt.Errorf("unsupported opt for buildTar: %v", opt)) + } + } buf := new(bytes.Buffer) tw := tar.NewWriter(buf) for _, ent := range ents { - if err := ent.appendTar(tw, prefix); err != nil { + if err := ent.appendTar(tw, prefix, format); err != nil { t.Fatalf("building input tar: %v", err) } } if err := tw.Close(); err != nil { t.Errorf("closing write of input tar: %v", err) } - data := buf.Bytes() + data := append(buf.Bytes(), make([]byte, 100)...) // append empty bytes at the tail to see lossless works return io.NewSectionReader(bytes.NewReader(data), 0, int64(len(data))) } func dir(name string, opts ...interface{}) tarEntry { - return tarEntryFunc(func(tw *tar.Writer, prefix string) error { + return tarEntryFunc(func(tw *tar.Writer, prefix string, format tar.Format) error { var o owner mode := os.FileMode(0755) for _, opt := range opts { @@ -1723,6 +1794,7 @@ func dir(name string, opts ...interface{}) tarEntry { Mode: tm, Uid: o.uid, Gid: o.gid, + Format: format, }) }) } @@ -1737,7 +1809,7 @@ type owner struct { } func file(name, contents string, opts ...interface{}) tarEntry { - return tarEntryFunc(func(tw *tar.Writer, prefix string) error { + return tarEntryFunc(func(tw *tar.Writer, prefix string, format tar.Format) error { var xattrs xAttr var o owner mode := os.FileMode(0644) @@ -1760,6 +1832,9 @@ func file(name, contents string, opts ...interface{}) tarEntry { if err != nil { return err } + if len(xattrs) > 0 { + format = tar.FormatPAX // only PAX supports xattrs + } if err := tw.WriteHeader(&tar.Header{ Typeflag: tar.TypeReg, Name: prefix + name, @@ -1768,6 +1843,7 @@ func file(name, contents string, opts ...interface{}) tarEntry { Size: int64(len(contents)), Uid: o.uid, Gid: o.gid, + Format: format, }); err != nil { return err } @@ -1777,78 +1853,76 @@ func file(name, contents string, opts ...interface{}) tarEntry { } func symlink(name, target string) tarEntry { - return tarEntryFunc(func(tw *tar.Writer, prefix string) error { + return tarEntryFunc(func(tw *tar.Writer, prefix string, format tar.Format) error { return tw.WriteHeader(&tar.Header{ Typeflag: tar.TypeSymlink, Name: prefix + name, Linkname: target, Mode: 0644, + Format: format, }) }) } func link(name string, linkname string) tarEntry { now := time.Now() - return tarEntryFunc(func(w *tar.Writer, prefix string) error { + return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { return w.WriteHeader(&tar.Header{ - Typeflag: tar.TypeLink, - Name: prefix + name, - Linkname: linkname, - ModTime: now, - AccessTime: now, - ChangeTime: now, + Typeflag: tar.TypeLink, + Name: prefix + name, + Linkname: linkname, + ModTime: now, + Format: format, }) }) } func chardev(name string, major, minor int64) tarEntry { now := time.Now() - return tarEntryFunc(func(w *tar.Writer, prefix string) error { + return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { return w.WriteHeader(&tar.Header{ - Typeflag: tar.TypeChar, - Name: prefix + name, - Devmajor: major, - Devminor: minor, - ModTime: now, - AccessTime: now, - ChangeTime: now, + Typeflag: tar.TypeChar, + Name: prefix + name, + Devmajor: major, + Devminor: minor, + ModTime: now, + Format: format, }) }) } func blockdev(name string, major, minor int64) tarEntry { now := time.Now() - return tarEntryFunc(func(w *tar.Writer, prefix string) error { + return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { return w.WriteHeader(&tar.Header{ - Typeflag: tar.TypeBlock, - Name: prefix + name, - Devmajor: major, - Devminor: minor, - ModTime: now, - AccessTime: now, - ChangeTime: now, + Typeflag: tar.TypeBlock, + Name: prefix + name, + Devmajor: major, + Devminor: minor, + ModTime: now, + Format: format, }) }) } func fifo(name string) tarEntry { now := time.Now() - return tarEntryFunc(func(w *tar.Writer, prefix string) error { + return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { return w.WriteHeader(&tar.Header{ - Typeflag: tar.TypeFifo, - Name: prefix + name, - ModTime: now, - AccessTime: now, - ChangeTime: now, + Typeflag: tar.TypeFifo, + Name: prefix + name, + ModTime: now, + Format: format, }) }) } func prefetchLandmark() tarEntry { - return tarEntryFunc(func(w *tar.Writer, prefix string) error { + return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { if err := w.WriteHeader(&tar.Header{ Name: PrefetchLandmark, Typeflag: tar.TypeReg, Size: int64(len([]byte{landmarkContents})), + Format: format, }); err != nil { return err } @@ -1861,11 +1935,12 @@ func prefetchLandmark() tarEntry { } func noPrefetchLandmark() tarEntry { - return tarEntryFunc(func(w *tar.Writer, prefix string) error { + return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { if err := w.WriteHeader(&tar.Header{ Name: NoPrefetchLandmark, Typeflag: tar.TypeReg, Size: int64(len([]byte{landmarkContents})), + Format: format, }); err != nil { return err } @@ -1899,11 +1974,12 @@ func regDigest(t *testing.T, name string, contentStr string, digestMap map[strin n += size } - return tarEntryFunc(func(w *tar.Writer, prefix string) error { + return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { if err := w.WriteHeader(&tar.Header{ Typeflag: tar.TypeReg, Name: prefix + name, Size: int64(len(content)), + Format: format, }); err != nil { return err } diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/types.go b/vendor/github.com/containerd/stargz-snapshotter/estargz/types.go index 1b1075a64..384ff7fd7 100644 --- a/vendor/github.com/containerd/stargz-snapshotter/estargz/types.go +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/types.go @@ -290,7 +290,7 @@ type Compressor interface { WriteTOCAndFooter(w io.Writer, off int64, toc *JTOC, diffHash hash.Hash) (tocDgst digest.Digest, err error) } -// Deompressor represents the helper mothods to be used for parsing eStargz. +// Decompressor represents the helper mothods to be used for parsing eStargz. type Decompressor interface { // Reader returns ReadCloser to be used for decompressing file payload. Reader(r io.Reader) (io.ReadCloser, error) @@ -299,10 +299,12 @@ type Decompressor interface { FooterSize() int64 // ParseFooter parses the footer and returns the offset and (compressed) size of TOC. + // payloadBlobSize is the (compressed) size of the blob payload (i.e. the size between + // the top until the TOC JSON). // // Here, tocSize is optional. If tocSize <= 0, it's by default the size of the range // from tocOffset until the beginning of the footer (blob size - tocOff - FooterSize). - ParseFooter(p []byte) (tocOffset, tocSize int64, err error) + ParseFooter(p []byte) (blobPayloadSize, tocOffset, tocSize int64, err error) // ParseTOC parses TOC from the passed reader. The reader provides the partial contents // of the underlying blob that has the range specified by ParseFooter method. diff --git a/vendor/github.com/containers/common/libimage/copier.go b/vendor/github.com/containers/common/libimage/copier.go index 636b97bfd..4f5c7d0a1 100644 --- a/vendor/github.com/containers/common/libimage/copier.go +++ b/vendor/github.com/containers/common/libimage/copier.go @@ -2,7 +2,6 @@ package libimage import ( "context" - "encoding/json" "io" "os" "strings" diff --git a/vendor/github.com/containers/common/libimage/image_config.go b/vendor/github.com/containers/common/libimage/image_config.go index b57121182..140202440 100644 --- a/vendor/github.com/containers/common/libimage/image_config.go +++ b/vendor/github.com/containers/common/libimage/image_config.go @@ -1,7 +1,6 @@ package libimage import ( - "encoding/json" "fmt" "path/filepath" "strconv" diff --git a/vendor/github.com/containers/common/libimage/inspect.go b/vendor/github.com/containers/common/libimage/inspect.go index 349709155..007cbdd89 100644 --- a/vendor/github.com/containers/common/libimage/inspect.go +++ b/vendor/github.com/containers/common/libimage/inspect.go @@ -2,7 +2,6 @@ package libimage import ( "context" - "encoding/json" "time" "github.com/containers/image/v5/manifest" @@ -51,19 +50,39 @@ type RootFS struct { Layers []digest.Digest `json:"Layers"` } -// Inspect inspects the image. Use `withSize` to also perform the -// comparatively expensive size computation of the image. -func (i *Image) Inspect(ctx context.Context, withSize bool) (*ImageData, error) { +// InspectOptions allow for customizing inspecting images. +type InspectOptions struct { + // Compute the size of the image (expensive). + WithSize bool + // Compute the parent of the image (expensive). + WithParent bool +} + +// Inspect inspects the image. +func (i *Image) Inspect(ctx context.Context, options *InspectOptions) (*ImageData, error) { logrus.Debugf("Inspecting image %s", i.ID()) + if options == nil { + options = &InspectOptions{} + } + if i.cached.completeInspectData != nil { - if withSize && i.cached.completeInspectData.Size == int64(-1) { + if options.WithSize && i.cached.completeInspectData.Size == int64(-1) { size, err := i.Size() if err != nil { return nil, err } i.cached.completeInspectData.Size = size } + if options.WithParent && i.cached.completeInspectData.Parent == "" { + parentImage, err := i.Parent(ctx) + if err != nil { + return nil, err + } + if parentImage != nil { + i.cached.completeInspectData.Parent = parentImage.ID() + } + } return i.cached.completeInspectData, nil } @@ -76,10 +95,7 @@ func (i *Image) Inspect(ctx context.Context, withSize bool) (*ImageData, error) if err != nil { return nil, err } - parentImage, err := i.Parent(ctx) - if err != nil { - return nil, err - } + repoTags, err := i.RepoTags() if err != nil { return nil, err @@ -94,7 +110,7 @@ func (i *Image) Inspect(ctx context.Context, withSize bool) (*ImageData, error) } size := int64(-1) - if withSize { + if options.WithSize { size, err = i.Size() if err != nil { return nil, err @@ -125,8 +141,14 @@ func (i *Image) Inspect(ctx context.Context, withSize bool) (*ImageData, error) NamesHistory: i.NamesHistory(), } - if parentImage != nil { - data.Parent = parentImage.ID() + if options.WithParent { + parentImage, err := i.Parent(ctx) + if err != nil { + return nil, err + } + if parentImage != nil { + data.Parent = parentImage.ID() + } } // Determine the format of the image. How we determine certain data diff --git a/vendor/github.com/containers/common/libimage/load.go b/vendor/github.com/containers/common/libimage/load.go index f2b57c43a..74a1870e0 100644 --- a/vendor/github.com/containers/common/libimage/load.go +++ b/vendor/github.com/containers/common/libimage/load.go @@ -35,6 +35,17 @@ func (r *Runtime) Load(ctx context.Context, path string, options *LoadOptions) ( var loadErrors []error for _, f := range []func() ([]string, string, error){ + // DOCKER-ARCHIVE - must be first (see containers/podman/issues/10809) + func() ([]string, string, error) { + logrus.Debugf("-> Attempting to load %q as a Docker archive", path) + ref, err := dockerArchiveTransport.ParseReference(path) + if err != nil { + return nil, dockerArchiveTransport.Transport.Name(), err + } + images, err := r.loadMultiImageDockerArchive(ctx, ref, &options.CopyOptions) + return images, dockerArchiveTransport.Transport.Name(), err + }, + // OCI func() ([]string, string, error) { logrus.Debugf("-> Attempting to load %q as an OCI directory", path) @@ -67,17 +78,6 @@ func (r *Runtime) Load(ctx context.Context, path string, options *LoadOptions) ( images, err := r.copyFromDefault(ctx, ref, &options.CopyOptions) return images, dirTransport.Transport.Name(), err }, - - // DOCKER-ARCHIVE - func() ([]string, string, error) { - logrus.Debugf("-> Attempting to load %q as a Docker archive", path) - ref, err := dockerArchiveTransport.ParseReference(path) - if err != nil { - return nil, dockerArchiveTransport.Transport.Name(), err - } - images, err := r.loadMultiImageDockerArchive(ctx, ref, &options.CopyOptions) - return images, dockerArchiveTransport.Transport.Name(), err - }, } { loadedImages, transportName, err := f() if err == nil { diff --git a/vendor/github.com/containers/common/libimage/pull.go b/vendor/github.com/containers/common/libimage/pull.go index 1c322c37e..1d1bc201b 100644 --- a/vendor/github.com/containers/common/libimage/pull.go +++ b/vendor/github.com/containers/common/libimage/pull.go @@ -477,10 +477,10 @@ func (r *Runtime) copySingleImageFromRegistry(ctx context.Context, imageName str if pullPolicy == config.PullPolicyNever { if localImage != nil { - logrus.Debugf("Pull policy %q but no local image has been found for %s", pullPolicy, imageName) + logrus.Debugf("Pull policy %q and %s resolved to local image %s", pullPolicy, imageName, resolvedImageName) return []string{resolvedImageName}, nil } - logrus.Debugf("Pull policy %q and %s resolved to local image %s", pullPolicy, imageName, resolvedImageName) + logrus.Debugf("Pull policy %q but no local image has been found for %s", pullPolicy, imageName) return nil, errors.Wrap(storage.ErrImageUnknown, imageName) } diff --git a/vendor/github.com/containers/common/libimage/runtime.go b/vendor/github.com/containers/common/libimage/runtime.go index dabadbec0..7f25df200 100644 --- a/vendor/github.com/containers/common/libimage/runtime.go +++ b/vendor/github.com/containers/common/libimage/runtime.go @@ -13,10 +13,14 @@ import ( "github.com/containers/image/v5/types" "github.com/containers/storage" deepcopy "github.com/jinzhu/copier" + jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) +// Faster than the standard library, see https://github.com/json-iterator/go. +var json = jsoniter.ConfigCompatibleWithStandardLibrary + // RuntimeOptions allow for creating a customized Runtime. type RuntimeOptions struct { // The base system context of the runtime which will be used throughout diff --git a/vendor/github.com/containers/common/libimage/search.go b/vendor/github.com/containers/common/libimage/search.go index df29bc7da..7e20e4331 100644 --- a/vendor/github.com/containers/common/libimage/search.go +++ b/vendor/github.com/containers/common/libimage/search.go @@ -284,8 +284,9 @@ func searchRepositoryTags(ctx context.Context, sys *types.SystemContext, registr paramsArr := []SearchResult{} for i := 0; i < limit; i++ { params := SearchResult{ - Name: imageRef.DockerReference().Name(), - Tag: tags[i], + Name: imageRef.DockerReference().Name(), + Tag: tags[i], + Index: registry, } paramsArr = append(paramsArr, params) } diff --git a/vendor/github.com/containers/common/pkg/config/config.go b/vendor/github.com/containers/common/pkg/config/config.go index 3b4c7fa04..d5be77edd 100644 --- a/vendor/github.com/containers/common/pkg/config/config.go +++ b/vendor/github.com/containers/common/pkg/config/config.go @@ -140,7 +140,7 @@ type ContainersConfig struct { // will be truncated. It can be expressed as a human-friendly string // that is parsed to bytes. // Negative values indicate that the log file won't be truncated. - LogSizeMax int64 `toml:"log_size_max,omitempty"` + LogSizeMax int64 `toml:"log_size_max,omitempty,omitzero"` // Specifies default format tag for container log messages. // This is useful for creating a specific tag for container log messages. @@ -155,7 +155,7 @@ type ContainersConfig struct { // PidsLimit is the number of processes each container is restricted to // by the cgroup process number controller. - PidsLimit int64 `toml:"pids_limit,omitempty"` + PidsLimit int64 `toml:"pids_limit,omitempty,omitzero"` // PidNS indicates how to create a pid namespace for the container PidNS string `toml:"pidns,omitempty"` @@ -192,7 +192,7 @@ type ContainersConfig struct { UserNS string `toml:"userns,omitempty"` // UserNSSize how many UIDs to allocate for automatically created UserNS - UserNSSize int `toml:"userns_size,omitempty"` + UserNSSize int `toml:"userns_size,omitempty,omitzero"` } // EngineConfig contains configuration options used to set up a engine runtime @@ -259,7 +259,7 @@ type EngineConfig struct { // ImageParallelCopies indicates the maximum number of image layers // to be copied simultaneously. If this is zero, container engines // will fall back to containers/image defaults. - ImageParallelCopies uint `toml:"image_parallel_copies,omitempty"` + ImageParallelCopies uint `toml:"image_parallel_copies,omitempty,omitzero"` // ImageDefaultFormat specified the manifest Type (oci, v2s2, or v2s1) // to use when pulling, pushing, building container images. By default @@ -308,7 +308,7 @@ type EngineConfig struct { // NumLocks is the number of locks to make available for containers and // pods. - NumLocks uint32 `toml:"num_locks,omitempty"` + NumLocks uint32 `toml:"num_locks,omitempty,omitzero"` // OCIRuntime is the OCI runtime to use. OCIRuntime string `toml:"runtime,omitempty"` @@ -380,7 +380,7 @@ type EngineConfig struct { // ServiceTimeout is the number of seconds to wait without a connection // before the `podman system service` times out and exits - ServiceTimeout uint `toml:"service_timeout,omitempty"` + ServiceTimeout uint `toml:"service_timeout,omitempty,omitzero"` // StaticDir is the path to a persistent directory to store container // files. @@ -388,7 +388,7 @@ type EngineConfig struct { // StopTimeout is the number of seconds to wait for container to exit // before sending kill signal. - StopTimeout uint `toml:"stop_timeout,omitempty"` + StopTimeout uint `toml:"stop_timeout,omitempty,omitzero"` // ImageCopyTmpDir is the default location for storing temporary // container image content, Can be overridden with the TMPDIR @@ -413,7 +413,7 @@ type EngineConfig struct { // ChownCopiedFiles tells the container engine whether to chown files copied // into a container to the container's primary uid/gid. - ChownCopiedFiles bool `toml:"chown_copied_files"` + ChownCopiedFiles bool `toml:"chown_copied_files,omitempty"` } // SetOptions contains a subset of options in a Config. It's used to indicate if @@ -492,13 +492,13 @@ type SecretConfig struct { // MachineConfig represents the "machine" TOML config table type MachineConfig struct { // Number of CPU's a machine is created with. - CPUs uint64 `toml:"cpus,omitempty"` + CPUs uint64 `toml:"cpus,omitempty,omitzero"` // DiskSize is the size of the disk in GB created when init-ing a podman-machine VM - DiskSize uint64 `toml:"disk_size,omitempty"` + DiskSize uint64 `toml:"disk_size,omitempty,omitzero"` // MachineImage is the image used when init-ing a podman-machine VM Image string `toml:"image,omitempty"` // Memory in MB a machine is created with. - Memory uint64 `toml:"memory,omitempty"` + Memory uint64 `toml:"memory,omitempty,omitzero"` } // Destination represents destination for remote service @@ -1067,17 +1067,6 @@ func ReadCustomConfig() (*Config, error) { if err != nil { return nil, err } - // hack since Ommitempty does not seem to work with Write - c, err := Default() - if err != nil { - if os.IsNotExist(errors.Cause(err)) { - c, err = DefaultConfig() - } - if err != nil { - return nil, err - } - } - newConfig := &Config{} if _, err := os.Stat(path); err == nil { if err := readConfigFromFile(path, newConfig); err != nil { @@ -1088,11 +1077,6 @@ func ReadCustomConfig() (*Config, error) { return nil, err } } - newConfig.Containers.LogSizeMax = c.Containers.LogSizeMax - newConfig.Containers.PidsLimit = c.Containers.PidsLimit - newConfig.Containers.UserNSSize = c.Containers.UserNSSize - newConfig.Engine.NumLocks = c.Engine.NumLocks - newConfig.Engine.StopTimeout = c.Engine.StopTimeout return newConfig, nil } diff --git a/vendor/github.com/containers/image/v5/docker/docker_image_dest.go b/vendor/github.com/containers/image/v5/docker/docker_image_dest.go index 80701a761..7111c5612 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_image_dest.go +++ b/vendor/github.com/containers/image/v5/docker/docker_image_dest.go @@ -16,7 +16,6 @@ import ( "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/internal/blobinfocache" - "github.com/containers/image/v5/internal/iolimits" "github.com/containers/image/v5/internal/putblobdigest" "github.com/containers/image/v5/internal/uploadreader" "github.com/containers/image/v5/manifest" @@ -432,8 +431,9 @@ func (d *dockerImageDestination) PutManifest(ctx context.Context, m []byte, inst } defer res.Body.Close() if !successStatus(res.StatusCode) { - err = errors.Wrapf(registryHTTPResponseToError(res), "uploading manifest %s to %s", refTail, d.ref.ref.Name()) - if isManifestInvalidError(errors.Cause(err)) { + rawErr := registryHTTPResponseToError(res) + err := errors.Wrapf(rawErr, "uploading manifest %s to %s", refTail, d.ref.ref.Name()) + if isManifestInvalidError(rawErr) { err = types.ManifestTypeRejectedError{Err: err} } return err @@ -648,10 +648,6 @@ sigExists: } defer res.Body.Close() if res.StatusCode != http.StatusCreated { - body, err := iolimits.ReadAtMost(res.Body, iolimits.MaxErrorBodySize) - if err == nil { - logrus.Debugf("Error body %s", string(body)) - } logrus.Debugf("Error uploading signature, status %d, %#v", res.StatusCode, res) return errors.Wrapf(registryHTTPResponseToError(res), "uploading signature to %s in %s", path, d.c.registry) } diff --git a/vendor/github.com/containers/image/v5/docker/docker_image_src.go b/vendor/github.com/containers/image/v5/docker/docker_image_src.go index 1333cf9e2..f2e9eb17b 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_image_src.go +++ b/vendor/github.com/containers/image/v5/docker/docker_image_src.go @@ -370,12 +370,6 @@ func (s *dockerImageSource) GetBlobAt(ctx context.Context, info types.BlobInfo, if err != nil { return nil, nil, err } - if err := httpResponseToError(res, "Error fetching partial blob"); err != nil { - if res.Body != nil { - res.Body.Close() - } - return nil, nil, err - } switch res.StatusCode { case http.StatusOK: @@ -396,9 +390,16 @@ func (s *dockerImageSource) GetBlobAt(ctx context.Context, info types.BlobInfo, go handle206Response(streams, errs, res.Body, chunks, mediaType, params) return streams, errs, nil + case http.StatusBadRequest: + res.Body.Close() + return nil, nil, internalTypes.BadPartialRequestError{Status: res.Status} default: + err := httpResponseToError(res, "Error fetching partial blob") + if err == nil { + err = errors.Errorf("invalid status code returned when fetching blob %d (%s)", res.StatusCode, http.StatusText(res.StatusCode)) + } res.Body.Close() - return nil, nil, errors.Errorf("invalid status code returned when fetching blob %d (%s)", res.StatusCode, http.StatusText(res.StatusCode)) + return nil, nil, err } } diff --git a/vendor/github.com/containers/image/v5/docker/errors.go b/vendor/github.com/containers/image/v5/docker/errors.go index 6f2c5fde5..6f707db7d 100644 --- a/vendor/github.com/containers/image/v5/docker/errors.go +++ b/vendor/github.com/containers/image/v5/docker/errors.go @@ -5,7 +5,6 @@ import ( "fmt" "net/http" - internalTypes "github.com/containers/image/v5/internal/types" "github.com/docker/distribution/registry/client" perrors "github.com/pkg/errors" ) @@ -29,19 +28,16 @@ func (e ErrUnauthorizedForCredentials) Error() string { // httpResponseToError translates the https.Response into an error, possibly prefixing it with the supplied context. It returns // nil if the response is not considered an error. +// NOTE: Almost all callers in this package should use registryHTTPResponseToError instead. func httpResponseToError(res *http.Response, context string) error { switch res.StatusCode { case http.StatusOK: return nil - case http.StatusPartialContent: - return nil case http.StatusTooManyRequests: return ErrTooManyRequests case http.StatusUnauthorized: err := client.HandleErrorResponse(res) return ErrUnauthorizedForCredentials{Err: err} - case http.StatusBadRequest: - return internalTypes.BadPartialRequestError{Status: res.Status} default: if context != "" { context = context + ": " @@ -53,13 +49,13 @@ func httpResponseToError(res *http.Response, context string) error { // registryHTTPResponseToError creates a Go error from an HTTP error response of a docker/distribution // registry func registryHTTPResponseToError(res *http.Response) error { - errResponse := client.HandleErrorResponse(res) - if e, ok := perrors.Cause(errResponse).(*client.UnexpectedHTTPResponseError); ok { + err := client.HandleErrorResponse(res) + if e, ok := err.(*client.UnexpectedHTTPResponseError); ok { response := string(e.Response) if len(response) > 50 { response = response[:50] + "..." } - errResponse = fmt.Errorf("StatusCode: %d, %s", e.StatusCode, response) + err = fmt.Errorf("StatusCode: %d, %s", e.StatusCode, response) } - return errResponse + return err } diff --git a/vendor/github.com/containers/image/v5/version/version.go b/vendor/github.com/containers/image/v5/version/version.go index b9f8c3e9f..7f68c7cd0 100644 --- a/vendor/github.com/containers/image/v5/version/version.go +++ b/vendor/github.com/containers/image/v5/version/version.go @@ -8,7 +8,7 @@ const ( // VersionMinor is for functionality in a backwards-compatible manner VersionMinor = 16 // VersionPatch is for backwards-compatible bug fixes - VersionPatch = 0 + VersionPatch = 1 // VersionDev indicates development branch. Releases will be empty string. VersionDev = "" diff --git a/vendor/github.com/containers/storage/VERSION b/vendor/github.com/containers/storage/VERSION index afc132ffa..bf50e910e 100644 --- a/vendor/github.com/containers/storage/VERSION +++ b/vendor/github.com/containers/storage/VERSION @@ -1 +1 @@ -1.36.0+dev +1.37.0 diff --git a/vendor/github.com/containers/storage/drivers/driver_linux.go b/vendor/github.com/containers/storage/drivers/driver_linux.go index dddf8a8b4..0fe3eea7a 100644 --- a/vendor/github.com/containers/storage/drivers/driver_linux.go +++ b/vendor/github.com/containers/storage/drivers/driver_linux.go @@ -50,6 +50,40 @@ const ( FsMagicOverlay = FsMagic(0x794C7630) // FsMagicFUSE filesystem id for FUSE FsMagicFUSE = FsMagic(0x65735546) + // FsMagicAcfs filesystem id for Acfs + FsMagicAcfs = FsMagic(0x61636673) + // FsMagicAfs filesystem id for Afs + FsMagicAfs = FsMagic(0x5346414f) + // FsMagicCephFs filesystem id for Ceph + FsMagicCephFs = FsMagic(0x00C36400) + // FsMagicCIFS filesystem id for CIFS + FsMagicCIFS = FsMagic(0xFF534D42) + // FsMagicFHGFS filesystem id for FHGFS + FsMagicFHGFSFs = FsMagic(0x19830326) + // FsMagicIBRIX filesystem id for IBRIX + FsMagicIBRIX = FsMagic(0x013111A8) + // FsMagicKAFS filesystem id for KAFS + FsMagicKAFS = FsMagic(0x6B414653) + // FsMagicLUSTRE filesystem id for LUSTRE + FsMagicLUSTRE = FsMagic(0x0BD00BD0) + // FsMagicNCP filesystem id for NCP + FsMagicNCP = FsMagic(0x564C) + // FsMagicNFSD filesystem id for NFSD + FsMagicNFSD = FsMagic(0x6E667364) + // FsMagicOCFS2 filesystem id for OCFS2 + FsMagicOCFS2 = FsMagic(0x7461636F) + // FsMagicPANFS filesystem id for PANFS + FsMagicPANFS = FsMagic(0xAAD7AAEA) + // FsMagicPRLFS filesystem id for PRLFS + FsMagicPRLFS = FsMagic(0x7C7C6673) + // FsMagicSMB2 filesystem id for SMB2 + FsMagicSMB2 = FsMagic(0xFE534D42) + // FsMagicSNFS filesystem id for SNFS + FsMagicSNFS = FsMagic(0xBEEFDEAD) + // FsMagicVBOXSF filesystem id for VBOXSF + FsMagicVBOXSF = FsMagic(0x786F4256) + // FsMagicVXFS filesystem id for VXFS + FsMagicVXFS = FsMagic(0xA501FCF5) ) var ( diff --git a/vendor/github.com/containers/storage/drivers/overlay/overlay.go b/vendor/github.com/containers/storage/drivers/overlay/overlay.go index 62130c73e..1efe7316d 100644 --- a/vendor/github.com/containers/storage/drivers/overlay/overlay.go +++ b/vendor/github.com/containers/storage/drivers/overlay/overlay.go @@ -248,6 +248,23 @@ func (d *Driver) getSupportsVolatile() (bool, error) { return supportsVolatile, nil } +// isNetworkFileSystem checks if the specified file system is supported by native overlay +// as backing store when running in a user namespace. +func isNetworkFileSystem(fsMagic graphdriver.FsMagic) bool { + switch fsMagic { + // a bunch of network file systems... + case graphdriver.FsMagicNfsFs, graphdriver.FsMagicSmbFs, graphdriver.FsMagicAcfs, + graphdriver.FsMagicAfs, graphdriver.FsMagicCephFs, graphdriver.FsMagicCIFS, + graphdriver.FsMagicFHGFSFs, graphdriver.FsMagicGPFS, graphdriver.FsMagicIBRIX, + graphdriver.FsMagicKAFS, graphdriver.FsMagicLUSTRE, graphdriver.FsMagicNCP, + graphdriver.FsMagicNFSD, graphdriver.FsMagicOCFS2, graphdriver.FsMagicPANFS, + graphdriver.FsMagicPRLFS, graphdriver.FsMagicSMB2, graphdriver.FsMagicSNFS, + graphdriver.FsMagicVBOXSF, graphdriver.FsMagicVXFS: + return true + } + return false +} + // Init returns the a native diff driver for overlay filesystem. // If overlay filesystem is not supported on the host, a wrapped graphdriver.ErrNotSupported is returned as error. // If an overlay filesystem is not supported over an existing filesystem then a wrapped graphdriver.ErrIncompatibleFS is returned. @@ -266,18 +283,27 @@ func Init(home string, options graphdriver.Options) (graphdriver.Driver, error) } if opts.mountProgram != "" { + if unshare.IsRootless() && isNetworkFileSystem(fsMagic) && opts.forceMask == nil { + m := os.FileMode(0700) + opts.forceMask = &m + logrus.Warnf("Network file system detected as backing store. Enforcing overlay option `force_mask=\"%o\"`. Add it to storage.conf to silence this warning", m) + } + if err := ioutil.WriteFile(getMountProgramFlagFile(home), []byte("true"), 0600); err != nil { return nil, err } } else { - // check if they are running over btrfs, aufs, zfs, overlay, or ecryptfs if opts.forceMask != nil { return nil, errors.New("'force_mask' is supported only with 'mount_program'") } + // check if they are running over btrfs, aufs, zfs, overlay, or ecryptfs switch fsMagic { case graphdriver.FsMagicAufs, graphdriver.FsMagicZfs, graphdriver.FsMagicOverlay, graphdriver.FsMagicEcryptfs: return nil, errors.Wrapf(graphdriver.ErrIncompatibleFS, "'overlay' is not supported over %s, a mount_program is required", backingFs) } + if unshare.IsRootless() && isNetworkFileSystem(fsMagic) { + return nil, errors.Wrapf(graphdriver.ErrIncompatibleFS, "A network file system with user namespaces is not supported. Please use a mount_program") + } } rootUID, rootGID, err := idtools.GetRootUIDGID(options.UIDMaps, options.GIDMaps) @@ -1431,6 +1457,11 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO label = d.optsAppendMappings(label, options.UidMaps, options.GidMaps) } + // if forceMask is in place, tell fuse-overlayfs to write the permissions mask to an unprivileged xattr as well. + if d.options.forceMask != nil { + label = label + ",xattr_permissions=2" + } + mountProgram := exec.Command(d.options.mountProgram, "-o", label, target) mountProgram.Dir = d.home var b bytes.Buffer diff --git a/vendor/github.com/containers/storage/go.mod b/vendor/github.com/containers/storage/go.mod index ac7f45c3f..6cd809745 100644 --- a/vendor/github.com/containers/storage/go.mod +++ b/vendor/github.com/containers/storage/go.mod @@ -6,7 +6,7 @@ require ( github.com/BurntSushi/toml v0.4.1 github.com/Microsoft/go-winio v0.5.0 github.com/Microsoft/hcsshim v0.8.22 - github.com/containerd/stargz-snapshotter/estargz v0.8.0 + github.com/containerd/stargz-snapshotter/estargz v0.9.0 github.com/docker/go-units v0.4.0 github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect github.com/google/go-intervals v0.0.2 diff --git a/vendor/github.com/containers/storage/go.sum b/vendor/github.com/containers/storage/go.sum index 3da64ce8c..1b602d484 100644 --- a/vendor/github.com/containers/storage/go.sum +++ b/vendor/github.com/containers/storage/go.sum @@ -31,8 +31,8 @@ github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMX github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/stargz-snapshotter/estargz v0.8.0 h1:oA1wx8kTFfImfsT5bScbrZd8gK+WtQnn15q82Djvm0Y= -github.com/containerd/stargz-snapshotter/estargz v0.8.0/go.mod h1:mwIwuwb+D8FX2t45Trwi0hmWmZm5VW7zPP/rekwhWQU= +github.com/containerd/stargz-snapshotter/estargz v0.9.0 h1:PkB6BSTfOKX23erT2GkoUKkJEcXfNcyKskIViK770v8= +github.com/containerd/stargz-snapshotter/estargz v0.9.0/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0= github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -122,7 +122,6 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= diff --git a/vendor/github.com/containers/storage/pkg/system/syscall_unix.go b/vendor/github.com/containers/storage/pkg/system/syscall_unix.go index 49dbdd378..1bb852d11 100644 --- a/vendor/github.com/containers/storage/pkg/system/syscall_unix.go +++ b/vendor/github.com/containers/storage/pkg/system/syscall_unix.go @@ -1,8 +1,11 @@ -// +build linux freebsd +// +build linux freebsd darwin package system -import "golang.org/x/sys/unix" +import ( + "github.com/pkg/errors" + "golang.org/x/sys/unix" +) // Unmount is a platform-specific helper function to call // the unmount syscall. @@ -15,3 +18,8 @@ func Unmount(dest string) error { func CommandLineToArgv(commandLine string) ([]string, error) { return []string{commandLine}, nil } + +// IsEBUSY checks if the specified error is EBUSY. +func IsEBUSY(err error) bool { + return errors.Is(err, unix.EBUSY) +} diff --git a/vendor/github.com/containers/storage/pkg/system/syscall_windows.go b/vendor/github.com/containers/storage/pkg/system/syscall_windows.go index 23e9b207c..f4d8692cd 100644 --- a/vendor/github.com/containers/storage/pkg/system/syscall_windows.go +++ b/vendor/github.com/containers/storage/pkg/system/syscall_windows.go @@ -120,3 +120,8 @@ func HasWin32KSupport() bool { // APIs. return ntuserApiset.Load() == nil } + +// IsEBUSY checks if the specified error is EBUSY. +func IsEBUSY(err error) bool { + return false +} diff --git a/vendor/github.com/containers/storage/store.go b/vendor/github.com/containers/storage/store.go index 6f6f69807..169c7d151 100644 --- a/vendor/github.com/containers/storage/store.go +++ b/vendor/github.com/containers/storage/store.go @@ -23,6 +23,7 @@ import ( "github.com/containers/storage/pkg/parsers" "github.com/containers/storage/pkg/stringid" "github.com/containers/storage/pkg/stringutils" + "github.com/containers/storage/pkg/system" "github.com/containers/storage/types" "github.com/hashicorp/go-multierror" digest "github.com/opencontainers/go-digest" @@ -2498,7 +2499,15 @@ func (s *store) DeleteContainer(id string) error { gcpath := filepath.Join(s.GraphRoot(), middleDir, container.ID) wg.Add(1) go func() { - errChan <- os.RemoveAll(gcpath) + var err error + for attempts := 0; attempts < 50; attempts++ { + err = os.RemoveAll(gcpath) + if err == nil || !system.IsEBUSY(err) { + break + } + time.Sleep(time.Millisecond * 100) + } + errChan <- err wg.Done() }() diff --git a/vendor/github.com/onsi/ginkgo/CHANGELOG.md b/vendor/github.com/onsi/ginkgo/CHANGELOG.md index 494abdbfb..a26bc530f 100644 --- a/vendor/github.com/onsi/ginkgo/CHANGELOG.md +++ b/vendor/github.com/onsi/ginkgo/CHANGELOG.md @@ -1,3 +1,10 @@ +## 1.16.5 + +Ginkgo 2.0 now has a Release Candidate. 1.16.5 advertises the existence of the RC. +1.16.5 deprecates GinkgoParallelNode in favor of GinkgoParallelProcess + +You can silence the RC advertisement by setting an `ACK_GINKG_RC=true` environment variable or creating a file in your home directory called `.ack-ginkgo-rc` + ## 1.16.4 ### Fixes diff --git a/vendor/github.com/onsi/ginkgo/README.md b/vendor/github.com/onsi/ginkgo/README.md index 05321e6ea..a25ca5e03 100644 --- a/vendor/github.com/onsi/ginkgo/README.md +++ b/vendor/github.com/onsi/ginkgo/README.md @@ -1,23 +1,18 @@ ![Ginkgo: A Go BDD Testing Framework](https://onsi.github.io/ginkgo/images/ginkgo.png) -[![Build Status](https://travis-ci.org/onsi/ginkgo.svg?branch=master)](https://travis-ci.org/onsi/ginkgo) [![test](https://github.com/onsi/ginkgo/workflows/test/badge.svg?branch=master)](https://github.com/onsi/ginkgo/actions?query=workflow%3Atest+branch%3Amaster) Jump to the [docs](https://onsi.github.io/ginkgo/) | [ä¸æ–‡æ–‡æ¡£](https://ke-chain.github.io/ginkgodoc) to learn more. To start rolling your Ginkgo tests *now* [keep reading](#set-me-up)! If you have a question, comment, bug report, feature request, etc. please open a GitHub issue, or visit the [Ginkgo Slack channel](https://app.slack.com/client/T029RQSE6/CQQ50BBNW). -# Ginkgo 2.0 is coming soon! +# Ginkgo 2.0 Release Candidate is available! -An effort is underway to develop and deliver Ginkgo 2.0. The work is happening in the [v2](https://github.com/onsi/ginkgo/tree/v2) branch and a changelog and migration guide is being maintained on that branch [here](https://github.com/onsi/ginkgo/blob/v2/docs/MIGRATING_TO_V2.md). Issue [#711](https://github.com/onsi/ginkgo/issues/711) is the central place for discussion and links to the original [proposal doc](https://docs.google.com/document/d/1h28ZknXRsTLPNNiOjdHIO-F2toCzq4xoZDXbfYaBdoQ/edit#). +An effort is underway to develop and deliver Ginkgo 2.0. The work is happening in the [ver2](https://github.com/onsi/ginkgo/tree/ver2) branch and a changelog and migration guide is being maintained on that branch [here](https://github.com/onsi/ginkgo/blob/ver2/docs/MIGRATING_TO_V2.md). Issue [#711](https://github.com/onsi/ginkgo/issues/711) is the central place for discussion. -As described in the [changelog](https://github.com/onsi/ginkgo/blob/v2/docs/MIGRATING_TO_V2.md) and [proposal](https://docs.google.com/document/d/1h28ZknXRsTLPNNiOjdHIO-F2toCzq4xoZDXbfYaBdoQ/edit#), Ginkgo 2.0 will clean up the Ginkgo codebase, deprecate and remove some v1 functionality, and add several new much-requested features. To help users get ready for the migration, Ginkgo v1 has started emitting deprecation warnings for features that will no longer be supported with links to documentation for how to migrate away from these features. If you have concerns or comments please chime in on [#711](https://github.com/onsi/ginkgo/issues/711). +As described in the [changelog](https://github.com/onsi/ginkgo/blob/ver2/docs/MIGRATING_TO_V2.md) and [proposal](https://docs.google.com/document/d/1h28ZknXRsTLPNNiOjdHIO-F2toCzq4xoZDXbfYaBdoQ/edit#), Ginkgo 2.0 will clean up the Ginkgo codebase, deprecate and remove some v1 functionality, and add several new much-requested features. To help users get ready for the migration, Ginkgo v1 has started emitting deprecation warnings for features that will no longer be supported with links to documentation for how to migrate away from these features. If you have concerns or comments please chime in on [#711](https://github.com/onsi/ginkgo/issues/711). -The current timeline for completion of 2.0 looks like: - -- Early April 2021: first public release of 2.0, deprecation warnings land in v1. -- May 2021: first beta/rc of 2.0 with most new functionality in place. -- June/July 2021: 2.0 ships and fully replaces the 1.x codebase on master. +Please start exploring and using the V2 release! To get started follow the [Using the Release Candidate](https://github.com/onsi/ginkgo/blob/ver2/docs/MIGRATING_TO_V2.md#using-the-beta) directions in the migration guide. ## TLDR Ginkgo builds on Go's `testing` package, allowing expressive [Behavior-Driven Development](https://en.wikipedia.org/wiki/Behavior-driven_development) ("BDD") style tests. diff --git a/vendor/github.com/onsi/ginkgo/config/config.go b/vendor/github.com/onsi/ginkgo/config/config.go index 5f3f43969..3130c7789 100644 --- a/vendor/github.com/onsi/ginkgo/config/config.go +++ b/vendor/github.com/onsi/ginkgo/config/config.go @@ -20,7 +20,7 @@ import ( "fmt" ) -const VERSION = "1.16.4" +const VERSION = "1.16.5" type GinkgoConfigType struct { RandomSeed int64 diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/bootstrap_command.go b/vendor/github.com/onsi/ginkgo/ginkgo/bootstrap_command.go index 6f5af3913..ea10e9796 100644 --- a/vendor/github.com/onsi/ginkgo/ginkgo/bootstrap_command.go +++ b/vendor/github.com/onsi/ginkgo/ginkgo/bootstrap_command.go @@ -37,6 +37,7 @@ func BuildBootstrapCommand() *Command { }, Command: func(args []string, additionalArgs []string) { generateBootstrap(agouti, noDot, internal, customBootstrapFile) + emitRCAdvertisement() }, } } diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/generate_command.go b/vendor/github.com/onsi/ginkgo/ginkgo/generate_command.go index 27758beba..f79271676 100644 --- a/vendor/github.com/onsi/ginkgo/ginkgo/generate_command.go +++ b/vendor/github.com/onsi/ginkgo/ginkgo/generate_command.go @@ -36,6 +36,7 @@ func BuildGenerateCommand() *Command { }, Command: func(args []string, additionalArgs []string) { generateSpec(args, agouti, noDot, internal, customTestFile) + emitRCAdvertisement() }, } } diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/help_command.go b/vendor/github.com/onsi/ginkgo/ginkgo/help_command.go index 23b1d2f11..db3f40406 100644 --- a/vendor/github.com/onsi/ginkgo/ginkgo/help_command.go +++ b/vendor/github.com/onsi/ginkgo/ginkgo/help_command.go @@ -20,6 +20,7 @@ func BuildHelpCommand() *Command { func printHelp(args []string, additionalArgs []string) { if len(args) == 0 { usage() + emitRCAdvertisement() } else { command, found := commandMatching(args[0]) if !found { @@ -27,5 +28,6 @@ func printHelp(args []string, additionalArgs []string) { } usageForCommand(command, true) + emitRCAdvertisement() } } diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/main.go b/vendor/github.com/onsi/ginkgo/ginkgo/main.go index ac725bf40..ae0e1daf6 100644 --- a/vendor/github.com/onsi/ginkgo/ginkgo/main.go +++ b/vendor/github.com/onsi/ginkgo/ginkgo/main.go @@ -131,9 +131,11 @@ import ( "fmt" "os" "os/exec" + "path/filepath" "strings" "github.com/onsi/ginkgo/config" + "github.com/onsi/ginkgo/formatter" "github.com/onsi/ginkgo/ginkgo/testsuite" ) @@ -243,6 +245,7 @@ func usageForCommand(command *Command, longForm bool) { func complainAndQuit(complaint string) { fmt.Fprintf(os.Stderr, "%s\nFor usage instructions:\n\tginkgo help\n", complaint) + emitRCAdvertisement() os.Exit(1) } @@ -306,3 +309,29 @@ func pluralizedWord(singular, plural string, count int) string { } return plural } + +func emitRCAdvertisement() { + ackRC := os.Getenv("ACK_GINKGO_RC") + if ackRC != "" { + return + } + home, err := os.UserHomeDir() + if err == nil { + _, err := os.Stat(filepath.Join(home, ".ack-ginkgo-rc")) + if err == nil { + return + } + } + + out := formatter.F("\n{{light-yellow}}Ginkgo 2.0 is coming soon!{{/}}\n") + out += formatter.F("{{light-yellow}}=========================={{/}}\n") + out += formatter.F("{{bold}}{{green}}Ginkgo 2.0{{/}} is under active development and will introduce several new features, improvements, and a small handful of breaking changes.\n") + out += formatter.F("A release candidate for 2.0 is now available and 2.0 should GA in Fall 2021. {{bold}}Please give the RC a try and send us feedback!{{/}}\n") + out += formatter.F(" - To learn more, view the migration guide at {{cyan}}{{underline}}https://github.com/onsi/ginkgo/blob/ver2/docs/MIGRATING_TO_V2.md{{/}}\n") + out += formatter.F(" - For instructions on using the Release Candidate visit {{cyan}}{{underline}}https://github.com/onsi/ginkgo/blob/ver2/docs/MIGRATING_TO_V2.md#using-the-beta{{/}}\n") + out += formatter.F(" - To comment, chime in at {{cyan}}{{underline}}https://github.com/onsi/ginkgo/issues/711{{/}}\n\n") + out += formatter.F("To {{bold}}{{coral}}silence this notice{{/}}, set the environment variable: {{bold}}ACK_GINKGO_RC=true{{/}}\n") + out += formatter.F("Alternatively you can: {{bold}}touch $HOME/.ack-ginkgo-rc{{/}}") + + fmt.Println(out) +} diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/run_command.go b/vendor/github.com/onsi/ginkgo/ginkgo/run_command.go index c7f80d143..f3d4e99a5 100644 --- a/vendor/github.com/onsi/ginkgo/ginkgo/run_command.go +++ b/vendor/github.com/onsi/ginkgo/ginkgo/run_command.go @@ -161,6 +161,7 @@ func (r *SpecRunner) RunSpecs(args []string, additionalArgs []string) { } } else { fmt.Printf("Test Suite Failed\n") + emitRCAdvertisement() os.Exit(1) } } diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/version_command.go b/vendor/github.com/onsi/ginkgo/ginkgo/version_command.go index f586908e8..a5b68c216 100644 --- a/vendor/github.com/onsi/ginkgo/ginkgo/version_command.go +++ b/vendor/github.com/onsi/ginkgo/ginkgo/version_command.go @@ -21,4 +21,5 @@ func BuildVersionCommand() *Command { func printVersion([]string, []string) { fmt.Printf("Ginkgo Version %s\n", config.VERSION) + emitRCAdvertisement() } diff --git a/vendor/github.com/onsi/ginkgo/ginkgo_dsl.go b/vendor/github.com/onsi/ginkgo/ginkgo_dsl.go index 4a6e1e1ee..ccd7685e3 100644 --- a/vendor/github.com/onsi/ginkgo/ginkgo_dsl.go +++ b/vendor/github.com/onsi/ginkgo/ginkgo_dsl.go @@ -73,9 +73,15 @@ func GinkgoRandomSeed() int64 { return config.GinkgoConfig.RandomSeed } -//GinkgoParallelNode returns the parallel node number for the current ginkgo process -//The node number is 1-indexed +//GinkgoParallelNode is deprecated, use GinkgoParallelProcess instead func GinkgoParallelNode() int { + deprecationTracker.TrackDeprecation(types.Deprecations.ParallelNode(), codelocation.New(1)) + return GinkgoParallelProcess() +} + +//GinkgoParallelProcess returns the parallel process number for the current ginkgo process +//The process number is 1-indexed +func GinkgoParallelProcess() int { return config.GinkgoConfig.ParallelNode } @@ -109,6 +115,7 @@ func GinkgoT(optionalOffset ...int) GinkgoTInterface { //in the testing package's T. type GinkgoTInterface interface { Cleanup(func()) + Setenv(key, value string) Error(args ...interface{}) Errorf(format string, args ...interface{}) Fail() diff --git a/vendor/github.com/onsi/ginkgo/go.mod b/vendor/github.com/onsi/ginkgo/go.mod index 86a5a97be..171144326 100644 --- a/vendor/github.com/onsi/ginkgo/go.mod +++ b/vendor/github.com/onsi/ginkgo/go.mod @@ -1,6 +1,6 @@ module github.com/onsi/ginkgo -go 1.15 +go 1.16 require ( github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 diff --git a/vendor/github.com/onsi/ginkgo/internal/testingtproxy/testing_t_proxy.go b/vendor/github.com/onsi/ginkgo/internal/testingtproxy/testing_t_proxy.go index d7bbb7a96..4dcfaf4cd 100644 --- a/vendor/github.com/onsi/ginkgo/internal/testingtproxy/testing_t_proxy.go +++ b/vendor/github.com/onsi/ginkgo/internal/testingtproxy/testing_t_proxy.go @@ -34,6 +34,11 @@ func (t *ginkgoTestingTProxy) Cleanup(func()) { // No-op } +func (t *ginkgoTestingTProxy) Setenv(kev, value string) { + fmt.Println("Setenv is a noop for Ginkgo at the moment but will be implemented in V2") + // No-op until Cleanup is implemented +} + func (t *ginkgoTestingTProxy) Error(args ...interface{}) { t.fail(fmt.Sprintln(args...), t.offset) } diff --git a/vendor/github.com/onsi/ginkgo/types/deprecation_support.go b/vendor/github.com/onsi/ginkgo/types/deprecation_support.go index 305c134b7..d5a6658f3 100644 --- a/vendor/github.com/onsi/ginkgo/types/deprecation_support.go +++ b/vendor/github.com/onsi/ginkgo/types/deprecation_support.go @@ -52,6 +52,14 @@ func (d deprecations) Measure() Deprecation { } } +func (d deprecations) ParallelNode() Deprecation { + return Deprecation{ + Message: "GinkgoParallelNode is deprecated and will be removed in Ginkgo V2. Please use GinkgoParallelProcess instead.", + DocLink: "renamed-ginkgoparallelnode", + Version: "1.16.5", + } +} + func (d deprecations) Convert() Deprecation { return Deprecation{ Message: "The convert command is deprecated in Ginkgo V2", @@ -99,16 +107,18 @@ func (d *DeprecationTracker) DidTrackDeprecations() bool { } func (d *DeprecationTracker) DeprecationsReport() string { - out := formatter.F("{{light-yellow}}You're using deprecated Ginkgo functionality:{{/}}\n") + out := formatter.F("\n{{light-yellow}}You're using deprecated Ginkgo functionality:{{/}}\n") out += formatter.F("{{light-yellow}}============================================={{/}}\n") - out += formatter.F("Ginkgo 2.0 is under active development and will introduce (a small number of) breaking changes.\n") - out += formatter.F("To learn more, view the migration guide at {{cyan}}{{underline}}https://github.com/onsi/ginkgo/blob/v2/docs/MIGRATING_TO_V2.md{{/}}\n") - out += formatter.F("To comment, chime in at {{cyan}}{{underline}}https://github.com/onsi/ginkgo/issues/711{{/}}\n\n") + out += formatter.F("{{bold}}{{green}}Ginkgo 2.0{{/}} is under active development and will introduce several new features, improvements, and a small handful of breaking changes.\n") + out += formatter.F("A release candidate for 2.0 is now available and 2.0 should GA in Fall 2021. {{bold}}Please give the RC a try and send us feedback!{{/}}\n") + out += formatter.F(" - To learn more, view the migration guide at {{cyan}}{{underline}}https://github.com/onsi/ginkgo/blob/ver2/docs/MIGRATING_TO_V2.md{{/}}\n") + out += formatter.F(" - For instructions on using the Release Candidate visit {{cyan}}{{underline}}https://github.com/onsi/ginkgo/blob/ver2/docs/MIGRATING_TO_V2.md#using-the-beta{{/}}\n") + out += formatter.F(" - To comment, chime in at {{cyan}}{{underline}}https://github.com/onsi/ginkgo/issues/711{{/}}\n\n") for deprecation, locations := range d.deprecations { out += formatter.Fi(1, "{{yellow}}"+deprecation.Message+"{{/}}\n") if deprecation.DocLink != "" { - out += formatter.Fi(1, "{{bold}}Learn more at:{{/}} {{cyan}}{{underline}}https://github.com/onsi/ginkgo/blob/v2/docs/MIGRATING_TO_V2.md#%s{{/}}\n", deprecation.DocLink) + out += formatter.Fi(1, "{{bold}}Learn more at:{{/}} {{cyan}}{{underline}}https://github.com/onsi/ginkgo/blob/ver2/docs/MIGRATING_TO_V2.md#%s{{/}}\n", deprecation.DocLink) } for _, location := range locations { out += formatter.Fi(2, "{{gray}}%s{{/}}\n", location) diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/label/label_linux.go b/vendor/github.com/opencontainers/selinux/go-selinux/label/label_linux.go index 14e1e38c2..12de0ae5d 100644 --- a/vendor/github.com/opencontainers/selinux/go-selinux/label/label_linux.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/label/label_linux.go @@ -103,9 +103,11 @@ func SetFileCreateLabel(fileLabel string) error { return selinux.SetFSCreateLabel(fileLabel) } -// Relabel changes the label of path to the filelabel string. +// Relabel changes the label of path and all the entries beneath the path. // It changes the MCS label to s0 if shared is true. // This will allow all containers to share the content. +// +// The path itself is guaranteed to be relabeled last. func Relabel(path string, fileLabel string, shared bool) error { if !selinux.GetEnabled() || fileLabel == "" { return nil diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go b/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go index 0eedcaa78..cad467507 100644 --- a/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go @@ -255,6 +255,8 @@ func CopyLevel(src, dest string) (string, error) { // Chcon changes the fpath file object to the SELinux label label. // If fpath is a directory and recurse is true, then Chcon walks the // directory tree setting the label. +// +// The fpath itself is guaranteed to be relabeled last. func Chcon(fpath string, label string, recurse bool) error { return chcon(fpath, label, recurse) } diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go index 295b2bc4e..b045843ad 100644 --- a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go @@ -9,6 +9,7 @@ import ( "fmt" "io" "io/ioutil" + "math/big" "os" "path" "path/filepath" @@ -16,7 +17,6 @@ import ( "strings" "sync" - "github.com/bits-and-blooms/bitset" "golang.org/x/sys/unix" ) @@ -44,7 +44,7 @@ type selinuxState struct { type level struct { sens uint - cats *bitset.BitSet + cats *big.Int } type mlsRange struct { @@ -455,8 +455,8 @@ func computeCreateContext(source string, target string, class string) (string, e } // catsToBitset stores categories in a bitset. -func catsToBitset(cats string) (*bitset.BitSet, error) { - bitset := &bitset.BitSet{} +func catsToBitset(cats string) (*big.Int, error) { + bitset := new(big.Int) catlist := strings.Split(cats, ",") for _, r := range catlist { @@ -471,14 +471,14 @@ func catsToBitset(cats string) (*bitset.BitSet, error) { return nil, err } for i := catstart; i <= catend; i++ { - bitset.Set(i) + bitset.SetBit(bitset, int(i), 1) } } else { cat, err := parseLevelItem(ranges[0], category) if err != nil { return nil, err } - bitset.Set(cat) + bitset.SetBit(bitset, int(cat), 1) } } @@ -548,37 +548,30 @@ func rangeStrToMLSRange(rangeStr string) (*mlsRange, error) { // bitsetToStr takes a category bitset and returns it in the // canonical selinux syntax -func bitsetToStr(c *bitset.BitSet) string { +func bitsetToStr(c *big.Int) string { var str string - i, e := c.NextSet(0) - len := 0 - for e { - if len == 0 { + + length := 0 + for i := int(c.TrailingZeroBits()); i < c.BitLen(); i++ { + if c.Bit(i) == 0 { + continue + } + if length == 0 { if str != "" { str += "," } - str += "c" + strconv.Itoa(int(i)) - } - - next, e := c.NextSet(i + 1) - if e { - // consecutive cats - if next == i+1 { - len++ - i = next - continue - } + str += "c" + strconv.Itoa(i) } - if len == 1 { - str += ",c" + strconv.Itoa(int(i)) - } else if len > 1 { - str += ".c" + strconv.Itoa(int(i)) + if c.Bit(i+1) == 1 { + length++ + continue } - if !e { - break + if length == 1 { + str += ",c" + strconv.Itoa(i) + } else if length > 1 { + str += ".c" + strconv.Itoa(i) } - len = 0 - i = next + length = 0 } return str @@ -591,13 +584,16 @@ func (l1 *level) equal(l2 *level) bool { if l1.sens != l2.sens { return false } - return l1.cats.Equal(l2.cats) + if l2.cats == nil || l1.cats == nil { + return l2.cats == l1.cats + } + return l1.cats.Cmp(l2.cats) == 0 } // String returns an mlsRange as a string. func (m mlsRange) String() string { low := "s" + strconv.Itoa(int(m.low.sens)) - if m.low.cats != nil && m.low.cats.Count() > 0 { + if m.low.cats != nil && m.low.cats.BitLen() > 0 { low += ":" + bitsetToStr(m.low.cats) } @@ -606,7 +602,7 @@ func (m mlsRange) String() string { } high := "s" + strconv.Itoa(int(m.high.sens)) - if m.high.cats != nil && m.high.cats.Count() > 0 { + if m.high.cats != nil && m.high.cats.BitLen() > 0 { high += ":" + bitsetToStr(m.high.cats) } @@ -656,10 +652,12 @@ func calculateGlbLub(sourceRange, targetRange string) (string, error) { /* find the intersecting categories */ if s.low.cats != nil && t.low.cats != nil { - outrange.low.cats = s.low.cats.Intersection(t.low.cats) + outrange.low.cats = new(big.Int) + outrange.low.cats.And(s.low.cats, t.low.cats) } if s.high.cats != nil && t.high.cats != nil { - outrange.high.cats = s.high.cats.Intersection(t.high.cats) + outrange.high.cats = new(big.Int) + outrange.high.cats.And(s.high.cats, t.high.cats) } return outrange.String(), nil diff --git a/vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go b/vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go index 011fe862a..202c80da5 100644 --- a/vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go +++ b/vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go @@ -51,6 +51,9 @@ func WalkN(root string, walkFn WalkFunc, num int) error { var ( err error wg sync.WaitGroup + + rootLen = len(root) + rootEntry *walkArgs ) wg.Add(1) go func() { @@ -59,6 +62,11 @@ func WalkN(root string, walkFn WalkFunc, num int) error { close(files) return err } + if len(p) == rootLen { + // Root entry is processed separately below. + rootEntry = &walkArgs{path: p, info: &info} + return nil + } // add a file to the queue unless a callback sent an error select { case e := <-errCh: @@ -92,6 +100,10 @@ func WalkN(root string, walkFn WalkFunc, num int) error { wg.Wait() + if err == nil { + err = walkFn(rootEntry.path, *rootEntry.info, nil) + } + return err } diff --git a/vendor/github.com/opencontainers/selinux/pkg/pwalkdir/pwalkdir.go b/vendor/github.com/opencontainers/selinux/pkg/pwalkdir/pwalkdir.go index 222820750..a5796b2c4 100644 --- a/vendor/github.com/opencontainers/selinux/pkg/pwalkdir/pwalkdir.go +++ b/vendor/github.com/opencontainers/selinux/pkg/pwalkdir/pwalkdir.go @@ -1,3 +1,4 @@ +//go:build go1.16 // +build go1.16 package pwalkdir @@ -51,6 +52,9 @@ func WalkN(root string, walkFn fs.WalkDirFunc, num int) error { var ( err error wg sync.WaitGroup + + rootLen = len(root) + rootEntry *walkArgs ) wg.Add(1) go func() { @@ -59,6 +63,11 @@ func WalkN(root string, walkFn fs.WalkDirFunc, num int) error { close(files) return err } + if len(p) == rootLen { + // Root entry is processed separately below. + rootEntry = &walkArgs{path: p, entry: entry} + return nil + } // Add a file to the queue unless a callback sent an error. select { case e := <-errCh: @@ -92,6 +101,10 @@ func WalkN(root string, walkFn fs.WalkDirFunc, num int) error { wg.Wait() + if err == nil { + err = walkFn(rootEntry.path, rootEntry.entry, nil) + } + return err } diff --git a/vendor/github.com/vbauerster/mpb/v7/bar.go b/vendor/github.com/vbauerster/mpb/v7/bar.go index 22f608317..dabe1a475 100644 --- a/vendor/github.com/vbauerster/mpb/v7/bar.go +++ b/vendor/github.com/vbauerster/mpb/v7/bar.go @@ -268,15 +268,19 @@ func (b *Bar) SetPriority(priority int) { // if bar is already in complete state. If drop is true bar will be // removed as well. func (b *Bar) Abort(drop bool) { + done := make(chan struct{}) select { case b.operateState <- func(s *bState) { if s.completed == true { + close(done) return } - if drop { - go b.container.dropBar(b) - } else { - go func() { + // container must be run during lifetime of this inner goroutine + // we control this by done channel declared above + go func() { + if drop { + b.container.dropBar(b) + } else { var uncompleted int b.container.traverseBars(func(bar *Bar) bool { if b != bar && !bar.Completed() { @@ -286,16 +290,15 @@ func (b *Bar) Abort(drop bool) { return true }) if uncompleted == 0 { - select { - case b.container.refreshCh <- time.Now(): - case <-b.container.done: - } + b.container.refreshCh <- time.Now() } - }() - } + } + close(done) // release hold of Abort + }() b.cancel() }: - <-b.done + // guarantee: container is alive during lifetime of this hold + <-done case <-b.done: } } diff --git a/vendor/github.com/vbauerster/mpb/v7/go.mod b/vendor/github.com/vbauerster/mpb/v7/go.mod index 1ecbbe062..fe10588ef 100644 --- a/vendor/github.com/vbauerster/mpb/v7/go.mod +++ b/vendor/github.com/vbauerster/mpb/v7/go.mod @@ -4,7 +4,7 @@ require ( github.com/VividCortex/ewma v1.2.0 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/mattn/go-runewidth v0.0.13 - golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34 + golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 ) go 1.14 diff --git a/vendor/github.com/vbauerster/mpb/v7/go.sum b/vendor/github.com/vbauerster/mpb/v7/go.sum index a964dcccd..ce769eaef 100644 --- a/vendor/github.com/vbauerster/mpb/v7/go.sum +++ b/vendor/github.com/vbauerster/mpb/v7/go.sum @@ -6,5 +6,5 @@ github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34 h1:GkvMjFtXUmahfDtashnc1mnrCtuBVcwse5QV2lUk/tI= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 h1:xrCZDmdtoloIiooiA9q0OQb9r8HejIHYoHGhGCe1pGg= +golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh index 0bcb8c322..850aafec1 100644 --- a/vendor/golang.org/x/sys/unix/mkerrors.sh +++ b/vendor/golang.org/x/sys/unix/mkerrors.sh @@ -229,6 +229,7 @@ struct ltchars { #include <linux/input.h> #include <linux/kexec.h> #include <linux/keyctl.h> +#include <linux/landlock.h> #include <linux/loop.h> #include <linux/lwtunnel.h> #include <linux/magic.h> @@ -497,6 +498,7 @@ ccflags="$@" $2 ~ /^O?XTABS$/ || $2 ~ /^TC[IO](ON|OFF)$/ || $2 ~ /^IN_/ || + $2 ~ /^LANDLOCK_/ || $2 ~ /^LOCK_(SH|EX|NB|UN)$/ || $2 ~ /^LO_(KEY|NAME)_SIZE$/ || $2 ~ /^LOOP_(CLR|CTL|GET|SET)_/ || diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go index df8628e57..b90214d35 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -2288,6 +2288,9 @@ type RemoteIovec struct { //sys ProcessVMReadv(pid int, localIov []Iovec, remoteIov []RemoteIovec, flags uint) (n int, err error) = SYS_PROCESS_VM_READV //sys ProcessVMWritev(pid int, localIov []Iovec, remoteIov []RemoteIovec, flags uint) (n int, err error) = SYS_PROCESS_VM_WRITEV +//sys PidfdOpen(pid int, flags int) (fd int, err error) = SYS_PIDFD_OPEN +//sys PidfdGetfd(pidfd int, targetfd int, flags int) (fd int, err error) = SYS_PIDFD_GETFD + /* * Unimplemented */ diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux.go b/vendor/golang.org/x/sys/unix/zerrors_linux.go index 8894c4af4..b959fe195 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux.go @@ -1333,6 +1333,20 @@ const ( KEY_SPEC_THREAD_KEYRING = -0x1 KEY_SPEC_USER_KEYRING = -0x4 KEY_SPEC_USER_SESSION_KEYRING = -0x5 + LANDLOCK_ACCESS_FS_EXECUTE = 0x1 + LANDLOCK_ACCESS_FS_MAKE_BLOCK = 0x800 + LANDLOCK_ACCESS_FS_MAKE_CHAR = 0x40 + LANDLOCK_ACCESS_FS_MAKE_DIR = 0x80 + LANDLOCK_ACCESS_FS_MAKE_FIFO = 0x400 + LANDLOCK_ACCESS_FS_MAKE_REG = 0x100 + LANDLOCK_ACCESS_FS_MAKE_SOCK = 0x200 + LANDLOCK_ACCESS_FS_MAKE_SYM = 0x1000 + LANDLOCK_ACCESS_FS_READ_DIR = 0x8 + LANDLOCK_ACCESS_FS_READ_FILE = 0x4 + LANDLOCK_ACCESS_FS_REMOVE_DIR = 0x10 + LANDLOCK_ACCESS_FS_REMOVE_FILE = 0x20 + LANDLOCK_ACCESS_FS_WRITE_FILE = 0x2 + LANDLOCK_CREATE_RULESET_VERSION = 0x1 LINUX_REBOOT_CMD_CAD_OFF = 0x0 LINUX_REBOOT_CMD_CAD_ON = 0x89abcdef LINUX_REBOOT_CMD_HALT = 0xcdef0123 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux.go b/vendor/golang.org/x/sys/unix/zsyscall_linux.go index 2dbe3da7a..f98d2e36d 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux.go @@ -1945,6 +1945,28 @@ func ProcessVMWritev(pid int, localIov []Iovec, remoteIov []RemoteIovec, flags u // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func PidfdOpen(pid int, flags int) (fd int, err error) { + r0, _, e1 := Syscall(SYS_PIDFD_OPEN, uintptr(pid), uintptr(flags), 0) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func PidfdGetfd(pidfd int, targetfd int, flags int) (fd int, err error) { + r0, _, e1 := Syscall(SYS_PIDFD_GETFD, uintptr(pidfd), uintptr(targetfd), uintptr(flags)) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func pipe2(p *[2]_C_int, flags int) (err error) { _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go index 1f99c024a..4eec078e5 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go @@ -31,6 +31,8 @@ type Timeval struct { Usec int32 } +type Time_t int32 + type Rusage struct { Utime Timeval Stime Timeval diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go index ddf0305a5..7622904a5 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go @@ -31,6 +31,8 @@ type Timeval struct { Usec int64 } +type Time_t int64 + type Rusage struct { Utime Timeval Stime Timeval diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go index dce0a5c80..19223ce8e 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go @@ -33,6 +33,8 @@ type Timeval struct { _ [4]byte } +type Time_t int32 + type Rusage struct { Utime Timeval Stime Timeval diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go index e23244702..8e3e33f67 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go @@ -31,6 +31,8 @@ type Timeval struct { Usec int64 } +type Time_t int64 + type Rusage struct { Utime Timeval Stime Timeval diff --git a/vendor/golang.org/x/sys/unix/ztypes_illumos_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_illumos_amd64.go index 236f37ef6..4c485261d 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_illumos_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_illumos_amd64.go @@ -13,6 +13,8 @@ const ( I_STR = 0x5308 I_POP = 0x5303 I_PUSH = 0x5302 + I_LINK = 0x530c + I_UNLINK = 0x530d I_PLINK = 0x5316 I_PUNLINK = 0x5317 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux.go b/vendor/golang.org/x/sys/unix/ztypes_linux.go index 4b73bb3b6..06dcd787b 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux.go @@ -3923,3 +3923,16 @@ const ( NFC_SDP_ATTR_URI = 0x1 NFC_SDP_ATTR_SAP = 0x2 ) + +type LandlockRulesetAttr struct { + Access_fs uint64 +} + +type LandlockPathBeneathAttr struct { + Allowed_access uint64 + Parent_fd int32 +} + +const ( + LANDLOCK_RULE_PATH_BENEATH = 0x1 +) diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_386.go b/vendor/golang.org/x/sys/unix/ztypes_linux_386.go index 72f2e96f3..3219adeda 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_386.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_386.go @@ -635,3 +635,7 @@ const ( PPS_GETCAP = 0x800470a3 PPS_FETCH = 0xc00470a4 ) + +const ( + PIDFD_NONBLOCK = 0x800 +) diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go index d5f018d13..16acd3bcb 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go @@ -653,3 +653,7 @@ const ( PPS_GETCAP = 0x800870a3 PPS_FETCH = 0xc00870a4 ) + +const ( + PIDFD_NONBLOCK = 0x800 +) diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go b/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go index 675446d93..c4982a229 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go @@ -630,3 +630,7 @@ const ( PPS_GETCAP = 0x800470a3 PPS_FETCH = 0xc00470a4 ) + +const ( + PIDFD_NONBLOCK = 0x800 +) diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go index 711d0711c..98bb8a41a 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go @@ -632,3 +632,7 @@ const ( PPS_GETCAP = 0x800870a3 PPS_FETCH = 0xc00870a4 ) + +const ( + PIDFD_NONBLOCK = 0x800 +) diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go index c1131c741..d5bfc3565 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go @@ -636,3 +636,7 @@ const ( PPS_GETCAP = 0x400470a3 PPS_FETCH = 0xc00470a4 ) + +const ( + PIDFD_NONBLOCK = 0x80 +) diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go index 91d5574ff..b52c568dc 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go @@ -635,3 +635,7 @@ const ( PPS_GETCAP = 0x400870a3 PPS_FETCH = 0xc00870a4 ) + +const ( + PIDFD_NONBLOCK = 0x80 +) diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go index 5d721497b..a340b84b9 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go @@ -635,3 +635,7 @@ const ( PPS_GETCAP = 0x400870a3 PPS_FETCH = 0xc00870a4 ) + +const ( + PIDFD_NONBLOCK = 0x80 +) diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go index a5addd06a..b43d8e2ce 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go @@ -636,3 +636,7 @@ const ( PPS_GETCAP = 0x400470a3 PPS_FETCH = 0xc00470a4 ) + +const ( + PIDFD_NONBLOCK = 0x80 +) diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go index bb6b03dfc..efd7313a7 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go @@ -642,3 +642,7 @@ const ( PPS_GETCAP = 0x400470a3 PPS_FETCH = 0xc00470a4 ) + +const ( + PIDFD_NONBLOCK = 0x800 +) diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go index 7637243b7..22cedda57 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go @@ -642,3 +642,7 @@ const ( PPS_GETCAP = 0x400870a3 PPS_FETCH = 0xc00870a4 ) + +const ( + PIDFD_NONBLOCK = 0x800 +) diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go index a1a28e525..452a76df1 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go @@ -642,3 +642,7 @@ const ( PPS_GETCAP = 0x400870a3 PPS_FETCH = 0xc00870a4 ) + +const ( + PIDFD_NONBLOCK = 0x800 +) diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go index e0a8a1362..96c667df4 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go @@ -660,3 +660,7 @@ const ( PPS_GETCAP = 0x800870a3 PPS_FETCH = 0xc00870a4 ) + +const ( + PIDFD_NONBLOCK = 0x800 +) diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go b/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go index 21d6e56c7..af04ee174 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go @@ -656,3 +656,7 @@ const ( PPS_GETCAP = 0x800870a3 PPS_FETCH = 0xc00870a4 ) + +const ( + PIDFD_NONBLOCK = 0x800 +) diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go index 0531e98f6..6f385cf6a 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go @@ -637,3 +637,7 @@ const ( PPS_GETCAP = 0x400870a3 PPS_FETCH = 0xc00870a4 ) + +const ( + PIDFD_NONBLOCK = 0x4000 +) diff --git a/vendor/modules.txt b/vendor/modules.txt index 3ad22d633..830de276b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -37,8 +37,6 @@ github.com/VividCortex/ewma github.com/acarl005/stripansi # github.com/beorn7/perks v1.0.1 github.com/beorn7/perks/quantile -# github.com/bits-and-blooms/bitset v1.2.0 -github.com/bits-and-blooms/bitset # github.com/blang/semver v3.5.1+incompatible github.com/blang/semver # github.com/buger/goterm v0.0.0-20181115115552-c206103e1f37 @@ -63,7 +61,7 @@ github.com/containerd/containerd/log github.com/containerd/containerd/pkg/userns github.com/containerd/containerd/platforms github.com/containerd/containerd/sys -# github.com/containerd/stargz-snapshotter/estargz v0.8.0 +# github.com/containerd/stargz-snapshotter/estargz v0.9.0 github.com/containerd/stargz-snapshotter/estargz github.com/containerd/stargz-snapshotter/estargz/errorutil # github.com/containernetworking/cni v1.0.1 @@ -97,7 +95,7 @@ github.com/containers/buildah/pkg/rusage github.com/containers/buildah/pkg/sshagent github.com/containers/buildah/pkg/util github.com/containers/buildah/util -# github.com/containers/common v0.46.1-0.20210928081721-32e20295f1c6 +# github.com/containers/common v0.46.1-0.20211008123044-d846f5aaec0e github.com/containers/common/libimage github.com/containers/common/libimage/manifests github.com/containers/common/pkg/apparmor @@ -129,7 +127,7 @@ github.com/containers/common/pkg/umask github.com/containers/common/version # github.com/containers/conmon v2.0.20+incompatible github.com/containers/conmon/runner/config -# github.com/containers/image/v5 v5.16.0 +# github.com/containers/image/v5 v5.16.1 github.com/containers/image/v5/copy github.com/containers/image/v5/directory github.com/containers/image/v5/directory/explicitfilepath @@ -202,7 +200,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.36.1-0.20210929132900-162a0bf730ce +# github.com/containers/storage v1.37.0 github.com/containers/storage github.com/containers/storage/drivers github.com/containers/storage/drivers/aufs @@ -286,7 +284,7 @@ github.com/docker/distribution/registry/client/auth/challenge github.com/docker/distribution/registry/client/transport github.com/docker/distribution/registry/storage/cache github.com/docker/distribution/registry/storage/cache/memory -# github.com/docker/docker v20.10.8+incompatible +# github.com/docker/docker v20.10.9+incompatible github.com/docker/docker/api github.com/docker/docker/api/types github.com/docker/docker/api/types/blkiodev @@ -460,7 +458,7 @@ github.com/nxadm/tail/ratelimiter github.com/nxadm/tail/util github.com/nxadm/tail/watch github.com/nxadm/tail/winfile -# github.com/onsi/ginkgo v1.16.4 +# github.com/onsi/ginkgo v1.16.5 github.com/onsi/ginkgo github.com/onsi/ginkgo/config github.com/onsi/ginkgo/extensions/table @@ -526,7 +524,7 @@ github.com/opencontainers/runtime-tools/generate github.com/opencontainers/runtime-tools/generate/seccomp github.com/opencontainers/runtime-tools/specerror github.com/opencontainers/runtime-tools/validate -# github.com/opencontainers/selinux v1.8.5 +# github.com/opencontainers/selinux v1.9.1 github.com/opencontainers/selinux/go-selinux github.com/opencontainers/selinux/go-selinux/label github.com/opencontainers/selinux/pkg/pwalk @@ -612,7 +610,7 @@ github.com/vbauerster/mpb/v6 github.com/vbauerster/mpb/v6/cwriter github.com/vbauerster/mpb/v6/decor github.com/vbauerster/mpb/v6/internal -# github.com/vbauerster/mpb/v7 v7.1.4 +# github.com/vbauerster/mpb/v7 v7.1.5 github.com/vbauerster/mpb/v7 github.com/vbauerster/mpb/v7/cwriter github.com/vbauerster/mpb/v7/decor @@ -676,7 +674,7 @@ golang.org/x/net/trace # golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sync/errgroup golang.org/x/sync/semaphore -# golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34 +# golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 golang.org/x/sys/cpu golang.org/x/sys/execabs golang.org/x/sys/internal/unsafeheader |