diff options
210 files changed, 2083 insertions, 1472 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 06f4a565c..81bbe7c8f 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -30,8 +30,10 @@ env: PRIOR_FEDORA_NAME: "fedora-35" UBUNTU_NAME: "ubuntu-2110" - # Google-cloud VM Images + # Image identifiers IMAGE_SUFFIX: "c6211193021923328" + FEDORA_AMI_ID: "ami-06a41d8a81ab56afa" + # Complete image names FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}" PRIOR_FEDORA_CACHE_IMAGE_NAME: "prior-fedora-${IMAGE_SUFFIX}" UBUNTU_CACHE_IMAGE_NAME: "ubuntu-${IMAGE_SUFFIX}" @@ -66,6 +68,8 @@ timeout_in: 60m gcp_credentials: ENCRYPTED[a28959877b2c9c36f151781b0a05407218cda646c7d047fc556e42f55e097e897ab63ee78369dae141dcf0b46a9d0cdd] +aws_credentials: ENCRYPTED[4ca070bffe28eb9b27d63c568b52970dd46f119c3a83b8e443241e895dbf1737580b4d84eed27a311a2b74287ef9f79f] + # Attempt to prevent flakes by confirming all required external/3rd-party # services are available and functional. @@ -577,6 +581,35 @@ rootless_integration_test_task: always: *int_logs_artifacts +podman_machine_task: + name: *std_name_fmt + alias: podman_machine + only_if: *not_tag_branch_build_docs + # Manually-triggered task: This is "expensive" to run. + # DO NOT ADD THIS TASK AS DEPENDENCY FOR `success_task` + # it will cause 'success' to block. + trigger_type: manual + depends_on: + - build + - local_integration_test + - remote_integration_test + - container_integration_test + - rootless_integration_test + ec2_instance: + image: "${VM_IMAGE_NAME}" + type: m5zn.metal # Bare-metal instance is required + region: us-east-1 + env: + TEST_FLAVOR: "machine" + PRIV_NAME: "rootless" # intended use-case + DISTRO_NV: "${FEDORA_NAME}" + VM_IMAGE_NAME: "${FEDORA_AMI_ID}" + clone_script: *get_gosrc + setup_script: *setup + main_script: *main + always: *int_logs_artifacts + + # Always run subsequent to integration tests. While parallelism is lost # with runtime, debugging system-test failures can be more challenging # for some golang developers. Otherwise the following tasks run across @@ -842,6 +875,9 @@ success_task: - remote_integration_test - container_integration_test - rootless_integration_test + # Manually triggered task. If made automatic, remove bypass + # in contrib/cirrus/cirrus_yaml_test.py for this task. + # - podman_machine - local_system_test - remote_system_test - rootless_system_test @@ -112,6 +112,10 @@ LIBSECCOMP_COMMIT := v2.3.3 # Rarely if ever should integration tests take more than 50min, # caller may override in special circumstances if needed. GINKGOTIMEOUT ?= -timeout=90m +# By default, run test/e2e +GINKGOWHAT ?= test/e2e/. +# By default, run tests in parallel across 3 nodes. +GINKGONODES ?= 3 # Conditional required to produce empty-output if binary not built yet. RELEASE_VERSION = $(shell if test -x test/version/version; then test/version/version; fi) @@ -524,7 +528,7 @@ test: localunit localintegration remoteintegration localsystem remotesystem ## .PHONY: ginkgo-run ginkgo-run: ACK_GINKGO_RC=true ginkgo version - ACK_GINKGO_RC=true ginkgo -v $(TESTFLAGS) -tags "$(TAGS)" $(GINKGOTIMEOUT) -cover -flakeAttempts 3 -progress -trace -noColor -nodes 3 -debug test/e2e/. $(HACK) + ACK_GINKGO_RC=true ginkgo -v $(TESTFLAGS) -tags "$(TAGS)" $(GINKGOTIMEOUT) -cover -flakeAttempts 3 -progress -trace -noColor -nodes $(GINKGONODES) -debug $(GINKGOWHAT) $(HACK) .PHONY: ginkgo ginkgo: @@ -540,6 +544,10 @@ localintegration: test-binaries ginkgo .PHONY: remoteintegration remoteintegration: test-binaries ginkgo-remote +.PHONY: localmachine +localmachine: test-binaries + $(MAKE) ginkgo-run GINKGONODES=1 GINKGOWHAT=pkg/machine/e2e/. HACK= + .PHONY: localbenchmarks localbenchmarks: test-binaries PATH=$(PATH):$(shell pwd)/hack ACK_GINKGO_RC=true ginkgo \ diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index ae30f2875..6e6c33f9b 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -966,7 +966,7 @@ func AutocompleteLogOpt(cmd *cobra.Command, args []string, toComplete string) ([ // AutocompletePullOption - Autocomplete pull options for create and run command. // -> "always", "missing", "never" func AutocompletePullOption(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - pullOptions := []string{"always", "missing", "never"} + pullOptions := []string{"always", "missing", "never", "newer"} return pullOptions, cobra.ShellCompDirectiveNoFileComp } diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go index f05549a8d..923d0517f 100644 --- a/cmd/podman/common/create.go +++ b/cmd/podman/common/create.go @@ -390,7 +390,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, createFlags.StringVar( &cf.Pull, pullFlagName, cf.Pull, - `Pull image before creating ("always"|"missing"|"never")`, + `Pull image policy`, ) _ = cmd.RegisterFlagCompletionFunc(pullFlagName, AutocompletePullOption) @@ -544,13 +544,6 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, ) _ = cmd.RegisterFlagCompletionFunc(userFlagName, AutocompleteUserFlag) - utsFlagName := "uts" - createFlags.String( - utsFlagName, "", - "UTS namespace to use", - ) - _ = cmd.RegisterFlagCompletionFunc(utsFlagName, AutocompleteNamespace) - mountFlagName := "mount" createFlags.StringArrayVar( &cf.Mount, @@ -684,6 +677,14 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, ) _ = cmd.RegisterFlagCompletionFunc(usernsFlagName, AutocompleteUserNamespace) + utsFlagName := "uts" + createFlags.StringVar( + &cf.UTS, + utsFlagName, "", + "UTS namespace to use", + ) + _ = cmd.RegisterFlagCompletionFunc(utsFlagName, AutocompleteNamespace) + cgroupParentFlagName := "cgroup-parent" createFlags.StringVar( &cf.CgroupParent, diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index 05a59ce7b..7d0f4d9ae 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -224,7 +224,6 @@ func CreateInit(c *cobra.Command, vals entities.ContainerCreateOptions, isInfra return vals, errors.New("--cpu-quota and --cpus cannot be set together") } vals.IPC = c.Flag("ipc").Value.String() - vals.UTS = c.Flag("uts").Value.String() vals.PID = c.Flag("pid").Value.String() vals.CgroupNS = c.Flag("cgroupns").Value.String() diff --git a/cmd/podman/containers/wait.go b/cmd/podman/containers/wait.go index 5b8480781..be92a3cbe 100644 --- a/cmd/podman/containers/wait.go +++ b/cmd/podman/containers/wait.go @@ -41,9 +41,9 @@ var ( ) var ( - waitOptions = entities.WaitOptions{} - waitCondition string - waitInterval string + waitOptions = entities.WaitOptions{} + waitConditions []string + waitInterval string ) func waitFlags(cmd *cobra.Command) { @@ -54,7 +54,7 @@ func waitFlags(cmd *cobra.Command) { _ = cmd.RegisterFlagCompletionFunc(intervalFlagName, completion.AutocompleteNone) conditionFlagName := "condition" - flags.StringVar(&waitCondition, conditionFlagName, "stopped", "Condition to wait on") + flags.StringSliceVar(&waitConditions, conditionFlagName, []string{}, "Condition to wait on") _ = cmd.RegisterFlagCompletionFunc(conditionFlagName, common.AutocompleteWaitCondition) } @@ -92,11 +92,13 @@ func wait(cmd *cobra.Command, args []string) error { return errors.New("--latest and containers cannot be used together") } - cond, err := define.StringToContainerStatus(waitCondition) - if err != nil { - return err + for _, condition := range waitConditions { + cond, err := define.StringToContainerStatus(condition) + if err != nil { + return err + } + waitOptions.Condition = append(waitOptions.Condition, cond) } - waitOptions.Condition = []define.ContainerStatus{cond} responses, err := registry.ContainerEngine().ContainerWait(context.Background(), args, waitOptions) if err != nil { diff --git a/cmd/podman/machine/info.go b/cmd/podman/machine/info.go new file mode 100644 index 000000000..9932027d8 --- /dev/null +++ b/cmd/podman/machine/info.go @@ -0,0 +1,182 @@ +//go:build amd64 || arm64 +// +build amd64 arm64 + +package machine + +import ( + "fmt" + "html/template" + "os" + "runtime" + + "github.com/containers/common/pkg/completion" + "github.com/containers/common/pkg/config" + "github.com/containers/common/pkg/report" + "github.com/containers/podman/v4/cmd/podman/common" + "github.com/containers/podman/v4/cmd/podman/registry" + "github.com/containers/podman/v4/cmd/podman/validate" + "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/machine" + "github.com/ghodss/yaml" + "github.com/spf13/cobra" +) + +var infoDescription = `Display information pertaining to the machine host.` + +var ( + infoCmd = &cobra.Command{ + Use: "info [options]", + Short: "Display machine host info", + Long: infoDescription, + PersistentPreRunE: rootlessOnly, + RunE: info, + Args: validate.NoArgs, + ValidArgsFunction: completion.AutocompleteNone, + Example: `podman machine info`, + } +) + +var ( + inFormat string +) + +// Info contains info on the machine host and version info +type Info struct { + Host *HostInfo `json:"Host"` + Version define.Version `json:"Version"` +} + +// HostInfo contains info on the machine host +type HostInfo struct { + Arch string `json:"Arch"` + CurrentMachine string `json:"CurrentMachine"` + DefaultMachine string `json:"DefaultMachine"` + EventsDir string `json:"EventsDir"` + MachineConfigDir string `json:"MachineConfigDir"` + MachineImageDir string `json:"MachineImageDir"` + MachineState string `json:"MachineState"` + NumberOfMachines int `json:"NumberOfMachines"` + OS string `json:"OS"` + VMType string `json:"VMType"` +} + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Command: infoCmd, + Parent: machineCmd, + }) + + flags := infoCmd.Flags() + formatFlagName := "format" + flags.StringVarP(&inFormat, formatFlagName, "f", "", "Change the output format to JSON or a Go template") + _ = infoCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&define.Info{})) +} + +func info(cmd *cobra.Command, args []string) error { + info := Info{} + version, err := define.GetVersion() + if err != nil { + return fmt.Errorf("error getting version info %w", err) + } + info.Version = version + + host, err := hostInfo() + if err != nil { + return err + } + info.Host = host + + switch { + case report.IsJSON(inFormat): + b, err := json.MarshalIndent(info, "", " ") + if err != nil { + return err + } + fmt.Println(string(b)) + case cmd.Flags().Changed("format"): + tmpl := template.New(cmd.Name()).Funcs(template.FuncMap(report.DefaultFuncs)) + inFormat = report.NormalizeFormat(inFormat) + tmpl, err := tmpl.Parse(inFormat) + if err != nil { + return err + } + return tmpl.Execute(os.Stdout, info) + default: + b, err := yaml.Marshal(info) + if err != nil { + return err + } + fmt.Println(string(b)) + } + + return nil +} + +func hostInfo() (*HostInfo, error) { + host := HostInfo{} + + host.Arch = runtime.GOARCH + host.OS = runtime.GOOS + + provider := GetSystemDefaultProvider() + var listOpts machine.ListOptions + listResponse, err := provider.List(listOpts) + if err != nil { + return nil, fmt.Errorf("failed to get machines %w", err) + } + + host.NumberOfMachines = len(listResponse) + + cfg, err := config.ReadCustomConfig() + if err != nil { + return nil, err + } + + // Default state of machine is stopped + host.MachineState = "Stopped" + for _, vm := range listResponse { + // Set default machine if found + if vm.Name == cfg.Engine.ActiveService { + host.DefaultMachine = vm.Name + } + // If machine is running or starting, it is automatically the current machine + if vm.Running { + host.CurrentMachine = vm.Name + host.MachineState = "Running" + } else if vm.Starting { + host.CurrentMachine = vm.Name + host.MachineState = "Starting" + } + } + // If no machines are starting or running, set current machine to default machine + // If no default machines are found, do not report a default machine or a state + if host.CurrentMachine == "" { + if host.DefaultMachine == "" { + host.MachineState = "" + } else { + host.CurrentMachine = host.DefaultMachine + } + } + + host.VMType = provider.VMType() + + dataDir, err := machine.GetDataDir(host.VMType) + if err != nil { + return nil, fmt.Errorf("failed to get machine image dir") + } + host.MachineImageDir = dataDir + + confDir, err := machine.GetConfDir(host.VMType) + if err != nil { + return nil, fmt.Errorf("failed to get machine config dir %w", err) + } + host.MachineConfigDir = confDir + + eventsDir, err := eventSockDir() + if err != nil { + return nil, fmt.Errorf("failed to get events dir: %w", err) + } + host.EventsDir = eventsDir + + return &host, nil +} diff --git a/cmd/podman/machine/list.go b/cmd/podman/machine/list.go index b1e31566f..dd4a86697 100644 --- a/cmd/podman/machine/list.go +++ b/cmd/podman/machine/list.go @@ -16,6 +16,7 @@ import ( "github.com/containers/podman/v4/cmd/podman/common" "github.com/containers/podman/v4/cmd/podman/registry" "github.com/containers/podman/v4/cmd/podman/validate" + "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/machine" "github.com/docker/go-units" "github.com/spf13/cobra" @@ -44,23 +45,6 @@ type listFlagType struct { quiet bool } -type ListReporter struct { - Name string - Default bool - Created string - Running bool - Starting bool - LastUp string - Stream string - VMType string - CPUs uint64 - Memory string - DiskSize string - Port int - RemoteUsername string - IdentityPath string -} - func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ Command: lsCmd, @@ -70,7 +54,7 @@ func init() { flags := lsCmd.Flags() formatFlagName := "format" flags.StringVar(&listFlag.format, formatFlagName, "{{.Name}}\t{{.VMType}}\t{{.Created}}\t{{.LastUp}}\t{{.CPUs}}\t{{.Memory}}\t{{.DiskSize}}\n", "Format volume output using JSON or a Go template") - _ = lsCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&ListReporter{})) + _ = lsCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&entities.ListReporter{})) flags.BoolVar(&listFlag.noHeading, "noheading", false, "Do not print headers") flags.BoolVarP(&listFlag.quiet, "quiet", "q", false, "Show only machine names") } @@ -123,8 +107,8 @@ func list(cmd *cobra.Command, args []string) error { return outputTemplate(cmd, machineReporter) } -func outputTemplate(cmd *cobra.Command, responses []*ListReporter) error { - headers := report.Headers(ListReporter{}, map[string]string{ +func outputTemplate(cmd *cobra.Command, responses []*entities.ListReporter) error { + headers := report.Headers(entities.ListReporter{}, map[string]string{ "LastUp": "LAST UP", "VmType": "VM TYPE", "CPUs": "CPUS", @@ -183,15 +167,15 @@ func streamName(imageStream string) string { return imageStream } -func toMachineFormat(vms []*machine.ListResponse) ([]*ListReporter, error) { +func toMachineFormat(vms []*machine.ListResponse) ([]*entities.ListReporter, error) { cfg, err := config.ReadCustomConfig() if err != nil { return nil, err } - machineResponses := make([]*ListReporter, 0, len(vms)) + machineResponses := make([]*entities.ListReporter, 0, len(vms)) for _, vm := range vms { - response := new(ListReporter) + response := new(entities.ListReporter) response.Default = vm.Name == cfg.Engine.ActiveService response.Name = vm.Name response.Running = vm.Running @@ -211,15 +195,15 @@ func toMachineFormat(vms []*machine.ListResponse) ([]*ListReporter, error) { return machineResponses, nil } -func toHumanFormat(vms []*machine.ListResponse) ([]*ListReporter, error) { +func toHumanFormat(vms []*machine.ListResponse) ([]*entities.ListReporter, error) { cfg, err := config.ReadCustomConfig() if err != nil { return nil, err } - humanResponses := make([]*ListReporter, 0, len(vms)) + humanResponses := make([]*entities.ListReporter, 0, len(vms)) for _, vm := range vms { - response := new(ListReporter) + response := new(entities.ListReporter) if vm.Name == cfg.Engine.ActiveService { response.Name = vm.Name + "*" response.Default = true diff --git a/cmd/podman/machine/machine.go b/cmd/podman/machine/machine.go index 5a8a06b9d..0618337cc 100644 --- a/cmd/podman/machine/machine.go +++ b/cmd/podman/machine/machine.go @@ -101,12 +101,6 @@ func resolveEventSock() ([]string, error) { return []string{sock}, nil } - xdg, err := util.GetRuntimeDir() - if err != nil { - logrus.Warnf("Failed to get runtime dir, machine events will not be published: %s", err) - return nil, nil - } - re := regexp.MustCompile(`machine_events.*\.sock`) sockPaths := make([]string, 0) fn := func(path string, info os.DirEntry, err error) error { @@ -125,8 +119,12 @@ func resolveEventSock() ([]string, error) { sockPaths = append(sockPaths, path) return nil } + sockDir, err := eventSockDir() + if err != nil { + logrus.Warnf("Failed to get runtime dir, machine events will not be published: %s", err) + } - if err := filepath.WalkDir(filepath.Join(xdg, "podman"), fn); err != nil { + if err := filepath.WalkDir(sockDir, fn); err != nil { if errors.Is(err, os.ErrNotExist) { return nil, nil } @@ -135,6 +133,14 @@ func resolveEventSock() ([]string, error) { return sockPaths, nil } +func eventSockDir() (string, error) { + xdg, err := util.GetRuntimeDir() + if err != nil { + return "", err + } + return filepath.Join(xdg, "podman"), nil +} + func newMachineEvent(status events.Status, event events.Event) { openEventSock.Do(initMachineEvents) diff --git a/cmd/podman/networks/prune.go b/cmd/podman/networks/prune.go index fa621ebac..ee5389aa7 100644 --- a/cmd/podman/networks/prune.go +++ b/cmd/podman/networks/prune.go @@ -52,10 +52,7 @@ func init() { } func networkPrune(cmd *cobra.Command, _ []string) error { - var ( - errs utils.OutputErrors - err error - ) + var err error if !force { reader := bufio.NewReader(os.Stdin) fmt.Println("WARNING! This will remove all networks not used by at least one container.") @@ -77,13 +74,5 @@ func networkPrune(cmd *cobra.Command, _ []string) error { setExitCode(err) return err } - for _, r := range responses { - if r.Error == nil { - fmt.Println(r.Name) - } else { - setExitCode(r.Error) - errs = append(errs, r.Error) - } - } - return errs.PrintErrors() + return utils.PrintNetworkPruneResults(responses, false) } diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go index 45ad2dfd0..aea8a7229 100644 --- a/cmd/podman/pods/create.go +++ b/cmd/podman/pods/create.go @@ -277,6 +277,7 @@ func create(cmd *cobra.Command, args []string) error { if err != nil { return err } + podSpec.Volumes = podSpec.InfraContainerSpec.Volumes podSpec.ImageVolumes = podSpec.InfraContainerSpec.ImageVolumes podSpec.OverlayVolumes = podSpec.InfraContainerSpec.OverlayVolumes diff --git a/cmd/podman/utils/utils.go b/cmd/podman/utils/utils.go index 2ae123388..a265faf51 100644 --- a/cmd/podman/utils/utils.go +++ b/cmd/podman/utils/utils.go @@ -85,16 +85,16 @@ func PrintImagePruneResults(imagePruneReports []*reports.PruneReport, heading bo return nil } -func PrintNetworkPruneResults(networkPruneReport []*reports.PruneReport, heading bool) error { +func PrintNetworkPruneResults(networkPruneReport []*entities.NetworkPruneReport, heading bool) error { var errs OutputErrors if heading && len(networkPruneReport) > 0 { fmt.Println("Deleted Networks") } for _, r := range networkPruneReport { - if r.Err == nil { - fmt.Println(r.Id) + if r.Error == nil { + fmt.Println(r.Name) } else { - errs = append(errs, r.Err) + errs = append(errs, r.Error) } } return errs.PrintErrors() diff --git a/contrib/cirrus/cirrus_yaml_test.py b/contrib/cirrus/cirrus_yaml_test.py index a7fff8d3f..3968b8b1b 100755 --- a/contrib/cirrus/cirrus_yaml_test.py +++ b/contrib/cirrus/cirrus_yaml_test.py @@ -26,7 +26,7 @@ class TestCaseBase(unittest.TestCase): class TestDependsOn(TestCaseBase): ALL_TASK_NAMES = None - SUCCESS_DEPS_EXCLUDE = set(['success', 'artifacts', + SUCCESS_DEPS_EXCLUDE = set(['success', 'artifacts', 'podman_machine', 'test_image_build', 'release', 'release_test']) def setUp(self): diff --git a/contrib/cirrus/runner.sh b/contrib/cirrus/runner.sh index d49286ad3..762a3b501 100755 --- a/contrib/cirrus/runner.sh +++ b/contrib/cirrus/runner.sh @@ -379,6 +379,13 @@ dotest() { |& logformatter } +_run_machine() { + # TODO: This is a manually-triggered task, if that ever changes need to + # add something like: + # _bail_if_test_can_be_skipped docs test/e2e test/system test/python + make localmachine |& logformatter +} + # Optimization: will exit if the only PR diffs are under docs/ or tests/ # with the exception of any given arguments. E.g., don't run e2e or upgrade # or bud tests if the only PR changes are in test/system. diff --git a/contrib/cirrus/setup_environment.sh b/contrib/cirrus/setup_environment.sh index 9bd35bd06..4952f8dd2 100755 --- a/contrib/cirrus/setup_environment.sh +++ b/contrib/cirrus/setup_environment.sh @@ -99,11 +99,28 @@ esac if ((CONTAINER==0)); then # Not yet running inside a container # Discovered reemergence of BFQ scheduler bug in kernel 5.8.12-200 # which causes a kernel panic when system is under heavy I/O load. - # Previously discovered in F32beta and confirmed fixed. It's been - # observed in F31 kernels as well. Deploy workaround for all VMs - # to ensure a more stable I/O scheduler (elevator). - echo "mq-deadline" > /sys/block/sda/queue/scheduler - warn "I/O scheduler: $(cat /sys/block/sda/queue/scheduler)" + # Disable the I/O scheduler (a.k.a. elevator) for all environments, + # leaving optimization up to underlying storage infrastructure. + testfs="/" # mountpoint that experiences the most I/O during testing + msg "Querying block device owning partition hosting the '$testfs' filesystem" + # Need --nofsroot b/c btrfs appends subvolume label to `source` name + testdev=$(findmnt --canonicalize --noheadings --nofsroot \ + --output source --mountpoint $testfs) + msg " found partition: '$testdev'" + testdisk=$(lsblk --noheadings --output pkname --paths $testdev) + msg " found block dev: '$testdisk'" + testsched="/sys/block/$(basename $testdisk)/queue/scheduler" + if [[ -n "$testdev" ]] && [[ -n "$testdisk" ]] && [[ -e "$testsched" ]]; then + msg " Found active I/O scheduler: $(cat $testsched)" + if [[ ! "$(<$testsched)" =~ \[none\] ]]; then + msg " Disabling elevator for '$testsched'" + echo "none" > "$testsched" + else + msg " Elevator already disabled" + fi + else + warn "Sys node for elevator doesn't exist: '$testsched'" + fi fi # Which distribution are we testing on. @@ -296,6 +313,13 @@ case "$TEST_FLAVOR" in install_test_configs ;; + machine) + rpm -ivh $PACKAGE_DOWNLOAD_DIR/podman-gvproxy* + remove_packaged_podman_files + make install.tools + make install PREFIX=/usr ETCDIR=/etc + install_test_configs + ;; gitlab) # This only runs on Ubuntu for now if [[ "$OS_RELEASE_ID" != "ubuntu" ]]; then diff --git a/docs/play_kube_support.md b/docs/play_kube_support.md index cf983bc04..3cfd3fa50 100644 --- a/docs/play_kube_support.md +++ b/docs/play_kube_support.md @@ -150,3 +150,26 @@ Note: **N/A** means that the option cannot be supported in a single-node Podman | selector | | | resources.limits | | | resources.requests | ✅ | + +## ConfigMap Fields + +| Field | Support | +|------------|---------| +| binaryData | | +| data | ✅ | +| immutable | | + +## Deployment Fields + +| Field | Support | +|---------------------------------------|---------| +| replicas | ✅ | +| selector | ✅ | +| template | ✅ | +| minReadySeconds | | +| strategy.type | | +| strategy.rollingUpdate.maxSurge | | +| strategy.rollingUpdate.maxUnavailable | | +| revisionHistoryLimit | | +| progressDeadlineSeconds | | +| paused | | diff --git a/docs/source/markdown/podman-build.1.md b/docs/source/markdown/podman-build.1.md index 71b92bcb3..bf710022e 100644 --- a/docs/source/markdown/podman-build.1.md +++ b/docs/source/markdown/podman-build.1.md @@ -576,27 +576,14 @@ While `podman build` is happy to use base images and build images for any platform that exists, `RUN` instructions will not be able to succeed without the help of emulation provided by packages like `qemu-user-static`. -#### **--pull** +#### **--pull**=**always**|**missing**|**never**|**newer** -When the option is enabled or set explicitly to `true` (with *--pull=true*) -pull the image from the first registry it is found in as listed in registries.conf. -Raise an error if the image could not be pulled, even if the image is present locally. +Pull image policy. The default is **always**. -If the option is disabled (with *--pull=false*), pull the image from the -registry only if the image is not present locally. Raise an error if the image is not -in the registries and not present locally. - -If the pull option is set to `always` (with *--pull=always*), -pull the image from the first registry it is found in as listed in registries.conf. -Raise an error if not found in the registries, even if the image is present locally. - -If the pull option is set to `missing` (with *--pull=missing*), -Pull the image only if it is not present in the local storage. Raise an error if it -could neither be found in the local storage or on a registry. - -If the pull option is set to `never` (with *--pull=never*), -Do not pull the image from the registry, use only the local version. Raise an error -if the image is not present locally. +- **always**, **true**: Always pull the image and throw an error if the pull fails. +- **missing**: Pull the image only if it could not be found in the local containers storage. Throw an error if no image could be found and the pull fails. +- **never**, **false**: Never pull the image but use the one from the local containers storage. Throw an error if no image could be found. +- **newer**: Pull if the image on the registry is newer than the one in the local containers storage. An image is considered to be newer when the digests are different. Comparing the time stamps is prone to errors. Pull errors are suppressed if a local image was found. #### **--quiet**, **-q** diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md index 403327d82..09c7d99c1 100644 --- a/docs/source/markdown/podman-create.1.md +++ b/docs/source/markdown/podman-create.1.md @@ -368,9 +368,10 @@ on the host system. #### **--gidmap**=*container_gid:host_gid:amount* -GID map for the user namespace. Using this flag will run the container with user namespace enabled. It conflicts with the `--userns` and `--subgidname` flags. - -The following example maps uids 0-2000 in the container to the uids 30000-31999 on the host and gids 0-2000 in the container to the gids 30000-31999 on the host. `--gidmap=0:30000:2000` +Run the container in a new user namespace using the supplied GID mapping. This +option conflicts with the **--userns** and **--subgidname** options. This +option provides a way to map host GIDs to container GIDs in the same way as +__--uidmap__ maps host UIDs to container UIDs. For details see __--uidmap__. Note: the **--gidmap** flag cannot be called in conjunction with the **--pod** flag as a gidmap cannot be set on the container level when in a pod. @@ -866,14 +867,14 @@ port to a random port on the host within an *ephemeral port range* defined by `/proc/sys/net/ipv4/ip_local_port_range`. To find the mapping between the host ports and the exposed ports, use `podman port`. -#### **--pull**=*missing* +#### **--pull**=**always**|**missing**|**never**|**newer** -Pull image before creating ("always"|"missing"|"never") (default "missing"). - 'missing': default value, attempt to pull the latest image from the registries listed in registries.conf if a local image does not exist.Raise an error if the image is not in any listed registry and is not present locally. - 'always': Pull the image from the first registry it is found in as listed in registries.conf. Raise an error if not found in the registries, even if the image is present locally. - 'never': do not pull the image from the registry, use only the local version. Raise an error if the image is not present locally. +Pull image policy. The default is **missing**. -Defaults to *missing*. +- **always**: Always pull the image and throw an error if the pull fails. +- **missing**: Pull the image only if it could not be found in the local containers storage. Throw an error if no image could be found and the pull fails. +- **never**: Never pull the image but use the one from the local containers storage. Throw an error if no image could be found. +- **newer**: Pull if the image on the registry is newer than the one in the local containers storage. An image is considered to be newer when the digests are different. Comparing the time stamps is prone to errors. Pull errors are suppressed if a local image was found. #### **--quiet**, **-q** @@ -1120,7 +1121,7 @@ Remote connections use local containers.conf for defaults #### **--uidmap**=*container_uid*:*from_uid*:*amount* -Run the container in a new user namespace using the supplied mapping. This +Run the container in a new user namespace using the supplied UID mapping. This option conflicts with the **--userns** and **--subuidname** options. This option provides a way to map host UIDs to container UIDs. It can be passed several times to map different ranges. diff --git a/docs/source/markdown/podman-info.1.md b/docs/source/markdown/podman-info.1.md index fc2d0fa60..28e4f3291 100644 --- a/docs/source/markdown/podman-info.1.md +++ b/docs/source/markdown/podman-info.1.md @@ -24,9 +24,10 @@ Show additional information Change output format to "json" or a Go template. -## EXAMPLE +## EXAMPLES + +Run `podman info` for a YAML formatted response: -Run podman info with plain text response: ``` $ podman info host: @@ -149,7 +150,9 @@ version: OsArch: linux/amd64 Version: 4.0.0 ``` -Run podman info with JSON formatted response: + +Run `podman info --format json` for a JSON formatted response: + ``` $ podman info --format json { @@ -289,11 +292,68 @@ $ podman info --format json } } ``` -Run podman info and only get the registries information. + +#### Extracting the list of container registries with a Go template + +If shell completion is enabled, type `podman info --format={{.` and then press `[TAB]` twice. + +``` +$ podman info --format={{. +{{.Host. {{.Plugins. {{.Registries}} {{.Store. {{.Version. +``` + +Press `R` `[TAB]` `[ENTER]` to print the registries information. + +``` +$ podman info -f {{.Registries}} +map[search:[registry.fedoraproject.org registry.access.redhat.com docker.io quay.io]] +$ +``` + +The output still contains a map and an array. The map value can be extracted with + +``` +$ podman info -f '{{index .Registries "search"}}' +[registry.fedoraproject.org registry.access.redhat.com docker.io quay.io] +``` + +The array can be printed as one entry per line + +``` +$ podman info -f '{{range index .Registries "search"}}{{.}}\n{{end}}' +registry.fedoraproject.org +registry.access.redhat.com +docker.io +quay.io + ``` -$ podman info --format={{".Registries"}} -map[registries:[docker.io quay.io registry.fedoraproject.org registry.access.redhat.com]] + +#### Extracting the list of container registries from JSON with jq + +The command-line JSON processor [__jq__](https://stedolan.github.io/jq/) can be used to extract the list +of container registries. + ``` +$ podman info -f json | jq '.registries["search"]' +[ + "registry.fedoraproject.org", + "registry.access.redhat.com", + "docker.io", + "quay.io" +] +``` + +The array can be printed as one entry per line + +``` +$ podman info -f json | jq -r '.registries["search"] | .[]' +registry.fedoraproject.org +registry.access.redhat.com +docker.io +quay.io +``` + +Note, the Go template struct fields start with upper case. When running `podman info` or `podman info --format=json`, the same names start with lower case. ## SEE ALSO **[podman(1)](podman.1.md)**, **[containers-registries.conf(5)](https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md)**, **[containers-storage.conf(5)](https://github.com/containers/storage/blob/main/docs/containers-storage.conf.5.md)** diff --git a/docs/source/markdown/podman-machine-info.1.md b/docs/source/markdown/podman-machine-info.1.md new file mode 100644 index 000000000..33c207d32 --- /dev/null +++ b/docs/source/markdown/podman-machine-info.1.md @@ -0,0 +1,36 @@ +% podman-machine-info(1) + +## NAME +podman\-machine\-info - Display machine host info + +## SYNOPSIS +**podman machine info** + +## DESCRIPTION + +Display information pertaining to the machine host. +Rootless only, as all `podman machine` commands can be only be used with rootless Podman. + +## OPTIONS + +#### **--format**=*format*, **-f** + +Change output format to "json" or a Go template. + +#### **--help** + +Print usage statement. + +## EXAMPLES + +``` +$ podman machine info +$ podman machine info --format json +$ podman machine info --format {{.Host.Arch}} +``` + +## SEE ALSO +**[podman(1)](podman.1.md)**, **[podman-machine(1)](podman-machine.1.md)** + +## HISTORY +June 2022, Originally compiled by Ashley Cui <acui@redhat.com> diff --git a/docs/source/markdown/podman-machine-init.1.md b/docs/source/markdown/podman-machine-init.1.md index 2adb15e6a..21c98b2c7 100644 --- a/docs/source/markdown/podman-machine-init.1.md +++ b/docs/source/markdown/podman-machine-init.1.md @@ -76,15 +76,33 @@ Set the timezone for the machine and containers. Valid values are `local` or a `timezone` such as `America/Chicago`. A value of `local`, which is the default, means to use the timezone of the machine host. -#### **--volume**, **-v**=*source:target* +#### **--volume**, **-v**=*source:target[:options]* Mounts a volume from source to target. Create a mount. If /host-dir:/machine-dir is specified as the `*source:target*`, Podman mounts _host-dir_ in the host to _machine-dir_ in the Podman machine. -The root filesystem is mounted read-only in the default operating system, -so mounts must be created under the /mnt directory. +Additional options may be specified as a comma-separated string. Recognized +options are: +* **ro**: mount volume read-only +* **rw**: mount volume read/write (default) +* **security_model=[model]**: specify 9p security model (see below) + +The 9p security model [determines] https://wiki.qemu.org/Documentation/9psetup#Starting_the_Guest_directly +if and how the 9p filesystem translates some filesystem operations before +actual storage on the host. The +default value of *mapped-xattr* specifies that 9p store symlinks and some file +attributes as extended attributes on the host. This is suitable when the host +and the guest do not need to interoperate on the shared filesystem, but has +caveats for actual shared access; notably, symlinks on the host are not usable +on the guest and vice versa. If interoperability is required, then choose +*none* instead, but keep in mind that the guest will not be able to do things +that the user running the virtual machine cannot do, e.g. create files owned by +another user. Using *none* is almost certainly the best choice for read-only +volumes. + +Example: `-v "$HOME/git:$HOME/git:ro,security_model=none"` Default volume mounts are defined in *containers.conf*. Unless changed, the default values is `$HOME:$HOME`. diff --git a/docs/source/markdown/podman-machine.1.md b/docs/source/markdown/podman-machine.1.md index c55226e02..6197b8d4e 100644 --- a/docs/source/markdown/podman-machine.1.md +++ b/docs/source/markdown/podman-machine.1.md @@ -20,6 +20,7 @@ All `podman machine` commands are rootless only. | Command | Man Page | Description | |---------|------------------------------------------------------|-----------------------------------| +| info | [podman-machine-info(1)](podman-machine-info.1.md) | Display machine host info | | init | [podman-machine-init(1)](podman-machine-init.1.md) | Initialize a new virtual machine | | inspect | [podman-machine-inspect(1)](podman-machine-inspect.1.md) | Inspect one or more virtual machines | | list | [podman-machine-list(1)](podman-machine-list.1.md) | List virtual machines | @@ -30,7 +31,7 @@ All `podman machine` commands are rootless only. | stop | [podman-machine-stop(1)](podman-machine-stop.1.md) | Stop a virtual machine | ## SEE ALSO -**[podman(1)](podman.1.md)**, **[podman-machine-init(1)](podman-machine-init.1.md)**, **[podman-machine-list(1)](podman-machine-list.1.md)**, **[podman-machine-rm(1)](podman-machine-rm.1.md)**, **[podman-machine-ssh(1)](podman-machine-ssh.1.md)**, **[podman-machine-start(1)](podman-machine-start.1.md)**, **[podman-machine-stop(1)](podman-machine-stop.1.md)**, **[podman-machine-inspect(1)](podman-machine-inspect.1.md)** +**[podman(1)](podman.1.md)**, **[podman-machine-info(1)](podman-machine-info.1.md)**, **[podman-machine-init(1)](podman-machine-init.1.md)**, **[podman-machine-list(1)](podman-machine-list.1.md)**, **[podman-machine-rm(1)](podman-machine-rm.1.md)**, **[podman-machine-ssh(1)](podman-machine-ssh.1.md)**, **[podman-machine-start(1)](podman-machine-start.1.md)**, **[podman-machine-stop(1)](podman-machine-stop.1.md)**, **[podman-machine-inspect(1)](podman-machine-inspect.1.md)** ## HISTORY March 2021, Originally compiled by Ashley Cui <acui@redhat.com> diff --git a/docs/source/markdown/podman-pod-clone.1.md b/docs/source/markdown/podman-pod-clone.1.md index a18f7dbfe..d90d1efb9 100644 --- a/docs/source/markdown/podman-pod-clone.1.md +++ b/docs/source/markdown/podman-pod-clone.1.md @@ -211,6 +211,15 @@ Valid _mode_ values are: - *nomap*: creates a user namespace where the current rootless user's UID:GID are not mapped into the container. This option is ignored for containers created by the root user. +#### **--uts**=*mode* + +Set the UTS namespace mode for the pod. The following values are supported: + +- **host**: use the host's UTS namespace inside the pod. +- **private**: create a new namespace for the pod (default). +- **ns:[path]**: run the pod in the given existing UTS namespace. + + #### **--volume**, **-v**[=*[[SOURCE-VOLUME|HOST-DIR:]CONTAINER-DIR[:OPTIONS]]*] Create a bind mount. If ` -v /HOST-DIR:/CONTAINER-DIR` is specified, Podman diff --git a/docs/source/markdown/podman-pod-create.1.md b/docs/source/markdown/podman-pod-create.1.md index 75d2bb611..53d1e3327 100644 --- a/docs/source/markdown/podman-pod-create.1.md +++ b/docs/source/markdown/podman-pod-create.1.md @@ -381,6 +381,14 @@ Valid _mode_ values are: - *nomap*: creates a user namespace where the current rootless user's UID:GID are not mapped into the container. This option is not allowed for containers created by the root user. +#### **--uts**=*mode* + +Set the UTS namespace mode for the pod. The following values are supported: + +- **host**: use the host's UTS namespace inside the pod. +- **private**: create a new namespace for the pod (default). +- **ns:[path]**: run the pod in the given existing UTS namespace. + #### **--volume**, **-v**[=*[[SOURCE-VOLUME|HOST-DIR:]CONTAINER-DIR[:OPTIONS]]*] Create a bind mount. If you specify, ` -v /HOST-DIR:/CONTAINER-DIR`, Podman diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md index 8f71c3706..e628a806a 100644 --- a/docs/source/markdown/podman-run.1.md +++ b/docs/source/markdown/podman-run.1.md @@ -404,16 +404,10 @@ on the host system. #### **--gidmap**=*container_gid*:*host_gid*:*amount* -Run the container in a new user namespace using the supplied mapping. This option conflicts with the **--userns** and **--subgidname** flags. -This option can be passed several times to map different ranges. If calling **podman run** as an unprivileged user, the user needs to have the right to use the mapping. See **subuid**(5). -The example maps gids **0-1999** in the container to the gids **30000-31999** on the host: **--gidmap=0:30000:2000**. - -**Important note:** The new user namespace mapping based on **--gidmap** is based on the initial mapping made in the _/etc/subgid_ file. -Assuming there is a _/etc/subgid_ mapping **groupname:100000:65536**, then **groupname** is initially mapped to a namespace starting with -gid **100000** for **65536** ids. From here the **--gidmap** mapping to the new namespace starts from **0** again, but is based on the initial mapping. -Meaning **groupname** is initially mapped to gid **100000** which is referenced as **0** in the following **--gidmap** mapping. In terms of the example -above: The group **groupname** is mapped to group **100000** of the initial namespace then the -**30000**st id of this namespace (which is gid 130000 in this namespace) is mapped to container namespace group id **0**. (groupname -> 100000 / 30000 -> 0) +Run the container in a new user namespace using the supplied GID mapping. This +option conflicts with the **--userns** and **--subgidname** options. This +option provides a way to map host GIDs to container GIDs in the same way as +__--uidmap__ maps host UIDs to container UIDs. For details see __--uidmap__. Note: the **--gidmap** flag cannot be called in conjunction with the **--pod** flag as a gidmap cannot be set on the container level when in a pod. @@ -905,13 +899,14 @@ When using this option, Podman will bind any exposed port to a random port on th within an ephemeral port range defined by */proc/sys/net/ipv4/ip_local_port_range*. To find the mapping between the host ports and the exposed ports, use **podman port**. -#### **--pull**=**always**|**missing**|**never** +#### **--pull**=**always**|**missing**|**never**|**newer** -Pull image before running. The default is **missing**. +Pull image policy. The default is **missing**. -- **missing**: attempt to pull the latest image from the registries listed in registries.conf if a local image does not exist.Raise an error if the image is not in any listed registry and is not present locally. -- **always**: Pull the image from the first registry it is found in as listed in registries.conf. Raise an error if not found in the registries, even if the image is present locally. -- **never**: do not pull the image from the registry, use only the local version. Raise an error if the image is not present locally. +- **always**: Always pull the image and throw an error if the pull fails. +- **missing**: Pull the image only if it could not be found in the local containers storage. Throw an error if no image could be found and the pull fails. +- **never**: Never pull the image but use the one from the local containers storage. Throw an error if no image could be found. +- **newer**: Pull if the image on the registry is newer than the one in the local containers storage. An image is considered to be newer when the digests are different. Comparing the time stamps is prone to errors. Pull errors are suppressed if a local image was found. #### **--quiet**, **-q** @@ -1187,7 +1182,7 @@ Remote connections use local containers.conf for defaults #### **--uidmap**=*container_uid*:*from_uid*:*amount* -Run the container in a new user namespace using the supplied mapping. This +Run the container in a new user namespace using the supplied UID mapping. This option conflicts with the **--userns** and **--subuidname** options. This option provides a way to map host UIDs to container UIDs. It can be passed several times to map different ranges. @@ -12,7 +12,7 @@ require ( github.com/containernetworking/cni v1.1.1 github.com/containernetworking/plugins v1.1.1 github.com/containers/buildah v1.26.1-0.20220609225314-e66309ebde8c - github.com/containers/common v0.48.1-0.20220630172158-178929cf063e + github.com/containers/common v0.48.1-0.20220705175712-dd1c331887b9 github.com/containers/conmon v2.0.20+incompatible github.com/containers/image/v5 v5.21.2-0.20220617075545-929f14a56f5c github.com/containers/ocicrypt v1.1.5 @@ -51,7 +51,6 @@ require ( github.com/opencontainers/runtime-spec v1.0.3-0.20211214071223-8958f93039ab github.com/opencontainers/runtime-tools v0.9.1-0.20220110225228-7e2d60f1e41f github.com/opencontainers/selinux v1.10.1 - github.com/pkg/errors v0.9.1 github.com/pmezard/go-difflib v1.0.0 github.com/rootless-containers/rootlesskit v1.0.1 github.com/sirupsen/logrus v1.8.1 @@ -338,8 +338,8 @@ github.com/containernetworking/plugins v1.1.1/go.mod h1:Sr5TH/eBsGLXK/h71HeLfX19 github.com/containers/buildah v1.26.1-0.20220609225314-e66309ebde8c h1:/fKyiLFFuceBPZGJ0Lig7ElURhfsslAOw1BOcItD+X8= github.com/containers/buildah v1.26.1-0.20220609225314-e66309ebde8c/go.mod h1:b0L+u2Dam7soWGn5sVTK31L++Xrf80AbGvK5z9D2+lw= github.com/containers/common v0.48.1-0.20220608111710-dbecabbe82c9/go.mod h1:WBLwq+i7bicCpH54V70HM6s7jqDAESTlYnd05XXp0ac= -github.com/containers/common v0.48.1-0.20220630172158-178929cf063e h1:Vf5tsGrLC2B2omVBP3AdDA7YlE/VoMdNyQ5yPF8GRoY= -github.com/containers/common v0.48.1-0.20220630172158-178929cf063e/go.mod h1:Zt3D/IhgFyG1oaBrqsbn9NdH/4fkjsO2Y0ahP12ieu4= +github.com/containers/common v0.48.1-0.20220705175712-dd1c331887b9 h1:KeGIf6Z1R+16Sq+5/fhkoCCKa7wjQ6Ksnmo0beU1E2U= +github.com/containers/common v0.48.1-0.20220705175712-dd1c331887b9/go.mod h1:Zt3D/IhgFyG1oaBrqsbn9NdH/4fkjsO2Y0ahP12ieu4= 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.21.2-0.20220511203756-fe4fd4ed8be4/go.mod h1:OsX9sFexyGF0FCNAjfcVFv3IwMqDyLyV/WQY/roLPcE= diff --git a/hack/podman-registry b/hack/podman-registry index 3f0aa2aea..f6a266883 100755 --- a/hack/podman-registry +++ b/hack/podman-registry @@ -122,6 +122,25 @@ function must_pass() { fi } +################### +# wait_for_port # Returns once port is available on localhost +################### +function wait_for_port() { + local port=$1 # Numeric port + + local host=127.0.0.1 + local _timeout=5 + + # Wait + while [ $_timeout -gt 0 ]; do + { exec {unused_fd}<> /dev/tcp/$host/$port; } &>/dev/null && return + sleep 1 + _timeout=$(( $_timeout - 1 )) + done + + die "Timed out waiting for port $port" +} + # END helper functions ############################################################################### # BEGIN action processing @@ -130,7 +149,7 @@ function do_start() { # If called without a port, assign a random one in the 5xxx range if [ -z "${PODMAN_REGISTRY_PORT}" ]; then for port in $(shuf -i 5000-5999);do - if ! { exec 3<> /dev/tcp/127.0.0.1/$port; } &>/dev/null; then + if ! { exec {unused_fd}<> /dev/tcp/127.0.0.1/$port; } &>/dev/null; then PODMAN_REGISTRY_PORT=$port break fi @@ -203,6 +222,9 @@ function do_start() { -e "REGISTRY_HTTP_TLS_KEY=/auth/domain.key" \ registry:2.6 + # Confirm that registry started and port is active + wait_for_port $PODMAN_REGISTRY_PORT + # Dump settings. Our caller will use these to access the registry. for v in IMAGE PORT USER PASS; do echo "PODMAN_REGISTRY_${v}=\"$(eval echo \$PODMAN_REGISTRY_${v})\"" diff --git a/libpod/container.go b/libpod/container.go index 786d9c3d4..4e2d93860 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -1335,3 +1335,52 @@ func (c *Container) getNetworkStatus() map[string]types.StatusBlock { } return nil } + +func (c *Container) NamespaceMode(ns spec.LinuxNamespaceType, ctrSpec *spec.Spec) string { + switch ns { + case spec.UTSNamespace: + if c.config.UTSNsCtr != "" { + return fmt.Sprintf("container:%s", c.config.UTSNsCtr) + } + case spec.CgroupNamespace: + if c.config.CgroupNsCtr != "" { + return fmt.Sprintf("container:%s", c.config.CgroupNsCtr) + } + case spec.IPCNamespace: + if c.config.IPCNsCtr != "" { + return fmt.Sprintf("container:%s", c.config.IPCNsCtr) + } + case spec.PIDNamespace: + if c.config.PIDNsCtr != "" { + return fmt.Sprintf("container:%s", c.config.PIDNsCtr) + } + case spec.UserNamespace: + if c.config.UserNsCtr != "" { + return fmt.Sprintf("container:%s", c.config.UserNsCtr) + } + case spec.NetworkNamespace: + if c.config.NetNsCtr != "" { + return fmt.Sprintf("container:%s", c.config.NetNsCtr) + } + case spec.MountNamespace: + if c.config.MountNsCtr != "" { + return fmt.Sprintf("container:%s", c.config.MountNsCtr) + } + } + + if ctrSpec.Linux != nil { + // Locate the spec's given namespace. + // If there is none, it's namespace=host. + // If there is one and it has a path, it's "ns:". + // If there is no path, it's default - the empty string. + for _, availableNS := range ctrSpec.Linux.Namespaces { + if availableNS.Type == ns { + if availableNS.Path != "" { + return fmt.Sprintf("ns:%s", availableNS.Path) + } + return "private" + } + } + } + return "host" +} diff --git a/libpod/container_api.go b/libpod/container_api.go index c96845546..742eb6d3e 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -11,6 +11,7 @@ import ( "sync" "time" + "github.com/containers/common/pkg/resize" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/libpod/events" "github.com/containers/podman/v4/pkg/signal" @@ -103,7 +104,7 @@ func (c *Container) Start(ctx context.Context, recursive bool) error { // Attach call occurs before Start). // In overall functionality, it is identical to the Start call, with the added // side effect that an attach session will also be started. -func (c *Container) StartAndAttach(ctx context.Context, streams *define.AttachStreams, keys string, resize <-chan define.TerminalSize, recursive bool) (<-chan error, error) { +func (c *Container) StartAndAttach(ctx context.Context, streams *define.AttachStreams, keys string, resize <-chan resize.TerminalSize, recursive bool) (<-chan error, error) { if !c.batched { c.lock.Lock() defer c.lock.Unlock() @@ -239,7 +240,7 @@ func (c *Container) Kill(signal uint) error { // Attach attaches to a container. // This function returns when the attach finishes. It does not hold the lock for // the duration of its runtime, only using it at the beginning to verify state. -func (c *Container) Attach(streams *define.AttachStreams, keys string, resize <-chan define.TerminalSize) error { +func (c *Container) Attach(streams *define.AttachStreams, keys string, resize <-chan resize.TerminalSize) error { if c.LogDriver() == define.PassthroughLogging { return fmt.Errorf("this container is using the 'passthrough' log driver, cannot attach: %w", define.ErrNoLogs) } @@ -335,7 +336,7 @@ func (c *Container) HTTPAttach(r *http.Request, w http.ResponseWriter, streams * // AttachResize resizes the container's terminal, which is displayed by Attach // and HTTPAttach. -func (c *Container) AttachResize(newSize define.TerminalSize) error { +func (c *Container) AttachResize(newSize resize.TerminalSize) error { if !c.batched { c.lock.Lock() defer c.lock.Unlock() @@ -550,6 +551,10 @@ func (c *Container) WaitForExit(ctx context.Context, pollInterval time.Duration) exitCode, err := c.runtime.state.GetContainerExitCode(id) if err != nil { + if errors.Is(err, define.ErrNoSuchExitCode) && c.ensureState(define.ContainerStateConfigured, define.ContainerStateCreated) { + // The container never ran. + return true, 0, nil + } return true, -1, err } diff --git a/libpod/container_exec.go b/libpod/container_exec.go index ebc5e59ae..d3c80e896 100644 --- a/libpod/container_exec.go +++ b/libpod/container_exec.go @@ -11,6 +11,7 @@ import ( "strconv" "time" + "github.com/containers/common/pkg/resize" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/libpod/events" "github.com/containers/storage/pkg/stringid" @@ -278,13 +279,13 @@ func (c *Container) ExecStart(sessionID string) error { return c.save() } -func (c *Container) ExecStartAndAttach(sessionID string, streams *define.AttachStreams, newSize *define.TerminalSize) error { +func (c *Container) ExecStartAndAttach(sessionID string, streams *define.AttachStreams, newSize *resize.TerminalSize) error { return c.execStartAndAttach(sessionID, streams, newSize, false) } // ExecStartAndAttach starts and attaches to an exec session in a container. // newSize resizes the tty to this size before the process is started, must be nil if the exec session has no tty -func (c *Container) execStartAndAttach(sessionID string, streams *define.AttachStreams, newSize *define.TerminalSize, isHealthcheck bool) error { +func (c *Container) execStartAndAttach(sessionID string, streams *define.AttachStreams, newSize *resize.TerminalSize, isHealthcheck bool) error { if !c.batched { c.lock.Lock() defer c.lock.Unlock() @@ -423,7 +424,7 @@ func (c *Container) execStartAndAttach(sessionID string, streams *define.AttachS // ExecHTTPStartAndAttach starts and performs an HTTP attach to an exec session. // newSize resizes the tty to this size before the process is started, must be nil if the exec session has no tty func (c *Container) ExecHTTPStartAndAttach(sessionID string, r *http.Request, w http.ResponseWriter, - streams *HTTPAttachStreams, detachKeys *string, cancel <-chan bool, hijackDone chan<- bool, newSize *define.TerminalSize) error { + streams *HTTPAttachStreams, detachKeys *string, cancel <-chan bool, hijackDone chan<- bool, newSize *resize.TerminalSize) error { // TODO: How do we combine streams with the default streams set in the exec session? // Ensure that we don't leak a goroutine here @@ -711,7 +712,7 @@ func (c *Container) ExecRemove(sessionID string, force bool) error { // ExecResize resizes the TTY of the given exec session. Only available if the // exec session created a TTY. -func (c *Container) ExecResize(sessionID string, newSize define.TerminalSize) error { +func (c *Container) ExecResize(sessionID string, newSize resize.TerminalSize) error { if !c.batched { c.lock.Lock() defer c.lock.Unlock() @@ -753,14 +754,14 @@ func (c *Container) ExecResize(sessionID string, newSize define.TerminalSize) er return c.ociRuntime.ExecAttachResize(c, sessionID, newSize) } -func (c *Container) Exec(config *ExecConfig, streams *define.AttachStreams, resize <-chan define.TerminalSize) (int, error) { +func (c *Container) Exec(config *ExecConfig, streams *define.AttachStreams, resize <-chan resize.TerminalSize) (int, error) { return c.exec(config, streams, resize, false) } // Exec emulates the old Libpod exec API, providing a single call to create, // run, and remove an exec session. Returns exit code and error. Exit code is // not guaranteed to be set sanely if error is not nil. -func (c *Container) exec(config *ExecConfig, streams *define.AttachStreams, resize <-chan define.TerminalSize, isHealthcheck bool) (int, error) { +func (c *Container) exec(config *ExecConfig, streams *define.AttachStreams, resizeChan <-chan resize.TerminalSize, isHealthcheck bool) (int, error) { sessionID, err := c.ExecCreate(config) if err != nil { return -1, err @@ -773,13 +774,13 @@ func (c *Container) exec(config *ExecConfig, streams *define.AttachStreams, resi // API there. // TODO: Refactor so this is closed here, before we remove the exec // session. - var size *define.TerminalSize - if resize != nil { - s := <-resize + var size *resize.TerminalSize + if resizeChan != nil { + s := <-resizeChan size = &s go func() { logrus.Debugf("Sending resize events to exec session %s", sessionID) - for resizeRequest := range resize { + for resizeRequest := range resizeChan { if err := c.ExecResize(sessionID, resizeRequest); err != nil { if errors.Is(err, define.ErrExecSessionStateInvalid) { // The exec session stopped diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go index 04233455d..fa2130a28 100644 --- a/libpod/container_inspect.go +++ b/libpod/container_inspect.go @@ -794,28 +794,8 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named hostConfig.PidMode = pidMode // UTS namespace mode - utsMode := "" - if c.config.UTSNsCtr != "" { - utsMode = fmt.Sprintf("container:%s", c.config.UTSNsCtr) - } else if ctrSpec.Linux != nil { - // Locate the spec's UTS namespace. - // If there is none, it's uts=host. - // If there is one and it has a path, it's "ns:". - // If there is no path, it's default - the empty string. - for _, ns := range ctrSpec.Linux.Namespaces { - if ns.Type == spec.UTSNamespace { - if ns.Path != "" { - utsMode = fmt.Sprintf("ns:%s", ns.Path) - } else { - utsMode = "private" - } - break - } - } - if utsMode == "" { - utsMode = "host" - } - } + utsMode := c.NamespaceMode(spec.UTSNamespace, ctrSpec) + hostConfig.UTSMode = utsMode // User namespace mode diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 560b4a1c1..6a98466c2 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -1104,6 +1104,12 @@ func (c *Container) cleanupRuntime(ctx context.Context) error { return nil } + // We may be doing this redundantly for some call paths but we need to + // make sure the exit code is being read at this point. + if err := c.checkExitFile(); err != nil { + return err + } + // If necessary, delete attach and ctl files if err := c.removeConmonFiles(); err != nil { return err diff --git a/libpod/define/errors.go b/libpod/define/errors.go index 9757a85b1..b858e1989 100644 --- a/libpod/define/errors.go +++ b/libpod/define/errors.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/containers/common/libnetwork/types" + "github.com/containers/common/pkg/util" ) var ( @@ -92,7 +93,7 @@ var ( // ErrDetach indicates that an attach session was manually detached by // the user. - ErrDetach = errors.New("detached from container") + ErrDetach = util.ErrDetach // ErrWillDeadlock indicates that the requested operation will cause a // deadlock. This is usually caused by upgrade issues, and is resolved diff --git a/libpod/define/pod_inspect.go b/libpod/define/pod_inspect.go index 935e0f5f9..2afef48c4 100644 --- a/libpod/define/pod_inspect.go +++ b/libpod/define/pod_inspect.go @@ -122,6 +122,8 @@ type InspectPodInfraConfig struct { PidNS string `json:"pid_ns,omitempty"` // UserNS is the usernamespace that all the containers in the pod will join. UserNS string `json:"userns,omitempty"` + // UtsNS is the uts namespace that all containers in the pod will join + UtsNS string `json:"uts_ns,omitempty"` } // InspectPodContainerInfo contains information on a container in a pod. diff --git a/libpod/define/terminal.go b/libpod/define/terminal.go deleted file mode 100644 index ce8955544..000000000 --- a/libpod/define/terminal.go +++ /dev/null @@ -1,7 +0,0 @@ -package define - -// TerminalSize represents the width and height of a terminal. -type TerminalSize struct { - Width uint16 - Height uint16 -} diff --git a/libpod/oci.go b/libpod/oci.go index 90862969c..70053db1b 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -3,6 +3,7 @@ package libpod import ( "net/http" + "github.com/containers/common/pkg/resize" "github.com/containers/podman/v4/libpod/define" ) @@ -66,7 +67,7 @@ type OCIRuntime interface { // client. HTTPAttach(ctr *Container, r *http.Request, w http.ResponseWriter, streams *HTTPAttachStreams, detachKeys *string, cancel <-chan bool, hijackDone chan<- bool, streamAttach, streamLogs bool) error // AttachResize resizes the terminal in use by the given container. - AttachResize(ctr *Container, newSize define.TerminalSize) error + AttachResize(ctr *Container, newSize resize.TerminalSize) error // ExecContainer executes a command in a running container. // Returns an int (PID of exec session), error channel (errors from @@ -76,7 +77,7 @@ type OCIRuntime interface { // running, in a goroutine that will return via the chan error in the // return signature. // newSize resizes the tty to this size before the process is started, must be nil if the exec session has no tty - ExecContainer(ctr *Container, sessionID string, options *ExecOptions, streams *define.AttachStreams, newSize *define.TerminalSize) (int, chan error, error) + ExecContainer(ctr *Container, sessionID string, options *ExecOptions, streams *define.AttachStreams, newSize *resize.TerminalSize) (int, chan error, error) // ExecContainerHTTP executes a command in a running container and // attaches its standard streams to a provided hijacked HTTP session. // Maintains the same invariants as ExecContainer (returns on session @@ -84,14 +85,14 @@ type OCIRuntime interface { // The HTTP attach itself maintains the same invariants as HTTPAttach. // newSize resizes the tty to this size before the process is started, must be nil if the exec session has no tty ExecContainerHTTP(ctr *Container, sessionID string, options *ExecOptions, r *http.Request, w http.ResponseWriter, - streams *HTTPAttachStreams, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool, newSize *define.TerminalSize) (int, chan error, error) + streams *HTTPAttachStreams, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool, newSize *resize.TerminalSize) (int, chan error, error) // ExecContainerDetached executes a command in a running container, but // does not attach to it. Returns the PID of the exec session and an // error (if starting the exec session failed) ExecContainerDetached(ctr *Container, sessionID string, options *ExecOptions, stdin bool) (int, error) // ExecAttachResize resizes the terminal of a running exec session. Only // allowed with sessions that were created with a TTY. - ExecAttachResize(ctr *Container, sessionID string, newSize define.TerminalSize) error + ExecAttachResize(ctr *Container, sessionID string, newSize resize.TerminalSize) error // ExecStopContainer stops a given exec session in a running container. // SIGTERM with be sent initially, then SIGKILL after the given timeout. // If timeout is 0, SIGKILL will be sent immediately, and SIGTERM will @@ -161,7 +162,7 @@ type AttachOptions struct { DetachKeys *string // InitialSize is the initial size of the terminal. Set before the // attach begins. - InitialSize *define.TerminalSize + InitialSize *resize.TerminalSize // AttachReady signals when the attach has successfully completed and // streaming has begun. AttachReady chan<- bool diff --git a/libpod/oci_conmon_attach_linux.go b/libpod/oci_conmon_attach_linux.go index 61578cb3d..aa55aa6f5 100644 --- a/libpod/oci_conmon_attach_linux.go +++ b/libpod/oci_conmon_attach_linux.go @@ -13,10 +13,10 @@ import ( "syscall" "github.com/containers/common/pkg/config" + "github.com/containers/common/pkg/resize" + "github.com/containers/common/pkg/util" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/errorhandling" - "github.com/containers/podman/v4/pkg/kubeutils" - "github.com/containers/podman/v4/utils" "github.com/moby/term" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" @@ -130,7 +130,7 @@ func (r *ConmonOCIRuntime) Attach(c *Container, params *AttachOptions) error { // 4. attachToExec sends on startFd, signalling it has attached to the socket and child is ready to go // 5. child receives on startFd, runs the runtime exec command // attachToExec is responsible for closing startFd and attachFd -func (c *Container) attachToExec(streams *define.AttachStreams, keys *string, sessionID string, startFd, attachFd *os.File, newSize *define.TerminalSize) error { +func (c *Container) attachToExec(streams *define.AttachStreams, keys *string, sessionID string, startFd, attachFd *os.File, newSize *resize.TerminalSize) error { if !streams.AttachOutput && !streams.AttachError && !streams.AttachInput { return fmt.Errorf("must provide at least one stream to attach to: %w", define.ErrInvalidArg) } @@ -205,8 +205,8 @@ func processDetachKeys(keys string) ([]byte, error) { return detachKeys, nil } -func registerResizeFunc(resize <-chan define.TerminalSize, bundlePath string) { - kubeutils.HandleResizing(resize, func(size define.TerminalSize) { +func registerResizeFunc(r <-chan resize.TerminalSize, bundlePath string) { + resize.HandleResizing(r, func(size resize.TerminalSize) { controlPath := filepath.Join(bundlePath, "ctl") controlFile, err := os.OpenFile(controlPath, unix.O_WRONLY, 0) if err != nil { @@ -232,7 +232,7 @@ func setupStdioChannels(streams *define.AttachStreams, conn *net.UnixConn, detac go func() { var err error if streams.AttachInput { - _, err = utils.CopyDetachable(conn, streams.InputStream, detachKeys) + _, err = util.CopyDetachable(conn, streams.InputStream, detachKeys) } stdinDone <- err }() diff --git a/libpod/oci_conmon_exec_linux.go b/libpod/oci_conmon_exec_linux.go index 88343bee8..16cd7ef9f 100644 --- a/libpod/oci_conmon_exec_linux.go +++ b/libpod/oci_conmon_exec_linux.go @@ -14,18 +14,19 @@ import ( "github.com/containers/common/pkg/capabilities" "github.com/containers/common/pkg/config" + "github.com/containers/common/pkg/resize" + cutil "github.com/containers/common/pkg/util" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/errorhandling" "github.com/containers/podman/v4/pkg/lookup" "github.com/containers/podman/v4/pkg/util" - "github.com/containers/podman/v4/utils" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) // ExecContainer executes a command in a running container -func (r *ConmonOCIRuntime) ExecContainer(c *Container, sessionID string, options *ExecOptions, streams *define.AttachStreams, newSize *define.TerminalSize) (int, chan error, error) { +func (r *ConmonOCIRuntime) ExecContainer(c *Container, sessionID string, options *ExecOptions, streams *define.AttachStreams, newSize *resize.TerminalSize) (int, chan error, error) { if options == nil { return -1, nil, fmt.Errorf("must provide an ExecOptions struct to ExecContainer: %w", define.ErrInvalidArg) } @@ -84,7 +85,7 @@ func (r *ConmonOCIRuntime) ExecContainer(c *Container, sessionID string, options // ExecContainerHTTP executes a new command in an existing container and // forwards its standard streams over an attach func (r *ConmonOCIRuntime) ExecContainerHTTP(ctr *Container, sessionID string, options *ExecOptions, req *http.Request, w http.ResponseWriter, - streams *HTTPAttachStreams, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool, newSize *define.TerminalSize) (int, chan error, error) { + streams *HTTPAttachStreams, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool, newSize *resize.TerminalSize) (int, chan error, error) { if streams != nil { if !streams.Stdin && !streams.Stdout && !streams.Stderr { return -1, nil, fmt.Errorf("must provide at least one stream to attach to: %w", define.ErrInvalidArg) @@ -196,7 +197,7 @@ func (r *ConmonOCIRuntime) ExecContainerDetached(ctr *Container, sessionID strin } // ExecAttachResize resizes the TTY of the given exec session. -func (r *ConmonOCIRuntime) ExecAttachResize(ctr *Container, sessionID string, newSize define.TerminalSize) error { +func (r *ConmonOCIRuntime) ExecAttachResize(ctr *Container, sessionID string, newSize resize.TerminalSize) error { controlFile, err := openControlFile(ctr, ctr.execBundlePath(sessionID)) if err != nil { return err @@ -487,7 +488,7 @@ func (r *ConmonOCIRuntime) startExec(c *Container, sessionID string, options *Ex } // Attach to a container over HTTP -func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.ResponseWriter, streams *HTTPAttachStreams, pipes *execPipes, detachKeys []byte, isTerminal bool, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool, execCmd *exec.Cmd, conmonPipeDataChan chan<- conmonPipeData, ociLog string, newSize *define.TerminalSize, runtimeName string) (deferredErr error) { +func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.ResponseWriter, streams *HTTPAttachStreams, pipes *execPipes, detachKeys []byte, isTerminal bool, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool, execCmd *exec.Cmd, conmonPipeDataChan chan<- conmonPipeData, ociLog string, newSize *resize.TerminalSize, runtimeName string) (deferredErr error) { // NOTE: As you may notice, the attach code is quite complex. // Many things happen concurrently and yet are interdependent. // If you ever change this function, make sure to write to the @@ -607,7 +608,7 @@ func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.Resp if attachStdin { go func() { logrus.Debugf("Beginning STDIN copy") - _, err := utils.CopyDetachable(conn, httpBuf, detachKeys) + _, err := cutil.CopyDetachable(conn, httpBuf, detachKeys) logrus.Debugf("STDIN copy completed") stdinChan <- err }() diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go index caf334a31..121e750f4 100644 --- a/libpod/oci_conmon_linux.go +++ b/libpod/oci_conmon_linux.go @@ -29,6 +29,8 @@ import ( "github.com/containers/common/pkg/cgroups" "github.com/containers/common/pkg/config" + "github.com/containers/common/pkg/resize" + cutil "github.com/containers/common/pkg/util" conmonConfig "github.com/containers/conmon/runner/config" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/libpod/logs" @@ -301,7 +303,7 @@ func (r *ConmonOCIRuntime) UpdateContainerStatus(ctr *Container) error { ctr.state.ExitCode = -1 ctr.state.FinishedTime = time.Now() ctr.state.State = define.ContainerStateExited - return nil + return ctr.runtime.state.AddContainerExitCode(ctr.ID(), ctr.state.ExitCode) } return fmt.Errorf("error getting container %s state. stderr/out: %s: %w", ctr.ID(), out, err) } @@ -691,7 +693,7 @@ func (r *ConmonOCIRuntime) HTTPAttach(ctr *Container, req *http.Request, w http. // Next, STDIN. Avoid entirely if attachStdin unset. if attachStdin { go func() { - _, err := utils.CopyDetachable(conn, httpBuf, detach) + _, err := cutil.CopyDetachable(conn, httpBuf, detach) logrus.Debugf("STDIN copy completed") stdinChan <- err }() @@ -746,7 +748,7 @@ func openControlFile(ctr *Container, parentDir string) (*os.File, error) { } // AttachResize resizes the terminal used by the given container. -func (r *ConmonOCIRuntime) AttachResize(ctr *Container, newSize define.TerminalSize) error { +func (r *ConmonOCIRuntime) AttachResize(ctr *Container, newSize resize.TerminalSize) error { controlFile, err := openControlFile(ctr, ctr.bundlePath()) if err != nil { return err diff --git a/libpod/oci_missing.go b/libpod/oci_missing.go index 6a756757f..2ab2b4577 100644 --- a/libpod/oci_missing.go +++ b/libpod/oci_missing.go @@ -6,6 +6,7 @@ import ( "path/filepath" "sync" + "github.com/containers/common/pkg/resize" "github.com/containers/podman/v4/libpod/define" "github.com/sirupsen/logrus" ) @@ -118,18 +119,18 @@ func (r *MissingRuntime) HTTPAttach(ctr *Container, req *http.Request, w http.Re } // AttachResize is not available as the runtime is missing -func (r *MissingRuntime) AttachResize(ctr *Container, newSize define.TerminalSize) error { +func (r *MissingRuntime) AttachResize(ctr *Container, newSize resize.TerminalSize) error { return r.printError() } // ExecContainer is not available as the runtime is missing -func (r *MissingRuntime) ExecContainer(ctr *Container, sessionID string, options *ExecOptions, streams *define.AttachStreams, newSize *define.TerminalSize) (int, chan error, error) { +func (r *MissingRuntime) ExecContainer(ctr *Container, sessionID string, options *ExecOptions, streams *define.AttachStreams, newSize *resize.TerminalSize) (int, chan error, error) { return -1, nil, r.printError() } // ExecContainerHTTP is not available as the runtime is missing func (r *MissingRuntime) ExecContainerHTTP(ctr *Container, sessionID string, options *ExecOptions, req *http.Request, w http.ResponseWriter, - streams *HTTPAttachStreams, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool, newSize *define.TerminalSize) (int, chan error, error) { + streams *HTTPAttachStreams, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool, newSize *resize.TerminalSize) (int, chan error, error) { return -1, nil, r.printError() } @@ -139,7 +140,7 @@ func (r *MissingRuntime) ExecContainerDetached(ctr *Container, sessionID string, } // ExecAttachResize is not available as the runtime is missing. -func (r *MissingRuntime) ExecAttachResize(ctr *Container, sessionID string, newSize define.TerminalSize) error { +func (r *MissingRuntime) ExecAttachResize(ctr *Container, sessionID string, newSize resize.TerminalSize) error { return r.printError() } diff --git a/libpod/pod_api.go b/libpod/pod_api.go index e04bb6b05..c1d54d55e 100644 --- a/libpod/pod_api.go +++ b/libpod/pod_api.go @@ -676,6 +676,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) { infraConfig.CPUSetCPUs = p.ResourceLim().CPU.Cpus infraConfig.PidNS = p.NamespaceMode(specs.PIDNamespace) infraConfig.UserNS = p.NamespaceMode(specs.UserNamespace) + infraConfig.UtsNS = p.NamespaceMode(specs.UTSNamespace) namedVolumes, mounts := infra.SortUserVolumes(infra.config.Spec) inspectMounts, err = infra.GetMounts(namedVolumes, infra.config.ImageVolumes, mounts) infraSecurity = infra.GetSecurityOptions() diff --git a/pkg/api/handlers/compat/auth.go b/pkg/api/handlers/compat/auth.go index 7804c8230..37d2b784d 100644 --- a/pkg/api/handlers/compat/auth.go +++ b/pkg/api/handlers/compat/auth.go @@ -3,6 +3,7 @@ package compat import ( "context" "encoding/json" + "errors" "fmt" "net/http" "strings" @@ -14,7 +15,6 @@ import ( api "github.com/containers/podman/v4/pkg/api/types" "github.com/containers/podman/v4/pkg/domain/entities" docker "github.com/docker/docker/api/types" - "github.com/pkg/errors" ) func stripAddressOfScheme(address string) string { @@ -28,7 +28,7 @@ func Auth(w http.ResponseWriter, r *http.Request) { var authConfig docker.AuthConfig err := json.NewDecoder(r.Body).Decode(&authConfig) if err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrapf(err, "failed to parse request")) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("failed to parse request: %w", err)) return } diff --git a/pkg/api/handlers/compat/changes.go b/pkg/api/handlers/compat/changes.go index af0143fcf..c3f612f04 100644 --- a/pkg/api/handlers/compat/changes.go +++ b/pkg/api/handlers/compat/changes.go @@ -1,6 +1,7 @@ package compat import ( + "fmt" "net/http" "github.com/containers/podman/v4/libpod" @@ -8,7 +9,6 @@ import ( "github.com/containers/podman/v4/pkg/api/handlers/utils" api "github.com/containers/podman/v4/pkg/api/types" "github.com/gorilla/schema" - "github.com/pkg/errors" ) func Changes(w http.ResponseWriter, r *http.Request) { @@ -20,7 +20,7 @@ func Changes(w http.ResponseWriter, r *http.Request) { DiffType string `schema:"diffType"` }{} if err := decoder.Decode(&query, r.URL.Query()); err != nil { - utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return } var diffType define.DiffType @@ -32,7 +32,7 @@ func Changes(w http.ResponseWriter, r *http.Request) { case "image": diffType = define.DiffImage default: - utils.Error(w, http.StatusBadRequest, errors.Errorf("invalid diffType value %q", query.DiffType)) + utils.Error(w, http.StatusBadRequest, fmt.Errorf("invalid diffType value %q", query.DiffType)) return } diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index 38fe0196a..ae063dc9f 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -397,6 +397,15 @@ func LibpodToContainer(l *libpod.Container, sz bool) (*handlers.Container, error }, nil } +func convertSecondaryIPPrefixLen(input *define.InspectNetworkSettings, output *types.NetworkSettings) { + for index, ip := range input.SecondaryIPAddresses { + output.SecondaryIPAddresses[index].PrefixLen = ip.PrefixLength + } + for index, ip := range input.SecondaryIPv6Addresses { + output.SecondaryIPv6Addresses[index].PrefixLen = ip.PrefixLength + } +} + func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON, error) { _, imageName := l.Image() inspect, err := l.Inspect(sz) @@ -587,6 +596,9 @@ func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON, if err := json.Unmarshal(n, &networkSettings); err != nil { return nil, err } + + convertSecondaryIPPrefixLen(inspect.NetworkSettings, &networkSettings) + // do not report null instead use an empty map if networkSettings.Networks == nil { networkSettings.Networks = map[string]*network.EndpointSettings{} diff --git a/pkg/api/handlers/compat/containers_attach.go b/pkg/api/handlers/compat/containers_attach.go index c8905808f..e804e628a 100644 --- a/pkg/api/handlers/compat/containers_attach.go +++ b/pkg/api/handlers/compat/containers_attach.go @@ -1,6 +1,8 @@ package compat import ( + "errors" + "fmt" "net/http" "github.com/containers/podman/v4/libpod" @@ -9,7 +11,6 @@ import ( "github.com/containers/podman/v4/pkg/api/server/idle" api "github.com/containers/podman/v4/pkg/api/types" "github.com/gorilla/schema" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -60,13 +61,13 @@ func AttachContainer(w http.ResponseWriter, r *http.Request) { streams = nil } if useStreams && !streams.Stdout && !streams.Stderr && !streams.Stdin { - utils.Error(w, http.StatusBadRequest, errors.Errorf("at least one of stdin, stdout, stderr must be true")) + utils.Error(w, http.StatusBadRequest, errors.New("at least one of stdin, stdout, stderr must be true")) return } // At least one of these must be set if !query.Stream && !query.Logs { - utils.Error(w, http.StatusBadRequest, errors.Errorf("at least one of Logs or Stream must be set")) + utils.Error(w, http.StatusBadRequest, errors.New("at least one of Logs or Stream must be set")) return } @@ -85,16 +86,16 @@ func AttachContainer(w http.ResponseWriter, r *http.Request) { // For Docker compatibility, we need to re-initialize containers in these states. if state == define.ContainerStateConfigured || state == define.ContainerStateExited || state == define.ContainerStateStopped { if err := ctr.Init(r.Context(), ctr.PodID() != ""); err != nil { - utils.Error(w, http.StatusConflict, errors.Wrapf(err, "error preparing container %s for attach", ctr.ID())) + utils.Error(w, http.StatusConflict, fmt.Errorf("error preparing container %s for attach: %w", ctr.ID(), err)) return } } else if !(state == define.ContainerStateCreated || state == define.ContainerStateRunning) { - utils.InternalServerError(w, errors.Wrapf(define.ErrCtrStateInvalid, "can only attach to created or running containers - currently in state %s", state.String())) + utils.InternalServerError(w, fmt.Errorf("can only attach to created or running containers - currently in state %s: %w", state.String(), define.ErrCtrStateInvalid)) return } logErr := func(e error) { - logrus.Error(errors.Wrapf(e, "error attaching to container %s", ctr.ID())) + logrus.Errorf("Error attaching to container %s: %v", ctr.ID(), e) } // Perform HTTP attach. diff --git a/pkg/api/handlers/compat/containers_export.go b/pkg/api/handlers/compat/containers_export.go index 743ce2d53..66e1dcca5 100644 --- a/pkg/api/handlers/compat/containers_export.go +++ b/pkg/api/handlers/compat/containers_export.go @@ -1,6 +1,7 @@ package compat import ( + "fmt" "io/ioutil" "net/http" "os" @@ -8,7 +9,6 @@ import ( "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/pkg/api/handlers/utils" api "github.com/containers/podman/v4/pkg/api/types" - "github.com/pkg/errors" ) func ExportContainer(w http.ResponseWriter, r *http.Request) { @@ -21,21 +21,21 @@ func ExportContainer(w http.ResponseWriter, r *http.Request) { } tmpfile, err := ioutil.TempFile("", "api.tar") if err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile")) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("unable to create tempfile: %w", err)) return } defer os.Remove(tmpfile.Name()) if err := tmpfile.Close(); err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "unable to close tempfile")) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("unable to close tempfile: %w", err)) return } if err := con.Export(tmpfile.Name()); err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "failed to save image")) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("failed to save image: %w", err)) return } rdr, err := os.Open(tmpfile.Name()) if err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "failed to read the exported tarfile")) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("failed to read the exported tarfile: %w", err)) return } defer rdr.Close() diff --git a/pkg/api/handlers/compat/containers_logs.go b/pkg/api/handlers/compat/containers_logs.go index fc894d815..77c4fdd2a 100644 --- a/pkg/api/handlers/compat/containers_logs.go +++ b/pkg/api/handlers/compat/containers_logs.go @@ -16,7 +16,6 @@ import ( api "github.com/containers/podman/v4/pkg/api/types" "github.com/containers/podman/v4/pkg/util" "github.com/gorilla/schema" - "github.com/pkg/errors" log "github.com/sirupsen/logrus" ) @@ -36,13 +35,13 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) { Tail: "all", } if err := decoder.Decode(&query, r.URL.Query()); err != nil { - utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return } if !(query.Stdout || query.Stderr) { msg := fmt.Sprintf("%s: you must choose at least one stream", http.StatusText(http.StatusBadRequest)) - utils.Error(w, http.StatusBadRequest, errors.Errorf("%s for %s", msg, r.URL.String())) + utils.Error(w, http.StatusBadRequest, fmt.Errorf("%s for %s", msg, r.URL.String())) return } @@ -96,7 +95,7 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) { logChannel := make(chan *logs.LogLine, tail+1) if err := runtime.Log(r.Context(), []*libpod.Container{ctnr}, options, logChannel); err != nil { - utils.InternalServerError(w, errors.Wrapf(err, "failed to obtain logs for Container '%s'", name)) + utils.InternalServerError(w, fmt.Errorf("failed to obtain logs for Container '%s': %w", name, err)) return } go func() { @@ -114,7 +113,7 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) { if !utils.IsLibpodRequest(r) { inspectData, err := ctnr.Inspect(false) if err != nil { - utils.InternalServerError(w, errors.Wrapf(err, "failed to obtain logs for Container '%s'", name)) + utils.InternalServerError(w, fmt.Errorf("failed to obtain logs for Container '%s': %w", name, err)) return } writeHeader = !inspectData.Config.Tty diff --git a/pkg/api/handlers/compat/containers_prune.go b/pkg/api/handlers/compat/containers_prune.go index 9b5390d64..95d6a639f 100644 --- a/pkg/api/handlers/compat/containers_prune.go +++ b/pkg/api/handlers/compat/containers_prune.go @@ -2,6 +2,8 @@ package compat import ( "bytes" + "errors" + "fmt" "net/http" "github.com/containers/podman/v4/libpod" @@ -11,14 +13,13 @@ import ( "github.com/containers/podman/v4/pkg/domain/entities/reports" "github.com/containers/podman/v4/pkg/domain/filters" "github.com/containers/podman/v4/pkg/util" - "github.com/pkg/errors" ) func PruneContainers(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) filtersMap, err := util.PrepareFilters(r) if err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return } diff --git a/pkg/api/handlers/compat/containers_stats.go b/pkg/api/handlers/compat/containers_stats.go index 12c5283fc..c115b4181 100644 --- a/pkg/api/handlers/compat/containers_stats.go +++ b/pkg/api/handlers/compat/containers_stats.go @@ -2,6 +2,7 @@ package compat import ( "encoding/json" + "fmt" "net/http" "time" @@ -13,7 +14,6 @@ import ( docker "github.com/docker/docker/api/types" "github.com/gorilla/schema" runccgroups "github.com/opencontainers/runc/libcontainer/cgroups" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -30,7 +30,7 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { Stream: true, } if err := decoder.Decode(&query, r.URL.Query()); err != nil { - utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return } if query.Stream && query.OneShot { // mismatch. one-shot can only be passed with stream=false @@ -47,7 +47,7 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { stats, err := ctnr.GetContainerStats(nil) if err != nil { - utils.InternalServerError(w, errors.Wrapf(err, "failed to obtain Container %s stats", name)) + utils.InternalServerError(w, fmt.Errorf("failed to obtain Container %s stats: %w", name, err)) return } diff --git a/pkg/api/handlers/compat/containers_top.go b/pkg/api/handlers/compat/containers_top.go index 6ca178cf7..9f598cd73 100644 --- a/pkg/api/handlers/compat/containers_top.go +++ b/pkg/api/handlers/compat/containers_top.go @@ -12,7 +12,6 @@ import ( "github.com/containers/podman/v4/pkg/api/handlers/utils" api "github.com/containers/podman/v4/pkg/api/types" "github.com/gorilla/schema" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -33,7 +32,7 @@ func TopContainer(w http.ResponseWriter, r *http.Request) { PsArgs: psArgs, } if err := decoder.Decode(&query, r.URL.Query()); err != nil { - utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return } diff --git a/pkg/api/handlers/compat/events.go b/pkg/api/handlers/compat/events.go index 6bcb7bd32..18fb35966 100644 --- a/pkg/api/handlers/compat/events.go +++ b/pkg/api/handlers/compat/events.go @@ -1,6 +1,7 @@ package compat import ( + "fmt" "net/http" "github.com/containers/podman/v4/libpod" @@ -11,7 +12,6 @@ import ( "github.com/containers/podman/v4/pkg/util" "github.com/gorilla/schema" jsoniter "github.com/json-iterator/go" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -34,7 +34,7 @@ func GetEvents(w http.ResponseWriter, r *http.Request) { Stream: true, } if err := decoder.Decode(&query, r.URL.Query()); err != nil { - utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return } @@ -44,7 +44,7 @@ func GetEvents(w http.ResponseWriter, r *http.Request) { libpodFilters, err := util.FiltersFromRequest(r) if err != nil { - utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return } eventChannel := make(chan *events.Event) diff --git a/pkg/api/handlers/compat/exec.go b/pkg/api/handlers/compat/exec.go index c7990f6e8..1b4dead8b 100644 --- a/pkg/api/handlers/compat/exec.go +++ b/pkg/api/handlers/compat/exec.go @@ -7,6 +7,7 @@ import ( "net/http" "strings" + "github.com/containers/common/pkg/resize" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/api/handlers" @@ -176,9 +177,9 @@ func ExecStartHandler(w http.ResponseWriter, r *http.Request) { logrus.Error(fmt.Errorf("error attaching to container %s exec session %s: %w", sessionCtr.ID(), sessionID, e)) } - var size *define.TerminalSize + var size *resize.TerminalSize if bodyParams.Tty && (bodyParams.Height > 0 || bodyParams.Width > 0) { - size = &define.TerminalSize{ + size = &resize.TerminalSize{ Height: bodyParams.Height, Width: bodyParams.Width, } diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go index 80fc17f56..a9185c3d3 100644 --- a/pkg/api/handlers/compat/images_build.go +++ b/pkg/api/handlers/compat/images_build.go @@ -3,6 +3,7 @@ package compat import ( "context" "encoding/json" + "errors" "fmt" "io" "io/ioutil" @@ -27,7 +28,6 @@ import ( "github.com/docker/docker/pkg/jsonmessage" "github.com/gorilla/schema" "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -65,7 +65,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { } err := os.RemoveAll(filepath.Dir(contextDirectory)) if err != nil { - logrus.Warn(errors.Wrapf(err, "failed to remove build scratch directory %q", filepath.Dir(contextDirectory))) + logrus.Warn(fmt.Errorf("failed to remove build scratch directory %q: %w", filepath.Dir(contextDirectory), err)) } }() @@ -338,7 +338,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { if len(tags) > 0 { possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, tags[0]) if err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("error normalizing image: %w", err)) return } output = possiblyNormalizedName @@ -350,7 +350,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { var err error isolation, err = parseLibPodIsolation(query.Isolation) if err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "failed to parse isolation")) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("failed to parse isolation: %w", err)) return } @@ -371,7 +371,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { for i := 1; i < len(tags); i++ { possiblyNormalizedTag, err := utils.NormalizeToDockerHub(r, tags[i]) if err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("error normalizing image: %w", err)) return } additionalTags = append(additionalTags, possiblyNormalizedTag) @@ -487,7 +487,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { } con := strings.SplitN(opt, "=", 2) if len(con) != 2 { - utils.BadRequest(w, "securityopt", query.SecurityOpt, errors.Errorf("Invalid --security-opt name=value pair: %q", opt)) + utils.BadRequest(w, "securityopt", query.SecurityOpt, fmt.Errorf("invalid --security-opt name=value pair: %q", opt)) return } @@ -499,7 +499,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { case "seccomp": seccomp = con[1] default: - utils.BadRequest(w, "securityopt", query.SecurityOpt, errors.Errorf("Invalid --security-opt 2: %q", opt)) + utils.BadRequest(w, "securityopt", query.SecurityOpt, fmt.Errorf("invalid --security-opt 2: %q", opt)) return } } @@ -540,7 +540,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { if fromImage != "" { possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, fromImage) if err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("error normalizing image: %w", err)) return } fromImage = possiblyNormalizedName diff --git a/pkg/api/handlers/compat/images_history.go b/pkg/api/handlers/compat/images_history.go index 70a11ddc5..ebb5acdd9 100644 --- a/pkg/api/handlers/compat/images_history.go +++ b/pkg/api/handlers/compat/images_history.go @@ -1,13 +1,13 @@ package compat import ( + "fmt" "net/http" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/pkg/api/handlers" "github.com/containers/podman/v4/pkg/api/handlers/utils" api "github.com/containers/podman/v4/pkg/api/types" - "github.com/pkg/errors" ) func HistoryImage(w http.ResponseWriter, r *http.Request) { @@ -16,13 +16,13 @@ func HistoryImage(w http.ResponseWriter, r *http.Request) { possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, name) if err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("error normalizing image: %w", err)) return } newImage, _, err := runtime.LibimageRuntime().LookupImage(possiblyNormalizedName, nil) if err != nil { - utils.ImageNotFound(w, possiblyNormalizedName, errors.Wrapf(err, "failed to find image %s", possiblyNormalizedName)) + utils.ImageNotFound(w, possiblyNormalizedName, fmt.Errorf("failed to find image %s: %w", possiblyNormalizedName, err)) return } history, err := newImage.History(r.Context()) diff --git a/pkg/api/handlers/compat/images_prune.go b/pkg/api/handlers/compat/images_prune.go index 02cadbbbe..cc60ceb1c 100644 --- a/pkg/api/handlers/compat/images_prune.go +++ b/pkg/api/handlers/compat/images_prune.go @@ -2,6 +2,7 @@ package compat import ( "bytes" + "errors" "fmt" "net/http" @@ -13,7 +14,6 @@ import ( "github.com/containers/podman/v4/pkg/domain/infra/abi" "github.com/containers/podman/v4/pkg/util" "github.com/docker/docker/api/types" - "github.com/pkg/errors" ) func PruneImages(w http.ResponseWriter, r *http.Request) { @@ -22,7 +22,7 @@ func PruneImages(w http.ResponseWriter, r *http.Request) { filterMap, err := util.PrepareFilters(r) if err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return } diff --git a/pkg/api/handlers/compat/images_push.go b/pkg/api/handlers/compat/images_push.go index 6765c30b6..bb82ef10d 100644 --- a/pkg/api/handlers/compat/images_push.go +++ b/pkg/api/handlers/compat/images_push.go @@ -2,6 +2,7 @@ package compat import ( "encoding/json" + "errors" "fmt" "io/ioutil" "net/http" @@ -17,7 +18,6 @@ import ( "github.com/containers/storage" "github.com/docker/docker/pkg/jsonmessage" "github.com/gorilla/schema" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -28,7 +28,7 @@ func PushImage(w http.ResponseWriter, r *http.Request) { digestFile, err := ioutil.TempFile("", "digest.txt") if err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile")) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("unable to create tempfile: %w", err)) return } defer digestFile.Close() @@ -50,7 +50,7 @@ func PushImage(w http.ResponseWriter, r *http.Request) { } if err := decoder.Decode(&query, r.URL.Query()); err != nil { - utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return } @@ -63,19 +63,19 @@ func PushImage(w http.ResponseWriter, r *http.Request) { } if _, err := utils.ParseStorageReference(imageName); err != nil { - utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "image source %q is not a containers-storage-transport reference", imageName)) + utils.Error(w, http.StatusBadRequest, fmt.Errorf("image source %q is not a containers-storage-transport reference: %w", imageName, err)) return } possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, imageName) if err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("error normalizing image: %w", err)) return } imageName = possiblyNormalizedName localImage, _, err := runtime.LibimageRuntime().LookupImage(possiblyNormalizedName, nil) if err != nil { - utils.ImageNotFound(w, imageName, errors.Wrapf(err, "failed to find image %s", imageName)) + utils.ImageNotFound(w, imageName, fmt.Errorf("failed to find image %s: %w", imageName, err)) return } rawManifest, _, err := localImage.Manifest(r.Context()) diff --git a/pkg/api/handlers/compat/images_search.go b/pkg/api/handlers/compat/images_search.go index 9f41c1b4f..a6fd3a3a1 100644 --- a/pkg/api/handlers/compat/images_search.go +++ b/pkg/api/handlers/compat/images_search.go @@ -13,7 +13,6 @@ import ( "github.com/containers/podman/v4/pkg/domain/infra/abi" "github.com/containers/storage" "github.com/gorilla/schema" - "github.com/pkg/errors" ) func SearchImages(w http.ResponseWriter, r *http.Request) { @@ -30,7 +29,7 @@ func SearchImages(w http.ResponseWriter, r *http.Request) { } if err := decoder.Decode(&query, r.URL.Query()); err != nil { - utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return } diff --git a/pkg/api/handlers/compat/images_tag.go b/pkg/api/handlers/compat/images_tag.go index da0a04e84..a1da7a4b9 100644 --- a/pkg/api/handlers/compat/images_tag.go +++ b/pkg/api/handlers/compat/images_tag.go @@ -1,6 +1,7 @@ package compat import ( + "errors" "fmt" "net/http" @@ -8,7 +9,6 @@ import ( "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/pkg/api/handlers/utils" api "github.com/containers/podman/v4/pkg/api/types" - "github.com/pkg/errors" ) func TagImage(w http.ResponseWriter, r *http.Request) { @@ -17,7 +17,7 @@ func TagImage(w http.ResponseWriter, r *http.Request) { name := utils.GetName(r) possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, name) if err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("error normalizing image: %w", err)) return } @@ -25,7 +25,7 @@ func TagImage(w http.ResponseWriter, r *http.Request) { lookupOptions := &libimage.LookupImageOptions{ManifestList: true} newImage, _, err := runtime.LibimageRuntime().LookupImage(possiblyNormalizedName, lookupOptions) if err != nil { - utils.ImageNotFound(w, name, errors.Wrapf(err, "failed to find image %s", name)) + utils.ImageNotFound(w, name, fmt.Errorf("failed to find image %s: %w", name, err)) return } @@ -42,7 +42,7 @@ func TagImage(w http.ResponseWriter, r *http.Request) { possiblyNormalizedTag, err := utils.NormalizeToDockerHub(r, tagName) if err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("error normalizing image: %w", err)) return } diff --git a/pkg/api/handlers/compat/info.go b/pkg/api/handlers/compat/info.go index 85547570a..d82513284 100644 --- a/pkg/api/handlers/compat/info.go +++ b/pkg/api/handlers/compat/info.go @@ -22,7 +22,6 @@ import ( "github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/swarm" "github.com/google/uuid" - "github.com/pkg/errors" log "github.com/sirupsen/logrus" ) @@ -33,18 +32,18 @@ func GetInfo(w http.ResponseWriter, r *http.Request) { infoData, err := runtime.Info() if err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrapf(err, "failed to obtain system memory info")) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("failed to obtain system memory info: %w", err)) return } configInfo, err := runtime.GetConfig() if err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrapf(err, "failed to obtain runtime config")) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("failed to obtain runtime config: %w", err)) return } versionInfo, err := define.GetVersion() if err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrapf(err, "failed to obtain podman versions")) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("failed to obtain podman versions: %w", err)) return } stateInfo := getContainersState(runtime) diff --git a/pkg/api/handlers/compat/resize.go b/pkg/api/handlers/compat/resize.go index f5da306da..a2caf6e35 100644 --- a/pkg/api/handlers/compat/resize.go +++ b/pkg/api/handlers/compat/resize.go @@ -6,6 +6,7 @@ import ( "net/http" "strings" + "github.com/containers/common/pkg/resize" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/api/handlers/utils" @@ -32,7 +33,7 @@ func ResizeTTY(w http.ResponseWriter, r *http.Request) { return } - sz := define.TerminalSize{ + sz := resize.TerminalSize{ Width: query.Width, Height: query.Height, } diff --git a/pkg/api/handlers/compat/version.go b/pkg/api/handlers/compat/version.go index cfc3468c2..0d34fbd98 100644 --- a/pkg/api/handlers/compat/version.go +++ b/pkg/api/handlers/compat/version.go @@ -13,7 +13,6 @@ import ( "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/domain/entities/types" "github.com/containers/podman/v4/version" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -28,7 +27,7 @@ func VersionHandler(w http.ResponseWriter, r *http.Request) { info, err := runtime.Info() if err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrapf(err, "failed to obtain system memory info")) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("failed to obtain system memory info: %w", err)) return } diff --git a/pkg/api/handlers/libpod/containers_create.go b/pkg/api/handlers/libpod/containers_create.go index 4fff9e345..e4964d602 100644 --- a/pkg/api/handlers/libpod/containers_create.go +++ b/pkg/api/handlers/libpod/containers_create.go @@ -3,6 +3,7 @@ package libpod import ( "context" "encoding/json" + "fmt" "net/http" "strconv" @@ -13,7 +14,6 @@ import ( "github.com/containers/podman/v4/pkg/specgen" "github.com/containers/podman/v4/pkg/specgen/generate" "github.com/containers/podman/v4/pkg/specgenutil" - "github.com/pkg/errors" ) // CreateContainer takes a specgenerator and makes a container. It returns @@ -34,7 +34,7 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { } if err := json.NewDecoder(r.Body).Decode(&sg); err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "Decode()")) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("decode(): %w", err)) return } if sg.Passwd == nil { diff --git a/pkg/api/handlers/libpod/containers_stats.go b/pkg/api/handlers/libpod/containers_stats.go index 46d722a3d..687d61c12 100644 --- a/pkg/api/handlers/libpod/containers_stats.go +++ b/pkg/api/handlers/libpod/containers_stats.go @@ -2,6 +2,8 @@ package libpod import ( "encoding/json" + "errors" + "fmt" "net/http" "github.com/containers/common/pkg/cgroups" @@ -12,7 +14,6 @@ import ( "github.com/containers/podman/v4/pkg/domain/infra/abi" "github.com/containers/podman/v4/pkg/rootless" "github.com/gorilla/schema" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -24,8 +25,7 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { if rootless.IsRootless() { // if so, then verify cgroup v2 available (more expensive check) if isV2, _ := cgroups.IsCgroup2UnifiedMode(); !isV2 { - msg := "Container stats resource only available for cgroup v2" - utils.Error(w, http.StatusConflict, errors.New(msg)) + utils.Error(w, http.StatusConflict, errors.New("container stats resource only available for cgroup v2")) return } } @@ -39,7 +39,7 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { Interval: 5, } if err := decoder.Decode(&query, r.URL.Query()); err != nil { - utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return } diff --git a/pkg/api/handlers/libpod/generate.go b/pkg/api/handlers/libpod/generate.go index b1ac6a65a..48c4c59e1 100644 --- a/pkg/api/handlers/libpod/generate.go +++ b/pkg/api/handlers/libpod/generate.go @@ -1,6 +1,7 @@ package libpod import ( + "fmt" "net/http" "github.com/containers/podman/v4/libpod" @@ -10,7 +11,6 @@ import ( "github.com/containers/podman/v4/pkg/domain/infra/abi" "github.com/containers/podman/v4/pkg/util" "github.com/gorilla/schema" - "github.com/pkg/errors" ) func GenerateSystemd(w http.ResponseWriter, r *http.Request) { @@ -37,7 +37,7 @@ func GenerateSystemd(w http.ResponseWriter, r *http.Request) { } if err := decoder.Decode(&query, r.URL.Query()); err != nil { - utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return } @@ -76,7 +76,7 @@ func GenerateSystemd(w http.ResponseWriter, r *http.Request) { report, err := containerEngine.GenerateSystemd(r.Context(), utils.GetName(r), options) if err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "error generating systemd units")) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("error generating systemd units: %w", err)) return } @@ -94,7 +94,7 @@ func GenerateKube(w http.ResponseWriter, r *http.Request) { } if err := decoder.Decode(&query, r.URL.Query()); err != nil { - utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return } @@ -102,7 +102,7 @@ func GenerateKube(w http.ResponseWriter, r *http.Request) { options := entities.GenerateKubeOptions{Service: query.Service} report, err := containerEngine.GenerateKube(r.Context(), query.Names, options) if err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "error generating YAML")) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("error generating YAML: %w", err)) return } diff --git a/pkg/api/handlers/libpod/images_pull.go b/pkg/api/handlers/libpod/images_pull.go index 2cd45fb63..7e24ae5ac 100644 --- a/pkg/api/handlers/libpod/images_pull.go +++ b/pkg/api/handlers/libpod/images_pull.go @@ -3,6 +3,8 @@ package libpod import ( "context" "encoding/json" + "errors" + "fmt" "net/http" "github.com/containers/common/libimage" @@ -15,7 +17,6 @@ import ( "github.com/containers/podman/v4/pkg/channel" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/gorilla/schema" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -41,7 +42,7 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { } if err := decoder.Decode(&query, r.URL.Query()); err != nil { - utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return } diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go index bdf0162c7..3235a2972 100644 --- a/pkg/api/handlers/libpod/manifests.go +++ b/pkg/api/handlers/libpod/manifests.go @@ -3,6 +3,7 @@ package libpod import ( "context" "encoding/json" + "errors" "fmt" "io/ioutil" "net/http" @@ -24,7 +25,6 @@ import ( "github.com/gorilla/mux" "github.com/gorilla/schema" "github.com/opencontainers/go-digest" - "github.com/pkg/errors" ) func ManifestCreate(w http.ResponseWriter, r *http.Request) { @@ -45,7 +45,7 @@ func ManifestCreate(w http.ResponseWriter, r *http.Request) { if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, http.StatusBadRequest, - errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return } @@ -54,7 +54,7 @@ func ManifestCreate(w http.ResponseWriter, r *http.Request) { n, err := url.QueryUnescape(name) if err != nil { utils.Error(w, http.StatusBadRequest, - errors.Wrapf(err, "failed to parse name parameter %q", name)) + fmt.Errorf("failed to parse name parameter %q: %w", name, err)) return } query.Name = n @@ -62,7 +62,7 @@ func ManifestCreate(w http.ResponseWriter, r *http.Request) { if _, err := reference.ParseNormalizedNamed(query.Name); err != nil { utils.Error(w, http.StatusBadRequest, - errors.Wrapf(err, "invalid image name %s", query.Name)) + fmt.Errorf("invalid image name %s: %w", query.Name, err)) return } @@ -94,7 +94,7 @@ func ManifestCreate(w http.ResponseWriter, r *http.Request) { body := new(entities.ManifestModifyOptions) if err := json.Unmarshal(buffer, body); err != nil { - utils.InternalServerError(w, errors.Wrap(err, "Decode()")) + utils.InternalServerError(w, fmt.Errorf("Decode(): %w", err)) return } @@ -166,7 +166,7 @@ func ManifestAddV3(w http.ResponseWriter, r *http.Request) { TLSVerify bool `schema:"tlsVerify"` }{} if err := json.NewDecoder(r.Body).Decode(&query); err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "Decode()")) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("Decode(): %w", err)) return } @@ -220,7 +220,7 @@ func ManifestRemoveDigestV3(w http.ResponseWriter, r *http.Request) { name := utils.GetName(r) if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, http.StatusBadRequest, - errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return } manifestList, err := runtime.LibimageRuntime().LookupManifestList(name) @@ -256,7 +256,7 @@ func ManifestPushV3(w http.ResponseWriter, r *http.Request) { } if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, http.StatusBadRequest, - errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return } if err := utils.IsRegistryReference(query.Destination); err != nil { @@ -292,7 +292,7 @@ func ManifestPushV3(w http.ResponseWriter, r *http.Request) { imageEngine := abi.ImageEngine{Libpod: runtime} digest, err := imageEngine.ManifestPush(context.Background(), source, query.Destination, options) if err != nil { - utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "error pushing image %q", query.Destination)) + utils.Error(w, http.StatusBadRequest, fmt.Errorf("error pushing image %q: %w", query.Destination, err)) return } utils.WriteResponse(w, http.StatusOK, entities.IDResponse{ID: digest}) @@ -313,7 +313,7 @@ func ManifestPush(w http.ResponseWriter, r *http.Request) { } if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, http.StatusBadRequest, - errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return } @@ -325,7 +325,7 @@ func ManifestPush(w http.ResponseWriter, r *http.Request) { authconf, authfile, err := auth.GetCredentials(r) if err != nil { - utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "failed to parse registry header for %s", r.URL.String())) + utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse registry header for %s: %w", r.URL.String(), err)) return } defer auth.RemoveAuthfile(authfile) @@ -351,7 +351,7 @@ func ManifestPush(w http.ResponseWriter, r *http.Request) { source := utils.GetName(r) digest, err := imageEngine.ManifestPush(context.Background(), source, destination, options) if err != nil { - utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "error pushing image %q", destination)) + utils.Error(w, http.StatusBadRequest, fmt.Errorf("error pushing image %q: %w", destination, err)) return } utils.WriteResponse(w, http.StatusOK, entities.IDResponse{ID: digest}) @@ -364,7 +364,7 @@ func ManifestModify(w http.ResponseWriter, r *http.Request) { body := new(entities.ManifestModifyOptions) if err := json.NewDecoder(r.Body).Decode(body); err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "Decode()")) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("Decode(): %w", err)) return } diff --git a/pkg/api/handlers/libpod/play.go b/pkg/api/handlers/libpod/play.go index 36e61c986..f8ce52a72 100644 --- a/pkg/api/handlers/libpod/play.go +++ b/pkg/api/handlers/libpod/play.go @@ -1,6 +1,7 @@ package libpod import ( + "fmt" "net" "net/http" @@ -12,7 +13,6 @@ import ( "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/domain/infra/abi" "github.com/gorilla/schema" - "github.com/pkg/errors" ) func PlayKube(w http.ResponseWriter, r *http.Request) { @@ -34,7 +34,7 @@ func PlayKube(w http.ResponseWriter, r *http.Request) { } if err := decoder.Decode(&query, r.URL.Query()); err != nil { - utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return } @@ -42,7 +42,7 @@ func PlayKube(w http.ResponseWriter, r *http.Request) { for _, ipString := range query.StaticIPs { ip := net.ParseIP(ipString) if ip == nil { - utils.Error(w, http.StatusBadRequest, errors.Errorf("Invalid IP address %s", ipString)) + utils.Error(w, http.StatusBadRequest, fmt.Errorf("invalid IP address %s", ipString)) return } staticIPs = append(staticIPs, ip) @@ -103,7 +103,7 @@ func PlayKube(w http.ResponseWriter, r *http.Request) { report, err := containerEngine.PlayKube(r.Context(), r.Body, options) _ = r.Body.Close() if err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "error playing YAML file")) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("error playing YAML file: %w", err)) return } utils.WriteResponse(w, http.StatusOK, report) @@ -116,7 +116,7 @@ func PlayKubeDown(w http.ResponseWriter, r *http.Request) { report, err := containerEngine.PlayKubeDown(r.Context(), r.Body, *options) _ = r.Body.Close() if err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "error tearing down YAML file")) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("error tearing down YAML file: %w", err)) return } utils.WriteResponse(w, http.StatusOK, report) diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go index 92fd94390..8b1d456ec 100644 --- a/pkg/api/handlers/libpod/pods.go +++ b/pkg/api/handlers/libpod/pods.go @@ -530,8 +530,12 @@ func PodStats(w http.ResponseWriter, r *http.Request) { query := struct { NamesOrIDs []string `schema:"namesOrIDs"` All bool `schema:"all"` + Stream bool `schema:"stream"` + Delay int `schema:"delay"` }{ // default would go here + Delay: 5, + Stream: false, } if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) @@ -544,6 +548,10 @@ func PodStats(w http.ResponseWriter, r *http.Request) { utils.InternalServerError(w, err) } + var flush = func() {} + if flusher, ok := w.(http.Flusher); ok { + flush = flusher.Flush + } // Collect the stats and send them over the wire. containerEngine := abi.ContainerEngine{Libpod: runtime} reports, err := containerEngine.PodStats(r.Context(), query.NamesOrIDs, options) @@ -554,10 +562,35 @@ func PodStats(w http.ResponseWriter, r *http.Request) { utils.Error(w, http.StatusNotFound, err) return } - utils.InternalServerError(w, err) return } - utils.WriteResponse(w, http.StatusOK, reports) + w.Header().Set("Content-Type", "application/json") + coder := json.NewEncoder(w) + coder.SetEscapeHTML(true) + + if err := coder.Encode(reports); err != nil { + logrus.Infof("Error from %s %q : %v", r.Method, r.URL, err) + } + flush() + if query.Stream { + for { + select { + case <-r.Context().Done(): + return + default: + time.Sleep(time.Duration(query.Delay) * time.Second) + reports, err = containerEngine.PodStats(r.Context(), query.NamesOrIDs, options) + if err != nil { + return + } + if err := coder.Encode(reports); err != nil { + logrus.Infof("Error from %s %q : %v", r.Method, r.URL, err) + return + } + flush() + } + } + } } diff --git a/pkg/api/handlers/libpod/secrets.go b/pkg/api/handlers/libpod/secrets.go index 3ea2c2ea8..6eba65f2b 100644 --- a/pkg/api/handlers/libpod/secrets.go +++ b/pkg/api/handlers/libpod/secrets.go @@ -1,6 +1,7 @@ package libpod import ( + "fmt" "net/http" "github.com/containers/podman/v4/libpod" @@ -9,7 +10,6 @@ import ( "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/domain/infra/abi" "github.com/gorilla/schema" - "github.com/pkg/errors" ) func CreateSecret(w http.ResponseWriter, r *http.Request) { @@ -27,7 +27,7 @@ func CreateSecret(w http.ResponseWriter, r *http.Request) { } opts := entities.SecretCreateOptions{} if err := decoder.Decode(&query, r.URL.Query()); err != nil { - utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return } diff --git a/pkg/api/handlers/libpod/swagger_spec.go b/pkg/api/handlers/libpod/swagger_spec.go index 8eeb041d2..bfe4ab9fd 100644 --- a/pkg/api/handlers/libpod/swagger_spec.go +++ b/pkg/api/handlers/libpod/swagger_spec.go @@ -1,11 +1,12 @@ package libpod import ( + "errors" + "fmt" "net/http" "os" "github.com/containers/podman/v4/pkg/api/handlers/utils" - "github.com/pkg/errors" ) // DefaultPodmanSwaggerSpec provides the default path to the podman swagger spec file @@ -18,7 +19,7 @@ func ServeSwagger(w http.ResponseWriter, r *http.Request) { } if _, err := os.Stat(path); err != nil { if errors.Is(err, os.ErrNotExist) { - utils.InternalServerError(w, errors.Errorf("swagger spec %q does not exist", path)) + utils.InternalServerError(w, fmt.Errorf("swagger spec %q does not exist", path)) return } utils.InternalServerError(w, err) diff --git a/pkg/api/handlers/libpod/system.go b/pkg/api/handlers/libpod/system.go index 9ceca99e8..7418dc4df 100644 --- a/pkg/api/handlers/libpod/system.go +++ b/pkg/api/handlers/libpod/system.go @@ -1,6 +1,7 @@ package libpod import ( + "fmt" "net/http" "github.com/containers/podman/v4/libpod" @@ -10,7 +11,6 @@ import ( "github.com/containers/podman/v4/pkg/domain/infra/abi" "github.com/containers/podman/v4/pkg/util" "github.com/gorilla/schema" - "github.com/pkg/errors" ) // SystemPrune removes unused data @@ -25,13 +25,13 @@ func SystemPrune(w http.ResponseWriter, r *http.Request) { if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, http.StatusBadRequest, - errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return } filterMap, err := util.PrepareFilters(r) if err != nil { utils.Error(w, http.StatusBadRequest, - errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return } diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go index 9eb712c30..b533e131c 100644 --- a/pkg/api/handlers/types.go +++ b/pkg/api/handlers/types.go @@ -2,6 +2,7 @@ package handlers import ( "context" + "fmt" "time" "github.com/containers/common/libimage" @@ -10,7 +11,6 @@ import ( dockerContainer "github.com/docker/docker/api/types/container" dockerNetwork "github.com/docker/docker/api/types/network" "github.com/docker/go-connections/nat" - "github.com/pkg/errors" ) type AuthConfig struct { @@ -237,17 +237,17 @@ func portsToPortSet(input map[string]struct{}) (nat.PortSet, error) { case "tcp", "": p, err := nat.NewPort("tcp", port) if err != nil { - return nil, errors.Wrapf(err, "unable to create tcp port from %s", k) + return nil, fmt.Errorf("unable to create tcp port from %s: %w", k, err) } ports[p] = struct{}{} case "udp": p, err := nat.NewPort("udp", port) if err != nil { - return nil, errors.Wrapf(err, "unable to create tcp port from %s", k) + return nil, fmt.Errorf("unable to create tcp port from %s: %w", k, err) } ports[p] = struct{}{} default: - return nil, errors.Errorf("invalid port proto %q in %q", proto, k) + return nil, fmt.Errorf("invalid port proto %q in %q", proto, k) } } return ports, nil diff --git a/pkg/api/handlers/utils/containers.go b/pkg/api/handlers/utils/containers.go index 80f8522fd..e198bdd6c 100644 --- a/pkg/api/handlers/utils/containers.go +++ b/pkg/api/handlers/utils/containers.go @@ -99,9 +99,8 @@ func WaitContainerDocker(w http.ResponseWriter, r *http.Request) { func WaitContainerLibpod(w http.ResponseWriter, r *http.Request) { var ( - err error - interval = time.Millisecond * 250 - conditions = []define.ContainerStatus{define.ContainerStateStopped, define.ContainerStateExited} + err error + interval = time.Millisecond * 250 ) decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := waitQueryLibpod{} @@ -118,17 +117,10 @@ func WaitContainerLibpod(w http.ResponseWriter, r *http.Request) { } } - if _, found := r.URL.Query()["condition"]; found { - if len(query.Condition) > 0 { - conditions = query.Condition - } - } - name := GetName(r) waitFn := createContainerWaitFn(r.Context(), name, interval) - - exitCode, err := waitFn(conditions...) + exitCode, err := waitFn(query.Condition...) if err != nil { if errors.Is(err, define.ErrNoSuchCtr) { ContainerNotFound(w, name, err) diff --git a/pkg/api/handlers/utils/handler.go b/pkg/api/handlers/utils/handler.go index 338d5a84b..9562ebbbc 100644 --- a/pkg/api/handlers/utils/handler.go +++ b/pkg/api/handlers/utils/handler.go @@ -1,6 +1,7 @@ package utils import ( + "errors" "fmt" "io" "net/http" @@ -13,7 +14,6 @@ import ( "github.com/containers/podman/v4/version" "github.com/gorilla/mux" jsoniter "github.com/json-iterator/go" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -42,11 +42,11 @@ func SupportedVersion(r *http.Request, condition string) (semver.Version, error) } safeVal, err := url.PathUnescape(val) if err != nil { - return version, errors.Wrapf(err, "unable to unescape given API version: %q", val) + return version, fmt.Errorf("unable to unescape given API version: %q: %w", val, err) } version, err = semver.ParseTolerant(safeVal) if err != nil { - return version, errors.Wrapf(err, "unable to parse given API version: %q from %q", safeVal, val) + return version, fmt.Errorf("unable to parse given API version: %q from %q: %w", safeVal, val, err) } inRange, err := semver.ParseRange(condition) @@ -178,7 +178,7 @@ func GetVar(r *http.Request, k string) string { val := mux.Vars(r)[k] safeVal, err := url.PathUnescape(val) if err != nil { - logrus.Error(errors.Wrapf(err, "failed to unescape mux key %s, value %s", k, val)) + logrus.Error(fmt.Errorf("failed to unescape mux key %s, value %s: %w", k, val, err)) return val } return safeVal diff --git a/pkg/api/server/listener_api.go b/pkg/api/server/listener_api.go index aaaf6688e..d1f3ea7a3 100644 --- a/pkg/api/server/listener_api.go +++ b/pkg/api/server/listener_api.go @@ -1,11 +1,10 @@ package server import ( + "fmt" "net" "os" "path/filepath" - - "github.com/pkg/errors" ) // ListenUnix follows stdlib net.Listen() API, providing a unix listener for given path @@ -14,18 +13,18 @@ func ListenUnix(network string, path string) (net.Listener, error) { // set up custom listener for API server err := os.MkdirAll(filepath.Dir(path), 0770) if err != nil { - return nil, errors.Wrapf(err, "api.ListenUnix() failed to create %s", filepath.Dir(path)) + return nil, fmt.Errorf("api.ListenUnix() failed to create %s: %w", filepath.Dir(path), err) } os.Remove(path) listener, err := net.Listen(network, path) if err != nil { - return nil, errors.Wrapf(err, "api.ListenUnix() failed to create net.Listen(%s, %s)", network, path) + return nil, fmt.Errorf("api.ListenUnix() failed to create net.Listen(%s, %s): %w", network, path, err) } _, err = os.Stat(path) if err != nil { - return nil, errors.Wrapf(err, "net.Listen(%s, %s) failed to report the failure to create socket", network, path) + return nil, fmt.Errorf("net.Listen(%s, %s) failed to report the failure to create socket: %w", network, path, err) } return listener, nil diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 3a78436d7..dd934fabd 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -3,6 +3,7 @@ package auth import ( "encoding/base64" "encoding/json" + "fmt" "io/ioutil" "net/http" "os" @@ -11,7 +12,6 @@ import ( imageAuth "github.com/containers/image/v5/pkg/docker/config" "github.com/containers/image/v5/types" dockerAPITypes "github.com/docker/docker/api/types" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -47,7 +47,7 @@ func GetCredentials(r *http.Request) (*types.DockerAuthConfig, string, error) { return nil, "", nil } if err != nil { - return nil, "", errors.Wrapf(err, "failed to parse %q header for %s", headerName, r.URL.String()) + return nil, "", fmt.Errorf("failed to parse %q header for %s: %w", headerName, r.URL.String(), err) } var authFile string @@ -56,7 +56,7 @@ func GetCredentials(r *http.Request) (*types.DockerAuthConfig, string, error) { } else { authFile, err = authConfigsToAuthFile(fileContents) if err != nil { - return nil, "", errors.Wrapf(err, "failed to parse %q header for %s", headerName, r.URL.String()) + return nil, "", fmt.Errorf("failed to parse %q header for %s: %w", headerName, r.URL.String(), err) } } return override, authFile, nil @@ -72,13 +72,13 @@ func getConfigCredentials(r *http.Request, headers []string) (*types.DockerAuthC for _, h := range headers { param, err := base64.URLEncoding.DecodeString(h) if err != nil { - return nil, nil, errors.Wrapf(err, "failed to decode %q", xRegistryConfigHeader) + return nil, nil, fmt.Errorf("failed to decode %q: %w", xRegistryConfigHeader, err) } ac := make(map[string]dockerAPITypes.AuthConfig) err = json.Unmarshal(param, &ac) if err != nil { - return nil, nil, errors.Wrapf(err, "failed to unmarshal %q", xRegistryConfigHeader) + return nil, nil, fmt.Errorf("failed to unmarshal %q: %w", xRegistryConfigHeader, err) } for k, v := range ac { @@ -238,10 +238,10 @@ func authConfigsToAuthFile(authConfigs map[string]types.DockerAuthConfig) (strin return "", err } if _, err := tmpFile.Write([]byte{'{', '}'}); err != nil { - return "", errors.Wrap(err, "error initializing temporary auth file") + return "", fmt.Errorf("error initializing temporary auth file: %w", err) } if err := tmpFile.Close(); err != nil { - return "", errors.Wrap(err, "error closing temporary auth file") + return "", fmt.Errorf("error closing temporary auth file: %w", err) } authFilePath := tmpFile.Name() @@ -255,7 +255,7 @@ func authConfigsToAuthFile(authConfigs map[string]types.DockerAuthConfig) (strin // that all credentials are valid. They'll be used on demand // later. if err := imageAuth.SetAuthentication(&sys, key, config.Username, config.Password); err != nil { - return "", errors.Wrapf(err, "error storing credentials in temporary auth file (key: %q / %q, user: %q)", authFileKey, key, config.Username) + return "", fmt.Errorf("error storing credentials in temporary auth file (key: %q / %q, user: %q): %w", authFileKey, key, config.Username, err) } } diff --git a/pkg/autoupdate/autoupdate.go b/pkg/autoupdate/autoupdate.go index 0c795faed..8d9991622 100644 --- a/pkg/autoupdate/autoupdate.go +++ b/pkg/autoupdate/autoupdate.go @@ -2,6 +2,7 @@ package autoupdate import ( "context" + "fmt" "os" "sort" @@ -17,7 +18,6 @@ import ( "github.com/containers/podman/v4/pkg/systemd" systemdDefine "github.com/containers/podman/v4/pkg/systemd/define" "github.com/coreos/go-systemd/v22/dbus" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -73,7 +73,7 @@ func LookupPolicy(s string) (Policy, error) { } sort.Strings(keys) - return "", errors.Errorf("invalid auto-update policy %q: valid policies are %+q", s, keys) + return "", fmt.Errorf("invalid auto-update policy %q: valid policies are %+q", s, keys) } // ValidateImageReference checks if the specified imageName is a fully-qualified @@ -85,17 +85,17 @@ func ValidateImageReference(imageName string) error { // Make sure the input image is a docker. imageRef, err := alltransports.ParseImageName(imageName) if err == nil && imageRef.Transport().Name() != docker.Transport.Name() { - return errors.Errorf("auto updates require the docker image transport but image is of transport %q", imageRef.Transport().Name()) + return fmt.Errorf("auto updates require the docker image transport but image is of transport %q", imageRef.Transport().Name()) } else if err != nil { repo, err := reference.Parse(imageName) if err != nil { - return errors.Wrap(err, "enforcing fully-qualified docker transport reference for auto updates") + return fmt.Errorf("enforcing fully-qualified docker transport reference for auto updates: %w", err) } if _, ok := repo.(reference.NamedTagged); !ok { - return errors.Errorf("auto updates require fully-qualified image references (no tag): %q", imageName) + return fmt.Errorf("auto updates require fully-qualified image references (no tag): %q", imageName) } if _, ok := repo.(reference.Digested); ok { - return errors.Errorf("auto updates require fully-qualified image references without digest: %q", imageName) + return fmt.Errorf("auto updates require fully-qualified image references without digest: %q", imageName) } } return nil @@ -151,7 +151,7 @@ func AutoUpdate(ctx context.Context, runtime *libpod.Runtime, options entities.A for imageID, policyMapper := range containerMap { image, exists := imageMap[imageID] if !exists { - errs = append(errs, errors.Errorf("container image ID %q not found in local storage", imageID)) + errs = append(errs, fmt.Errorf("container image ID %q not found in local storage", imageID)) return nil, errs } @@ -184,13 +184,13 @@ func autoUpdateRegistry(ctx context.Context, image *libimage.Image, ctr *libpod. cid := ctr.ID() rawImageName := ctr.RawImageName() if rawImageName == "" { - return nil, errors.Errorf("registry auto-updating container %q: raw-image name is empty", cid) + return nil, fmt.Errorf("registry auto-updating container %q: raw-image name is empty", cid) } labels := ctr.Labels() unit, exists := labels[systemdDefine.EnvVariable] if !exists { - return nil, errors.Errorf("auto-updating container %q: no %s label found", ctr.ID(), systemdDefine.EnvVariable) + return nil, fmt.Errorf("auto-updating container %q: no %s label found", ctr.ID(), systemdDefine.EnvVariable) } report := &entities.AutoUpdateReport{ @@ -214,7 +214,7 @@ func autoUpdateRegistry(ctx context.Context, image *libimage.Image, ctr *libpod. authfile := getAuthfilePath(ctr, options) needsUpdate, err := newerRemoteImageAvailable(ctx, image, rawImageName, authfile) if err != nil { - return report, errors.Wrapf(err, "registry auto-updating container %q: image check for %q failed", cid, rawImageName) + return report, fmt.Errorf("registry auto-updating container %q: image check for %q failed: %w", cid, rawImageName, err) } if !needsUpdate { @@ -228,7 +228,7 @@ func autoUpdateRegistry(ctx context.Context, image *libimage.Image, ctr *libpod. } if _, err := updateImage(ctx, runtime, rawImageName, authfile); err != nil { - return report, errors.Wrapf(err, "registry auto-updating container %q: image update for %q failed", cid, rawImageName) + return report, fmt.Errorf("registry auto-updating container %q: image update for %q failed: %w", cid, rawImageName, err) } updatedRawImages[rawImageName] = true @@ -245,10 +245,10 @@ func autoUpdateRegistry(ctx context.Context, image *libimage.Image, ctr *libpod. // To fallback, simply retag the old image and restart the service. if err := image.Tag(rawImageName); err != nil { - return report, errors.Wrap(err, "falling back to previous image") + return report, fmt.Errorf("falling back to previous image: %w", err) } if err := restartSystemdUnit(ctx, ctr, unit, conn); err != nil { - return report, errors.Wrap(err, "restarting unit with old image during fallback") + return report, fmt.Errorf("restarting unit with old image during fallback: %w", err) } report.Updated = "rolled back" @@ -260,13 +260,13 @@ func autoUpdateLocally(ctx context.Context, image *libimage.Image, ctr *libpod.C cid := ctr.ID() rawImageName := ctr.RawImageName() if rawImageName == "" { - return nil, errors.Errorf("locally auto-updating container %q: raw-image name is empty", cid) + return nil, fmt.Errorf("locally auto-updating container %q: raw-image name is empty", cid) } labels := ctr.Labels() unit, exists := labels[systemdDefine.EnvVariable] if !exists { - return nil, errors.Errorf("auto-updating container %q: no %s label found", ctr.ID(), systemdDefine.EnvVariable) + return nil, fmt.Errorf("auto-updating container %q: no %s label found", ctr.ID(), systemdDefine.EnvVariable) } report := &entities.AutoUpdateReport{ @@ -280,7 +280,7 @@ func autoUpdateLocally(ctx context.Context, image *libimage.Image, ctr *libpod.C needsUpdate, err := newerLocalImageAvailable(runtime, image, rawImageName) if err != nil { - return report, errors.Wrapf(err, "locally auto-updating container %q: image check for %q failed", cid, rawImageName) + return report, fmt.Errorf("locally auto-updating container %q: image check for %q failed: %w", cid, rawImageName, err) } if !needsUpdate { @@ -306,10 +306,10 @@ func autoUpdateLocally(ctx context.Context, image *libimage.Image, ctr *libpod.C // To fallback, simply retag the old image and restart the service. if err := image.Tag(rawImageName); err != nil { - return report, errors.Wrap(err, "falling back to previous image") + return report, fmt.Errorf("falling back to previous image: %w", err) } if err := restartSystemdUnit(ctx, ctr, unit, conn); err != nil { - return report, errors.Wrap(err, "restarting unit with old image during fallback") + return report, fmt.Errorf("restarting unit with old image during fallback: %w", err) } report.Updated = "rolled back" @@ -320,7 +320,7 @@ func autoUpdateLocally(ctx context.Context, image *libimage.Image, ctr *libpod.C func restartSystemdUnit(ctx context.Context, ctr *libpod.Container, unit string, conn *dbus.Conn) error { restartChan := make(chan string) if _, err := conn.RestartUnitContext(ctx, unit, "replace", restartChan); err != nil { - return errors.Wrapf(err, "auto-updating container %q: restarting systemd unit %q failed", ctr.ID(), unit) + return fmt.Errorf("auto-updating container %q: restarting systemd unit %q failed: %w", ctr.ID(), unit, err) } // Wait for the restart to finish and actually check if it was @@ -333,7 +333,7 @@ func restartSystemdUnit(ctx context.Context, ctr *libpod.Container, unit string, return nil default: - return errors.Errorf("auto-updating container %q: restarting systemd unit %q failed: expected %q but received %q", ctr.ID(), unit, "done", result) + return fmt.Errorf("auto-updating container %q: restarting systemd unit %q failed: expected %q but received %q", ctr.ID(), unit, "done", result) } } diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go index 6b3576f31..7dda955a2 100644 --- a/pkg/bindings/connection.go +++ b/pkg/bindings/connection.go @@ -2,6 +2,7 @@ package bindings import ( "context" + "errors" "fmt" "io" "net" @@ -15,7 +16,6 @@ import ( "github.com/blang/semver" "github.com/containers/podman/v4/pkg/terminal" "github.com/containers/podman/v4/version" - "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/agent" @@ -43,7 +43,7 @@ func GetClient(ctx context.Context) (*Connection, error) { if c, ok := ctx.Value(clientKey).(*Connection); ok { return c, nil } - return nil, errors.Errorf("%s not set in context", clientKey) + return nil, fmt.Errorf("%s not set in context", clientKey) } // ServiceVersion from context build by NewConnection() @@ -92,7 +92,7 @@ func NewConnectionWithIdentity(ctx context.Context, uri string, identity string) _url, err := url.Parse(uri) if err != nil { - return nil, errors.Wrapf(err, "Value of CONTAINER_HOST is not a valid url: %s", uri) + return nil, fmt.Errorf("value of CONTAINER_HOST is not a valid url: %s: %w", uri, err) } // Now we set up the http Client to use the connection above @@ -117,16 +117,16 @@ func NewConnectionWithIdentity(ctx context.Context, uri string, identity string) } connection = tcpClient(_url) default: - return nil, errors.Errorf("unable to create connection. %q is not a supported schema", _url.Scheme) + return nil, fmt.Errorf("unable to create connection. %q is not a supported schema", _url.Scheme) } if err != nil { - return nil, errors.Wrapf(err, "unable to connect to Podman. failed to create %sClient", _url.Scheme) + return nil, fmt.Errorf("unable to connect to Podman. failed to create %sClient: %w", _url.Scheme, err) } ctx = context.WithValue(ctx, clientKey, &connection) serviceVersion, err := pingNewConnection(ctx) if err != nil { - return nil, errors.Wrap(err, "unable to connect to Podman socket") + return nil, fmt.Errorf("unable to connect to Podman socket: %w", err) } ctx = context.WithValue(ctx, versionKey, serviceVersion) return ctx, nil @@ -177,11 +177,11 @@ func pingNewConnection(ctx context.Context) (*semver.Version, error) { // Server's job when Client version is equal or older return &versionSrv, nil case 1: - return nil, errors.Errorf("server API version is too old. Client %q server %q", + return nil, fmt.Errorf("server API version is too old. Client %q server %q", version.APIVersion[version.Libpod][version.MinimalAPI].String(), versionSrv.String()) } } - return nil, errors.Errorf("ping response was %d", response.StatusCode) + return nil, fmt.Errorf("ping response was %d", response.StatusCode) } func sshClient(_url *url.URL, secure bool, passPhrase string, identity string) (Connection, error) { @@ -193,7 +193,7 @@ func sshClient(_url *url.URL, secure bool, passPhrase string, identity string) ( if len(identity) > 0 { s, err := terminal.PublicKey(identity, []byte(passPhrase)) if err != nil { - return Connection{}, errors.Wrapf(err, "failed to parse identity %q", identity) + return Connection{}, fmt.Errorf("failed to parse identity %q: %w", identity, err) } signers = append(signers, s) @@ -289,7 +289,7 @@ func sshClient(_url *url.URL, secure bool, passPhrase string, identity string) ( }, ) if err != nil { - return Connection{}, errors.Wrapf(err, "connection to bastion host (%s) failed", _url.String()) + return Connection{}, fmt.Errorf("connection to bastion host (%s) failed: %w", _url.String(), err) } connection := Connection{URI: _url} @@ -379,7 +379,7 @@ func (c *Connection) GetDialer(ctx context.Context) (net.Conn, error) { return transport.DialContext(ctx, c.URI.Scheme, c.URI.String()) } - return nil, errors.New("Unable to get dial context") + return nil, errors.New("unable to get dial context") } // IsInformational returns true if the response code is 1xx diff --git a/pkg/bindings/containers/archive.go b/pkg/bindings/containers/archive.go index dd489d6f1..660d9da6b 100644 --- a/pkg/bindings/containers/archive.go +++ b/pkg/bindings/containers/archive.go @@ -2,6 +2,7 @@ package containers import ( "context" + "errors" "io" "net/http" "net/url" @@ -9,7 +10,6 @@ import ( "github.com/containers/podman/v4/pkg/bindings" "github.com/containers/podman/v4/pkg/copy" "github.com/containers/podman/v4/pkg/domain/entities" - "github.com/pkg/errors" ) // Stat checks if the specified path is on the container. Note that the stat diff --git a/pkg/bindings/containers/attach.go b/pkg/bindings/containers/attach.go index 303fc65bd..e23ee5ee9 100644 --- a/pkg/bindings/containers/attach.go +++ b/pkg/bindings/containers/attach.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/binary" + "errors" "fmt" "io" "net" @@ -14,11 +15,10 @@ import ( "strconv" "time" + "github.com/containers/common/pkg/util" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/bindings" - "github.com/containers/podman/v4/utils" "github.com/moby/term" - "github.com/pkg/errors" "github.com/sirupsen/logrus" terminal "golang.org/x/term" ) @@ -75,7 +75,7 @@ func Attach(ctx context.Context, nameOrID string, stdin io.Reader, stdout io.Wri detachKeysInBytes, err = term.ToBytes(options.GetDetachKeys()) if err != nil { - return errors.Wrapf(err, "invalid detach keys") + return fmt.Errorf("invalid detach keys: %w", err) } } if isSet.stdin { @@ -159,7 +159,7 @@ func Attach(ctx context.Context, nameOrID string, stdin io.Reader, stdout io.Wri go func() { logrus.Debugf("Copying STDIN to socket") - _, err := utils.CopyDetachable(socket, stdin, detachKeysInBytes) + _, err := util.CopyDetachable(socket, stdin, detachKeysInBytes) if err != nil && err != define.ErrDetach { logrus.Errorf("Failed to write input to service: %v", err) } @@ -261,7 +261,7 @@ func DemuxHeader(r io.Reader, buffer []byte) (fd, sz int, err error) { fd = int(buffer[0]) if fd < 0 || fd > 3 { - err = errors.Wrapf(ErrLostSync, fmt.Sprintf(`channel "%d" found, 0-3 supported`, fd)) + err = fmt.Errorf(`channel "%d" found, 0-3 supported: %w`, fd, ErrLostSync) return } @@ -497,7 +497,7 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, options *ExecStar if options.GetAttachInput() { go func() { logrus.Debugf("Copying STDIN to socket") - _, err := utils.CopyDetachable(socket, options.InputStream, []byte{}) + _, err := util.CopyDetachable(socket, options.InputStream, []byte{}) if err != nil { logrus.Errorf("Failed to write input to service: %v", err) } @@ -518,7 +518,7 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, options *ExecStar return fmt.Errorf("exec session %s has a terminal and must have STDOUT enabled", sessionID) } // If not multiplex'ed, read from server and write to stdout - _, err := utils.CopyDetachable(options.GetOutputStream(), socket, []byte{}) + _, err := util.CopyDetachable(options.GetOutputStream(), socket, []byte{}) if err != nil { return err } diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go index ea01bc7d9..80ec7bc6f 100644 --- a/pkg/bindings/containers/containers.go +++ b/pkg/bindings/containers/containers.go @@ -2,6 +2,8 @@ package containers import ( "context" + "errors" + "fmt" "io" "net/http" "net/url" @@ -12,7 +14,6 @@ import ( "github.com/containers/podman/v4/pkg/bindings" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/domain/entities/reports" - "github.com/pkg/errors" ) var ( @@ -447,7 +448,7 @@ func ContainerInit(ctx context.Context, nameOrID string, options *InitOptions) e defer response.Body.Close() if response.StatusCode == http.StatusNotModified { - return errors.Wrapf(define.ErrCtrStateInvalid, "container %s has already been created in runtime", nameOrID) + return fmt.Errorf("container %s has already been created in runtime: %w", nameOrID, define.ErrCtrStateInvalid) } return response.Process(nil) } diff --git a/pkg/bindings/containers/exec.go b/pkg/bindings/containers/exec.go index 3ad5d67d2..3d19fb812 100644 --- a/pkg/bindings/containers/exec.go +++ b/pkg/bindings/containers/exec.go @@ -3,6 +3,8 @@ package containers import ( "bytes" "context" + "errors" + "fmt" "net/http" "strings" @@ -11,7 +13,6 @@ import ( "github.com/containers/podman/v4/pkg/bindings" "github.com/containers/podman/v4/pkg/domain/entities" jsoniter "github.com/json-iterator/go" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -27,12 +28,12 @@ func ExecCreate(ctx context.Context, nameOrID string, config *handlers.ExecCreat } if config == nil { - return "", errors.Errorf("must provide a configuration for exec session") + return "", errors.New("must provide a configuration for exec session") } requestJSON, err := json.Marshal(config) if err != nil { - return "", errors.Wrapf(err, "error marshalling exec config to JSON") + return "", fmt.Errorf("error marshalling exec config to JSON: %w", err) } jsonReader := strings.NewReader(string(requestJSON)) diff --git a/pkg/bindings/containers/logs.go b/pkg/bindings/containers/logs.go index 8ea8ed7fa..9ebfd90da 100644 --- a/pkg/bindings/containers/logs.go +++ b/pkg/bindings/containers/logs.go @@ -2,13 +2,13 @@ package containers import ( "context" + "errors" "fmt" "io" "net/http" "strconv" "github.com/containers/podman/v4/pkg/bindings" - "github.com/pkg/errors" ) // Logs obtains a container's logs given the options provided. The logs are then sent to the diff --git a/pkg/bindings/errors.go b/pkg/bindings/errors.go index eb95764ba..29f087c22 100644 --- a/pkg/bindings/errors.go +++ b/pkg/bindings/errors.go @@ -2,10 +2,11 @@ package bindings import ( "encoding/json" + "errors" + "fmt" "io/ioutil" "github.com/containers/podman/v4/pkg/errorhandling" - "github.com/pkg/errors" ) var ( @@ -30,7 +31,7 @@ func (h APIResponse) Process(unmarshalInto interface{}) error { func (h APIResponse) ProcessWithError(unmarshalInto interface{}, unmarshalErrorInto interface{}) error { data, err := ioutil.ReadAll(h.Response.Body) if err != nil { - return errors.Wrap(err, "unable to process API response") + return fmt.Errorf("unable to process API response: %w", err) } if h.IsSuccess() || h.IsRedirection() { if unmarshalInto != nil { diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go index f14f866dd..6883585e2 100644 --- a/pkg/bindings/images/build.go +++ b/pkg/bindings/images/build.go @@ -5,6 +5,7 @@ import ( "compress/gzip" "context" "encoding/json" + "errors" "fmt" "io" "io/fs" @@ -28,7 +29,6 @@ import ( "github.com/docker/go-units" "github.com/hashicorp/go-multierror" jsoniter "github.com/json-iterator/go" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -543,14 +543,14 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO if err := dec.Decode(&s); err != nil { if errors.Is(err, io.ErrUnexpectedEOF) { - return nil, errors.Wrap(err, "server probably quit") + return nil, fmt.Errorf("server probably quit: %w", err) } // EOF means the stream is over in which case we need // to have read the id. if errors.Is(err, io.EOF) && id != "" { break } - return &entities.BuildReport{ID: id}, errors.Wrap(err, "decoding stream") + return &entities.BuildReport{ID: id}, fmt.Errorf("decoding stream: %w", err) } switch { @@ -574,11 +574,11 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO func nTar(excludes []string, sources ...string) (io.ReadCloser, error) { pm, err := fileutils.NewPatternMatcher(excludes) if err != nil { - return nil, errors.Wrapf(err, "error processing excludes list %v", excludes) + return nil, fmt.Errorf("error processing excludes list %v: %w", excludes, err) } if len(sources) == 0 { - return nil, errors.New("No source(s) provided for build") + return nil, errors.New("no source(s) provided for build") } pr, pw := io.Pipe() @@ -623,7 +623,7 @@ func nTar(excludes []string, sources ...string) (io.ReadCloser, error) { excluded, err := pm.Matches(name) //nolint:staticcheck if err != nil { - return errors.Wrapf(err, "error checking if %q is excluded", name) + return fmt.Errorf("error checking if %q is excluded: %w", name, err) } if excluded { // Note: filepath.SkipDir is not possible to use given .dockerignore semantics. @@ -726,7 +726,7 @@ func parseDockerignore(root string) ([]string, error) { var dockerIgnoreErr error ignore, dockerIgnoreErr = ioutil.ReadFile(filepath.Join(root, ".dockerignore")) if dockerIgnoreErr != nil && !os.IsNotExist(dockerIgnoreErr) { - return nil, errors.Wrapf(err, "error reading .containerignore: '%s'", root) + return nil, fmt.Errorf("error reading .containerignore: '%s': %w", root, err) } } rawexcludes := strings.Split(string(ignore), "\n") diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go index 57c8bd597..cd5147629 100644 --- a/pkg/bindings/images/images.go +++ b/pkg/bindings/images/images.go @@ -2,6 +2,7 @@ package images import ( "context" + "errors" "fmt" "io" "net/http" @@ -14,7 +15,6 @@ import ( "github.com/containers/podman/v4/pkg/bindings" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/domain/entities/reports" - "github.com/pkg/errors" ) // Exists a lightweight way to determine if an image exists in local storage. It returns a diff --git a/pkg/bindings/images/pull.go b/pkg/bindings/images/pull.go index de02c62fd..1a4aa3038 100644 --- a/pkg/bindings/images/pull.go +++ b/pkg/bindings/images/pull.go @@ -3,6 +3,7 @@ package images import ( "context" "encoding/json" + "errors" "fmt" "io" "io/ioutil" @@ -15,7 +16,6 @@ import ( "github.com/containers/podman/v4/pkg/bindings" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/errorhandling" - "github.com/pkg/errors" ) // Pull is the binding for libpod's v2 endpoints for pulling images. Note that @@ -91,7 +91,7 @@ func Pull(ctx context.Context, rawImage string, options *PullOptions) ([]string, images = report.Images case report.ID != "": default: - return images, errors.Errorf("failed to parse pull results stream, unexpected input: %v", report) + return images, fmt.Errorf("failed to parse pull results stream, unexpected input: %v", report) } } return images, errorhandling.JoinErrors(pullErrors) diff --git a/pkg/bindings/manifests/manifests.go b/pkg/bindings/manifests/manifests.go index a68dd5a4e..80153c4b4 100644 --- a/pkg/bindings/manifests/manifests.go +++ b/pkg/bindings/manifests/manifests.go @@ -2,6 +2,8 @@ package manifests import ( "context" + "errors" + "fmt" "io/ioutil" "net/http" "strconv" @@ -15,7 +17,6 @@ import ( "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/errorhandling" jsoniter "github.com/json-iterator/go" - "github.com/pkg/errors" ) // Create creates a manifest for the given name. Optional images to be associated with @@ -219,13 +220,13 @@ func Modify(ctx context.Context, name string, images []string, options *ModifyOp data, err := ioutil.ReadAll(response.Body) if err != nil { - return "", errors.Wrap(err, "unable to process API response") + return "", fmt.Errorf("unable to process API response: %w", err) } if response.IsSuccess() || response.IsRedirection() { var report entities.ManifestModifyReport if err = jsoniter.Unmarshal(data, &report); err != nil { - return "", errors.Wrap(err, "unable to decode API response") + return "", fmt.Errorf("unable to decode API response: %w", err) } err = errorhandling.JoinErrors(report.Errors) @@ -244,7 +245,7 @@ func Modify(ctx context.Context, name string, images []string, options *ModifyOp ResponseCode: response.StatusCode, } if err = jsoniter.Unmarshal(data, &errModel); err != nil { - return "", errors.Wrap(err, "unable to decode API response") + return "", fmt.Errorf("unable to decode API response: %w", err) } return "", &errModel } diff --git a/pkg/bindings/system/system.go b/pkg/bindings/system/system.go index 5ef78e444..dae80384b 100644 --- a/pkg/bindings/system/system.go +++ b/pkg/bindings/system/system.go @@ -3,6 +3,7 @@ package system import ( "context" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -11,7 +12,6 @@ import ( "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/bindings" "github.com/containers/podman/v4/pkg/domain/entities" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -37,7 +37,7 @@ func Events(ctx context.Context, eventChan chan entities.Event, cancelChan chan go func() { <-cancelChan err = response.Body.Close() - logrus.Error(errors.Wrap(err, "unable to close event response body")) + logrus.Errorf("Unable to close event response body: %v", err) }() } @@ -56,7 +56,7 @@ func Events(ctx context.Context, eventChan chan entities.Event, cancelChan chan case errors.Is(err, io.EOF): return nil default: - return errors.Wrap(err, "unable to decode event response") + return fmt.Errorf("unable to decode event response: %w", err) } } diff --git a/pkg/bindings/test/common_test.go b/pkg/bindings/test/common_test.go index 950fd21e6..6b0175f59 100644 --- a/pkg/bindings/test/common_test.go +++ b/pkg/bindings/test/common_test.go @@ -16,7 +16,6 @@ import ( "github.com/containers/podman/v4/pkg/specgen" "github.com/onsi/ginkgo" "github.com/onsi/gomega/gexec" - "github.com/pkg/errors" ) type testImage struct { @@ -127,7 +126,7 @@ func (b *bindingTest) runPodman(command []string) *gexec.Session { fmt.Printf("Running: %s %s\n", podmanBinary, strings.Join(cmd, " ")) session, err := gexec.Start(c, ginkgo.GinkgoWriter, ginkgo.GinkgoWriter) if err != nil { - panic(errors.Errorf("unable to run podman command: %q", cmd)) + panic(fmt.Errorf("unable to run podman command: %q", cmd)) } return session } diff --git a/pkg/channel/writer.go b/pkg/channel/writer.go index ecb68e906..f17486075 100644 --- a/pkg/channel/writer.go +++ b/pkg/channel/writer.go @@ -1,10 +1,9 @@ package channel import ( + "errors" "io" "sync" - - "github.com/pkg/errors" ) // WriteCloser is an io.WriteCloser that that proxies Write() calls to a channel diff --git a/pkg/checkpoint/checkpoint_restore.go b/pkg/checkpoint/checkpoint_restore.go index 396b521a1..e7c843143 100644 --- a/pkg/checkpoint/checkpoint_restore.go +++ b/pkg/checkpoint/checkpoint_restore.go @@ -2,6 +2,8 @@ package checkpoint import ( "context" + "errors" + "fmt" "io/ioutil" "os" @@ -16,7 +18,6 @@ import ( "github.com/containers/podman/v4/pkg/specgen/generate" "github.com/containers/podman/v4/pkg/specgenutil" spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -65,7 +66,7 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt // This should not happen as checkpoints with these options are not exported. if len(ctrConfig.Dependencies) > 0 { - return nil, errors.Errorf("Cannot import checkpoints of containers with dependencies") + return nil, errors.New("cannot import checkpoints of containers with dependencies") } // Volumes included in the checkpoint should not exist @@ -76,7 +77,7 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt return nil, err } if exists { - return nil, errors.Errorf("volume with name %s already exists. Use --ignore-volumes to not restore content of volumes", vol.Name) + return nil, fmt.Errorf("volume with name %s already exists. Use --ignore-volumes to not restore content of volumes", vol.Name) } } } @@ -106,11 +107,11 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt if restoreOptions.Pod != "" { // Restoring into a Pod requires much newer versions of CRIU if !criu.CheckForCriu(criu.PodCriuVersion) { - return nil, errors.Errorf("restoring containers into pods requires at least CRIU %d", criu.PodCriuVersion) + return nil, fmt.Errorf("restoring containers into pods requires at least CRIU %d", criu.PodCriuVersion) } // The runtime also has to support it if !crutils.CRRuntimeSupportsPodCheckpointRestore(runtime.GetOCIRuntimePath()) { - return nil, errors.Errorf("runtime %s does not support pod restore", runtime.GetOCIRuntimePath()) + return nil, fmt.Errorf("runtime %s does not support pod restore", runtime.GetOCIRuntimePath()) } // Restoring into an existing Pod ctrConfig.Pod = restoreOptions.Pod @@ -120,12 +121,12 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt // Let's make sure we a restoring into a pod with the same shared namespaces. pod, err := runtime.LookupPod(ctrConfig.Pod) if err != nil { - return nil, errors.Wrapf(err, "pod %q cannot be retrieved", ctrConfig.Pod) + return nil, fmt.Errorf("pod %q cannot be retrieved: %w", ctrConfig.Pod, err) } infraContainer, err := pod.InfraContainer() if err != nil { - return nil, errors.Wrapf(err, "cannot retrieve infra container from pod %q", ctrConfig.Pod) + return nil, fmt.Errorf("cannot retrieve infra container from pod %q: %w", ctrConfig.Pod, err) } // If a namespaces was shared (!= "") it needs to be set to the new infrastructure container @@ -133,14 +134,14 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt // container we abort. if ctrConfig.IPCNsCtr != "" { if !pod.SharesIPC() { - return nil, errors.Errorf("pod %s does not share the IPC namespace", ctrConfig.Pod) + return nil, fmt.Errorf("pod %s does not share the IPC namespace", ctrConfig.Pod) } ctrConfig.IPCNsCtr = infraContainer.ID() } if ctrConfig.NetNsCtr != "" { if !pod.SharesNet() { - return nil, errors.Errorf("pod %s does not share the network namespace", ctrConfig.Pod) + return nil, fmt.Errorf("pod %s does not share the network namespace", ctrConfig.Pod) } ctrConfig.NetNsCtr = infraContainer.ID() for net, opts := range ctrConfig.Networks { @@ -154,21 +155,21 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt if ctrConfig.PIDNsCtr != "" { if !pod.SharesPID() { - return nil, errors.Errorf("pod %s does not share the PID namespace", ctrConfig.Pod) + return nil, fmt.Errorf("pod %s does not share the PID namespace", ctrConfig.Pod) } ctrConfig.PIDNsCtr = infraContainer.ID() } if ctrConfig.UTSNsCtr != "" { if !pod.SharesUTS() { - return nil, errors.Errorf("pod %s does not share the UTS namespace", ctrConfig.Pod) + return nil, fmt.Errorf("pod %s does not share the UTS namespace", ctrConfig.Pod) } ctrConfig.UTSNsCtr = infraContainer.ID() } if ctrConfig.CgroupNsCtr != "" { if !pod.SharesCgroup() { - return nil, errors.Errorf("pod %s does not share the cgroup namespace", ctrConfig.Pod) + return nil, fmt.Errorf("pod %s does not share the cgroup namespace", ctrConfig.Pod) } ctrConfig.CgroupNsCtr = infraContainer.ID() } @@ -180,7 +181,7 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt // Fix parent cgroup cgroupPath, err := pod.CgroupPath() if err != nil { - return nil, errors.Wrapf(err, "cannot retrieve cgroup path from pod %q", ctrConfig.Pod) + return nil, fmt.Errorf("cannot retrieve cgroup path from pod %q: %w", ctrConfig.Pod, err) } ctrConfig.CgroupParent = cgroupPath @@ -228,14 +229,14 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt containerConfig := container.Config() ctrName := ctrConfig.Name if containerConfig.Name != ctrName { - return nil, errors.Errorf("Name of restored container (%s) does not match requested name (%s)", containerConfig.Name, ctrName) + return nil, fmt.Errorf("name of restored container (%s) does not match requested name (%s)", containerConfig.Name, ctrName) } if !newName { // Only check ID for a restore with the same name. // Using -n to request a new name for the restored container, will also create a new ID if containerConfig.ID != ctrID { - return nil, errors.Errorf("ID of restored container (%s) does not match requested ID (%s)", containerConfig.ID, ctrID) + return nil, fmt.Errorf("ID of restored container (%s) does not match requested ID (%s)", containerConfig.ID, ctrID) } } diff --git a/pkg/checkpoint/crutils/checkpoint_restore_utils.go b/pkg/checkpoint/crutils/checkpoint_restore_utils.go index 76c868cee..1437a09df 100644 --- a/pkg/checkpoint/crutils/checkpoint_restore_utils.go +++ b/pkg/checkpoint/crutils/checkpoint_restore_utils.go @@ -2,6 +2,8 @@ package crutils import ( "bytes" + "errors" + "fmt" "io" "io/ioutil" "os" @@ -12,7 +14,6 @@ import ( "github.com/checkpoint-restore/go-criu/v5/stats" "github.com/containers/storage/pkg/archive" "github.com/opencontainers/selinux/go-selinux/label" - "github.com/pkg/errors" ) // This file mainly exist to make the checkpoint/restore functions @@ -23,7 +24,7 @@ import ( func CRImportCheckpointWithoutConfig(destination, input string) error { archiveFile, err := os.Open(input) if err != nil { - return errors.Wrapf(err, "Failed to open checkpoint archive %s for import", input) + return fmt.Errorf("failed to open checkpoint archive %s for import: %w", input, err) } defer archiveFile.Close() @@ -35,7 +36,7 @@ func CRImportCheckpointWithoutConfig(destination, input string) error { }, } if err = archive.Untar(archiveFile, destination, options); err != nil { - return errors.Wrapf(err, "Unpacking of checkpoint archive %s failed", input) + return fmt.Errorf("unpacking of checkpoint archive %s failed: %w", input, err) } return nil @@ -47,7 +48,7 @@ func CRImportCheckpointWithoutConfig(destination, input string) error { func CRImportCheckpointConfigOnly(destination, input string) error { archiveFile, err := os.Open(input) if err != nil { - return errors.Wrapf(err, "Failed to open checkpoint archive %s for import", input) + return fmt.Errorf("failed to open checkpoint archive %s for import: %w", input, err) } defer archiveFile.Close() @@ -65,7 +66,7 @@ func CRImportCheckpointConfigOnly(destination, input string) error { }, } if err = archive.Untar(archiveFile, destination, options); err != nil { - return errors.Wrapf(err, "Unpacking of checkpoint archive %s failed", input) + return fmt.Errorf("unpacking of checkpoint archive %s failed: %w", input, err) } return nil @@ -81,14 +82,14 @@ func CRRemoveDeletedFiles(id, baseDirectory, containerRootDirectory string) erro } if err != nil { - return errors.Wrapf(err, "failed to read deleted files file") + return fmt.Errorf("failed to read deleted files file: %w", err) } for _, deleteFile := range deletedFiles { // Using RemoveAll as deletedFiles, which is generated from 'podman diff' // lists completely deleted directories as a single entry: 'D /root'. if err := os.RemoveAll(filepath.Join(containerRootDirectory, deleteFile)); err != nil { - return errors.Wrapf(err, "failed to delete files from container %s during restore", id) + return fmt.Errorf("failed to delete files from container %s during restore: %w", id, err) } } @@ -105,12 +106,12 @@ func CRApplyRootFsDiffTar(baseDirectory, containerRootDirectory string) error { if errors.Is(err, os.ErrNotExist) { return nil } - return errors.Wrap(err, "failed to open root file-system diff file") + return fmt.Errorf("failed to open root file-system diff file: %w", err) } defer rootfsDiffFile.Close() if err := archive.Untar(rootfsDiffFile, containerRootDirectory, nil); err != nil { - return errors.Wrapf(err, "failed to apply root file-system diff file %s", rootfsDiffPath) + return fmt.Errorf("failed to apply root file-system diff file %s: %w", rootfsDiffPath, err) } return nil @@ -158,11 +159,11 @@ func CRCreateRootFsDiffTar(changes *[]archive.Change, mountPoint, destination st IncludeFiles: rootfsIncludeFiles, }) if err != nil { - return includeFiles, errors.Wrapf(err, "error exporting root file-system diff to %q", rootfsDiffPath) + return includeFiles, fmt.Errorf("error exporting root file-system diff to %q: %w", rootfsDiffPath, err) } rootfsDiffFile, err := os.Create(rootfsDiffPath) if err != nil { - return includeFiles, errors.Wrapf(err, "error creating root file-system diff file %q", rootfsDiffPath) + return includeFiles, fmt.Errorf("error creating root file-system diff file %q: %w", rootfsDiffPath, err) } defer rootfsDiffFile.Close() if _, err = io.Copy(rootfsDiffFile, rootfsTar); err != nil { @@ -195,11 +196,11 @@ func CRCreateFileWithLabel(directory, fileName, fileLabel string) error { logFile, err := os.OpenFile(logFileName, os.O_CREATE, 0o600) if err != nil { - return errors.Wrapf(err, "failed to create file %q", logFileName) + return fmt.Errorf("failed to create file %q: %w", logFileName, err) } defer logFile.Close() if err = label.SetFileLabel(logFileName, fileLabel); err != nil { - return errors.Wrapf(err, "failed to label file %q", logFileName) + return fmt.Errorf("failed to label file %q: %w", logFileName, err) } return nil diff --git a/pkg/copy/fileinfo.go b/pkg/copy/fileinfo.go index 0ccca5b6e..7d4e67896 100644 --- a/pkg/copy/fileinfo.go +++ b/pkg/copy/fileinfo.go @@ -3,13 +3,14 @@ package copy import ( "encoding/base64" "encoding/json" + "errors" + "fmt" "net/http" "os" "path/filepath" "strings" "github.com/containers/podman/v4/libpod/define" - "github.com/pkg/errors" ) // XDockerContainerPathStatHeader is the *key* in http headers pointing to the @@ -18,7 +19,7 @@ const XDockerContainerPathStatHeader = "X-Docker-Container-Path-Stat" // ErrENOENT mimics the stdlib's ErrENOENT and can be used to implement custom logic // while preserving the user-visible error message. -var ErrENOENT = errors.New("No such file or directory") +var ErrENOENT = errors.New("no such file or directory") // FileInfo describes a file or directory and is returned by // (*CopyItem).Stat(). @@ -29,7 +30,7 @@ type FileInfo = define.FileInfo func EncodeFileInfo(info *FileInfo) (string, error) { buf, err := json.Marshal(&info) if err != nil { - return "", errors.Wrap(err, "failed to serialize file stats") + return "", fmt.Errorf("failed to serialize file stats: %w", err) } return base64.URLEncoding.EncodeToString(buf), nil } diff --git a/pkg/copy/parse.go b/pkg/copy/parse.go index 93edec5fa..50f1d211d 100644 --- a/pkg/copy/parse.go +++ b/pkg/copy/parse.go @@ -1,9 +1,8 @@ package copy import ( + "fmt" "strings" - - "github.com/pkg/errors" ) // ParseSourceAndDestination parses the source and destination input into a @@ -19,7 +18,7 @@ func ParseSourceAndDestination(source, destination string) (string, string, stri destContainer, destPath := parseUserInput(destination) if len(sourcePath) == 0 || len(destPath) == 0 { - return "", "", "", "", errors.Errorf("invalid arguments %q, %q: you must specify paths", source, destination) + return "", "", "", "", fmt.Errorf("invalid arguments %q, %q: you must specify paths", source, destination) } return sourceContainer, sourcePath, destContainer, destPath, nil diff --git a/pkg/domain/entities/container_ps.go b/pkg/domain/entities/container_ps.go index a5562e7c9..519d2b7da 100644 --- a/pkg/domain/entities/container_ps.go +++ b/pkg/domain/entities/container_ps.go @@ -1,13 +1,13 @@ package entities import ( + "errors" "sort" "strings" "time" "github.com/containers/common/libnetwork/types" "github.com/containers/podman/v4/pkg/ps/define" - "github.com/pkg/errors" ) // ListContainer describes a container suitable for listing @@ -166,7 +166,7 @@ func SortPsOutput(sortBy string, psOutput SortListContainers) (SortListContainer case "pod": sort.Sort(psSortedPod{psOutput}) default: - return nil, errors.Errorf("invalid option for --sort, options are: command, created, id, image, names, runningfor, size, or status") + return nil, errors.New("invalid option for --sort, options are: command, created, id, image, names, runningfor, size, or status") } return psOutput, nil } diff --git a/pkg/domain/entities/machine.go b/pkg/domain/entities/machine.go new file mode 100644 index 000000000..6ba53dbd1 --- /dev/null +++ b/pkg/domain/entities/machine.go @@ -0,0 +1,18 @@ +package entities + +type ListReporter struct { + Name string + Default bool + Created string + Running bool + Starting bool + LastUp string + Stream string + VMType string + CPUs uint64 + Memory string + DiskSize string + Port int + RemoteUsername string + IdentityPath string +} diff --git a/pkg/domain/entities/network.go b/pkg/domain/entities/network.go index d375c2e20..9e59953c6 100644 --- a/pkg/domain/entities/network.go +++ b/pkg/domain/entities/network.go @@ -81,8 +81,7 @@ type NetworkPruneReport struct { Error error } -// NetworkPruneOptions describes options for pruning -// unused cni networks +// NetworkPruneOptions describes options for pruning unused networks type NetworkPruneOptions struct { Filters map[string][]string } diff --git a/pkg/domain/entities/system.go b/pkg/domain/entities/system.go index 331d2bcdc..8dd0a61be 100644 --- a/pkg/domain/entities/system.go +++ b/pkg/domain/entities/system.go @@ -28,7 +28,7 @@ type SystemPruneReport struct { PodPruneReport []*PodPruneReport ContainerPruneReports []*reports.PruneReport ImagePruneReports []*reports.PruneReport - NetworkPruneReports []*reports.PruneReport + NetworkPruneReports []*NetworkPruneReport VolumePruneReports []*reports.PruneReport ReclaimedSpace uint64 } diff --git a/pkg/domain/filters/pods.go b/pkg/domain/filters/pods.go index c2ed359f5..78b97db64 100644 --- a/pkg/domain/filters/pods.go +++ b/pkg/domain/filters/pods.go @@ -1,6 +1,8 @@ package filters import ( + "errors" + "fmt" "strconv" "strings" @@ -8,7 +10,6 @@ import ( "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/util" - "github.com/pkg/errors" ) // GeneratePodFilterFunc takes a filter and filtervalue (key, value) @@ -59,7 +60,7 @@ func GeneratePodFilterFunc(filter string, filterValues []string, r *libpod.Runti case "ctr-status": for _, filterValue := range filterValues { if !cutil.StringInSlice(filterValue, []string{"created", "running", "paused", "stopped", "exited", "unknown"}) { - return nil, errors.Errorf("%s is not a valid status", filterValue) + return nil, fmt.Errorf("%s is not a valid status", filterValue) } } return func(p *libpod.Pod) bool { @@ -96,7 +97,7 @@ func GeneratePodFilterFunc(filter string, filterValues []string, r *libpod.Runti case "status": for _, filterValue := range filterValues { if !cutil.StringInSlice(filterValue, []string{"stopped", "running", "paused", "exited", "dead", "created", "degraded"}) { - return nil, errors.Errorf("%s is not a valid pod status", filterValue) + return nil, fmt.Errorf("%s is not a valid pod status", filterValue) } } return func(p *libpod.Pod) bool { @@ -158,5 +159,5 @@ func GeneratePodFilterFunc(filter string, filterValues []string, r *libpod.Runti return false }, nil } - return nil, errors.Errorf("%s is an invalid filter", filter) + return nil, fmt.Errorf("%s is an invalid filter", filter) } diff --git a/pkg/domain/filters/volumes.go b/pkg/domain/filters/volumes.go index a18e6332c..7c5047225 100644 --- a/pkg/domain/filters/volumes.go +++ b/pkg/domain/filters/volumes.go @@ -1,13 +1,13 @@ package filters import ( + "fmt" "net/url" "regexp" "strings" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/pkg/util" - "github.com/pkg/errors" ) func GenerateVolumeFilters(filters url.Values) ([]libpod.VolumeFilter, error) { @@ -72,7 +72,7 @@ func GenerateVolumeFilters(filters url.Values) ([]libpod.VolumeFilter, error) { // invert the result of IsDangling. invert = true default: - return nil, errors.Errorf("%q is not a valid value for the \"dangling\" filter - must be true or false", danglingVal) + return nil, fmt.Errorf("%q is not a valid value for the \"dangling\" filter - must be true or false", danglingVal) } vf = append(vf, func(v *libpod.Volume) bool { dangling, err := v.IsDangling() @@ -85,7 +85,7 @@ func GenerateVolumeFilters(filters url.Values) ([]libpod.VolumeFilter, error) { return dangling }) default: - return nil, errors.Errorf("%q is an invalid volume filter", filter) + return nil, fmt.Errorf("%q is an invalid volume filter", filter) } } } @@ -109,7 +109,7 @@ func GeneratePruneVolumeFilters(filters url.Values) ([]libpod.VolumeFilter, erro } vf = append(vf, f) default: - return nil, errors.Errorf("%q is an invalid volume filter", filter) + return nil, fmt.Errorf("%q is an invalid volume filter", filter) } } } diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 1688be57e..23a591604 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -101,6 +101,9 @@ func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []strin responses := make([]entities.WaitReport, 0, len(ctrs)) for _, c := range ctrs { response := entities.WaitReport{Id: c.ID()} + if options.Condition == nil { + options.Condition = []define.ContainerStatus{define.ContainerStateStopped, define.ContainerStateExited} + } exitCode, err := c.WaitForConditionWithInterval(ctx, options.Interval, options.Condition...) if err != nil { response.Error = err diff --git a/pkg/domain/infra/abi/generate.go b/pkg/domain/infra/abi/generate.go index 8b2193cb2..31885ce54 100644 --- a/pkg/domain/infra/abi/generate.go +++ b/pkg/domain/infra/abi/generate.go @@ -12,7 +12,6 @@ import ( k8sAPI "github.com/containers/podman/v4/pkg/k8s.io/api/core/v1" "github.com/containers/podman/v4/pkg/systemd/generate" "github.com/ghodss/yaml" - "github.com/pkg/errors" ) func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, options entities.GenerateSystemdOptions) (*entities.GenerateSystemdReport, error) { @@ -30,8 +29,8 @@ func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, // If it's not a container, we either have a pod or garbage. pod, err := ic.Libpod.LookupPod(nameOrID) if err != nil { - err = errors.Wrap(ctrErr, err.Error()) - return nil, errors.Wrapf(err, "%s does not refer to a container or pod", nameOrID) + err = fmt.Errorf("%v: %w", err.Error(), ctrErr) + return nil, fmt.Errorf("%s does not refer to a container or pod: %w", nameOrID, err) } // Generate the units for the pod and all its containers. @@ -64,7 +63,7 @@ func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string, // now that infra holds NS data, we need to support dependencies. // we cannot deal with ctrs already in a pod. if len(ctr.PodID()) > 0 { - return nil, errors.Errorf("container %s is associated with pod %s: use generate on the pod itself", ctr.ID(), ctr.PodID()) + return nil, fmt.Errorf("container %s is associated with pod %s: use generate on the pod itself", ctr.ID(), ctr.PodID()) } ctrs = append(ctrs, ctr) continue @@ -104,7 +103,7 @@ func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string, } // If it reaches here is because the name or id did not exist. - return nil, errors.Errorf("Name or ID %q not found", nameOrID) + return nil, fmt.Errorf("name or ID %q not found", nameOrID) } if !defaultKubeNS { diff --git a/pkg/domain/infra/abi/images_list.go b/pkg/domain/infra/abi/images_list.go index 8f5591e92..96e99fbf0 100644 --- a/pkg/domain/infra/abi/images_list.go +++ b/pkg/domain/infra/abi/images_list.go @@ -2,10 +2,10 @@ package abi import ( "context" + "fmt" "github.com/containers/common/libimage" "github.com/containers/podman/v4/pkg/domain/entities" - "github.com/pkg/errors" ) func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) ([]*entities.ImageSummary, error) { @@ -28,11 +28,11 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) for _, img := range images { repoDigests, err := img.RepoDigests() if err != nil { - return nil, errors.Wrapf(err, "getting repoDigests from image %q", img.ID()) + return nil, fmt.Errorf("getting repoDigests from image %q: %w", img.ID(), err) } isDangling, err := img.IsDangling(ctx) if err != nil { - return nil, errors.Wrapf(err, "error checking if image %q is dangling", img.ID()) + return nil, fmt.Errorf("error checking if image %q is dangling: %w", img.ID(), err) } e := entities.ImageSummary{ @@ -49,18 +49,18 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) } e.Labels, err = img.Labels(ctx) if err != nil { - return nil, errors.Wrapf(err, "error retrieving label for image %q: you may need to remove the image to resolve the error", img.ID()) + return nil, fmt.Errorf("error retrieving label for image %q: you may need to remove the image to resolve the error: %w", img.ID(), err) } ctnrs, err := img.Containers() if err != nil { - return nil, errors.Wrapf(err, "error retrieving containers for image %q: you may need to remove the image to resolve the error", img.ID()) + return nil, fmt.Errorf("error retrieving containers for image %q: you may need to remove the image to resolve the error: %w", img.ID(), err) } e.Containers = len(ctnrs) sz, err := img.Size() if err != nil { - return nil, errors.Wrapf(err, "error retrieving size of image %q: you may need to remove the image to resolve the error", img.ID()) + return nil, fmt.Errorf("error retrieving size of image %q: you may need to remove the image to resolve the error: %w", img.ID(), err) } e.Size = sz // This is good enough for now, but has to be @@ -69,7 +69,7 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) parent, err := img.Parent(ctx) if err != nil { - return nil, errors.Wrapf(err, "error retrieving parent of image %q: you may need to remove the image to resolve the error", img.ID()) + return nil, fmt.Errorf("error retrieving parent of image %q: you may need to remove the image to resolve the error: %w", img.ID(), err) } if parent != nil { e.ParentId = parent.ID() diff --git a/pkg/domain/infra/abi/parse/parse.go b/pkg/domain/infra/abi/parse/parse.go index 4e8c2e508..19699589b 100644 --- a/pkg/domain/infra/abi/parse/parse.go +++ b/pkg/domain/infra/abi/parse/parse.go @@ -1,13 +1,13 @@ package parse import ( + "fmt" "strconv" "strings" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" units "github.com/docker/go-units" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -32,7 +32,7 @@ func VolumeOptions(opts map[string]string) ([]libpod.VolumeCreateOption, error) case "size": size, err := units.FromHumanSize(splitO[1]) if err != nil { - return nil, errors.Wrapf(err, "cannot convert size %s to integer", splitO[1]) + return nil, fmt.Errorf("cannot convert size %s to integer: %w", splitO[1], err) } libpodOptions = append(libpodOptions, libpod.WithVolumeSize(uint64(size))) finalVal = append(finalVal, o) @@ -41,7 +41,7 @@ func VolumeOptions(opts map[string]string) ([]libpod.VolumeCreateOption, error) case "inodes": inodes, err := strconv.ParseUint(splitO[1], 10, 64) if err != nil { - return nil, errors.Wrapf(err, "cannot convert inodes %s to integer", splitO[1]) + return nil, fmt.Errorf("cannot convert inodes %s to integer: %w", splitO[1], err) } libpodOptions = append(libpodOptions, libpod.WithVolumeInodes(inodes)) finalVal = append(finalVal, o) @@ -49,11 +49,11 @@ func VolumeOptions(opts map[string]string) ([]libpod.VolumeCreateOption, error) volumeOptions["INODES"] = splitO[1] case "uid": if len(splitO) != 2 { - return nil, errors.Wrapf(define.ErrInvalidArg, "uid option must provide a UID") + return nil, fmt.Errorf("uid option must provide a UID: %w", define.ErrInvalidArg) } intUID, err := strconv.Atoi(splitO[1]) if err != nil { - return nil, errors.Wrapf(err, "cannot convert UID %s to integer", splitO[1]) + return nil, fmt.Errorf("cannot convert UID %s to integer: %w", splitO[1], err) } logrus.Debugf("Removing uid= from options and adding WithVolumeUID for UID %d", intUID) libpodOptions = append(libpodOptions, libpod.WithVolumeUID(intUID), libpod.WithVolumeNoChown()) @@ -62,11 +62,11 @@ func VolumeOptions(opts map[string]string) ([]libpod.VolumeCreateOption, error) volumeOptions["UID"] = splitO[1] case "gid": if len(splitO) != 2 { - return nil, errors.Wrapf(define.ErrInvalidArg, "gid option must provide a GID") + return nil, fmt.Errorf("gid option must provide a GID: %w", define.ErrInvalidArg) } intGID, err := strconv.Atoi(splitO[1]) if err != nil { - return nil, errors.Wrapf(err, "cannot convert GID %s to integer", splitO[1]) + return nil, fmt.Errorf("cannot convert GID %s to integer: %w", splitO[1], err) } logrus.Debugf("Removing gid= from options and adding WithVolumeGID for GID %d", intGID) libpodOptions = append(libpodOptions, libpod.WithVolumeGID(intGID), libpod.WithVolumeNoChown()) @@ -80,11 +80,11 @@ func VolumeOptions(opts map[string]string) ([]libpod.VolumeCreateOption, error) volumeOptions["NOQUOTA"] = "true" case "timeout": if len(splitO) != 2 { - return nil, errors.Wrapf(define.ErrInvalidArg, "timeout option must provide a valid timeout in seconds") + return nil, fmt.Errorf("timeout option must provide a valid timeout in seconds: %w", define.ErrInvalidArg) } intTimeout, err := strconv.Atoi(splitO[1]) if err != nil { - return nil, errors.Wrapf(err, "cannot convert Timeout %s to an integer", splitO[1]) + return nil, fmt.Errorf("cannot convert Timeout %s to an integer: %w", splitO[1], err) } logrus.Debugf("Removing timeout from options and adding WithTimeout for Timeout %d", intTimeout) libpodOptions = append(libpodOptions, libpod.WithVolumeDriverTimeout(intTimeout)) diff --git a/pkg/domain/infra/abi/pods_stats.go b/pkg/domain/infra/abi/pods_stats.go index 6123027b8..a270db769 100644 --- a/pkg/domain/infra/abi/pods_stats.go +++ b/pkg/domain/infra/abi/pods_stats.go @@ -2,6 +2,7 @@ package abi import ( "context" + "errors" "fmt" "github.com/containers/common/pkg/cgroups" @@ -10,7 +11,6 @@ import ( "github.com/containers/podman/v4/pkg/rootless" "github.com/containers/podman/v4/utils" "github.com/docker/go-units" - "github.com/pkg/errors" ) // PodStats implements printing stats about pods. @@ -28,7 +28,7 @@ func (ic *ContainerEngine) PodStats(ctx context.Context, namesOrIds []string, op // Get the (running) pods and convert them to the entities format. pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod) if err != nil { - return nil, errors.Wrap(err, "unable to get list of pods") + return nil, fmt.Errorf("unable to get list of pods: %w", err) } return ic.podsToStatsReport(pods) } diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go index 96690afef..0faae01c8 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -157,15 +157,15 @@ func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.Sys // TODO: Figure out cleaner way to handle all of the different PruneOptions // Remove all unused pods. - podPruneReport, err := ic.prunePodHelper(ctx) + podPruneReports, err := ic.prunePodHelper(ctx) if err != nil { return nil, err } - if len(podPruneReport) > 0 { + if len(podPruneReports) > 0 { found = true } - systemPruneReport.PodPruneReport = append(systemPruneReport.PodPruneReport, podPruneReport...) + systemPruneReport.PodPruneReport = append(systemPruneReport.PodPruneReport, podPruneReports...) // Remove all unused containers. containerPruneOptions := entities.ContainerPruneOptions{} @@ -201,38 +201,35 @@ func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.Sys networkPruneOptions := entities.NetworkPruneOptions{} networkPruneOptions.Filters = options.Filters - networkPruneReport, err := ic.NetworkPrune(ctx, networkPruneOptions) + networkPruneReports, err := ic.NetworkPrune(ctx, networkPruneOptions) if err != nil { return nil, err } - if len(networkPruneReport) > 0 { + if len(networkPruneReports) > 0 { found = true } - for _, net := range networkPruneReport { - systemPruneReport.NetworkPruneReports = append(systemPruneReport.NetworkPruneReports, &reports.PruneReport{ - Id: net.Name, - Err: net.Error, - Size: 0, - }) - } + + // Networks reclaimedSpace are always '0'. + systemPruneReport.NetworkPruneReports = append(systemPruneReport.NetworkPruneReports, networkPruneReports...) // Remove unused volume data. if options.Volume { volumePruneOptions := entities.VolumePruneOptions{} volumePruneOptions.Filters = (url.Values)(options.Filters) - volumePruneReport, err := ic.VolumePrune(ctx, volumePruneOptions) + volumePruneReports, err := ic.VolumePrune(ctx, volumePruneOptions) if err != nil { return nil, err } - if len(volumePruneReport) > 0 { + if len(volumePruneReports) > 0 { found = true } - reclaimedSpace += reports.PruneReportsSize(volumePruneReport) - systemPruneReport.VolumePruneReports = append(systemPruneReport.VolumePruneReports, volumePruneReport...) + reclaimedSpace += reports.PruneReportsSize(volumePruneReports) + systemPruneReport.VolumePruneReports = append(systemPruneReport.VolumePruneReports, volumePruneReports...) } } + systemPruneReport.ReclaimedSpace = reclaimedSpace return systemPruneReport, nil } diff --git a/pkg/domain/infra/abi/terminal/terminal.go b/pkg/domain/infra/abi/terminal/terminal.go index 692f8dcd5..37dadd92a 100644 --- a/pkg/domain/infra/abi/terminal/terminal.go +++ b/pkg/domain/infra/abi/terminal/terminal.go @@ -2,13 +2,13 @@ package terminal import ( "context" + "fmt" "os" "os/signal" - "github.com/containers/podman/v4/libpod/define" + "github.com/containers/common/pkg/resize" lsignal "github.com/containers/podman/v4/pkg/signal" "github.com/moby/term" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -18,20 +18,20 @@ type RawTtyFormatter struct { // getResize returns a TerminalSize command matching stdin's current // size on success, and nil on errors. -func getResize() *define.TerminalSize { +func getResize() *resize.TerminalSize { winsize, err := term.GetWinsize(os.Stdin.Fd()) if err != nil { logrus.Warnf("Could not get terminal size %v", err) return nil } - return &define.TerminalSize{ + return &resize.TerminalSize{ Width: winsize.Width, Height: winsize.Height, } } // Helper for prepareAttach - set up a goroutine to generate terminal resize events -func resizeTty(ctx context.Context, resize chan define.TerminalSize) { +func resizeTty(ctx context.Context, resize chan resize.TerminalSize) { sigchan := make(chan os.Signal, 1) signal.Notify(sigchan, lsignal.SIGWINCH) go func() { @@ -78,7 +78,7 @@ func (f *RawTtyFormatter) Format(entry *logrus.Entry) ([]byte, error) { return bytes, err } -func handleTerminalAttach(ctx context.Context, resize chan define.TerminalSize) (context.CancelFunc, *term.State, error) { +func handleTerminalAttach(ctx context.Context, resize chan resize.TerminalSize) (context.CancelFunc, *term.State, error) { logrus.Debugf("Handling terminal attach") subCtx, cancel := context.WithCancel(ctx) @@ -89,7 +89,7 @@ func handleTerminalAttach(ctx context.Context, resize chan define.TerminalSize) if err != nil { // allow caller to not have to do any cleaning up if we error here cancel() - return nil, nil, errors.Wrapf(err, "unable to save terminal state") + return nil, nil, fmt.Errorf("unable to save terminal state: %w", err) } logrus.SetFormatter(&RawTtyFormatter{}) diff --git a/pkg/domain/infra/abi/terminal/terminal_linux.go b/pkg/domain/infra/abi/terminal/terminal_linux.go index 62d36f28d..222590871 100644 --- a/pkg/domain/infra/abi/terminal/terminal_linux.go +++ b/pkg/domain/infra/abi/terminal/terminal_linux.go @@ -6,23 +6,23 @@ import ( "fmt" "os" + "github.com/containers/common/pkg/resize" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" - "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/term" ) // ExecAttachCtr execs and attaches to a container func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, execConfig *libpod.ExecConfig, streams *define.AttachStreams) (int, error) { - var resize chan define.TerminalSize + var resizechan chan resize.TerminalSize haveTerminal := term.IsTerminal(int(os.Stdin.Fd())) // Check if we are attached to a terminal. If we are, generate resize // events, and set the terminal to raw mode if haveTerminal && execConfig.Terminal { - resize = make(chan define.TerminalSize) - cancel, oldTermState, err := handleTerminalAttach(ctx, resize) + resizechan = make(chan resize.TerminalSize) + cancel, oldTermState, err := handleTerminalAttach(ctx, resizechan) if err != nil { return -1, err } @@ -33,14 +33,14 @@ func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, execConfig *libpo } }() } - return ctr.Exec(execConfig, streams, resize) + return ctr.Exec(execConfig, streams, resizechan) } // StartAttachCtr starts and (if required) attaches to a container // if you change the signature of this function from os.File to io.Writer, it will trigger a downstream // error. we may need to just lint disable this one. func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr, stdin *os.File, detachKeys string, sigProxy bool, startContainer bool) error { //nolint: interfacer - resize := make(chan define.TerminalSize) + resize := make(chan resize.TerminalSize) haveTerminal := term.IsTerminal(int(os.Stdin.Fd())) @@ -103,7 +103,7 @@ func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr, err = <-attachChan if err != nil { - return errors.Wrapf(err, "error attaching to container %s", ctr.ID()) + return fmt.Errorf("error attaching to container %s: %w", ctr.ID(), err) } return nil diff --git a/pkg/domain/infra/abi/trust.go b/pkg/domain/infra/abi/trust.go index 58f099bb6..0e3d8fad9 100644 --- a/pkg/domain/infra/abi/trust.go +++ b/pkg/domain/infra/abi/trust.go @@ -3,13 +3,14 @@ package abi import ( "context" "encoding/json" + "errors" + "fmt" "io/ioutil" "os" "strings" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/trust" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -35,11 +36,11 @@ func (ir *ImageEngine) ShowTrust(ctx context.Context, args []string, options ent } policyContentStruct, err := trust.GetPolicy(policyPath) if err != nil { - return nil, errors.Wrapf(err, "could not read trust policies") + return nil, fmt.Errorf("could not read trust policies: %w", err) } report.Policies, err = getPolicyShowOutput(policyContentStruct, report.SystemRegistriesDirPath) if err != nil { - return nil, errors.Wrapf(err, "could not show trust policies") + return nil, fmt.Errorf("could not show trust policies: %w", err) } return &report, nil } @@ -56,7 +57,7 @@ func (ir *ImageEngine) SetTrust(ctx context.Context, args []string, options enti pubkeysfile := options.PubKeysFile if len(pubkeysfile) == 0 && trustType == "signedBy" { - return errors.Errorf("At least one public key must be defined for type 'signedBy'") + return errors.New("at least one public key must be defined for type 'signedBy'") } policyPath := trust.DefaultPolicyPath(ir.Libpod.SystemContext()) @@ -70,7 +71,7 @@ func (ir *ImageEngine) SetTrust(ctx context.Context, args []string, options enti return err } if err := json.Unmarshal(policyContent, &policyContentStruct); err != nil { - return errors.Errorf("could not read trust policies") + return errors.New("could not read trust policies") } } if len(pubkeysfile) != 0 { @@ -84,7 +85,7 @@ func (ir *ImageEngine) SetTrust(ctx context.Context, args []string, options enti policyContentStruct.Default = newReposContent } else { if len(policyContentStruct.Default) == 0 { - return errors.Errorf("default trust policy must be set") + return errors.New("default trust policy must be set") } registryExists := false for transport, transportval := range policyContentStruct.Transports { @@ -107,7 +108,7 @@ func (ir *ImageEngine) SetTrust(ctx context.Context, args []string, options enti data, err := json.MarshalIndent(policyContentStruct, "", " ") if err != nil { - return errors.Wrapf(err, "error setting trust policy") + return fmt.Errorf("error setting trust policy: %w", err) } return ioutil.WriteFile(policyPath, data, 0644) } diff --git a/pkg/domain/infra/runtime_libpod.go b/pkg/domain/infra/runtime_libpod.go index 162025969..f76fab4ea 100644 --- a/pkg/domain/infra/runtime_libpod.go +++ b/pkg/domain/infra/runtime_libpod.go @@ -5,6 +5,7 @@ package infra import ( "context" + "errors" "fmt" "os" "os/signal" @@ -18,7 +19,6 @@ import ( "github.com/containers/podman/v4/pkg/rootless" "github.com/containers/storage/pkg/idtools" "github.com/containers/storage/types" - "github.com/pkg/errors" "github.com/sirupsen/logrus" flag "github.com/spf13/pflag" ) @@ -316,7 +316,7 @@ func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []strin uids, gids, err := rootless.GetConfiguredMappings() if err != nil { - return nil, errors.Wrapf(err, "cannot read mappings") + return nil, fmt.Errorf("cannot read mappings: %w", err) } maxUID, maxGID := 0, 0 for _, u := range uids { diff --git a/pkg/domain/infra/tunnel/auto-update.go b/pkg/domain/infra/tunnel/auto-update.go index ba41f0378..469da5a7a 100644 --- a/pkg/domain/infra/tunnel/auto-update.go +++ b/pkg/domain/infra/tunnel/auto-update.go @@ -2,9 +2,9 @@ package tunnel import ( "context" + "errors" "github.com/containers/podman/v4/pkg/domain/entities" - "github.com/pkg/errors" ) func (ic *ContainerEngine) AutoUpdate(ctx context.Context, options entities.AutoUpdateOptions) ([]*entities.AutoUpdateReport, []error) { diff --git a/pkg/domain/infra/tunnel/events.go b/pkg/domain/infra/tunnel/events.go index b472ad03a..30be92e23 100644 --- a/pkg/domain/infra/tunnel/events.go +++ b/pkg/domain/infra/tunnel/events.go @@ -2,13 +2,12 @@ package tunnel import ( "context" + "fmt" "strings" "github.com/containers/podman/v4/libpod/events" "github.com/containers/podman/v4/pkg/bindings/system" "github.com/containers/podman/v4/pkg/domain/entities" - - "github.com/pkg/errors" ) func (ic *ContainerEngine) Events(ctx context.Context, opts entities.EventsOptions) error { @@ -17,7 +16,7 @@ func (ic *ContainerEngine) Events(ctx context.Context, opts entities.EventsOptio for _, filter := range opts.Filter { split := strings.Split(filter, "=") if len(split) < 2 { - return errors.Errorf("invalid filter %q", filter) + return fmt.Errorf("invalid filter %q", filter) } filters[split[0]] = append(filters[split[0]], strings.Join(split[1:], "=")) } @@ -56,7 +55,7 @@ func (ic *ContainerEngine) GetLastContainerEvent(ctx context.Context, nameOrID s return nil, err } if len(containerEvents) < 1 { - return nil, errors.Wrapf(events.ErrEventNotFound, "%s not found", containerEvent.String()) + return nil, fmt.Errorf("%s not found: %w", containerEvent.String(), events.ErrEventNotFound) } // return the last element in the slice return containerEvents[len(containerEvents)-1], nil diff --git a/pkg/domain/infra/tunnel/helpers.go b/pkg/domain/infra/tunnel/helpers.go index 6c043465c..24b2b619d 100644 --- a/pkg/domain/infra/tunnel/helpers.go +++ b/pkg/domain/infra/tunnel/helpers.go @@ -2,13 +2,14 @@ package tunnel import ( "context" + "errors" + "fmt" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/bindings/containers" "github.com/containers/podman/v4/pkg/bindings/pods" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/errorhandling" - "github.com/pkg/errors" ) // FIXME: the `ignore` parameter is very likely wrong here as it should rather @@ -69,7 +70,7 @@ func getContainersAndInputByContext(contextWithConnection context.Context, all, } if !found && !ignore { - return nil, nil, errors.Wrapf(define.ErrNoSuchCtr, "unable to find container %q", nameOrID) + return nil, nil, fmt.Errorf("unable to find container %q: %w", nameOrID, define.ErrNoSuchCtr) } } return filtered, rawInputs, nil @@ -102,7 +103,7 @@ func getPodsByContext(contextWithConnection context.Context, all bool, namesOrID inspectData, err := pods.Inspect(contextWithConnection, nameOrID, nil) if err != nil { if errorhandling.Contains(err, define.ErrNoSuchPod) { - return nil, errors.Wrapf(define.ErrNoSuchPod, "unable to find pod %q", nameOrID) + return nil, fmt.Errorf("unable to find pod %q: %w", nameOrID, define.ErrNoSuchPod) } return nil, err } @@ -120,7 +121,7 @@ func getPodsByContext(contextWithConnection context.Context, all bool, namesOrID } if !found { - return nil, errors.Wrapf(define.ErrNoSuchPod, "unable to find pod %q", nameOrID) + return nil, fmt.Errorf("unable to find pod %q: %w", nameOrID, define.ErrNoSuchPod) } } return filtered, nil diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index 09f8ac4c3..18f750dcc 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -2,6 +2,7 @@ package tunnel import ( "context" + "errors" "fmt" "io/ioutil" "os" @@ -19,7 +20,6 @@ import ( "github.com/containers/podman/v4/pkg/domain/utils" "github.com/containers/podman/v4/pkg/errorhandling" utils2 "github.com/containers/podman/v4/utils" - "github.com/pkg/errors" ) func (ir *ImageEngine) Exists(_ context.Context, nameOrID string) (*entities.BoolReport, error) { @@ -131,7 +131,7 @@ func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, ) ref, err := reference.Parse(newTag) if err != nil { - return errors.Wrapf(err, "error parsing reference %q", newTag) + return fmt.Errorf("error parsing reference %q: %w", newTag, err) } if t, ok := ref.(reference.Tagged); ok { tag = t.Tag() @@ -140,7 +140,7 @@ func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, repo = r.Name() } if len(repo) < 1 { - return errors.Errorf("invalid image name %q", nameOrID) + return fmt.Errorf("invalid image name %q", nameOrID) } if err := images.Tag(ir.ClientCtx, nameOrID, tag, repo, options); err != nil { return err @@ -161,7 +161,7 @@ func (ir *ImageEngine) Untag(ctx context.Context, nameOrID string, tags []string ) ref, err := reference.Parse(newTag) if err != nil { - return errors.Wrapf(err, "error parsing reference %q", newTag) + return fmt.Errorf("error parsing reference %q: %w", newTag, err) } if t, ok := ref.(reference.Tagged); ok { tag = t.Tag() @@ -173,7 +173,7 @@ func (ir *ImageEngine) Untag(ctx context.Context, nameOrID string, tags []string repo = r.Name() } if len(repo) < 1 { - return errors.Errorf("invalid image name %q", nameOrID) + return fmt.Errorf("invalid image name %q", nameOrID) } if err := images.Untag(ir.ClientCtx, nameOrID, tag, repo, options); err != nil { return err @@ -194,7 +194,7 @@ func (ir *ImageEngine) Inspect(ctx context.Context, namesOrIDs []string, opts en return nil, nil, err } if errModel.ResponseCode == 404 { - errs = append(errs, errors.Wrapf(err, "unable to inspect %q", i)) + errs = append(errs, fmt.Errorf("unable to inspect %q: %w", i, err)) continue } return nil, nil, err @@ -215,7 +215,7 @@ func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions) return nil, err } if fInfo.IsDir() { - return nil, errors.Errorf("remote client supports archives only but %q is a directory", opts.Input) + return nil, fmt.Errorf("remote client supports archives only but %q is a directory", opts.Input) } return images.Load(ir.ClientCtx, f) } @@ -296,7 +296,7 @@ func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string, switch { case err == nil: if info.Mode().IsRegular() { - return errors.Errorf("%q already exists as a regular file", opts.Output) + return fmt.Errorf("%q already exists as a regular file", opts.Output) } case os.IsNotExist(err): if err := os.Mkdir(opts.Output, 0755); err != nil { diff --git a/pkg/domain/infra/tunnel/manifest.go b/pkg/domain/infra/tunnel/manifest.go index 09c37b896..d2554f198 100644 --- a/pkg/domain/infra/tunnel/manifest.go +++ b/pkg/domain/infra/tunnel/manifest.go @@ -3,6 +3,7 @@ package tunnel import ( "context" "encoding/json" + "errors" "fmt" "strings" @@ -10,7 +11,6 @@ import ( "github.com/containers/podman/v4/pkg/bindings/images" "github.com/containers/podman/v4/pkg/bindings/manifests" "github.com/containers/podman/v4/pkg/domain/entities" - "github.com/pkg/errors" ) // ManifestCreate implements manifest create via ImageEngine @@ -18,7 +18,7 @@ func (ir *ImageEngine) ManifestCreate(ctx context.Context, name string, images [ options := new(manifests.CreateOptions).WithAll(opts.All) imageID, err := manifests.Create(ir.ClientCtx, name, images, options) if err != nil { - return imageID, errors.Wrapf(err, "error creating manifest") + return imageID, fmt.Errorf("error creating manifest: %w", err) } return imageID, err } @@ -36,12 +36,12 @@ func (ir *ImageEngine) ManifestExists(ctx context.Context, name string) (*entiti func (ir *ImageEngine) ManifestInspect(_ context.Context, name string) ([]byte, error) { list, err := manifests.Inspect(ir.ClientCtx, name, nil) if err != nil { - return nil, errors.Wrapf(err, "error getting content of manifest list or image %s", name) + return nil, fmt.Errorf("error getting content of manifest list or image %s: %w", name, err) } buf, err := json.MarshalIndent(list, "", " ") if err != nil { - return buf, errors.Wrapf(err, "error rendering manifest for display") + return buf, fmt.Errorf("error rendering manifest for display: %w", err) } return buf, err } @@ -56,7 +56,7 @@ func (ir *ImageEngine) ManifestAdd(_ context.Context, name string, imageNames [] for _, annotationSpec := range opts.Annotation { spec := strings.SplitN(annotationSpec, "=", 2) if len(spec) != 2 { - return "", errors.Errorf("no value given for annotation %q", spec[0]) + return "", fmt.Errorf("no value given for annotation %q", spec[0]) } annotations[spec[0]] = spec[1] } @@ -72,7 +72,7 @@ func (ir *ImageEngine) ManifestAdd(_ context.Context, name string, imageNames [] id, err := manifests.Add(ir.ClientCtx, name, options) if err != nil { - return id, errors.Wrapf(err, "error adding to manifest list %s", name) + return id, fmt.Errorf("error adding to manifest list %s: %w", name, err) } return id, nil } @@ -86,7 +86,7 @@ func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, name, images string func (ir *ImageEngine) ManifestRemoveDigest(ctx context.Context, name string, image string) (string, error) { updatedListID, err := manifests.Remove(ir.ClientCtx, name, image, nil) if err != nil { - return updatedListID, errors.Wrapf(err, "error removing from manifest %s", name) + return updatedListID, fmt.Errorf("error removing from manifest %s: %w", name, err) } return fmt.Sprintf("%s :%s\n", updatedListID, image), nil } diff --git a/pkg/domain/infra/tunnel/network.go b/pkg/domain/infra/tunnel/network.go index 415999a96..6e27b8e56 100644 --- a/pkg/domain/infra/tunnel/network.go +++ b/pkg/domain/infra/tunnel/network.go @@ -2,13 +2,14 @@ package tunnel import ( "context" + "errors" + "fmt" "github.com/containers/common/libnetwork/types" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/bindings/network" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/errorhandling" - "github.com/pkg/errors" ) func (ic *ContainerEngine) NetworkList(ctx context.Context, opts entities.NetworkListOptions) ([]types.Network, error) { @@ -30,7 +31,7 @@ func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []stri return nil, nil, err } if errModel.ResponseCode == 404 { - errs = append(errs, errors.Wrapf(define.ErrNoSuchNetwork, "network %s", name)) + errs = append(errs, fmt.Errorf("network %s: %w", name, define.ErrNoSuchNetwork)) continue } return nil, nil, err @@ -95,7 +96,7 @@ func (ic *ContainerEngine) NetworkExists(ctx context.Context, networkname string }, nil } -// Network prune removes unused cni networks +// Network prune removes unused networks func (ic *ContainerEngine) NetworkPrune(ctx context.Context, options entities.NetworkPruneOptions) ([]*entities.NetworkPruneReport, error) { opts := new(network.PruneOptions).WithFilters(options.Filters) return network.Prune(ic.ClientCtx, opts) diff --git a/pkg/domain/infra/tunnel/secrets.go b/pkg/domain/infra/tunnel/secrets.go index 2412ed0a2..d26718b12 100644 --- a/pkg/domain/infra/tunnel/secrets.go +++ b/pkg/domain/infra/tunnel/secrets.go @@ -2,12 +2,12 @@ package tunnel import ( "context" + "fmt" "io" "github.com/containers/podman/v4/pkg/bindings/secrets" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/errorhandling" - "github.com/pkg/errors" ) func (ic *ContainerEngine) SecretCreate(ctx context.Context, name string, reader io.Reader, options entities.SecretCreateOptions) (*entities.SecretCreateReport, error) { @@ -33,7 +33,7 @@ func (ic *ContainerEngine) SecretInspect(ctx context.Context, nameOrIDs []string return nil, nil, err } if errModel.ResponseCode == 404 { - errs = append(errs, errors.Errorf("no such secret %q", name)) + errs = append(errs, fmt.Errorf("no such secret %q", name)) continue } return nil, nil, err @@ -73,7 +73,7 @@ func (ic *ContainerEngine) SecretRm(ctx context.Context, nameOrIDs []string, opt } if errModel.ResponseCode == 404 { allRm = append(allRm, &entities.SecretRmReport{ - Err: errors.Errorf("no secret with name or id %q: no such secret ", name), + Err: fmt.Errorf("no secret with name or id %q: no such secret ", name), ID: "", }) continue diff --git a/pkg/domain/infra/tunnel/volumes.go b/pkg/domain/infra/tunnel/volumes.go index 6ec35e836..b70d29783 100644 --- a/pkg/domain/infra/tunnel/volumes.go +++ b/pkg/domain/infra/tunnel/volumes.go @@ -2,12 +2,13 @@ package tunnel import ( "context" + "errors" + "fmt" "github.com/containers/podman/v4/pkg/bindings/volumes" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/domain/entities/reports" "github.com/containers/podman/v4/pkg/errorhandling" - "github.com/pkg/errors" ) func (ic *ContainerEngine) VolumeCreate(ctx context.Context, opts entities.VolumeCreateOptions) (*entities.IDOrNameResponse, error) { @@ -64,7 +65,7 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin return nil, nil, err } if errModel.ResponseCode == 404 { - errs = append(errs, errors.Errorf("no such volume %q", id)) + errs = append(errs, fmt.Errorf("no such volume %q", id)) continue } return nil, nil, err diff --git a/pkg/domain/utils/scp.go b/pkg/domain/utils/scp.go index a4ff6b950..3c73cddd1 100644 --- a/pkg/domain/utils/scp.go +++ b/pkg/domain/utils/scp.go @@ -20,7 +20,6 @@ import ( "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/terminal" "github.com/docker/distribution/reference" - "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/agent" @@ -44,7 +43,7 @@ func ExecuteTransfer(src, dst string, parentFlags []string, quiet bool) (*entiti confR, err := config.NewConfig("") // create a hand made config for the remote engine since we might use remote and native at once if err != nil { - return nil, nil, nil, nil, errors.Wrapf(err, "could not make config") + return nil, nil, nil, nil, fmt.Errorf("could not make config: %w", err) } cfg, err := config.ReadCustomConfig() // get ready to set ssh destination if necessary @@ -75,9 +74,9 @@ func ExecuteTransfer(src, dst string, parentFlags []string, quiet bool) (*entiti case len(locations) == 1: switch { case len(locations[0].Image) == 0: - return nil, nil, nil, nil, errors.Wrapf(define.ErrInvalidArg, "no source image specified") + return nil, nil, nil, nil, fmt.Errorf("no source image specified: %w", define.ErrInvalidArg) case len(locations[0].Image) > 0 && !locations[0].Remote && len(locations[0].User) == 0: // if we have podman image scp $IMAGE - return nil, nil, nil, nil, errors.Wrapf(define.ErrInvalidArg, "must specify a destination") + return nil, nil, nil, nil, fmt.Errorf("must specify a destination: %w", define.ErrInvalidArg) } } @@ -158,7 +157,7 @@ func ExecuteTransfer(src, dst string, parentFlags []string, quiet bool) (*entiti if source.User == "" { u, err := user.Current() if err != nil { - return nil, nil, nil, nil, errors.Wrapf(err, "could not obtain user, make sure the environmental variable $USER is set") + return nil, nil, nil, nil, fmt.Errorf("could not obtain user, make sure the environmental variable $USER is set: %w", err) } source.User = u.Username } @@ -231,11 +230,11 @@ func LoadToRemote(dest entities.ImageScpOptions, localFile string, tag string, u n, err := scpD.CopyTo(dial, localFile, remoteFile) if err != nil { errOut := strconv.Itoa(int(n)) + " Bytes copied before error" - return " ", "", errors.Wrapf(err, errOut) + return " ", "", fmt.Errorf("%v: %w", errOut, err) } var run string if tag != "" { - return "", "", errors.Wrapf(define.ErrInvalidArg, "Renaming of an image is currently not supported") + return "", "", fmt.Errorf("renaming of an image is currently not supported: %w", define.ErrInvalidArg) } podman := os.Args[0] run = podman + " image load --input=" + remoteFile + ";rm " + remoteFile // run ssh image load of the file copied via scp @@ -268,7 +267,7 @@ func SaveToRemote(image, localFile string, tag string, uri *url.URL, iden string defer dial.Close() if tag != "" { - return errors.Wrapf(define.ErrInvalidArg, "Renaming of an image is currently not supported") + return fmt.Errorf("renaming of an image is currently not supported: %w", define.ErrInvalidArg) } podman := os.Args[0] run := podman + " image save " + image + " --format=oci-archive --output=" + remoteFile // run ssh image load of the file copied via scp. Files are reverse in this case... @@ -282,7 +281,7 @@ func SaveToRemote(image, localFile string, tag string, uri *url.URL, iden string } if err != nil { errOut := strconv.Itoa(int(n)) + " Bytes copied before error" - return errors.Wrapf(err, errOut) + return fmt.Errorf("%v: %w", errOut, err) } return nil } @@ -307,7 +306,7 @@ func CreateConnection(url *url.URL, iden string) (*ssh.Client, string, error) { } dialAdd, err := ssh.Dial("tcp", url.Host, cfg) // dial the client if err != nil { - return nil, "", errors.Wrapf(err, "failed to connect") + return nil, "", fmt.Errorf("failed to connect: %w", err) } file, err := MakeRemoteFile(dialAdd) if err != nil { @@ -429,14 +428,14 @@ func ValidateImagePortion(location entities.ImageScpOptions, arg string) (entiti // validateSCPArgs takes the array of source and destination options and checks for common errors func ValidateSCPArgs(locations []*entities.ImageScpOptions) error { if len(locations) > 2 { - return errors.Wrapf(define.ErrInvalidArg, "cannot specify more than two arguments") + return fmt.Errorf("cannot specify more than two arguments: %w", define.ErrInvalidArg) } switch { case len(locations[0].Image) > 0 && len(locations[1].Image) > 0: locations[1].Tag = locations[1].Image locations[1].Image = "" case len(locations[0].Image) == 0 && len(locations[1].Image) == 0: - return errors.Wrapf(define.ErrInvalidArg, "a source image must be specified") + return fmt.Errorf("a source image must be specified: %w", define.ErrInvalidArg) } return nil } @@ -475,7 +474,7 @@ func ExecRemoteCommand(dial *ssh.Client, run string) ([]byte, error) { sess.Stdout = &buffer // output from client funneled into buffer sess.Stderr = &bufferErr // err form client funneled into buffer if err := sess.Run(run); err != nil { // run the command on the ssh client - return nil, errors.Wrapf(err, bufferErr.String()) + return nil, fmt.Errorf("%v: %w", bufferErr.String(), err) } return buffer.Bytes(), nil } @@ -488,12 +487,12 @@ func GetUserInfo(uri *url.URL) (*url.Userinfo, error) { if u, found := os.LookupEnv("_CONTAINERS_ROOTLESS_UID"); found { usr, err = user.LookupId(u) if err != nil { - return nil, errors.Wrapf(err, "failed to lookup rootless user") + return nil, fmt.Errorf("failed to lookup rootless user: %w", err) } } else { usr, err = user.Current() if err != nil { - return nil, errors.Wrapf(err, "failed to obtain current user") + return nil, fmt.Errorf("failed to obtain current user: %w", err) } } @@ -514,7 +513,7 @@ func ValidateAndConfigure(uri *url.URL, iden string) (*ssh.ClientConfig, error) value := iden s, err := terminal.PublicKey(value, []byte(passwd)) if err != nil { - return nil, errors.Wrapf(err, "failed to read identity %q", value) + return nil, fmt.Errorf("failed to read identity %q: %w", value, err) } signers = append(signers, s) logrus.Debugf("SSH Ident Key %q %s %s", value, ssh.FingerprintSHA256(s.PublicKey()), s.PublicKey().Type()) diff --git a/pkg/domain/utils/secrets_filters.go b/pkg/domain/utils/secrets_filters.go index e76bc592f..ab9b681ec 100644 --- a/pkg/domain/utils/secrets_filters.go +++ b/pkg/domain/utils/secrets_filters.go @@ -1,11 +1,11 @@ package utils import ( + "fmt" "strings" "github.com/containers/common/pkg/secrets" "github.com/containers/podman/v4/pkg/util" - "github.com/pkg/errors" ) func IfPassesSecretsFilter(s secrets.Secret, filters map[string][]string) (bool, error) { @@ -17,7 +17,7 @@ func IfPassesSecretsFilter(s secrets.Secret, filters map[string][]string) (bool, case "id": result = util.StringMatchRegexSlice(s.ID, filterValues) default: - return false, errors.Errorf("invalid filter %q", key) + return false, fmt.Errorf("invalid filter %q", key) } } return result, nil diff --git a/pkg/env/env.go b/pkg/env/env.go index 5989d0da5..8af9fd77c 100644 --- a/pkg/env/env.go +++ b/pkg/env/env.go @@ -8,8 +8,6 @@ import ( "fmt" "os" "strings" - - "github.com/pkg/errors" ) const whiteSpaces = " \t" @@ -56,7 +54,7 @@ func ParseFile(path string) (_ map[string]string, err error) { env := make(map[string]string) defer func() { if err != nil { - err = errors.Wrapf(err, "error parsing env file %q", path) + err = fmt.Errorf("error parsing env file %q: %w", path, err) } }() @@ -85,12 +83,12 @@ func parseEnv(env map[string]string, line string) error { // catch invalid variables such as "=" or "=A" if data[0] == "" { - return errors.Errorf("invalid environment variable: %q", line) + return fmt.Errorf("invalid environment variable: %q", line) } // trim the front of a variable, but nothing else name := strings.TrimLeft(data[0], whiteSpaces) if strings.ContainsAny(name, whiteSpaces) { - return errors.Errorf("name %q has white spaces, poorly formatted name", name) + return fmt.Errorf("name %q has white spaces, poorly formatted name", name) } if len(data) > 1 { diff --git a/pkg/hooks/1.0.0/hook.go b/pkg/hooks/1.0.0/hook.go index 244e8800f..71f940a64 100644 --- a/pkg/hooks/1.0.0/hook.go +++ b/pkg/hooks/1.0.0/hook.go @@ -3,12 +3,12 @@ package hook import ( "encoding/json" + "errors" "fmt" "os" "regexp" rspec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" ) // Version is the hook configuration version defined in this package. @@ -50,16 +50,16 @@ func (hook *Hook) Validate(extensionStages []string) (err error) { for key, value := range hook.When.Annotations { if _, err = regexp.Compile(key); err != nil { - return errors.Wrapf(err, "invalid annotation key %q", key) + return fmt.Errorf("invalid annotation key %q: %w", key, err) } if _, err = regexp.Compile(value); err != nil { - return errors.Wrapf(err, "invalid annotation value %q", value) + return fmt.Errorf("invalid annotation value %q: %w", value, err) } } for _, command := range hook.When.Commands { if _, err = regexp.Compile(command); err != nil { - return errors.Wrapf(err, "invalid command %q", command) + return fmt.Errorf("invalid command %q: %w", command, err) } } diff --git a/pkg/hooks/1.0.0/when.go b/pkg/hooks/1.0.0/when.go index a65af048e..a1351890f 100644 --- a/pkg/hooks/1.0.0/when.go +++ b/pkg/hooks/1.0.0/when.go @@ -1,10 +1,11 @@ package hook import ( + "errors" + "fmt" "regexp" rspec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" ) // When holds hook-injection conditions. @@ -52,12 +53,12 @@ func (when *When) Match(config *rspec.Spec, annotations map[string]string, hasBi for key, value := range annotations { match, err = regexp.MatchString(keyPattern, key) if err != nil { - return false, errors.Wrap(err, "annotation key") + return false, fmt.Errorf("annotation key: %w", err) } if match { match, err = regexp.MatchString(valuePattern, value) if err != nil { - return false, errors.Wrap(err, "annotation value") + return false, fmt.Errorf("annotation value: %w", err) } if match { break @@ -82,7 +83,7 @@ func (when *When) Match(config *rspec.Spec, annotations map[string]string, hasBi for _, cmdPattern := range when.Commands { match, err := regexp.MatchString(cmdPattern, command) if err != nil { - return false, errors.Wrap(err, "command") + return false, fmt.Errorf("command: %w", err) } if match { return true, nil diff --git a/pkg/hooks/exec/exec.go b/pkg/hooks/exec/exec.go index 2b7bc5f31..bc639245f 100644 --- a/pkg/hooks/exec/exec.go +++ b/pkg/hooks/exec/exec.go @@ -10,7 +10,6 @@ import ( "time" rspec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -46,7 +45,7 @@ func Run(ctx context.Context, hook *rspec.Hook, state []byte, stdout io.Writer, go func() { err := cmd.Wait() if err != nil { - err = errors.Wrapf(err, "executing %v", cmd.Args) + err = fmt.Errorf("executing %v: %w", cmd.Args, err) } exit <- err }() diff --git a/pkg/hooks/exec/runtimeconfigfilter.go b/pkg/hooks/exec/runtimeconfigfilter.go index 3ab3073b2..72d4b8979 100644 --- a/pkg/hooks/exec/runtimeconfigfilter.go +++ b/pkg/hooks/exec/runtimeconfigfilter.go @@ -4,12 +4,12 @@ import ( "bytes" "context" "encoding/json" + "fmt" "reflect" "time" "github.com/davecgh/go-spew/spew" spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" "github.com/pmezard/go-difflib/difflib" "github.com/sirupsen/logrus" ) @@ -43,7 +43,7 @@ func RuntimeConfigFilter(ctx context.Context, hooks []spec.Hook, config *spec.Sp err = json.Unmarshal(data, &newConfig) if err != nil { logrus.Debugf("invalid JSON from config-filter hook %d:\n%s", i, string(data)) - return nil, errors.Wrapf(err, "unmarshal output from config-filter hook %d", i) + return nil, fmt.Errorf("unmarshal output from config-filter hook %d: %w", i, err) } if !reflect.DeepEqual(config, &newConfig) { diff --git a/pkg/hooks/hooks.go b/pkg/hooks/hooks.go index ecd5b7dfc..14f98b1de 100644 --- a/pkg/hooks/hooks.go +++ b/pkg/hooks/hooks.go @@ -11,7 +11,6 @@ import ( current "github.com/containers/podman/v4/pkg/hooks/1.0.0" rspec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -105,7 +104,7 @@ func (m *Manager) Hooks(config *rspec.Spec, annotations map[string]string, hasBi for _, namedHook := range hooks { match, err := namedHook.hook.When.Match(config, annotations, hasBindMounts) if err != nil { - return extensionStageHooks, errors.Wrapf(err, "matching hook %q", namedHook.name) + return extensionStageHooks, fmt.Errorf("matching hook %q: %w", namedHook.name, err) } if match { logrus.Debugf("hook %s matched; adding to stages %v", namedHook.name, namedHook.hook.Stages) diff --git a/pkg/hooks/read.go b/pkg/hooks/read.go index c48d51f7d..379ed67ef 100644 --- a/pkg/hooks/read.go +++ b/pkg/hooks/read.go @@ -3,6 +3,8 @@ package hooks import ( "encoding/json" + "errors" + "fmt" "io/ioutil" "os" "path/filepath" @@ -10,7 +12,6 @@ import ( old "github.com/containers/podman/v4/pkg/hooks/0.1.0" current "github.com/containers/podman/v4/pkg/hooks/1.0.0" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -36,7 +37,7 @@ func Read(path string, extensionStages []string) (*current.Hook, error) { } hook, err := read(content) if err != nil { - return nil, errors.Wrapf(err, "parsing hook %q", path) + return nil, fmt.Errorf("parsing hook %q: %w", path, err) } err = hook.Validate(extensionStages) return hook, err @@ -45,16 +46,16 @@ func Read(path string, extensionStages []string) (*current.Hook, error) { func read(content []byte) (hook *current.Hook, err error) { var ver version if err := json.Unmarshal(content, &ver); err != nil { - return nil, errors.Wrap(err, "version check") + return nil, fmt.Errorf("version check: %w", err) } reader, ok := Readers[ver.Version] if !ok { - return nil, errors.Errorf("unrecognized hook version: %q", ver.Version) + return nil, fmt.Errorf("unrecognized hook version: %q", ver.Version) } hook, err = reader(content) if err != nil { - return hook, errors.Wrap(err, ver.Version) + return hook, fmt.Errorf("%v: %w", ver.Version, err) } return hook, err } @@ -83,7 +84,7 @@ func ReadDir(path string, extensionStages []string, hooks map[string]*current.Ho if res == nil { res = err } else { - res = errors.Wrapf(res, "%v", err) + res = fmt.Errorf("%v: %w", err, res) } continue } diff --git a/pkg/kubeutils/LICENSE b/pkg/kubeutils/LICENSE deleted file mode 100644 index 9b259bdfc..000000000 --- a/pkg/kubeutils/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - https://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/pkg/machine/config.go b/pkg/machine/config.go index fcc129338..29cd7bc00 100644 --- a/pkg/machine/config.go +++ b/pkg/machine/config.go @@ -4,7 +4,7 @@ package machine import ( - errors2 "errors" + "errors" "io/ioutil" "net" "net/url" @@ -13,7 +13,6 @@ import ( "time" "github.com/containers/storage/pkg/homedir" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -55,6 +54,7 @@ type Provider interface { IsValidVMName(name string) (bool, error) CheckExclusiveActiveVM() (bool, string, error) RemoveAndCleanMachines() error + VMType() string } type RemoteConnectionType string @@ -255,11 +255,11 @@ func (m *VMFile) GetPath() string { // the actual path func (m *VMFile) Delete() error { if m.Symlink != nil { - if err := os.Remove(*m.Symlink); err != nil && !errors2.Is(err, os.ErrNotExist) { + if err := os.Remove(*m.Symlink); err != nil && !errors.Is(err, os.ErrNotExist) { logrus.Errorf("unable to remove symlink %q", *m.Symlink) } } - if err := os.Remove(m.Path); err != nil && !errors2.Is(err, os.ErrNotExist) { + if err := os.Remove(m.Path); err != nil && !errors.Is(err, os.ErrNotExist) { return err } return nil @@ -273,14 +273,14 @@ func (m *VMFile) Read() ([]byte, error) { // NewMachineFile is a constructor for VMFile func NewMachineFile(path string, symlink *string) (*VMFile, error) { if len(path) < 1 { - return nil, errors2.New("invalid machine file path") + return nil, errors.New("invalid machine file path") } if symlink != nil && len(*symlink) < 1 { - return nil, errors2.New("invalid symlink path") + return nil, errors.New("invalid symlink path") } mf := VMFile{Path: path} if symlink != nil && len(path) > maxSocketPathLength { - if err := mf.makeSymlink(symlink); err != nil && !errors2.Is(err, os.ErrExist) { + if err := mf.makeSymlink(symlink); err != nil && !errors.Is(err, os.ErrExist) { return nil, err } } @@ -296,7 +296,7 @@ func (m *VMFile) makeSymlink(symlink *string) error { } sl := filepath.Join(homeDir, ".podman", *symlink) // make the symlink dir and throw away if it already exists - if err := os.MkdirAll(filepath.Dir(sl), 0700); err != nil && !errors2.Is(err, os.ErrNotExist) { + if err := os.MkdirAll(filepath.Dir(sl), 0700); err != nil && !errors.Is(err, os.ErrNotExist) { return err } m.Symlink = &sl diff --git a/pkg/machine/connection.go b/pkg/machine/connection.go index 841b2afa6..6ff761a92 100644 --- a/pkg/machine/connection.go +++ b/pkg/machine/connection.go @@ -4,10 +4,10 @@ package machine import ( + "errors" "fmt" "github.com/containers/common/pkg/config" - "github.com/pkg/errors" ) func AddConnection(uri fmt.Stringer, name, identity string, isDefault bool) error { @@ -72,7 +72,7 @@ func RemoveConnection(name string) error { if _, ok := cfg.Engine.ServiceDestinations[name]; ok { delete(cfg.Engine.ServiceDestinations, name) } else { - return errors.Errorf("unable to find connection named %q", name) + return fmt.Errorf("unable to find connection named %q", name) } return cfg.Write() } diff --git a/pkg/machine/e2e/config.go b/pkg/machine/e2e/config.go index 248a2f0ad..b3fe74b0c 100644 --- a/pkg/machine/e2e/config.go +++ b/pkg/machine/e2e/config.go @@ -3,7 +3,6 @@ package e2e import ( "encoding/json" "fmt" - "math/rand" "os" "os/exec" "path/filepath" @@ -13,6 +12,7 @@ import ( "github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/pkg/machine/qemu" "github.com/containers/podman/v4/pkg/util" + "github.com/containers/storage/pkg/stringid" . "github.com/onsi/ginkgo" //nolint:golint,stylecheck . "github.com/onsi/gomega" "github.com/onsi/gomega/gexec" @@ -136,14 +136,14 @@ func (m *machineTestBuilder) setTimeout(timeout time.Duration) *machineTestBuild // toQemuInspectInfo is only for inspecting qemu machines. Other providers will need // to make their own. -func (mb *machineTestBuilder) toQemuInspectInfo() ([]qemuMachineInspectInfo, int, error) { +func (mb *machineTestBuilder) toQemuInspectInfo() ([]machine.InspectInfo, int, error) { args := []string{"machine", "inspect"} args = append(args, mb.names...) session, err := runWrapper(mb.podmanBinary, args, defaultTimeout, true) if err != nil { return nil, -1, err } - mii := []qemuMachineInspectInfo{} + mii := []machine.InspectInfo{} err = json.Unmarshal(session.Bytes(), &mii) return mii, session.ExitCode(), err } @@ -179,10 +179,5 @@ func (m *machineTestBuilder) init() {} // randomString returns a string of given length composed of random characters func randomString(n int) string { - var randomLetters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") - b := make([]rune, n) - for i := range b { - b[i] = randomLetters[rand.Intn(len(randomLetters))] - } - return string(b) + return stringid.GenerateRandomID()[0:12] } diff --git a/pkg/machine/e2e/config_info.go b/pkg/machine/e2e/config_info.go new file mode 100644 index 000000000..410c7e518 --- /dev/null +++ b/pkg/machine/e2e/config_info.go @@ -0,0 +1,20 @@ +package e2e + +type infoMachine struct { + format string + cmd []string +} + +func (i *infoMachine) buildCmd(m *machineTestBuilder) []string { + cmd := []string{"machine", "info"} + if len(i.format) > 0 { + cmd = append(cmd, "--format", i.format) + } + i.cmd = cmd + return cmd +} + +func (i *infoMachine) withFormat(format string) *infoMachine { + i.format = format + return i +} diff --git a/pkg/machine/e2e/info_test.go b/pkg/machine/e2e/info_test.go new file mode 100644 index 000000000..eeabb78af --- /dev/null +++ b/pkg/machine/e2e/info_test.go @@ -0,0 +1,58 @@ +package e2e + +import ( + "github.com/containers/podman/v4/cmd/podman/machine" + jsoniter "github.com/json-iterator/go" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" +) + +var _ = Describe("podman machine info", func() { + var ( + mb *machineTestBuilder + testDir string + ) + + BeforeEach(func() { + testDir, mb = setup() + }) + AfterEach(func() { + teardown(originalHomeDir, testDir, mb) + }) + + It("machine info", func() { + info := new(infoMachine) + infoSession, err := mb.setCmd(info).run() + Expect(err).NotTo(HaveOccurred()) + Expect(infoSession).Should(Exit(0)) + + // Verify go template works and check for no running machines + info = new(infoMachine) + infoSession, err = mb.setCmd(info.withFormat("{{.Host.NumberOfMachines}}")).run() + Expect(err).NotTo(HaveOccurred()) + Expect(infoSession).Should(Exit(0)) + Expect(infoSession.outputToString()).To(Equal("0")) + + // Create a machine and check if info has been updated + i := new(initMachine) + initSession, err := mb.setCmd(i.withImagePath(mb.imagePath)).run() + Expect(err).To(BeNil()) + Expect(initSession).To(Exit(0)) + + info = new(infoMachine) + infoSession, err = mb.setCmd(info.withFormat("{{.Host.NumberOfMachines}}")).run() + Expect(err).NotTo(HaveOccurred()) + Expect(infoSession).Should(Exit(0)) + Expect(infoSession.outputToString()).To(Equal("1")) + + // Check if json is in correct format + infoSession, err = mb.setCmd(info.withFormat("json")).run() + Expect(err).NotTo(HaveOccurred()) + Expect(infoSession).Should(Exit(0)) + + infoReport := &machine.Info{} + err = jsoniter.Unmarshal(infoSession.Bytes(), infoReport) + Expect(err).To(BeNil()) + }) +}) diff --git a/pkg/machine/e2e/init_test.go b/pkg/machine/e2e/init_test.go index 6949eb0af..40f140cae 100644 --- a/pkg/machine/e2e/init_test.go +++ b/pkg/machine/e2e/init_test.go @@ -3,6 +3,7 @@ package e2e import ( "io/ioutil" "os" + "runtime" "time" "github.com/containers/podman/v4/pkg/machine" @@ -44,9 +45,9 @@ var _ = Describe("podman machine init", func() { Expect(len(inspectBefore)).To(BeNumerically(">", 0)) testMachine := inspectBefore[0] - Expect(testMachine.VM.Name).To(Equal(mb.names[0])) - Expect(testMachine.VM.CPUs).To(Equal(uint64(1))) - Expect(testMachine.VM.Memory).To(Equal(uint64(2048))) + Expect(testMachine.Name).To(Equal(mb.names[0])) + Expect(testMachine.Resources.CPUs).To(Equal(uint64(1))) + Expect(testMachine.Resources.Memory).To(Equal(uint64(2048))) }) @@ -61,7 +62,7 @@ var _ = Describe("podman machine init", func() { Expect(len(inspectBefore)).To(BeNumerically(">", 0)) Expect(err).To(BeNil()) Expect(len(inspectBefore)).To(BeNumerically(">", 0)) - Expect(inspectBefore[0].VM.Name).To(Equal(mb.names[0])) + Expect(inspectBefore[0].Name).To(Equal(mb.names[0])) s := startMachine{} ssession, err := mb.setCmd(s).setTimeout(time.Minute * 10).run() @@ -104,7 +105,15 @@ var _ = Describe("podman machine init", func() { memorySession, err := mb.setName(name).setCmd(sshMemory.withSSHComand([]string{"cat", "/proc/meminfo", "|", "numfmt", "--field", "2", "--from-unit=Ki", "--to-unit=Mi", "|", "sed", "'s/ kB/M/g'", "|", "grep", "MemTotal"})).run() Expect(err).To(BeNil()) Expect(memorySession).To(Exit(0)) - Expect(memorySession.outputToString()).To(ContainSubstring("3824")) + switch runtime.GOOS { + // os's handle memory differently + case "linux": + Expect(memorySession.outputToString()).To(ContainSubstring("3821")) + case "darwin": + Expect(memorySession.outputToString()).To(ContainSubstring("3824")) + default: + // add windows when testing on that platform + } sshTimezone := sshMachine{} timezoneSession, err := mb.setName(name).setCmd(sshTimezone.withSSHComand([]string{"date"})).run() diff --git a/pkg/machine/e2e/inspect_test.go b/pkg/machine/e2e/inspect_test.go index cdf13bb1a..93fb8cc2b 100644 --- a/pkg/machine/e2e/inspect_test.go +++ b/pkg/machine/e2e/inspect_test.go @@ -1,11 +1,9 @@ package e2e import ( - "encoding/json" "strings" "github.com/containers/podman/v4/pkg/machine" - "github.com/containers/podman/v4/pkg/machine/qemu" jsoniter "github.com/json-iterator/go" . "github.com/onsi/ginkgo" @@ -51,24 +49,6 @@ var _ = Describe("podman machine stop", func() { Expect(err).To(BeNil()) Expect(inspectSession).To(Exit(0)) Expect(inspectSession.Bytes()).To(ContainSubstring("foo1")) - - type fakeInfos struct { - Status string - VM qemu.MachineVM - } - infos := make([]fakeInfos, 0, 2) - err = json.Unmarshal(inspectSession.Bytes(), &infos) - Expect(err).ToNot(HaveOccurred()) - Expect(len(infos)).To(Equal(2)) - - // rm := new(rmMachine) - // // Must manually clean up due to multiple names - // for _, name := range []string{"foo1", "foo2"} { - // mb.setName(name).setCmd(rm.withForce()).run() - // mb.names = []string{} - // } - // mb.names = []string{} - }) It("inspect with go format", func() { diff --git a/pkg/machine/e2e/list_test.go b/pkg/machine/e2e/list_test.go index e2121e7bf..fb855c61e 100644 --- a/pkg/machine/e2e/list_test.go +++ b/pkg/machine/e2e/list_test.go @@ -2,9 +2,10 @@ package e2e import ( "strings" + "time" "github.com/containers/common/pkg/util" - "github.com/containers/podman/v4/cmd/podman/machine" + "github.com/containers/podman/v4/pkg/domain/entities" jsoniter "github.com/json-iterator/go" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -87,7 +88,7 @@ var _ = Describe("podman machine list", func() { startSession, err := mb.setCmd(s).runWithoutWait() Expect(err).To(BeNil()) l := new(listMachine) - for { // needs to be infinite because we need to check if running when inspect returns to avoid race conditions. + for i := 0; i < 30; i++ { listSession, err := mb.setCmd(l).run() Expect(listSession).To(Exit(0)) Expect(err).To(BeNil()) @@ -96,6 +97,7 @@ var _ = Describe("podman machine list", func() { } else { break } + time.Sleep(3 * time.Second) } Expect(startSession).To(Exit(0)) listSession, err := mb.setCmd(l).run() @@ -128,11 +130,11 @@ var _ = Describe("podman machine list", func() { // --format json list2 := new(listMachine) list2 = list2.withFormat("json") - listSession2, err := mb.setName("foo1").setCmd(list2).run() + listSession2, err := mb.setCmd(list2).run() Expect(err).To(BeNil()) Expect(listSession2).To(Exit(0)) - var listResponse []*machine.ListReporter + var listResponse []*entities.ListReporter err = jsoniter.Unmarshal(listSession.Bytes(), &listResponse) Expect(err).To(BeNil()) @@ -143,7 +145,6 @@ var _ = Describe("podman machine list", func() { Expect(listSession3).To(Exit(0)) listNames3 := listSession3.outputToStringSlice() Expect(listNames3).To(HaveLen(2)) - Expect(listNames3).To(ContainSubstring("NAME")) }) }) diff --git a/pkg/machine/e2e/machine_test.go b/pkg/machine/e2e/machine_test.go index 657014b05..7b063937d 100644 --- a/pkg/machine/e2e/machine_test.go +++ b/pkg/machine/e2e/machine_test.go @@ -22,7 +22,7 @@ func TestMain(m *testing.M) { } const ( - defaultStream string = "podman-testing" + defaultStream string = "testing" ) var ( @@ -97,6 +97,9 @@ func setup() (string, *machineTestBuilder) { if err := os.Setenv("HOME", homeDir); err != nil { Fail("failed to set home dir") } + if err := os.Setenv("XDG_RUNTIME_DIR", homeDir); err != nil { + Fail("failed to set xdg_runtime dir") + } if err := os.Unsetenv("SSH_AUTH_SOCK"); err != nil { Fail("unable to unset SSH_AUTH_SOCK") } @@ -120,9 +123,9 @@ func setup() (string, *machineTestBuilder) { } func teardown(origHomeDir string, testDir string, mb *machineTestBuilder) { - s := new(stopMachine) + r := new(rmMachine) for _, name := range mb.names { - if _, err := mb.setName(name).setCmd(s).run(); err != nil { + if _, err := mb.setName(name).setCmd(r.withForce()).run(); err != nil { fmt.Printf("error occurred rm'ing machine: %q\n", err) } } diff --git a/pkg/machine/e2e/set_test.go b/pkg/machine/e2e/set_test.go index 15215a44d..80cb89488 100644 --- a/pkg/machine/e2e/set_test.go +++ b/pkg/machine/e2e/set_test.go @@ -1,6 +1,8 @@ package e2e import ( + "runtime" + . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" . "github.com/onsi/gomega/gexec" @@ -57,8 +59,15 @@ var _ = Describe("podman machine set", func() { memorySession, err := mb.setName(name).setCmd(sshMemory.withSSHComand([]string{"cat", "/proc/meminfo", "|", "numfmt", "--field", "2", "--from-unit=Ki", "--to-unit=Mi", "|", "sed", "'s/ kB/M/g'", "|", "grep", "MemTotal"})).run() Expect(err).To(BeNil()) Expect(memorySession).To(Exit(0)) - Expect(memorySession.outputToString()).To(ContainSubstring("3824")) - + switch runtime.GOOS { + // it seems macos and linux handle memory differently + case "linux": + Expect(memorySession.outputToString()).To(ContainSubstring("3821")) + case "darwin": + Expect(memorySession.outputToString()).To(ContainSubstring("3824")) + default: + // windows can go here if we ever run tests there + } // Setting a running machine results in 125 runner, err := mb.setName(name).setCmd(set.withCPUs(4)).run() Expect(err).To(BeNil()) diff --git a/pkg/machine/fcos.go b/pkg/machine/fcos.go index 59ef6d975..4ccb99e96 100644 --- a/pkg/machine/fcos.go +++ b/pkg/machine/fcos.go @@ -17,7 +17,6 @@ import ( "github.com/coreos/stream-metadata-go/fedoracoreos" "github.com/coreos/stream-metadata-go/release" "github.com/coreos/stream-metadata-go/stream" - "github.com/pkg/errors" digest "github.com/opencontainers/go-digest" "github.com/sirupsen/logrus" @@ -156,7 +155,7 @@ func GetFCOSDownload(imageStream string) (*FcosDownloadInfo, error) { case "stable": streamType = fedoracoreos.StreamStable default: - return nil, errors.Errorf("invalid stream %s: valid streams are `testing` and `stable`", imageStream) + return nil, fmt.Errorf("invalid stream %s: valid streams are `testing` and `stable`", imageStream) } streamurl := getStreamURL(streamType) resp, err := http.Get(streamurl.String()) diff --git a/pkg/machine/fedora.go b/pkg/machine/fedora.go index 14a173da6..46e450418 100644 --- a/pkg/machine/fedora.go +++ b/pkg/machine/fedora.go @@ -4,6 +4,7 @@ package machine import ( + "errors" "fmt" "io" "io/ioutil" @@ -13,7 +14,6 @@ import ( "path/filepath" "regexp" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -107,12 +107,12 @@ func getFedoraDownload(releaseStream string) (string, *url.URL, int64, error) { newLocation := rawURL + file downloadURL, err := url.Parse(newLocation) if err != nil { - return "", nil, -1, errors.Wrapf(err, "invalid URL generated from discovered Fedora file: %s", newLocation) + return "", nil, -1, fmt.Errorf("invalid URL generated from discovered Fedora file: %s: %w", newLocation, err) } resp, err := http.Head(newLocation) if err != nil { - return "", nil, -1, errors.Wrapf(err, "head request failed: %s", newLocation) + return "", nil, -1, fmt.Errorf("head request failed: %s: %w", newLocation, err) } _ = resp.Body.Close() diff --git a/pkg/machine/keys.go b/pkg/machine/keys.go index 463271427..0c7d7114e 100644 --- a/pkg/machine/keys.go +++ b/pkg/machine/keys.go @@ -4,6 +4,7 @@ package machine import ( + "errors" "fmt" "io" "io/ioutil" @@ -12,7 +13,6 @@ import ( "path/filepath" "strings" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index 879ed5109..6134e69e1 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -5,6 +5,7 @@ package qemu import ( "bufio" + "bytes" "context" "encoding/base64" "encoding/json" @@ -138,8 +139,10 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) { cmd = append(cmd, []string{ "-device", "virtio-serial", // qemu needs to establish the long name; other connections can use the symlink'd - "-chardev", "socket,path=" + vm.ReadySocket.Path + ",server=on,wait=off,id=" + vm.Name + "_ready", - "-device", "virtserialport,chardev=" + vm.Name + "_ready" + ",name=org.fedoraproject.port.0", + // Note both id and chardev start with an extra "a" because qemu requires that it + // starts with an letter but users can also use numbers + "-chardev", "socket,path=" + vm.ReadySocket.Path + ",server=on,wait=off,id=a" + vm.Name + "_ready", + "-device", "virtserialport,chardev=a" + vm.Name + "_ready" + ",name=org.fedoraproject.port.0", "-pidfile", vm.VMPidFilePath.GetPath()}...) vm.CmdLine = cmd return vm, nil @@ -318,6 +321,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { source := paths[0] target := source readonly := false + securityModel := "mapped-xattr" if len(paths) > 1 { target = paths[1] } @@ -325,18 +329,20 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { options := paths[2] volopts := strings.Split(options, ",") for _, o := range volopts { - switch o { - case "rw": + switch { + case o == "rw": readonly = false - case "ro": + case o == "ro": readonly = true + case strings.HasPrefix(o, "security_model="): + securityModel = strings.Split(o, "=")[1] default: fmt.Printf("Unknown option: %s\n", o) } } } if volumeType == VolumeTypeVirtfs { - virtfsOptions := fmt.Sprintf("local,path=%s,mount_tag=%s,security_model=mapped-xattr", source, tag) + virtfsOptions := fmt.Sprintf("local,path=%s,mount_tag=%s,security_model=%s", source, tag, securityModel) if readonly { virtfsOptions += ",readonly" } @@ -568,15 +574,25 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { files := []*os.File{dnr, dnw, dnw, fd} attr.Files = files logrus.Debug(v.CmdLine) - cmd := v.CmdLine + cmdLine := v.CmdLine // Disable graphic window when not in debug mode // Done in start, so we're not suck with the debug level we used on init if !logrus.IsLevelEnabled(logrus.DebugLevel) { - cmd = append(cmd, "-display", "none") + cmdLine = append(cmdLine, "-display", "none") } - _, err = os.StartProcess(v.CmdLine[0], cmd, attr) + stderrBuf := &bytes.Buffer{} + + cmd := &exec.Cmd{ + Args: cmdLine, + Path: cmdLine[0], + Stdin: dnr, + Stdout: dnw, + Stderr: stderrBuf, + ExtraFiles: []*os.File{fd}, + } + err = cmd.Start() if err != nil { // check if qemu was not found if !errors.Is(err, os.ErrNotExist) { @@ -587,15 +603,17 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { if err != nil { return err } - cmd[0], err = cfg.FindHelperBinary(QemuCommand, true) + cmdLine[0], err = cfg.FindHelperBinary(QemuCommand, true) if err != nil { return err } - _, err = os.StartProcess(cmd[0], cmd, attr) + cmd.Path = cmdLine[0] + err = cmd.Start() if err != nil { return fmt.Errorf("unable to execute %q: %w", cmd, err) } } + defer cmd.Process.Release() //nolint:errcheck fmt.Println("Waiting for VM ...") socketPath, err := getRuntimeDir() if err != nil { @@ -610,6 +628,16 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { if err == nil { break } + // check if qemu is still alive + var status syscall.WaitStatus + pid, err := syscall.Wait4(cmd.Process.Pid, &status, syscall.WNOHANG, nil) + if err != nil { + return fmt.Errorf("failed to read qemu process status: %w", err) + } + if pid > 0 { + // child exited + return fmt.Errorf("qemu exited unexpectedly with exit code %d, stderr: %s", status.ExitStatus(), stderrBuf.String()) + } time.Sleep(wait) wait++ } @@ -1217,7 +1245,10 @@ func (v *MachineVM) startHostNetworking() (string, apiForwardingState, error) { fmt.Println(cmd) } _, err = os.StartProcess(cmd[0], cmd, attr) - return forwardSock, state, fmt.Errorf("unable to execute: %q: %w", cmd, err) + if err != nil { + return "", 0, fmt.Errorf("unable to execute: %q: %w", cmd, err) + } + return forwardSock, state, nil } func (v *MachineVM) setupAPIForwarding(cmd []string) ([]string, string, apiForwardingState) { @@ -1698,6 +1729,9 @@ func isProcessAlive(pid int) bool { if err == nil || err == unix.EPERM { return true } - return false } + +func (p *Provider) VMType() string { + return vmtype +} diff --git a/pkg/machine/wsl/machine.go b/pkg/machine/wsl/machine.go index 04215d545..450d8b877 100644 --- a/pkg/machine/wsl/machine.go +++ b/pkg/machine/wsl/machine.go @@ -6,6 +6,7 @@ package wsl import ( "bufio" "encoding/json" + "errors" "fmt" "io" "io/fs" @@ -22,7 +23,6 @@ import ( "github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/utils" "github.com/containers/storage/pkg/homedir" - "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/text/encoding/unicode" "golang.org/x/text/transform" @@ -277,7 +277,7 @@ func readAndMigrate(configPath string, name string) (*MachineVM, error) { b, err := os.ReadFile(configPath) if err != nil { if errors.Is(err, os.ErrNotExist) { - return nil, errors.Wrap(machine.ErrNoSuchVM, name) + return nil, fmt.Errorf("%v: %w", name, machine.ErrNoSuchVM) } return vm, err } @@ -407,7 +407,7 @@ func (v *MachineVM) writeConfig() error { return err } if err := ioutil.WriteFile(jsonFile, b, 0644); err != nil { - return errors.Wrap(err, "could not write machine json config") + return fmt.Errorf("could not write machine json config: %w", err) } return nil @@ -445,38 +445,38 @@ func provisionWSLDist(v *MachineVM) (string, error) { distDir := filepath.Join(vmDataDir, "wsldist") distTarget := filepath.Join(distDir, v.Name) if err := os.MkdirAll(distDir, 0755); err != nil { - return "", errors.Wrap(err, "could not create wsldist directory") + return "", fmt.Errorf("could not create wsldist directory: %w", err) } dist := toDist(v.Name) fmt.Println("Importing operating system into WSL (this may take 5+ minutes on a new WSL install)...") if err = runCmdPassThrough("wsl", "--import", dist, distTarget, v.ImagePath); err != nil { - return "", errors.Wrap(err, "WSL import of guest OS failed") + return "", fmt.Errorf("the WSL import of guest OS failed: %w", err) } fmt.Println("Installing packages (this will take awhile)...") if err = runCmdPassThrough("wsl", "-d", dist, "dnf", "upgrade", "-y"); err != nil { - return "", errors.Wrap(err, "package upgrade on guest OS failed") + return "", fmt.Errorf("package upgrade on guest OS failed: %w", err) } fmt.Println("Enabling Copr") if err = runCmdPassThrough("wsl", "-d", dist, "dnf", "install", "-y", "'dnf-command(copr)'"); err != nil { - return "", errors.Wrap(err, "enabling copr failed") + return "", fmt.Errorf("enabling copr failed: %w", err) } fmt.Println("Enabling podman4 repo") if err = runCmdPassThrough("wsl", "-d", dist, "dnf", "-y", "copr", "enable", "rhcontainerbot/podman4"); err != nil { - return "", errors.Wrap(err, "enabling copr failed") + return "", fmt.Errorf("enabling copr failed: %w", err) } if err = runCmdPassThrough("wsl", "-d", dist, "dnf", "install", "podman", "podman-docker", "openssh-server", "procps-ng", "-y"); err != nil { - return "", errors.Wrap(err, "package installation on guest OS failed") + return "", fmt.Errorf("package installation on guest OS failed: %w", err) } // Fixes newuidmap if err = runCmdPassThrough("wsl", "-d", dist, "dnf", "reinstall", "shadow-utils", "-y"); err != nil { - return "", errors.Wrap(err, "package reinstallation of shadow-utils on guest OS failed") + return "", fmt.Errorf("package reinstallation of shadow-utils on guest OS failed: %w", err) } // Windows 11 (NT Version = 10, Build 22000) generates harmless but scary messages on every @@ -495,28 +495,28 @@ func createKeys(v *MachineVM, dist string, sshDir string) error { user := v.RemoteUsername if err := os.MkdirAll(sshDir, 0700); err != nil { - return errors.Wrap(err, "could not create ssh directory") + return fmt.Errorf("could not create ssh directory: %w", err) } if err := runCmdPassThrough("wsl", "--terminate", dist); err != nil { - return errors.Wrap(err, "could not cycle WSL dist") + return fmt.Errorf("could not cycle WSL dist: %w", err) } key, err := machine.CreateSSHKeysPrefix(sshDir, v.Name, true, true, "wsl", "-d", dist) if err != nil { - return errors.Wrap(err, "could not create ssh keys") + return fmt.Errorf("could not create ssh keys: %w", err) } if err := pipeCmdPassThrough("wsl", key+"\n", "-d", dist, "sh", "-c", "mkdir -p /root/.ssh;"+ "cat >> /root/.ssh/authorized_keys; chmod 600 /root/.ssh/authorized_keys"); err != nil { - return errors.Wrap(err, "could not create root authorized keys on guest OS") + return fmt.Errorf("could not create root authorized keys on guest OS: %w", err) } userAuthCmd := withUser("mkdir -p /home/[USER]/.ssh;"+ "cat >> /home/[USER]/.ssh/authorized_keys; chown -R [USER]:[USER] /home/[USER]/.ssh;"+ "chmod 600 /home/[USER]/.ssh/authorized_keys", user) if err := pipeCmdPassThrough("wsl", key+"\n", "-d", dist, "sh", "-c", userAuthCmd); err != nil { - return errors.Wrapf(err, "could not create '%s' authorized keys on guest OS", v.RemoteUsername) + return fmt.Errorf("could not create '%s' authorized keys on guest OS: %w", v.RemoteUsername, err) } return nil @@ -525,25 +525,25 @@ func createKeys(v *MachineVM, dist string, sshDir string) error { func configureSystem(v *MachineVM, dist string) error { user := v.RemoteUsername if err := runCmdPassThrough("wsl", "-d", dist, "sh", "-c", fmt.Sprintf(appendPort, v.Port, v.Port)); err != nil { - return errors.Wrap(err, "could not configure SSH port for guest OS") + return fmt.Errorf("could not configure SSH port for guest OS: %w", err) } if err := pipeCmdPassThrough("wsl", withUser(configServices, user), "-d", dist, "sh"); err != nil { - return errors.Wrap(err, "could not configure systemd settings for guest OS") + return fmt.Errorf("could not configure systemd settings for guest OS: %w", err) } if err := pipeCmdPassThrough("wsl", sudoers, "-d", dist, "sh", "-c", "cat >> /etc/sudoers"); err != nil { - return errors.Wrap(err, "could not add wheel to sudoers") + return fmt.Errorf("could not add wheel to sudoers: %w", err) } if err := pipeCmdPassThrough("wsl", overrideSysusers, "-d", dist, "sh", "-c", "cat > /etc/systemd/system/systemd-sysusers.service.d/override.conf"); err != nil { - return errors.Wrap(err, "could not generate systemd-sysusers override for guest OS") + return fmt.Errorf("could not generate systemd-sysusers override for guest OS: %w", err) } lingerCmd := withUser("cat > /home/[USER]/.config/systemd/[USER]/linger-example.service", user) if err := pipeCmdPassThrough("wsl", lingerService, "-d", dist, "sh", "-c", lingerCmd); err != nil { - return errors.Wrap(err, "could not generate linger service for guest OS") + return fmt.Errorf("could not generate linger service for guest OS: %w", err) } if err := enableUserLinger(v, dist); err != nil { @@ -551,15 +551,15 @@ func configureSystem(v *MachineVM, dist string) error { } if err := pipeCmdPassThrough("wsl", withUser(lingerSetup, user), "-d", dist, "sh"); err != nil { - return errors.Wrap(err, "could not configure systemd settomgs for guest OS") + return fmt.Errorf("could not configure systemd settomgs for guest OS: %w", err) } if err := pipeCmdPassThrough("wsl", containersConf, "-d", dist, "sh", "-c", "cat > /etc/containers/containers.conf"); err != nil { - return errors.Wrap(err, "could not create containers.conf for guest OS") + return fmt.Errorf("could not create containers.conf for guest OS: %w", err) } if err := runCmdPassThrough("wsl", "-d", dist, "sh", "-c", "echo wsl > /etc/containers/podman-machine"); err != nil { - return errors.Wrap(err, "could not create podman-machine file for guest OS") + return fmt.Errorf("could not create podman-machine file for guest OS: %w", err) } return nil @@ -584,7 +584,7 @@ func configureProxy(dist string, useProxy bool) error { if err := pipeCmdPassThrough("wsl", content, "-d", dist, "sh", "-c", proxyConfigAttempt); err != nil { const failMessage = "Failure creating proxy configuration" if exitErr, isExit := err.(*exec.ExitError); isExit && exitErr.ExitCode() != 42 { - return errors.Wrap(err, failMessage) + return fmt.Errorf("%v: %w", failMessage, err) } fmt.Println("Installing proxy support") @@ -592,7 +592,7 @@ func configureProxy(dist string, useProxy bool) error { "cat > /usr/local/bin/proxyinit; chmod 755 /usr/local/bin/proxyinit") if err = pipeCmdPassThrough("wsl", content, "-d", dist, "/usr/local/bin/proxyinit"); err != nil { - return errors.Wrap(err, failMessage) + return fmt.Errorf("%v: %w", failMessage, err) } } @@ -602,7 +602,7 @@ func configureProxy(dist string, useProxy bool) error { func enableUserLinger(v *MachineVM, dist string) error { lingerCmd := "mkdir -p /var/lib/systemd/linger; touch /var/lib/systemd/linger/" + v.RemoteUsername if err := runCmdPassThrough("wsl", "-d", dist, "sh", "-c", lingerCmd); err != nil { - return errors.Wrap(err, "could not enable linger for remote user on guest OS") + return fmt.Errorf("could not enable linger for remote user on guest OS: %w", err) } return nil @@ -611,26 +611,26 @@ func enableUserLinger(v *MachineVM, dist string) error { func installScripts(dist string) error { if err := pipeCmdPassThrough("wsl", enterns, "-d", dist, "sh", "-c", "cat > /usr/local/bin/enterns; chmod 755 /usr/local/bin/enterns"); err != nil { - return errors.Wrap(err, "could not create enterns script for guest OS") + return fmt.Errorf("could not create enterns script for guest OS: %w", err) } if err := pipeCmdPassThrough("wsl", profile, "-d", dist, "sh", "-c", "cat > /etc/profile.d/enterns.sh"); err != nil { - return errors.Wrap(err, "could not create motd profile script for guest OS") + return fmt.Errorf("could not create motd profile script for guest OS: %w", err) } if err := pipeCmdPassThrough("wsl", wslmotd, "-d", dist, "sh", "-c", "cat > /etc/wslmotd"); err != nil { - return errors.Wrap(err, "could not create a WSL MOTD for guest OS") + return fmt.Errorf("could not create a WSL MOTD for guest OS: %w", err) } if err := pipeCmdPassThrough("wsl", bootstrap, "-d", dist, "sh", "-c", "cat > /root/bootstrap; chmod 755 /root/bootstrap"); err != nil { - return errors.Wrap(err, "could not create bootstrap script for guest OS") + return fmt.Errorf("could not create bootstrap script for guest OS: %w", err) } if err := pipeCmdPassThrough("wsl", proxyConfigSetup, "-d", dist, "sh", "-c", "cat > /usr/local/bin/proxyinit; chmod 755 /usr/local/bin/proxyinit"); err != nil { - return errors.Wrap(err, "could not create proxyinit script for guest OS") + return fmt.Errorf("could not create proxyinit script for guest OS: %w", err) } return nil @@ -673,10 +673,10 @@ func checkAndInstallWSL(opts machine.InitOptions) (bool, error) { func attemptFeatureInstall(opts machine.InitOptions, admin bool) error { if !winVersionAtLeast(10, 0, 18362) { - return errors.Errorf("Your version of Windows does not support WSL. Update to Windows 10 Build 19041 or later") + return errors.New("your version of Windows does not support WSL. Update to Windows 10 Build 19041 or later") } else if !winVersionAtLeast(10, 0, 19041) { fmt.Fprint(os.Stderr, wslOldVersion) - return errors.Errorf("WSL can not be automatically installed") + return errors.New("the WSL can not be automatically installed") } message := "WSL is not installed on this system, installing it.\n\n" @@ -690,7 +690,7 @@ func attemptFeatureInstall(opts machine.InitOptions, admin bool) error { "If you prefer, you may abort now, and perform a manual installation using the \"wsl --install\" command." if !opts.ReExec && MessageBox(message, "Podman Machine", false) != 1 { - return errors.Errorf("WSL installation aborted") + return errors.New("the WSL installation aborted") } if !opts.ReExec && !admin { @@ -726,12 +726,12 @@ func installWsl() error { defer log.Close() if err := runCmdPassThroughTee(log, "dism", "/online", "/enable-feature", "/featurename:Microsoft-Windows-Subsystem-Linux", "/all", "/norestart"); isMsiError(err) { - return errors.Wrap(err, "could not enable WSL Feature") + return fmt.Errorf("could not enable WSL Feature: %w", err) } if err = runCmdPassThroughTee(log, "dism", "/online", "/enable-feature", "/featurename:VirtualMachinePlatform", "/all", "/norestart"); isMsiError(err) { - return errors.Wrap(err, "could not enable Virtual Machine Feature") + return fmt.Errorf("could not enable Virtual Machine Feature: %w", err) } log.Close() @@ -765,7 +765,7 @@ func installWslKernel() error { } if err != nil { - return errors.Wrap(err, "could not install WSL Kernel") + return fmt.Errorf("could not install WSL Kernel: %w", err) } return nil @@ -922,23 +922,23 @@ func (v *MachineVM) Set(_ string, opts machine.SetOptions) ([]error, error) { if opts.Rootful != nil && v.Rootful != *opts.Rootful { err := v.setRootful(*opts.Rootful) if err != nil { - setErrors = append(setErrors, errors.Wrapf(err, "error setting rootful option")) + setErrors = append(setErrors, fmt.Errorf("error setting rootful option: %w", err)) } else { v.Rootful = *opts.Rootful } } if opts.CPUs != nil { - setErrors = append(setErrors, errors.Errorf("changing CPUs not supported for WSL machines")) + setErrors = append(setErrors, errors.New("changing CPUs not supported for WSL machines")) } if opts.Memory != nil { - setErrors = append(setErrors, errors.Errorf("changing memory not supported for WSL machines")) + setErrors = append(setErrors, errors.New("changing memory not supported for WSL machines")) } if opts.DiskSize != nil { - setErrors = append(setErrors, errors.Errorf("changing Disk Size not supported for WSL machines")) + setErrors = append(setErrors, errors.New("changing Disk Size not supported for WSL machines")) } return setErrors, v.writeConfig() @@ -946,7 +946,7 @@ func (v *MachineVM) Set(_ string, opts machine.SetOptions) ([]error, error) { func (v *MachineVM) Start(name string, _ machine.StartOptions) error { if v.isRunning() { - return errors.Errorf("%q is already running", name) + return fmt.Errorf("%q is already running", name) } dist := toDist(name) @@ -957,7 +957,7 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { err := runCmdPassThrough("wsl", "-d", dist, "/root/bootstrap") if err != nil { - return errors.Wrap(err, "WSL bootstrap script failed") + return fmt.Errorf("the WSL bootstrap script failed: %w", err) } if !v.Rootful { @@ -999,7 +999,7 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { func launchWinProxy(v *MachineVM) (bool, string, error) { machinePipe := toDist(v.Name) if !pipeAvailable(machinePipe) { - return false, "", errors.Errorf("could not start api proxy since expected pipe is not available: %s", machinePipe) + return false, "", fmt.Errorf("could not start api proxy since expected pipe is not available: %s", machinePipe) } globalName := false @@ -1047,7 +1047,7 @@ func launchWinProxy(v *MachineVM) (bool, string, error) { return globalName, pipePrefix + waitPipe, waitPipeExists(waitPipe, 30, func() error { active, exitCode := getProcessState(cmd.Process.Pid) if !active { - return errors.Errorf("win-sshproxy.exe failed to start, exit code: %d (see windows event logs)", exitCode) + return fmt.Errorf("win-sshproxy.exe failed to start, exit code: %d (see windows event logs)", exitCode) } return nil @@ -1185,7 +1185,7 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error { } if !wsl || !sysd { - return errors.Errorf("%q is not running", v.Name) + return fmt.Errorf("%q is not running", v.Name) } _, _, _ = v.updateTimeStamps(true) @@ -1197,12 +1197,12 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error { cmd := exec.Command("wsl", "-d", dist, "sh") cmd.Stdin = strings.NewReader(waitTerm) if err = cmd.Start(); err != nil { - return errors.Wrap(err, "Error executing wait command") + return fmt.Errorf("executing wait command: %w", err) } exitCmd := exec.Command("wsl", "-d", dist, "/usr/local/bin/enterns", "systemctl", "exit", "0") if err = exitCmd.Run(); err != nil { - return errors.Wrap(err, "Error stopping sysd") + return fmt.Errorf("stopping sysd: %w", err) } if err = cmd.Wait(); err != nil { @@ -1283,7 +1283,7 @@ func (v *MachineVM) Remove(name string, opts machine.RemoveOptions) (string, fun var files []string if v.isRunning() { - return "", nil, errors.Errorf("running vm %q cannot be destroyed", v.Name) + return "", nil, fmt.Errorf("running vm %q cannot be destroyed", v.Name) } // Collect all the files that need to be destroyed @@ -1355,7 +1355,7 @@ func (v *MachineVM) isRunning() bool { // Added ssh function to VM interface: pkg/machine/config/go : line 58 func (v *MachineVM) SSH(name string, opts machine.SSHOptions) error { if !v.isRunning() { - return errors.Errorf("vm %q is not running.", v.Name) + return fmt.Errorf("vm %q is not running.", v.Name) } username := opts.Username @@ -1655,3 +1655,7 @@ func (p *Provider) RemoveAndCleanMachines() error { } return prevErr } + +func (p *Provider) VMType() string { + return vmtype +} diff --git a/pkg/machine/wsl/util_windows.go b/pkg/machine/wsl/util_windows.go index b5c28e015..43f54fdd4 100644 --- a/pkg/machine/wsl/util_windows.go +++ b/pkg/machine/wsl/util_windows.go @@ -2,6 +2,7 @@ package wsl import ( "encoding/base64" + "errors" "fmt" "io/ioutil" "os" @@ -11,7 +12,6 @@ import ( "unicode/utf16" "unsafe" - "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sys/windows" "golang.org/x/sys/windows/registry" @@ -157,9 +157,9 @@ func relaunchElevatedWait() error { case syscall.WAIT_OBJECT_0: break case syscall.WAIT_FAILED: - return errors.Wrap(err, "could not wait for process, failed") + return fmt.Errorf("could not wait for process, failed: %w", err) default: - return errors.Errorf("could not wait for process, unknown error") + return errors.New("could not wait for process, unknown error") } var code uint32 if err := syscall.GetExitCodeProcess(handle, &code); err != nil { @@ -174,7 +174,7 @@ func relaunchElevatedWait() error { func wrapMaybe(err error, message string) error { if err != nil { - return errors.Wrap(err, message) + return fmt.Errorf("%v: %w", message, err) } return errors.New(message) @@ -182,10 +182,10 @@ func wrapMaybe(err error, message string) error { func wrapMaybef(err error, format string, args ...interface{}) error { if err != nil { - return errors.Wrapf(err, format, args...) + return fmt.Errorf(format+": %w", append(args, err)...) } - return errors.Errorf(format, args...) + return fmt.Errorf(format, args...) } func reboot() error { @@ -202,14 +202,14 @@ func reboot() error { dataDir, err := homedir.GetDataHome() if err != nil { - return errors.Wrap(err, "could not determine data directory") + return fmt.Errorf("could not determine data directory: %w", err) } if err := os.MkdirAll(dataDir, 0755); err != nil { - return errors.Wrap(err, "could not create data directory") + return fmt.Errorf("could not create data directory: %w", err) } commFile := filepath.Join(dataDir, "podman-relaunch.dat") if err := ioutil.WriteFile(commFile, []byte(encoded), 0600); err != nil { - return errors.Wrap(err, "could not serialize command state") + return fmt.Errorf("could not serialize command state: %w", err) } command := fmt.Sprintf(pShellLaunch, commFile) @@ -244,7 +244,7 @@ func reboot() error { procExit := user32.NewProc("ExitWindowsEx") if ret, _, err := procExit.Call(EWX_REBOOT|EWX_RESTARTAPPS|EWX_FORCEIFHUNG, SHTDN_REASON_MAJOR_APPLICATION|SHTDN_REASON_MINOR_INSTALLATION|SHTDN_REASON_FLAG_PLANNED); ret != 1 { - return errors.Wrap(err, "reboot failed") + return fmt.Errorf("reboot failed: %w", err) } return nil @@ -262,19 +262,19 @@ func obtainShutdownPrivilege() error { var hToken uintptr if ret, _, err := OpenProcessToken.Call(uintptr(proc), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, uintptr(unsafe.Pointer(&hToken))); ret != 1 { - return errors.Wrap(err, "error opening process token") + return fmt.Errorf("error opening process token: %w", err) } var privs TokenPrivileges if ret, _, err := LookupPrivilegeValue.Call(uintptr(0), uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(SeShutdownName))), uintptr(unsafe.Pointer(&(privs.privileges[0].luid)))); ret != 1 { - return errors.Wrap(err, "error looking up shutdown privilege") + return fmt.Errorf("error looking up shutdown privilege: %w", err) } privs.privilegeCount = 1 privs.privileges[0].attributes = SE_PRIVILEGE_ENABLED if ret, _, err := AdjustTokenPrivileges.Call(hToken, 0, uintptr(unsafe.Pointer(&privs)), 0, uintptr(0), 0); ret != 1 { - return errors.Wrap(err, "error enabling shutdown privilege on token") + return fmt.Errorf("error enabling shutdown privilege on token: %w", err) } return nil @@ -295,13 +295,13 @@ func getProcessState(pid int) (active bool, exitCode int) { func addRunOnceRegistryEntry(command string) error { k, _, err := registry.CreateKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\RunOnce`, registry.WRITE) if err != nil { - return errors.Wrap(err, "could not open RunOnce registry entry") + return fmt.Errorf("could not open RunOnce registry entry: %w", err) } defer k.Close() if err := k.SetExpandStringValue("podman-machine", command); err != nil { - return errors.Wrap(err, "could not open RunOnce registry entry") + return fmt.Errorf("could not open RunOnce registry entry: %w", err) } return nil diff --git a/pkg/parallel/parallel.go b/pkg/parallel/parallel.go index 4da7e0f89..7209f8aca 100644 --- a/pkg/parallel/parallel.go +++ b/pkg/parallel/parallel.go @@ -2,9 +2,10 @@ package parallel import ( "context" + "errors" + "fmt" "sync" - "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sync/semaphore" ) @@ -59,7 +60,7 @@ func Enqueue(ctx context.Context, fn func() error) <-chan error { defer close(retChan) if err := jobControl.Acquire(ctx, 1); err != nil { - retChan <- errors.Wrapf(err, "error acquiring job control semaphore") + retChan <- fmt.Errorf("error acquiring job control semaphore: %w", err) return } diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go index fde621b72..b0012b32b 100644 --- a/pkg/rootless/rootless_linux.go +++ b/pkg/rootless/rootless_linux.go @@ -6,6 +6,7 @@ package rootless import ( "bufio" "bytes" + "errors" "fmt" "io" "io/ioutil" @@ -23,7 +24,6 @@ import ( "github.com/containers/storage/pkg/idtools" pmount "github.com/containers/storage/pkg/mount" "github.com/containers/storage/pkg/unshare" - "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/syndtr/gocapability/capability" "golang.org/x/sys/unix" @@ -126,7 +126,7 @@ func tryMappingTool(uid bool, pid int, hostID int, mappings []idtools.IDMap) err } path, err := exec.LookPath(tool) if err != nil { - return errors.Wrapf(err, "command required for rootless mode with multiple IDs") + return fmt.Errorf("command required for rootless mode with multiple IDs: %w", err) } appendTriplet := func(l []string, a, b, c int) []string { @@ -143,7 +143,7 @@ func tryMappingTool(uid bool, pid int, hostID int, mappings []idtools.IDMap) err what = "GID" where = "/etc/subgid" } - return errors.Errorf("invalid configuration: the specified mapping %d:%d in %q includes the user %s", i.HostID, i.Size, where, what) + return fmt.Errorf("invalid configuration: the specified mapping %d:%d in %q includes the user %s", i.HostID, i.Size, where, what) } args = appendTriplet(args, i.ContainerID+1, i.HostID, i.Size) } @@ -160,7 +160,7 @@ func tryMappingTool(uid bool, pid int, hostID int, mappings []idtools.IDMap) err } else if !isSet { errorStr = fmt.Sprintf("%s: should have %s or have filecaps %s", errorStr, idtype, idtype) } - return errors.Wrapf(err, errorStr) + return fmt.Errorf("%v: %w", errorStr, err) } return nil } @@ -182,7 +182,7 @@ func joinUserAndMountNS(pid uint, pausePid string) (bool, int, error) { pidC := C.reexec_userns_join(C.int(pid), cPausePid) if int(pidC) < 0 { - return false, -1, errors.Errorf("cannot re-exec process to join the existing user namespace") + return false, -1, fmt.Errorf("cannot re-exec process to join the existing user namespace") } ret := C.reexec_in_user_namespace_wait(pidC, 0) @@ -313,7 +313,7 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (_ boo pidC := C.reexec_in_user_namespace(C.int(r.Fd()), cPausePid, cFileToRead, fileOutputFD) pid = int(pidC) if pid < 0 { - return false, -1, errors.Errorf("cannot re-exec process") + return false, -1, fmt.Errorf("cannot re-exec process") } uids, gids, err := GetConfiguredMappings() @@ -343,13 +343,13 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (_ boo setgroups := fmt.Sprintf("/proc/%d/setgroups", pid) err = ioutil.WriteFile(setgroups, []byte("deny\n"), 0666) if err != nil { - return false, -1, errors.Wrapf(err, "cannot write setgroups file") + return false, -1, fmt.Errorf("cannot write setgroups file: %w", err) } logrus.Debugf("write setgroups file exited with 0") err = ioutil.WriteFile(uidMap, []byte(fmt.Sprintf("%d %d 1\n", 0, os.Geteuid())), 0666) if err != nil { - return false, -1, errors.Wrapf(err, "cannot write uid_map") + return false, -1, fmt.Errorf("cannot write uid_map: %w", err) } logrus.Debugf("write uid_map exited with 0") } @@ -369,19 +369,19 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (_ boo if !gidsMapped { err = ioutil.WriteFile(gidMap, []byte(fmt.Sprintf("%d %d 1\n", 0, os.Getegid())), 0666) if err != nil { - return false, -1, errors.Wrapf(err, "cannot write gid_map") + return false, -1, fmt.Errorf("cannot write gid_map: %w", err) } } _, err = w.Write([]byte("0")) if err != nil { - return false, -1, errors.Wrapf(err, "write to sync pipe") + return false, -1, fmt.Errorf("write to sync pipe: %w", err) } b := make([]byte, 1) _, err = w.Read(b) if err != nil { - return false, -1, errors.Wrapf(err, "read from sync pipe") + return false, -1, fmt.Errorf("read from sync pipe: %w", err) } if fileOutput != nil { @@ -474,7 +474,7 @@ func TryJoinFromFilePaths(pausePidPath string, needNewNamespace bool, paths []st pausePid, err = strconv.Atoi(string(data)) if err != nil { - lastErr = errors.Wrapf(err, "cannot parse file %q", path) + lastErr = fmt.Errorf("cannot parse file %q: %w", path, err) continue } } else { @@ -503,7 +503,7 @@ func TryJoinFromFilePaths(pausePidPath string, needNewNamespace bool, paths []st n, err := r.Read(b) if err != nil { - lastErr = errors.Wrapf(err, "cannot read %q", path) + lastErr = fmt.Errorf("cannot read %q: %w", path, err) continue } @@ -525,7 +525,7 @@ func TryJoinFromFilePaths(pausePidPath string, needNewNamespace bool, paths []st if lastErr != nil { return false, 0, lastErr } - return false, 0, errors.Wrapf(unix.ESRCH, "could not find any running process") + return false, 0, fmt.Errorf("could not find any running process: %w", unix.ESRCH) } // ReadMappingsProc parses and returns the ID mappings at the specified path. @@ -545,7 +545,7 @@ func ReadMappingsProc(path string) ([]idtools.IDMap, error) { if err == io.EOF { return mappings, nil } - return nil, errors.Wrapf(err, "cannot read line from %s", path) + return nil, fmt.Errorf("cannot read line from %s: %w", path, err) } if line == nil { return mappings, nil @@ -553,7 +553,7 @@ func ReadMappingsProc(path string) ([]idtools.IDMap, error) { containerID, hostID, size := 0, 0, 0 if _, err := fmt.Sscanf(string(line), "%d %d %d", &containerID, &hostID, &size); err != nil { - return nil, errors.Wrapf(err, "cannot parse %s", string(line)) + return nil, fmt.Errorf("cannot parse %s: %w", string(line), err) } mappings = append(mappings, idtools.IDMap{ContainerID: containerID, HostID: hostID, Size: size}) } diff --git a/pkg/rootless/rootless_unsupported.go b/pkg/rootless/rootless_unsupported.go index fe164e235..a77e7e077 100644 --- a/pkg/rootless/rootless_unsupported.go +++ b/pkg/rootless/rootless_unsupported.go @@ -4,10 +4,10 @@ package rootless import ( + "errors" "os" "github.com/containers/storage/pkg/idtools" - "github.com/pkg/errors" ) // IsRootless returns whether the user is rootless diff --git a/pkg/seccomp/seccomp.go b/pkg/seccomp/seccomp.go index 4502c608f..d19d45960 100644 --- a/pkg/seccomp/seccomp.go +++ b/pkg/seccomp/seccomp.go @@ -1,9 +1,8 @@ package seccomp import ( + "fmt" "sort" - - "github.com/pkg/errors" ) // ContainerImageLabel is the key of the image annotation embedding a seccomp @@ -50,5 +49,5 @@ func LookupPolicy(s string) (Policy, error) { } sort.Strings(keys) - return -1, errors.Errorf("invalid seccomp policy %q: valid policies are %+q", s, keys) + return -1, fmt.Errorf("invalid seccomp policy %q: valid policies are %+q", s, keys) } diff --git a/pkg/specgen/config_unsupported.go b/pkg/specgen/config_unsupported.go index a6bf77277..becfd2eaf 100644 --- a/pkg/specgen/config_unsupported.go +++ b/pkg/specgen/config_unsupported.go @@ -4,9 +4,10 @@ package specgen import ( + "errors" + "github.com/containers/common/libimage" spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" ) func (s *SpecGenerator) getSeccompConfig(configSpec *spec.Spec, img *libimage.Image) (*spec.LinuxSeccomp, error) { diff --git a/pkg/specgen/container_validate.go b/pkg/specgen/container_validate.go index 5616a4511..63d94b6b3 100644 --- a/pkg/specgen/container_validate.go +++ b/pkg/specgen/container_validate.go @@ -1,6 +1,8 @@ package specgen import ( + "errors" + "fmt" "strconv" "strings" @@ -8,7 +10,6 @@ import ( "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/rootless" "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" ) var ( @@ -23,7 +24,7 @@ var ( ) func exclusiveOptions(opt1, opt2 string) error { - return errors.Errorf("%s and %s are mutually exclusive options", opt1, opt2) + return fmt.Errorf("%s and %s are mutually exclusive options", opt1, opt2) } // Validate verifies that the given SpecGenerator is valid and satisfies required @@ -33,18 +34,18 @@ func (s *SpecGenerator) Validate() error { // associated with them because those should be on the infra container. if len(s.Pod) > 0 && s.NetNS.NSMode == FromPod { if len(s.Networks) > 0 { - return errors.Wrap(define.ErrNetworkOnPodContainer, "networks must be defined when the pod is created") + return fmt.Errorf("networks must be defined when the pod is created: %w", define.ErrNetworkOnPodContainer) } if len(s.PortMappings) > 0 || s.PublishExposedPorts { - return errors.Wrap(define.ErrNetworkOnPodContainer, "published or exposed ports must be defined when the pod is created") + return fmt.Errorf("published or exposed ports must be defined when the pod is created: %w", define.ErrNetworkOnPodContainer) } if len(s.HostAdd) > 0 { - return errors.Wrap(define.ErrNetworkOnPodContainer, "extra host entries must be specified on the pod") + return fmt.Errorf("extra host entries must be specified on the pod: %w", define.ErrNetworkOnPodContainer) } } if s.NetNS.IsContainer() && len(s.HostAdd) > 0 { - return errors.Wrap(ErrInvalidSpecConfig, "cannot set extra host entries when the container is joined to another containers network namespace") + return fmt.Errorf("cannot set extra host entries when the container is joined to another containers network namespace: %w", ErrInvalidSpecConfig) } // @@ -52,22 +53,23 @@ func (s *SpecGenerator) Validate() error { // // Rootfs and Image cannot both populated if len(s.ContainerStorageConfig.Image) > 0 && len(s.ContainerStorageConfig.Rootfs) > 0 { - return errors.Wrap(ErrInvalidSpecConfig, "both image and rootfs cannot be simultaneously") + return fmt.Errorf("both image and rootfs cannot be simultaneously: %w", ErrInvalidSpecConfig) } // Cannot set hostname and utsns if len(s.ContainerBasicConfig.Hostname) > 0 && !s.ContainerBasicConfig.UtsNS.IsPrivate() { if s.ContainerBasicConfig.UtsNS.IsPod() { - return errors.Wrap(ErrInvalidSpecConfig, "cannot set hostname when joining the pod UTS namespace") + return fmt.Errorf("cannot set hostname when joining the pod UTS namespace: %w", ErrInvalidSpecConfig) } - return errors.Wrap(ErrInvalidSpecConfig, "cannot set hostname when running in the host UTS namespace") + + return fmt.Errorf("cannot set hostname when running in the host UTS namespace: %w", ErrInvalidSpecConfig) } // systemd values must be true, false, or always if len(s.ContainerBasicConfig.Systemd) > 0 && !util.StringInSlice(strings.ToLower(s.ContainerBasicConfig.Systemd), SystemDValues) { - return errors.Wrapf(ErrInvalidSpecConfig, "--systemd values must be one of %q", strings.Join(SystemDValues, ", ")) + return fmt.Errorf("--systemd values must be one of %q: %w", strings.Join(SystemDValues, ", "), ErrInvalidSpecConfig) } // sdnotify values must be container, conmon, or ignore if len(s.ContainerBasicConfig.SdNotifyMode) > 0 && !util.StringInSlice(strings.ToLower(s.ContainerBasicConfig.SdNotifyMode), SdNotifyModeValues) { - return errors.Wrapf(ErrInvalidSpecConfig, "--sdnotify values must be one of %q", strings.Join(SdNotifyModeValues, ", ")) + return fmt.Errorf("--sdnotify values must be one of %q: %w", strings.Join(SdNotifyModeValues, ", "), ErrInvalidSpecConfig) } // @@ -79,12 +81,12 @@ func (s *SpecGenerator) Validate() error { } // imagevolumemode must be one of ignore, tmpfs, or anonymous if given if len(s.ContainerStorageConfig.ImageVolumeMode) > 0 && !util.StringInSlice(strings.ToLower(s.ContainerStorageConfig.ImageVolumeMode), ImageVolumeModeValues) { - return errors.Errorf("invalid ImageVolumeMode %q, value must be one of %s", + return fmt.Errorf("invalid ImageVolumeMode %q, value must be one of %s", s.ContainerStorageConfig.ImageVolumeMode, strings.Join(ImageVolumeModeValues, ",")) } // shmsize conflicts with IPC namespace if s.ContainerStorageConfig.ShmSize != nil && (s.ContainerStorageConfig.IpcNS.IsHost() || s.ContainerStorageConfig.IpcNS.IsNone()) { - return errors.Errorf("cannot set shmsize when running in the %s IPC Namespace", s.ContainerStorageConfig.IpcNS) + return fmt.Errorf("cannot set shmsize when running in the %s IPC Namespace", s.ContainerStorageConfig.IpcNS) } // @@ -92,7 +94,7 @@ func (s *SpecGenerator) Validate() error { // // userns and idmappings conflict if s.UserNS.IsPrivate() && s.IDMappings == nil { - return errors.Wrap(ErrInvalidSpecConfig, "IDMappings are required when not creating a User namespace") + return fmt.Errorf("IDMappings are required when not creating a User namespace: %w", ErrInvalidSpecConfig) } // @@ -142,11 +144,11 @@ func (s *SpecGenerator) Validate() error { for _, limit := range tmpnproc { limitSplit := strings.SplitN(limit, "=", 2) if len(limitSplit) < 2 { - return errors.Wrapf(invalidUlimitFormatError, "missing = in %s", limit) + return fmt.Errorf("missing = in %s: %w", limit, invalidUlimitFormatError) } valueSplit := strings.SplitN(limitSplit[1], ":", 2) if len(valueSplit) < 2 { - return errors.Wrapf(invalidUlimitFormatError, "missing : in %s", limit) + return fmt.Errorf("missing : in %s: %w", limit, invalidUlimitFormatError) } hard, err := strconv.Atoi(valueSplit[0]) if err != nil { @@ -196,7 +198,7 @@ func (s *SpecGenerator) Validate() error { } if s.NetNS.NSMode != Bridge && len(s.Networks) > 0 { // Note that we also get the ip and mac in the networks map - return errors.New("Networks and static ip/mac address can only be used with Bridge mode networking") + return errors.New("networks and static ip/mac address can only be used with Bridge mode networking") } return nil diff --git a/pkg/specgen/generate/config_linux.go b/pkg/specgen/generate/config_linux.go index 60d87a8fd..a46966161 100644 --- a/pkg/specgen/generate/config_linux.go +++ b/pkg/specgen/generate/config_linux.go @@ -13,7 +13,6 @@ import ( "github.com/containers/podman/v4/pkg/util" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" - "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) @@ -46,7 +45,7 @@ func DevicesFromPath(g *generate.Generator, devicePath string) error { } if len(devs) > 2 { if devmode != "" { - return errors.Wrapf(unix.EINVAL, "invalid device specification %s", devicePath) + return fmt.Errorf("invalid device specification %s: %w", devicePath, unix.EINVAL) } devmode = devs[2] } @@ -60,7 +59,7 @@ func DevicesFromPath(g *generate.Generator, devicePath string) error { device = fmt.Sprintf("%s:%s", device, devmode) } if err := addDevice(g, device); err != nil { - return errors.Wrapf(err, "failed to add %s device", dpath) + return fmt.Errorf("failed to add %s device: %w", dpath, err) } } return nil @@ -68,7 +67,7 @@ func DevicesFromPath(g *generate.Generator, devicePath string) error { return err } if !found { - return errors.Wrapf(unix.EINVAL, "no devices found in %s", devicePath) + return fmt.Errorf("no devices found in %s: %w", devicePath, unix.EINVAL) } return nil } @@ -131,7 +130,7 @@ func addDevice(g *generate.Generator, device string) error { } dev, err := util.DeviceFromPath(src) if err != nil { - return errors.Wrapf(err, "%s is not a valid device", src) + return fmt.Errorf("%s is not a valid device: %w", src, err) } if rootless.IsRootless() { if _, err := os.Stat(src); err != nil { diff --git a/pkg/specgen/generate/config_linux_cgo.go b/pkg/specgen/generate/config_linux_cgo.go index efab6679a..74ba4aeeb 100644 --- a/pkg/specgen/generate/config_linux_cgo.go +++ b/pkg/specgen/generate/config_linux_cgo.go @@ -5,6 +5,8 @@ package generate import ( "context" + "errors" + "fmt" "io/ioutil" "github.com/containers/common/libimage" @@ -12,7 +14,6 @@ import ( "github.com/containers/podman/v4/pkg/seccomp" "github.com/containers/podman/v4/pkg/specgen" spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -39,7 +40,7 @@ func getSeccompConfig(s *specgen.SpecGenerator, configSpec *spec.Spec, img *libi logrus.Debug("Loading seccomp profile from the security config") seccompConfig, err = goSeccomp.LoadProfile(imagePolicy, configSpec) if err != nil { - return nil, errors.Wrap(err, "loading seccomp profile failed") + return nil, fmt.Errorf("loading seccomp profile failed: %w", err) } return seccompConfig, nil } @@ -48,17 +49,17 @@ func getSeccompConfig(s *specgen.SpecGenerator, configSpec *spec.Spec, img *libi logrus.Debugf("Loading seccomp profile from %q", s.SeccompProfilePath) seccompProfile, err := ioutil.ReadFile(s.SeccompProfilePath) if err != nil { - return nil, errors.Wrap(err, "opening seccomp profile failed") + return nil, fmt.Errorf("opening seccomp profile failed: %w", err) } seccompConfig, err = goSeccomp.LoadProfile(string(seccompProfile), configSpec) if err != nil { - return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", s.SeccompProfilePath) + return nil, fmt.Errorf("loading seccomp profile (%s) failed: %w", s.SeccompProfilePath, err) } } else { logrus.Debug("Loading default seccomp profile") seccompConfig, err = goSeccomp.GetDefaultProfile(configSpec) if err != nil { - return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", s.SeccompProfilePath) + return nil, fmt.Errorf("loading seccomp profile (%s) failed: %w", s.SeccompProfilePath, err) } } diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go index 8fdd87adf..2248c9235 100644 --- a/pkg/specgen/generate/container.go +++ b/pkg/specgen/generate/container.go @@ -3,6 +3,7 @@ package generate import ( "context" "encoding/json" + "errors" "fmt" "os" "strings" @@ -17,7 +18,6 @@ import ( "github.com/containers/podman/v4/pkg/signal" "github.com/containers/podman/v4/pkg/specgen" spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) @@ -115,7 +115,7 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat // Get Default Environment from containers.conf defaultEnvs, err := envLib.ParseSlice(rtc.GetDefaultEnvEx(s.EnvHost, s.HTTPProxy)) if err != nil { - return nil, errors.Wrap(err, "error parsing fields in containers.conf") + return nil, fmt.Errorf("error parsing fields in containers.conf: %w", err) } var envs map[string]string @@ -125,7 +125,7 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat // already, overriding the default environments envs, err = envLib.ParseSlice(inspectData.Config.Env) if err != nil { - return nil, errors.Wrap(err, "Env fields from image failed to parse") + return nil, fmt.Errorf("env fields from image failed to parse: %w", err) } defaultEnvs = envLib.Join(envLib.DefaultEnvVariables(), envLib.Join(defaultEnvs, envs)) } @@ -141,7 +141,7 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat // any case. osEnv, err := envLib.ParseSlice(os.Environ()) if err != nil { - return nil, errors.Wrap(err, "error parsing host environment variables") + return nil, fmt.Errorf("error parsing host environment variables: %w", err) } // Caller Specified defaults if s.EnvHost { diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index 6b2e90b22..51d290bb4 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -3,6 +3,8 @@ package generate import ( "context" "encoding/json" + "errors" + "fmt" "path/filepath" "strings" @@ -15,7 +17,6 @@ import ( "github.com/containers/podman/v4/pkg/util" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/selinux/go-selinux/label" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -34,7 +35,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener if s.Pod != "" { pod, err = rt.LookupPod(s.Pod) if err != nil { - return nil, nil, nil, errors.Wrapf(err, "error retrieving pod %s", s.Pod) + return nil, nil, nil, fmt.Errorf("error retrieving pod %s: %w", s.Pod, err) } if pod.HasInfraContainer() { infra, err = pod.InfraContainer() @@ -133,8 +134,14 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener options = append(options, libpod.WithRootFSFromImage(newImage.ID(), resolvedImageName, s.RawImageName)) } + + _, err = rt.LookupPod(s.Hostname) + if len(s.Hostname) > 0 && !s.UtsNS.IsPrivate() && err == nil { + // ok, we are incorrectly setting the pod as the hostname, lets undo that before validation + s.Hostname = "" + } if err := s.Validate(); err != nil { - return nil, nil, nil, errors.Wrap(err, "invalid config provided") + return nil, nil, nil, fmt.Errorf("invalid config provided: %w", err) } finalMounts, finalVolumes, finalOverlays, err := finalizeMounts(ctx, s, rt, rtc, newImage) @@ -326,7 +333,7 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l } } default: - return nil, errors.Wrapf(err, "invalid value %q systemd option requires 'true, false, always'", s.Systemd) + return nil, fmt.Errorf("invalid value %q systemd option requires 'true, false, always': %w", s.Systemd, err) } logrus.Debugf("using systemd mode: %t", useSystemd) if useSystemd { @@ -335,7 +342,7 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l if s.StopSignal == nil { stopSignal, err := util.ParseSignal("RTMIN+3") if err != nil { - return nil, errors.Wrapf(err, "error parsing systemd signal") + return nil, fmt.Errorf("error parsing systemd signal: %w", err) } s.StopSignal = &stopSignal } @@ -530,7 +537,7 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l for _, ctr := range s.DependencyContainers { depCtr, err := rt.LookupContainer(ctr) if err != nil { - return nil, errors.Wrapf(err, "%q is not a valid container, cannot be used as a dependency", ctr) + return nil, fmt.Errorf("%q is not a valid container, cannot be used as a dependency: %w", ctr, err) } deps = append(deps, depCtr) } diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index c254b8192..454a1e1d0 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -3,6 +3,7 @@ package kube import ( "context" "encoding/json" + "errors" "fmt" "math" "net" @@ -29,7 +30,6 @@ import ( "github.com/docker/docker/pkg/system" "github.com/docker/go-units" spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -146,7 +146,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener // pod name should be non-empty for Deployment objects to be able to create // multiple pods having containers with unique names if len(opts.PodName) < 1 { - return nil, errors.Errorf("got empty pod name on container creation when playing kube") + return nil, errors.New("got empty pod name on container creation when playing kube") } s.Name = fmt.Sprintf("%s-%s", opts.PodName, opts.Container.Name) @@ -163,7 +163,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener for _, o := range opts.LogOptions { split := strings.SplitN(o, "=", 2) if len(split) < 2 { - return nil, errors.Errorf("invalid log option %q", o) + return nil, fmt.Errorf("invalid log option %q", o) } switch strings.ToLower(split[0]) { case "driver": @@ -179,7 +179,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener default: switch len(split[1]) { case 0: - return nil, errors.Wrapf(define.ErrInvalidArg, "invalid log option") + return nil, fmt.Errorf("invalid log option: %w", define.ErrInvalidArg) default: // tags for journald only if s.LogConfiguration.Driver == "" || s.LogConfiguration.Driver == define.JournaldLogging { @@ -196,7 +196,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener setupSecurityContext(s, opts.Container.SecurityContext, opts.PodSecurityContext) err := setupLivenessProbe(s, opts.Container, opts.RestartPolicy) if err != nil { - return nil, errors.Wrap(err, "Failed to configure livenessProbe") + return nil, fmt.Errorf("failed to configure livenessProbe: %w", err) } // Since we prefix the container name with pod name to work-around the uniqueness requirement, @@ -207,7 +207,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener s.ResourceLimits = &spec.LinuxResources{} milliCPU, err := quantityToInt64(opts.Container.Resources.Limits.Cpu()) if err != nil { - return nil, errors.Wrap(err, "Failed to set CPU quota") + return nil, fmt.Errorf("failed to set CPU quota: %w", err) } if milliCPU > 0 { period, quota := util.CoresToPeriodAndQuota(float64(milliCPU)) @@ -219,12 +219,12 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener limit, err := quantityToInt64(opts.Container.Resources.Limits.Memory()) if err != nil { - return nil, errors.Wrap(err, "Failed to set memory limit") + return nil, fmt.Errorf("failed to set memory limit: %w", err) } memoryRes, err := quantityToInt64(opts.Container.Resources.Requests.Memory()) if err != nil { - return nil, errors.Wrap(err, "Failed to set memory reservation") + return nil, fmt.Errorf("failed to set memory reservation: %w", err) } if limit > 0 || memoryRes > 0 { @@ -337,7 +337,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener for _, volume := range opts.Container.VolumeMounts { volumeSource, exists := opts.Volumes[volume.Name] if !exists { - return nil, errors.Errorf("Volume mount %s specified for container but not configured in volumes", volume.Name) + return nil, fmt.Errorf("volume mount %s specified for container but not configured in volumes", volume.Name) } // Skip if the volume is optional. This means that a configmap for a configmap volume was not found but it was // optional so we can move on without throwing an error @@ -399,7 +399,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener } s.Devices = append(s.Devices, device) default: - return nil, errors.Errorf("Unsupported volume source type") + return nil, errors.New("unsupported volume source type") } } @@ -432,21 +432,21 @@ func parseMountPath(mountPath string, readOnly bool, propagationMode *v1.MountPr options := []string{} splitVol := strings.Split(mountPath, ":") if len(splitVol) > 2 { - return "", options, errors.Errorf("%q incorrect volume format, should be ctr-dir[:option]", mountPath) + return "", options, fmt.Errorf("%q incorrect volume format, should be ctr-dir[:option]", mountPath) } dest := splitVol[0] if len(splitVol) > 1 { options = strings.Split(splitVol[1], ",") } if err := parse.ValidateVolumeCtrDir(dest); err != nil { - return "", options, errors.Wrapf(err, "parsing MountPath") + return "", options, fmt.Errorf("parsing MountPath: %w", err) } if readOnly { options = append(options, "ro") } opts, err := parse.ValidateVolumeOpts(options) if err != nil { - return "", opts, errors.Wrapf(err, "parsing MountOptions") + return "", opts, fmt.Errorf("parsing MountOptions: %w", err) } if propagationMode != nil { switch *propagationMode { @@ -457,7 +457,7 @@ func parseMountPath(mountPath string, readOnly bool, propagationMode *v1.MountPr case v1.MountPropagationBidirectional: opts = append(opts, "rshared") default: - return "", opts, errors.Errorf("unknown propagation mode %q", *propagationMode) + return "", opts, fmt.Errorf("unknown propagation mode %q", *propagationMode) } } return dest, opts, nil @@ -504,7 +504,7 @@ func setupLivenessProbe(s *specgen.SpecGenerator, containerYAML v1.Container, re func makeHealthCheck(inCmd string, interval int32, retries int32, timeout int32, startPeriod int32) (*manifest.Schema2HealthConfig, error) { // Every healthcheck requires a command if len(inCmd) == 0 { - return nil, errors.New("Must define a healthcheck command for all healthchecks") + return nil, errors.New("must define a healthcheck command for all healthchecks") } // first try to parse option value as JSON array of strings... @@ -630,7 +630,7 @@ func quantityToInt64(quantity *resource.Quantity) (int64, error) { return i, nil } - return 0, errors.Errorf("Quantity cannot be represented as int64: %v", quantity) + return 0, fmt.Errorf("quantity cannot be represented as int64: %v", quantity) } // read a k8s secret in JSON format from the secret manager @@ -642,7 +642,7 @@ func k8sSecretFromSecretManager(name string, secretsManager *secrets.SecretsMana var secrets map[string][]byte if err := json.Unmarshal(jsonSecret, &secrets); err != nil { - return nil, errors.Errorf("Secret %v is not valid JSON: %v", name, err) + return nil, fmt.Errorf("secret %v is not valid JSON: %v", name, err) } return secrets, nil } @@ -653,7 +653,7 @@ func envVarsFrom(envFrom v1.EnvFromSource, opts *CtrSpecGenOptions) (map[string] if envFrom.ConfigMapRef != nil { cmRef := envFrom.ConfigMapRef - err := errors.Errorf("Configmap %v not found", cmRef.Name) + err := fmt.Errorf("configmap %v not found", cmRef.Name) for _, c := range opts.ConfigMaps { if cmRef.Name == c.Name { @@ -689,14 +689,14 @@ func envVarValue(env v1.EnvVar, opts *CtrSpecGenOptions) (*string, error) { if env.ValueFrom != nil { if env.ValueFrom.ConfigMapKeyRef != nil { cmKeyRef := env.ValueFrom.ConfigMapKeyRef - err := errors.Errorf("Cannot set env %v: configmap %v not found", env.Name, cmKeyRef.Name) + err := fmt.Errorf("cannot set env %v: configmap %v not found", env.Name, cmKeyRef.Name) for _, c := range opts.ConfigMaps { if cmKeyRef.Name == c.Name { if value, ok := c.Data[cmKeyRef.Key]; ok { return &value, nil } - err = errors.Errorf("Cannot set env %v: key %s not found in configmap %v", env.Name, cmKeyRef.Key, cmKeyRef.Name) + err = fmt.Errorf("cannot set env %v: key %s not found in configmap %v", env.Name, cmKeyRef.Key, cmKeyRef.Name) break } } @@ -714,10 +714,10 @@ func envVarValue(env v1.EnvVar, opts *CtrSpecGenOptions) (*string, error) { value := string(val) return &value, nil } - err = errors.Errorf("Secret %v has not %v key", secKeyRef.Name, secKeyRef.Key) + err = fmt.Errorf("secret %v has not %v key", secKeyRef.Name, secKeyRef.Key) } if secKeyRef.Optional == nil || !*secKeyRef.Optional { - return nil, errors.Errorf("Cannot set env %v: %v", env.Name, err) + return nil, fmt.Errorf("cannot set env %v: %v", env.Name, err) } return nil, nil } @@ -761,8 +761,8 @@ func envVarValueFieldRef(env v1.EnvVar, opts *CtrSpecGenOptions) (*string, error return &annotationValue, nil } - return nil, errors.Errorf( - "Can not set env %v. Reason: fieldPath %v is either not valid or not supported", + return nil, fmt.Errorf( + "can not set env %v. Reason: fieldPath %v is either not valid or not supported", env.Name, fieldPath, ) } @@ -796,15 +796,15 @@ func envVarValueResourceFieldRef(env v1.EnvVar, opts *CtrSpecGenOptions) (*strin value = resources.Requests.Cpu() isValidDivisor = isCPUDivisor(divisor) default: - return nil, errors.Errorf( - "Can not set env %v. Reason: resource %v is either not valid or not supported", + return nil, fmt.Errorf( + "can not set env %v. Reason: resource %v is either not valid or not supported", env.Name, resourceName, ) } if !isValidDivisor { - return nil, errors.Errorf( - "Can not set env %s. Reason: divisor value %s is not valid", + return nil, fmt.Errorf( + "can not set env %s. Reason: divisor value %s is not valid", env.Name, divisor.String(), ) } diff --git a/pkg/specgen/generate/kube/seccomp.go b/pkg/specgen/generate/kube/seccomp.go index 8f93b34ff..6e3accd8b 100644 --- a/pkg/specgen/generate/kube/seccomp.go +++ b/pkg/specgen/generate/kube/seccomp.go @@ -1,12 +1,12 @@ package kube import ( + "fmt" "path/filepath" "strings" "github.com/containers/podman/v4/libpod" v1 "github.com/containers/podman/v4/pkg/k8s.io/api/core/v1" - "github.com/pkg/errors" ) // KubeSeccompPaths holds information about a pod YAML's seccomp configuration @@ -42,7 +42,7 @@ func InitializeSeccompPaths(annotations map[string]string, profileRoot string) ( // this could be caused by a user inputting either of // container.seccomp.security.alpha.kubernetes.io{,/} // both of which are invalid - return nil, errors.Errorf("Invalid seccomp path: %s", prefixAndCtr[0]) + return nil, fmt.Errorf("invalid seccomp path: %s", prefixAndCtr[0]) } path, err := verifySeccompPath(seccomp, profileRoot) @@ -80,6 +80,6 @@ func verifySeccompPath(path string, profileRoot string) (string, error) { if parts[0] == "localhost" { return filepath.Join(profileRoot, parts[1]), nil } - return "", errors.Errorf("invalid seccomp path: %s", path) + return "", fmt.Errorf("invalid seccomp path: %s", path) } } diff --git a/pkg/specgen/generate/kube/volume.go b/pkg/specgen/generate/kube/volume.go index 1d6d49b9d..f5c0c241d 100644 --- a/pkg/specgen/generate/kube/volume.go +++ b/pkg/specgen/generate/kube/volume.go @@ -1,12 +1,13 @@ package kube import ( + "errors" + "fmt" "os" "github.com/containers/common/pkg/parse" "github.com/containers/podman/v4/libpod" v1 "github.com/containers/podman/v4/pkg/k8s.io/api/core/v1" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -56,13 +57,13 @@ func VolumeFromHostPath(hostPath *v1.HostPathVolumeSource) (*KubeVolume, error) } // Label a newly created volume if err := libpod.LabelVolumePath(hostPath.Path); err != nil { - return nil, errors.Wrapf(err, "error giving %s a label", hostPath.Path) + return nil, fmt.Errorf("error giving %s a label: %w", hostPath.Path, err) } case v1.HostPathFileOrCreate: if _, err := os.Stat(hostPath.Path); os.IsNotExist(err) { f, err := os.OpenFile(hostPath.Path, os.O_RDONLY|os.O_CREATE, kubeFilePermission) if err != nil { - return nil, errors.Wrap(err, "error creating HostPath") + return nil, fmt.Errorf("error creating HostPath: %w", err) } if err := f.Close(); err != nil { logrus.Warnf("Error in closing newly created HostPath file: %v", err) @@ -70,23 +71,23 @@ func VolumeFromHostPath(hostPath *v1.HostPathVolumeSource) (*KubeVolume, error) } // unconditionally label a newly created volume if err := libpod.LabelVolumePath(hostPath.Path); err != nil { - return nil, errors.Wrapf(err, "error giving %s a label", hostPath.Path) + return nil, fmt.Errorf("error giving %s a label: %w", hostPath.Path, err) } case v1.HostPathSocket: st, err := os.Stat(hostPath.Path) if err != nil { - return nil, errors.Wrap(err, "error checking HostPathSocket") + return nil, fmt.Errorf("error checking HostPathSocket: %w", err) } if st.Mode()&os.ModeSocket != os.ModeSocket { - return nil, errors.Errorf("checking HostPathSocket: path %s is not a socket", hostPath.Path) + return nil, fmt.Errorf("checking HostPathSocket: path %s is not a socket", hostPath.Path) } case v1.HostPathBlockDev: dev, err := os.Stat(hostPath.Path) if err != nil { - return nil, errors.Wrap(err, "error checking HostPathBlockDevice") + return nil, fmt.Errorf("error checking HostPathBlockDevice: %w", err) } if dev.Mode()&os.ModeCharDevice == os.ModeCharDevice { - return nil, errors.Errorf("checking HostPathDevice: path %s is not a block device", hostPath.Path) + return nil, fmt.Errorf("checking HostPathDevice: path %s is not a block device", hostPath.Path) } return &KubeVolume{ Type: KubeVolumeTypeBlockDevice, @@ -95,10 +96,10 @@ func VolumeFromHostPath(hostPath *v1.HostPathVolumeSource) (*KubeVolume, error) case v1.HostPathCharDev: dev, err := os.Stat(hostPath.Path) if err != nil { - return nil, errors.Wrap(err, "error checking HostPathCharDevice") + return nil, fmt.Errorf("error checking HostPathCharDevice: %w", err) } if dev.Mode()&os.ModeCharDevice != os.ModeCharDevice { - return nil, errors.Errorf("checking HostPathCharDevice: path %s is not a character device", hostPath.Path) + return nil, fmt.Errorf("checking HostPathCharDevice: path %s is not a character device", hostPath.Path) } return &KubeVolume{ Type: KubeVolumeTypeCharDevice, @@ -110,12 +111,12 @@ func VolumeFromHostPath(hostPath *v1.HostPathVolumeSource) (*KubeVolume, error) // do nothing here because we will verify the path exists in validateVolumeHostDir break default: - return nil, errors.Errorf("Invalid HostPath type %v", hostPath.Type) + return nil, fmt.Errorf("invalid HostPath type %v", hostPath.Type) } } if err := parse.ValidateVolumeHostDir(hostPath.Path); err != nil { - return nil, errors.Wrapf(err, "error in parsing HostPath in YAML") + return nil, fmt.Errorf("error in parsing HostPath in YAML: %w", err) } return &KubeVolume{ @@ -152,7 +153,7 @@ func VolumeFromConfigMap(configMapVolumeSource *v1.ConfigMapVolumeSource, config kv.Optional = *configMapVolumeSource.Optional return kv, nil } - return nil, errors.Errorf("no such ConfigMap %q", configMapVolumeSource.Name) + return nil, fmt.Errorf("no such ConfigMap %q", configMapVolumeSource.Name) } // If there are Items specified in the volumeSource, that overwrites the Data from the configmap @@ -180,7 +181,7 @@ func VolumeFromSource(volumeSource v1.VolumeSource, configMaps []v1.ConfigMap) ( case volumeSource.ConfigMap != nil: return VolumeFromConfigMap(volumeSource.ConfigMap, configMaps) default: - return nil, errors.Errorf("HostPath, ConfigMap, and PersistentVolumeClaim are currently the only supported VolumeSource") + return nil, errors.New("HostPath, ConfigMap, and PersistentVolumeClaim are currently the only supported VolumeSource") } } @@ -191,7 +192,7 @@ func InitializeVolumes(specVolumes []v1.Volume, configMaps []v1.ConfigMap) (map[ for _, specVolume := range specVolumes { volume, err := VolumeFromSource(specVolume.VolumeSource, configMaps) if err != nil { - return nil, errors.Wrapf(err, "failed to create volume %q", specVolume.Name) + return nil, fmt.Errorf("failed to create volume %q: %w", specVolume.Name, err) } volumes[specVolume.Name] = volume diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go index 4224d16ce..f0d4e9153 100644 --- a/pkg/specgen/generate/namespaces.go +++ b/pkg/specgen/generate/namespaces.go @@ -1,6 +1,7 @@ package generate import ( + "errors" "fmt" "os" "strings" @@ -15,7 +16,6 @@ import ( "github.com/containers/podman/v4/pkg/util" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -94,7 +94,7 @@ func GetDefaultNamespaceMode(nsType string, cfg *config.Config, pod *libpod.Pod) return ns, err } - return toReturn, errors.Wrapf(define.ErrInvalidArg, "invalid namespace type %q passed", nsType) + return toReturn, fmt.Errorf("invalid namespace type %q passed: %w", nsType, define.ErrInvalidArg) } // namespaceOptions generates container creation options for all @@ -113,18 +113,18 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. if err != nil { // This is likely to be of the fatal kind (pod was // removed) so hard fail - return nil, errors.Wrapf(err, "error looking up pod %s infra container", pod.ID()) + return nil, fmt.Errorf("error looking up pod %s infra container: %w", pod.ID(), err) } if infraID != "" { ctr, err := rt.GetContainer(infraID) if err != nil { - return nil, errors.Wrapf(err, "error retrieving pod %s infra container %s", pod.ID(), infraID) + return nil, fmt.Errorf("error retrieving pod %s infra container %s: %w", pod.ID(), infraID, err) } infraCtr = ctr } } - errNoInfra := errors.Wrapf(define.ErrInvalidArg, "cannot use pod namespace as container is not joining a pod or pod has no infra container") + errNoInfra := fmt.Errorf("cannot use pod namespace as container is not joining a pod or pod has no infra container: %w", define.ErrInvalidArg) // PID switch s.PidNS.NSMode { @@ -136,7 +136,7 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. case specgen.FromContainer: pidCtr, err := rt.LookupContainer(s.PidNS.Value) if err != nil { - return nil, errors.Wrapf(err, "error looking up container to share pid namespace with") + return nil, fmt.Errorf("error looking up container to share pid namespace with: %w", err) } toReturn = append(toReturn, libpod.WithPIDNSFrom(pidCtr)) } @@ -155,10 +155,10 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. case specgen.FromContainer: ipcCtr, err := rt.LookupContainer(s.IpcNS.Value) if err != nil { - return nil, errors.Wrapf(err, "error looking up container to share ipc namespace with") + return nil, fmt.Errorf("error looking up container to share ipc namespace with: %w", err) } if ipcCtr.ConfigNoCopy().NoShmShare { - return nil, errors.Errorf("joining IPC of container %s is not allowed: non-shareable IPC (hint: use IpcMode:shareable for the donor container)", ipcCtr.ID()) + return nil, fmt.Errorf("joining IPC of container %s is not allowed: non-shareable IPC (hint: use IpcMode:shareable for the donor container)", ipcCtr.ID()) } toReturn = append(toReturn, libpod.WithIPCNSFrom(ipcCtr)) if !ipcCtr.ConfigNoCopy().NoShm { @@ -176,11 +176,18 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. if pod == nil || infraCtr == nil { return nil, errNoInfra } - toReturn = append(toReturn, libpod.WithUTSNSFrom(infraCtr)) + if pod.NamespaceMode(spec.UTSNamespace) == host { + // adding infra as a nsCtr is not what we want to do when uts == host + // this leads the new ctr to try to add an ns path which is should not in this mode + logrus.Debug("pod has host uts, not adding infra as a nsCtr") + s.UtsNS = specgen.Namespace{NSMode: specgen.Host} + } else { + toReturn = append(toReturn, libpod.WithUTSNSFrom(infraCtr)) + } case specgen.FromContainer: utsCtr, err := rt.LookupContainer(s.UtsNS.Value) if err != nil { - return nil, errors.Wrapf(err, "error looking up container to share uts namespace with") + return nil, fmt.Errorf("error looking up container to share uts namespace with: %w", err) } toReturn = append(toReturn, libpod.WithUTSNSFrom(utsCtr)) } @@ -215,7 +222,7 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. case specgen.FromContainer: userCtr, err := rt.LookupContainer(s.UserNS.Value) if err != nil { - return nil, errors.Wrapf(err, "error looking up container to share user namespace with") + return nil, fmt.Errorf("error looking up container to share user namespace with: %w", err) } toReturn = append(toReturn, libpod.WithUserNSFrom(userCtr)) } @@ -227,7 +234,7 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. if pod == nil { toReturn = append(toReturn, libpod.WithIDMappings(*s.IDMappings)) } else if pod.HasInfraContainer() && (len(s.IDMappings.UIDMap) > 0 || len(s.IDMappings.GIDMap) > 0) { - return nil, errors.Wrapf(define.ErrInvalidArg, "cannot specify a new uid/gid map when entering a pod with an infra container") + return nil, fmt.Errorf("cannot specify a new uid/gid map when entering a pod with an infra container: %w", define.ErrInvalidArg) } } if s.User != "" { @@ -247,7 +254,7 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. case specgen.FromContainer: cgroupCtr, err := rt.LookupContainer(s.CgroupNS.Value) if err != nil { - return nil, errors.Wrapf(err, "error looking up container to share cgroup namespace with") + return nil, fmt.Errorf("error looking up container to share cgroup namespace with: %w", err) } toReturn = append(toReturn, libpod.WithCgroupNSFrom(cgroupCtr)) } @@ -275,7 +282,7 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. case specgen.FromContainer: netCtr, err := rt.LookupContainer(s.NetNS.Value) if err != nil { - return nil, errors.Wrapf(err, "error looking up container to share net namespace with") + return nil, fmt.Errorf("error looking up container to share net namespace with: %w", err) } toReturn = append(toReturn, libpod.WithNetNSFrom(netCtr)) case specgen.Slirp: @@ -355,7 +362,7 @@ func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt switch s.PidNS.NSMode { case specgen.Path: if _, err := os.Stat(s.PidNS.Value); err != nil { - return errors.Wrap(err, "cannot find specified PID namespace path") + return fmt.Errorf("cannot find specified PID namespace path: %w", err) } if err := g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), s.PidNS.Value); err != nil { return err @@ -374,7 +381,7 @@ func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt switch s.IpcNS.NSMode { case specgen.Path: if _, err := os.Stat(s.IpcNS.Value); err != nil { - return errors.Wrap(err, "cannot find specified IPC namespace path") + return fmt.Errorf("cannot find specified IPC namespace path: %w", err) } if err := g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), s.IpcNS.Value); err != nil { return err @@ -393,7 +400,7 @@ func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt switch s.UtsNS.NSMode { case specgen.Path: if _, err := os.Stat(s.UtsNS.Value); err != nil { - return errors.Wrap(err, "cannot find specified UTS namespace path") + return fmt.Errorf("cannot find specified UTS namespace path: %w", err) } if err := g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), s.UtsNS.Value); err != nil { return err @@ -416,13 +423,13 @@ func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt case s.UtsNS.NSMode == specgen.FromContainer: utsCtr, err := rt.LookupContainer(s.UtsNS.Value) if err != nil { - return errors.Wrapf(err, "error looking up container to share uts namespace with") + return fmt.Errorf("error looking up container to share uts namespace with: %w", err) } hostname = utsCtr.Hostname() case (s.NetNS.NSMode == specgen.Host && hostname == "") || s.UtsNS.NSMode == specgen.Host: tmpHostname, err := os.Hostname() if err != nil { - return errors.Wrap(err, "unable to retrieve hostname of the host") + return fmt.Errorf("unable to retrieve hostname of the host: %w", err) } hostname = tmpHostname default: @@ -451,7 +458,7 @@ func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt switch s.CgroupNS.NSMode { case specgen.Path: if _, err := os.Stat(s.CgroupNS.Value); err != nil { - return errors.Wrap(err, "cannot find specified cgroup namespace path") + return fmt.Errorf("cannot find specified cgroup namespace path: %w", err) } if err := g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), s.CgroupNS.Value); err != nil { return err @@ -470,7 +477,7 @@ func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt switch s.NetNS.NSMode { case specgen.Path: if _, err := os.Stat(s.NetNS.Value); err != nil { - return errors.Wrap(err, "cannot find specified network namespace path") + return fmt.Errorf("cannot find specified network namespace path: %w", err) } if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), s.NetNS.Value); err != nil { return err @@ -514,7 +521,7 @@ func GetNamespaceOptions(ns []string, netnsIsHost bool) ([]libpod.PodCreateOptio case "net": options = append(options, libpod.WithPodNet()) case "mnt": - return erroredOptions, errors.Errorf("Mount sharing functionality not supported on pod level") + return erroredOptions, fmt.Errorf("mount sharing functionality not supported on pod level") case "pid": options = append(options, libpod.WithPodPID()) case "user": @@ -527,7 +534,7 @@ func GetNamespaceOptions(ns []string, netnsIsHost bool) ([]libpod.PodCreateOptio case "none": return erroredOptions, nil default: - return erroredOptions, errors.Errorf("Invalid kernel namespace to share: %s. Options are: cgroup, ipc, net, pid, uts or none", toShare) + return erroredOptions, fmt.Errorf("invalid kernel namespace to share: %s. Options are: cgroup, ipc, net, pid, uts or none", toShare) } } return options, nil diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index 1044854f4..bb5f2d0ec 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -3,6 +3,7 @@ package generate import ( "context" "encoding/json" + "fmt" "path" "strings" @@ -15,7 +16,6 @@ import ( "github.com/containers/podman/v4/pkg/specgen" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" - "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) @@ -117,7 +117,7 @@ func makeCommand(s *specgen.SpecGenerator, imageData *libimage.ImageData, rtc *c finalCommand = append(finalCommand, command...) if len(finalCommand) == 0 { - return nil, errors.Errorf("no command or entrypoint provided, and no CMD or ENTRYPOINT from image") + return nil, fmt.Errorf("no command or entrypoint provided, and no CMD or ENTRYPOINT from image") } if s.Init { @@ -126,7 +126,7 @@ func makeCommand(s *specgen.SpecGenerator, imageData *libimage.ImageData, rtc *c initPath = rtc.Engine.InitPath } if initPath == "" { - return nil, errors.Errorf("no path to init binary found but container requested an init") + return nil, fmt.Errorf("no path to init binary found but container requested an init") } finalCommand = append([]string{define.ContainerInitPath, "--"}, finalCommand...) } @@ -348,7 +348,7 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt for k, v := range s.WeightDevice { statT := unix.Stat_t{} if err := unix.Stat(k, &statT); err != nil { - return nil, errors.Wrapf(err, "failed to inspect '%s' in --blkio-weight-device", k) + return nil, fmt.Errorf("failed to inspect '%s' in --blkio-weight-device: %w", k, err) } g.AddLinuxResourcesBlockIOWeightDevice((int64(unix.Major(uint64(statT.Rdev)))), (int64(unix.Minor(uint64(statT.Rdev)))), *v.Weight) //nolint: unconvert } diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go index 4ac8a0aa2..212d613fe 100644 --- a/pkg/specgen/generate/pod_create.go +++ b/pkg/specgen/generate/pod_create.go @@ -13,7 +13,6 @@ import ( "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/specgen" "github.com/containers/podman/v4/pkg/specgenutil" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -60,6 +59,7 @@ func MakePod(p *entities.PodSpec, rt *libpod.Runtime) (*libpod.Pod, error) { if err != nil { return nil, err } + spec.Pod = pod.ID() opts = append(opts, rt.WithPod(pod)) spec.CgroupParent = pod.CgroupParent() @@ -154,7 +154,7 @@ func MapSpec(p *specgen.PodSpecGenerator) (*specgen.SpecGenerator, error) { if len(p.InfraContainerSpec.PortMappings) > 0 || len(p.InfraContainerSpec.Networks) > 0 || p.InfraContainerSpec.NetNS.NSMode == specgen.NoNetwork { - return nil, errors.Wrapf(define.ErrInvalidArg, "cannot set host network if network-related configuration is specified") + return nil, fmt.Errorf("cannot set host network if network-related configuration is specified: %w", define.ErrInvalidArg) } p.InfraContainerSpec.NetNS.NSMode = specgen.Host case specgen.Slirp: @@ -168,11 +168,11 @@ func MapSpec(p *specgen.PodSpecGenerator) (*specgen.SpecGenerator, error) { if len(p.InfraContainerSpec.PortMappings) > 0 || len(p.InfraContainerSpec.Networks) > 0 || p.InfraContainerSpec.NetNS.NSMode == specgen.Host { - return nil, errors.Wrapf(define.ErrInvalidArg, "cannot disable pod network if network-related configuration is specified") + return nil, fmt.Errorf("cannot disable pod network if network-related configuration is specified: %w", define.ErrInvalidArg) } p.InfraContainerSpec.NetNS.NSMode = specgen.NoNetwork default: - return nil, errors.Errorf("pods presently do not support network mode %s", p.NetNS.NSMode) + return nil, fmt.Errorf("pods presently do not support network mode %s", p.NetNS.NSMode) } if len(p.InfraCommand) > 0 { diff --git a/pkg/specgen/generate/ports.go b/pkg/specgen/generate/ports.go index 4243630e2..572f256c1 100644 --- a/pkg/specgen/generate/ports.go +++ b/pkg/specgen/generate/ports.go @@ -13,7 +13,6 @@ import ( "github.com/containers/common/pkg/util" "github.com/containers/podman/v4/pkg/specgen" "github.com/containers/podman/v4/pkg/specgenutil" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -46,7 +45,7 @@ func joinTwoPortsToRangePortIfPossible(ports *[]types.PortMapping, allHostPorts, // if both host port ranges overlap and the container port range did not match // we have to error because we cannot assign the same host port to more than one container port if previousPort.HostPort+previousPort.Range-1 > port.HostPort { - return nil, errors.Errorf("conflicting port mappings for host port %d (protocol %s)", port.HostPort, port.Protocol) + return nil, fmt.Errorf("conflicting port mappings for host port %d (protocol %s)", port.HostPort, port.Protocol) } } // we could not join the ports so we append the old one to the list @@ -127,7 +126,7 @@ outer: rangePort = fmt.Sprintf("with range %d ", port.Range) } - return port, errors.Errorf("failed to find an open port to expose container port %d %son the host", port.ContainerPort, rangePort) + return port, fmt.Errorf("failed to find an open port to expose container port %d %son the host", port.ContainerPort, rangePort) } // Parse port maps to port mappings. @@ -163,7 +162,7 @@ func ParsePortMapping(portMappings []types.PortMapping, exposePorts map[uint16][ } if port.HostIP != "" { if ip := net.ParseIP(port.HostIP); ip == nil { - return nil, errors.Errorf("invalid IP address %q in port mapping", port.HostIP) + return nil, fmt.Errorf("invalid IP address %q in port mapping", port.HostIP) } } @@ -174,14 +173,14 @@ func ParsePortMapping(portMappings []types.PortMapping, exposePorts map[uint16][ } containerPort := port.ContainerPort if containerPort == 0 { - return nil, errors.Errorf("container port number must be non-0") + return nil, fmt.Errorf("container port number must be non-0") } hostPort := port.HostPort if uint32(portRange-1)+uint32(containerPort) > 65535 { - return nil, errors.Errorf("container port range exceeds maximum allowable port number") + return nil, fmt.Errorf("container port range exceeds maximum allowable port number") } if uint32(portRange-1)+uint32(hostPort) > 65535 { - return nil, errors.Errorf("host port range exceeds maximum allowable port number") + return nil, fmt.Errorf("host port range exceeds maximum allowable port number") } hostProtoMap, ok := portMap[port.HostIP] @@ -351,11 +350,11 @@ func createPortMappings(s *specgen.SpecGenerator, imageData *libimage.ImageData) for _, expose := range []map[uint16]string{expose, s.Expose} { for port, proto := range expose { if port == 0 { - return nil, nil, errors.Errorf("cannot expose 0 as it is not a valid port number") + return nil, nil, fmt.Errorf("cannot expose 0 as it is not a valid port number") } protocols, err := checkProtocol(proto, false) if err != nil { - return nil, nil, errors.Wrapf(err, "error validating protocols for exposed port %d", port) + return nil, nil, fmt.Errorf("error validating protocols for exposed port %d: %w", port, err) } toExpose[port] = appendProtocolsNoDuplicates(toExpose[port], protocols) } @@ -387,11 +386,11 @@ func checkProtocol(protocol string, allowSCTP bool) ([]string, error) { protocols[protoUDP] = struct{}{} case protoSCTP: if !allowSCTP { - return nil, errors.Errorf("protocol SCTP is not allowed for exposed ports") + return nil, fmt.Errorf("protocol SCTP is not allowed for exposed ports") } protocols[protoSCTP] = struct{}{} default: - return nil, errors.Errorf("unrecognized protocol %q in port mapping", p) + return nil, fmt.Errorf("unrecognized protocol %q in port mapping", p) } } @@ -402,7 +401,7 @@ func checkProtocol(protocol string, allowSCTP bool) ([]string, error) { // This shouldn't be possible, but check anyways if len(finalProto) == 0 { - return nil, errors.Errorf("no valid protocols specified for port mapping") + return nil, fmt.Errorf("no valid protocols specified for port mapping") } return finalProto, nil @@ -415,7 +414,7 @@ func GenExposedPorts(exposedPorts map[string]struct{}) (map[uint16]string, error } toReturn, err := specgenutil.CreateExpose(expose) if err != nil { - return nil, errors.Wrapf(err, "unable to convert image EXPOSE") + return nil, fmt.Errorf("unable to convert image EXPOSE: %w", err) } return toReturn, nil } diff --git a/pkg/specgen/generate/security.go b/pkg/specgen/generate/security.go index 7268ec318..aacefcbac 100644 --- a/pkg/specgen/generate/security.go +++ b/pkg/specgen/generate/security.go @@ -1,6 +1,7 @@ package generate import ( + "fmt" "strings" "github.com/containers/common/libimage" @@ -14,7 +15,6 @@ import ( "github.com/containers/podman/v4/pkg/util" "github.com/opencontainers/runtime-tools/generate" "github.com/opencontainers/selinux/go-selinux/label" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -32,11 +32,11 @@ func setLabelOpts(s *specgen.SpecGenerator, runtime *libpod.Runtime, pidConfig s } else if pidConfig.IsContainer() { ctr, err := runtime.LookupContainer(pidConfig.Value) if err != nil { - return errors.Wrapf(err, "container %q not found", pidConfig.Value) + return fmt.Errorf("container %q not found: %w", pidConfig.Value, err) } secopts, err := label.DupSecOpt(ctr.ProcessLabel()) if err != nil { - return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel()) + return fmt.Errorf("failed to duplicate label %q : %w", ctr.ProcessLabel(), err) } labelOpts = append(labelOpts, secopts...) } @@ -46,11 +46,11 @@ func setLabelOpts(s *specgen.SpecGenerator, runtime *libpod.Runtime, pidConfig s } else if ipcConfig.IsContainer() { ctr, err := runtime.LookupContainer(ipcConfig.Value) if err != nil { - return errors.Wrapf(err, "container %q not found", ipcConfig.Value) + return fmt.Errorf("container %q not found: %w", ipcConfig.Value, err) } secopts, err := label.DupSecOpt(ctr.ProcessLabel()) if err != nil { - return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel()) + return fmt.Errorf("failed to duplicate label %q : %w", ctr.ProcessLabel(), err) } labelOpts = append(labelOpts, secopts...) } @@ -63,7 +63,7 @@ func setupApparmor(s *specgen.SpecGenerator, rtc *config.Config, g *generate.Gen hasProfile := len(s.ApparmorProfile) > 0 if !apparmor.IsEnabled() { if hasProfile && s.ApparmorProfile != "unconfined" { - return errors.Errorf("Apparmor profile %q specified, but Apparmor is not enabled on this system", s.ApparmorProfile) + return fmt.Errorf("apparmor profile %q specified, but Apparmor is not enabled on this system", s.ApparmorProfile) } return nil } @@ -129,7 +129,7 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, // Pass capRequiredRequested in CapAdd field to normalize capabilities names capsRequired, err := capabilities.MergeCapabilities(nil, capsRequiredRequested, nil) if err != nil { - return errors.Wrapf(err, "capabilities requested by user or image are not valid: %q", strings.Join(capsRequired, ",")) + return fmt.Errorf("capabilities requested by user or image are not valid: %q: %w", strings.Join(capsRequired, ","), err) } // Verify all capRequired are in the capList for _, cap := range capsRequired { @@ -161,7 +161,7 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, } else { mergedCaps, err := capabilities.MergeCapabilities(nil, s.CapAdd, nil) if err != nil { - return errors.Wrapf(err, "capabilities requested by user are not valid: %q", strings.Join(s.CapAdd, ",")) + return fmt.Errorf("capabilities requested by user are not valid: %q: %w", strings.Join(s.CapAdd, ","), err) } boundingSet, err := capabilities.BoundingSet() if err != nil { @@ -245,17 +245,17 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, for sysctlKey, sysctlVal := range s.Sysctl { if s.IpcNS.IsHost() && strings.HasPrefix(sysctlKey, "fs.mqueue.") { - return errors.Wrapf(define.ErrInvalidArg, "sysctl %s=%s can't be set since IPC Namespace set to host", sysctlKey, sysctlVal) + return fmt.Errorf("sysctl %s=%s can't be set since IPC Namespace set to host: %w", sysctlKey, sysctlVal, define.ErrInvalidArg) } // Ignore net sysctls if --net=host if s.NetNS.IsHost() && strings.HasPrefix(sysctlKey, "net.") { - return errors.Wrapf(define.ErrInvalidArg, "sysctl %s=%s can't be set since Network Namespace set to host", sysctlKey, sysctlVal) + return fmt.Errorf("sysctl %s=%s can't be set since Network Namespace set to host: %w", sysctlKey, sysctlVal, define.ErrInvalidArg) } // Ignore uts sysctls if --uts=host if s.UtsNS.IsHost() && (strings.HasPrefix(sysctlKey, "kernel.domainname") || strings.HasPrefix(sysctlKey, "kernel.hostname")) { - return errors.Wrapf(define.ErrInvalidArg, "sysctl %s=%s can't be set since UTS Namespace set to host", sysctlKey, sysctlVal) + return fmt.Errorf("sysctl %s=%s can't be set since UTS Namespace set to host: %w", sysctlKey, sysctlVal, define.ErrInvalidArg) } g.AddLinuxSysctl(sysctlKey, sysctlVal) diff --git a/pkg/specgen/generate/storage.go b/pkg/specgen/generate/storage.go index 0a4d03780..867bb4b79 100644 --- a/pkg/specgen/generate/storage.go +++ b/pkg/specgen/generate/storage.go @@ -2,6 +2,7 @@ package generate import ( "context" + "errors" "fmt" "os" "path" @@ -16,11 +17,10 @@ import ( "github.com/containers/podman/v4/pkg/specgen" "github.com/containers/podman/v4/pkg/util" spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) -var errDuplicateDest = errors.Errorf("duplicate mount destination") +var errDuplicateDest = errors.New("duplicate mount destination") // Produce final mounts and named volumes for a container func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, img *libimage.Image) ([]spec.Mount, []*specgen.NamedVolume, []*specgen.OverlayVolume, error) { @@ -63,7 +63,7 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru } cleanDestination := filepath.Clean(m.Destination) if _, ok := unifiedMounts[cleanDestination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified mounts - multiple mounts at %q", cleanDestination) + return nil, nil, nil, fmt.Errorf("conflict in specified mounts - multiple mounts at %q: %w", cleanDestination, errDuplicateDest) } unifiedMounts[cleanDestination] = m } @@ -84,7 +84,7 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru } cleanDestination := filepath.Clean(v.Dest) if _, ok := unifiedVolumes[cleanDestination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified volumes - multiple volumes at %q", cleanDestination) + return nil, nil, nil, fmt.Errorf("conflict in specified volumes - multiple volumes at %q: %w", cleanDestination, errDuplicateDest) } unifiedVolumes[cleanDestination] = v } @@ -105,7 +105,7 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru } cleanDestination := filepath.Clean(v.Destination) if _, ok := unifiedOverlays[cleanDestination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified volumes - multiple volumes at %q", cleanDestination) + return nil, nil, nil, fmt.Errorf("conflict in specified volumes - multiple volumes at %q: %w", cleanDestination, errDuplicateDest) } unifiedOverlays[cleanDestination] = v } @@ -131,7 +131,7 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru return nil, nil, nil, err } if _, ok := unifiedMounts[initMount.Destination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict with mount added by --init to %q", initMount.Destination) + return nil, nil, nil, fmt.Errorf("conflict with mount added by --init to %q: %w", initMount.Destination, errDuplicateDest) } unifiedMounts[initMount.Destination] = initMount } @@ -161,12 +161,12 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru // Check for conflicts between named volumes and mounts for dest := range baseMounts { if _, ok := baseVolumes[dest]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) + return nil, nil, nil, fmt.Errorf("conflict at mount destination %v: %w", dest, errDuplicateDest) } } for dest := range baseVolumes { if _, ok := baseMounts[dest]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) + return nil, nil, nil, fmt.Errorf("conflict at mount destination %v: %w", dest, errDuplicateDest) } } // Final step: maps to arrays @@ -175,7 +175,7 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru if mount.Type == define.TypeBind { absSrc, err := filepath.Abs(mount.Source) if err != nil { - return nil, nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source) + return nil, nil, nil, fmt.Errorf("error getting absolute path of %s: %w", mount.Source, err) } mount.Source = absSrc } @@ -208,7 +208,7 @@ func getImageVolumes(ctx context.Context, img *libimage.Image, s *specgen.SpecGe inspect, err := img.Inspect(ctx, nil) if err != nil { - return nil, nil, errors.Wrapf(err, "error inspecting image to get image volumes") + return nil, nil, fmt.Errorf("error inspecting image to get image volumes: %w", err) } for volume := range inspect.Config.Volumes { logrus.Debugf("Image has volume at %q", volume) @@ -252,16 +252,16 @@ func getVolumesFrom(volumesFrom []string, runtime *libpod.Runtime) (map[string]s switch opt { case "z": if setZ { - return nil, nil, errors.Errorf("cannot set :z more than once in mount options") + return nil, nil, errors.New("cannot set :z more than once in mount options") } setZ = true case "ro", "rw": if setRORW { - return nil, nil, errors.Errorf("cannot set ro or rw options more than once") + return nil, nil, errors.New("cannot set ro or rw options more than once") } setRORW = true default: - return nil, nil, errors.Errorf("invalid option %q specified - volumes from another container can only use z,ro,rw options", opt) + return nil, nil, fmt.Errorf("invalid option %q specified - volumes from another container can only use z,ro,rw options", opt) } } options = splitOpts @@ -269,7 +269,7 @@ func getVolumesFrom(volumesFrom []string, runtime *libpod.Runtime) (map[string]s ctr, err := runtime.LookupContainer(splitVol[0]) if err != nil { - return nil, nil, errors.Wrapf(err, "error looking up container %q for volumes-from", splitVol[0]) + return nil, nil, fmt.Errorf("error looking up container %q for volumes-from: %w", splitVol[0], err) } logrus.Debugf("Adding volumes from container %s", ctr.ID()) @@ -290,7 +290,7 @@ func getVolumesFrom(volumesFrom []string, runtime *libpod.Runtime) (map[string]s // and append them in if we can find them. spec := ctr.Spec() if spec == nil { - return nil, nil, errors.Errorf("retrieving container %s spec for volumes-from", ctr.ID()) + return nil, nil, fmt.Errorf("retrieving container %s spec for volumes-from", ctr.ID()) } for _, mnt := range spec.Mounts { if mnt.Type != define.TypeBind { @@ -364,16 +364,16 @@ func addContainerInitBinary(s *specgen.SpecGenerator, path string) (spec.Mount, } if path == "" { - return mount, fmt.Errorf("please specify a path to the container-init binary") + return mount, errors.New("please specify a path to the container-init binary") } if !s.PidNS.IsPrivate() { - return mount, fmt.Errorf("cannot add init binary as PID 1 (PID namespace isn't private)") + return mount, errors.New("cannot add init binary as PID 1 (PID namespace isn't private)") } if s.Systemd == "always" { - return mount, fmt.Errorf("cannot use container-init binary with systemd=always") + return mount, errors.New("cannot use container-init binary with systemd=always") } if _, err := os.Stat(path); os.IsNotExist(err) { - return mount, errors.Wrap(err, "container-init binary not found on the host") + return mount, fmt.Errorf("container-init binary not found on the host: %w", err) } return mount, nil } diff --git a/pkg/specgen/generate/validate.go b/pkg/specgen/generate/validate.go index a1affef31..9c933d747 100644 --- a/pkg/specgen/generate/validate.go +++ b/pkg/specgen/generate/validate.go @@ -1,6 +1,8 @@ package generate import ( + "errors" + "fmt" "io/ioutil" "os" "path/filepath" @@ -9,7 +11,6 @@ import ( "github.com/containers/common/pkg/sysinfo" "github.com/containers/podman/v4/pkg/specgen" "github.com/containers/podman/v4/utils" - "github.com/pkg/errors" ) // Verify resource limits are sanely set when running on cgroup v1. @@ -23,7 +24,7 @@ func verifyContainerResourcesCgroupV1(s *specgen.SpecGenerator) ([]string, error } if s.ResourceLimits.Unified != nil { - return nil, errors.New("Cannot use --cgroup-conf without cgroup v2") + return nil, errors.New("cannot use --cgroup-conf without cgroup v2") } // Memory checks @@ -49,7 +50,7 @@ func verifyContainerResourcesCgroupV1(s *specgen.SpecGenerator) ([]string, error warnings = append(warnings, "Your kernel does not support memory swappiness capabilities, or the cgroup is not mounted. Memory swappiness discarded.") memory.Swappiness = nil } else if *memory.Swappiness > 100 { - return warnings, errors.Errorf("invalid value: %v, valid memory swappiness range is 0-100", *memory.Swappiness) + return warnings, fmt.Errorf("invalid value: %v, valid memory swappiness range is 0-100", *memory.Swappiness) } } if memory.Reservation != nil && !sysInfo.MemoryReservation { @@ -104,18 +105,18 @@ func verifyContainerResourcesCgroupV1(s *specgen.SpecGenerator) ([]string, error cpusAvailable, err := sysInfo.IsCpusetCpusAvailable(cpu.Cpus) if err != nil { - return warnings, errors.Errorf("invalid value %s for cpuset cpus", cpu.Cpus) + return warnings, fmt.Errorf("invalid value %s for cpuset cpus", cpu.Cpus) } if !cpusAvailable { - return warnings, errors.Errorf("requested CPUs are not available - requested %s, available: %s", cpu.Cpus, sysInfo.Cpus) + return warnings, fmt.Errorf("requested CPUs are not available - requested %s, available: %s", cpu.Cpus, sysInfo.Cpus) } memsAvailable, err := sysInfo.IsCpusetMemsAvailable(cpu.Mems) if err != nil { - return warnings, errors.Errorf("invalid value %s for cpuset mems", cpu.Mems) + return warnings, fmt.Errorf("invalid value %s for cpuset mems", cpu.Mems) } if !memsAvailable { - return warnings, errors.Errorf("requested memory nodes are not available - requested %s, available: %s", cpu.Mems, sysInfo.Mems) + return warnings, fmt.Errorf("requested memory nodes are not available - requested %s, available: %s", cpu.Mems, sysInfo.Mems) } } diff --git a/pkg/specgen/namespaces.go b/pkg/specgen/namespaces.go index f1343f6e2..03a2049f6 100644 --- a/pkg/specgen/namespaces.go +++ b/pkg/specgen/namespaces.go @@ -1,6 +1,7 @@ package specgen import ( + "errors" "fmt" "net" "os" @@ -14,7 +15,6 @@ import ( "github.com/containers/storage" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" - "github.com/pkg/errors" ) type NamespaceMode string @@ -158,18 +158,18 @@ func validateNetNS(n *Namespace) error { case "", Default, Host, Path, FromContainer, FromPod, Private, NoNetwork, Bridge: break default: - return errors.Errorf("invalid network %q", n.NSMode) + return fmt.Errorf("invalid network %q", n.NSMode) } // Path and From Container MUST have a string value set if n.NSMode == Path || n.NSMode == FromContainer { if len(n.Value) < 1 { - return errors.Errorf("namespace mode %s requires a value", n.NSMode) + return fmt.Errorf("namespace mode %s requires a value", n.NSMode) } } else if n.NSMode != Slirp { // All others except must NOT set a string value if len(n.Value) > 0 { - return errors.Errorf("namespace value %s cannot be provided with namespace mode %s", n.Value, n.NSMode) + return fmt.Errorf("namespace value %s cannot be provided with namespace mode %s", n.Value, n.NSMode) } } @@ -197,20 +197,20 @@ func (n *Namespace) validate() error { case "", Default, Host, Path, FromContainer, FromPod, Private: // Valid, do nothing case NoNetwork, Bridge, Slirp: - return errors.Errorf("cannot use network modes with non-network namespace") + return errors.New("cannot use network modes with non-network namespace") default: - return errors.Errorf("invalid namespace type %s specified", n.NSMode) + return fmt.Errorf("invalid namespace type %s specified", n.NSMode) } // Path and From Container MUST have a string value set if n.NSMode == Path || n.NSMode == FromContainer { if len(n.Value) < 1 { - return errors.Errorf("namespace mode %s requires a value", n.NSMode) + return fmt.Errorf("namespace mode %s requires a value", n.NSMode) } } else { // All others must NOT set a string value if len(n.Value) > 0 { - return errors.Errorf("namespace value %s cannot be provided with namespace mode %s", n.Value, n.NSMode) + return fmt.Errorf("namespace value %s cannot be provided with namespace mode %s", n.Value, n.NSMode) } } return nil @@ -231,19 +231,19 @@ func ParseNamespace(ns string) (Namespace, error) { case strings.HasPrefix(ns, "ns:"): split := strings.SplitN(ns, ":", 2) if len(split) != 2 { - return toReturn, errors.Errorf("must provide a path to a namespace when specifying \"ns:\"") + return toReturn, fmt.Errorf("must provide a path to a namespace when specifying \"ns:\"") } toReturn.NSMode = Path toReturn.Value = split[1] case strings.HasPrefix(ns, "container:"): split := strings.SplitN(ns, ":", 2) if len(split) != 2 { - return toReturn, errors.Errorf("must provide name or ID or a container when specifying \"container:\"") + return toReturn, fmt.Errorf("must provide name or ID or a container when specifying \"container:\"") } toReturn.NSMode = FromContainer toReturn.Value = split[1] default: - return toReturn, errors.Errorf("unrecognized namespace mode %s passed", ns) + return toReturn, fmt.Errorf("unrecognized namespace mode %s passed", ns) } return toReturn, nil @@ -266,7 +266,7 @@ func ParseCgroupNamespace(ns string) (Namespace, error) { case "private", "": toReturn.NSMode = Private default: - return toReturn, errors.Errorf("unrecognized cgroup namespace mode %s passed", ns) + return toReturn, fmt.Errorf("unrecognized cgroup namespace mode %s passed", ns) } } else { toReturn.NSMode = Host @@ -300,7 +300,7 @@ func ParseUserNamespace(ns string) (Namespace, error) { case strings.HasPrefix(ns, "auto:"): split := strings.SplitN(ns, ":", 2) if len(split) != 2 { - return toReturn, errors.Errorf("invalid setting for auto: mode") + return toReturn, errors.New("invalid setting for auto: mode") } toReturn.NSMode = Auto toReturn.Value = split[1] @@ -365,14 +365,14 @@ func ParseNetworkFlag(networks []string) (Namespace, map[string]types.PerNetwork case strings.HasPrefix(ns, "ns:"): split := strings.SplitN(ns, ":", 2) if len(split) != 2 { - return toReturn, nil, nil, errors.Errorf("must provide a path to a namespace when specifying \"ns:\"") + return toReturn, nil, nil, errors.New("must provide a path to a namespace when specifying \"ns:\"") } toReturn.NSMode = Path toReturn.Value = split[1] case strings.HasPrefix(ns, string(FromContainer)+":"): split := strings.SplitN(ns, ":", 2) if len(split) != 2 { - return toReturn, nil, nil, errors.Errorf("must provide name or ID or a container when specifying \"container:\"") + return toReturn, nil, nil, errors.New("must provide name or ID or a container when specifying \"container:\"") } toReturn.NSMode = FromContainer toReturn.Value = split[1] @@ -391,7 +391,7 @@ func ParseNetworkFlag(networks []string) (Namespace, map[string]types.PerNetwork } netOpts, err := parseBridgeNetworkOptions(parts[1]) if err != nil { - return toReturn, nil, nil, errors.Wrapf(err, "invalid option for network %s", parts[0]) + return toReturn, nil, nil, fmt.Errorf("invalid option for network %s: %w", parts[0], err) } podmanNetworks[parts[0]] = netOpts } @@ -402,24 +402,24 @@ func ParseNetworkFlag(networks []string) (Namespace, map[string]types.PerNetwork if len(networks) > 1 { if !toReturn.IsBridge() { - return toReturn, nil, nil, errors.Wrapf(define.ErrInvalidArg, "cannot set multiple networks without bridge network mode, selected mode %s", toReturn.NSMode) + return toReturn, nil, nil, fmt.Errorf("cannot set multiple networks without bridge network mode, selected mode %s: %w", toReturn.NSMode, define.ErrInvalidArg) } for _, network := range networks[1:] { parts := strings.SplitN(network, ":", 2) if parts[0] == "" { - return toReturn, nil, nil, errors.Wrapf(define.ErrInvalidArg, "network name cannot be empty") + return toReturn, nil, nil, fmt.Errorf("network name cannot be empty: %w", define.ErrInvalidArg) } if cutil.StringInSlice(parts[0], []string{string(Bridge), string(Slirp), string(FromPod), string(NoNetwork), string(Default), string(Private), string(Path), string(FromContainer), string(Host)}) { - return toReturn, nil, nil, errors.Wrapf(define.ErrInvalidArg, "can only set extra network names, selected mode %s conflicts with bridge", parts[0]) + return toReturn, nil, nil, fmt.Errorf("can only set extra network names, selected mode %s conflicts with bridge: %w", parts[0], define.ErrInvalidArg) } netOpts := types.PerNetworkOptions{} if len(parts) > 1 { var err error netOpts, err = parseBridgeNetworkOptions(parts[1]) if err != nil { - return toReturn, nil, nil, errors.Wrapf(err, "invalid option for network %s", parts[0]) + return toReturn, nil, nil, fmt.Errorf("invalid option for network %s: %w", parts[0], err) } } podmanNetworks[parts[0]] = netOpts @@ -441,7 +441,7 @@ func parseBridgeNetworkOptions(opts string) (types.PerNetworkOptions, error) { case "ip", "ip6": ip := net.ParseIP(split[1]) if ip == nil { - return netOpts, errors.Errorf("invalid ip address %q", split[1]) + return netOpts, fmt.Errorf("invalid ip address %q", split[1]) } netOpts.StaticIPs = append(netOpts.StaticIPs, ip) @@ -465,7 +465,7 @@ func parseBridgeNetworkOptions(opts string) (types.PerNetworkOptions, error) { netOpts.InterfaceName = split[1] default: - return netOpts, errors.Errorf("unknown bridge network option: %s", split[0]) + return netOpts, fmt.Errorf("unknown bridge network option: %s", split[0]) } } return netOpts, nil @@ -477,7 +477,7 @@ func SetupUserNS(idmappings *storage.IDMappingOptions, userns Namespace, g *gene switch userns.NSMode { case Path: if _, err := os.Stat(userns.Value); err != nil { - return user, errors.Wrap(err, "cannot find specified user namespace path") + return user, fmt.Errorf("cannot find specified user namespace path: %w", err) } if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), userns.Value); err != nil { return user, err @@ -526,7 +526,7 @@ func privateUserNamespace(idmappings *storage.IDMappingOptions, g *generate.Gene return err } if idmappings == nil || (len(idmappings.UIDMap) == 0 && len(idmappings.GIDMap) == 0) { - return errors.Errorf("must provide at least one UID or GID mapping to configure a user namespace") + return errors.New("must provide at least one UID or GID mapping to configure a user namespace") } for _, uidmap := range idmappings.UIDMap { g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size)) diff --git a/pkg/specgen/pod_validate.go b/pkg/specgen/pod_validate.go index 8d971a25e..724be213f 100644 --- a/pkg/specgen/pod_validate.go +++ b/pkg/specgen/pod_validate.go @@ -1,8 +1,10 @@ package specgen import ( + "errors" + "fmt" + "github.com/containers/podman/v4/pkg/util" - "github.com/pkg/errors" ) var ( @@ -13,7 +15,7 @@ var ( ) func exclusivePodOptions(opt1, opt2 string) error { - return errors.Wrapf(ErrInvalidPodSpecConfig, "%s and %s are mutually exclusive pod options", opt1, opt2) + return fmt.Errorf("%s and %s are mutually exclusive pod options: %w", opt1, opt2, ErrInvalidPodSpecConfig) } // Validate verifies the input is valid diff --git a/pkg/specgen/podspecgen.go b/pkg/specgen/podspecgen.go index 02ba06be1..64a79f4ee 100644 --- a/pkg/specgen/podspecgen.go +++ b/pkg/specgen/podspecgen.go @@ -77,6 +77,8 @@ type PodBasicConfig struct { // Any containers created within the pod will inherit the pod's userns settings. // Optional Userns Namespace `json:"userns,omitempty"` + // UtsNs is used to indicate the UTS mode the pod is in + UtsNs Namespace `json:"utsns,omitempty"` // Devices contains user specified Devices to be added to the Pod Devices []string `json:"pod_devices,omitempty"` // Sysctl sets kernel parameters for the pod diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index 42b89ece1..c31c3f035 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -1,6 +1,7 @@ package specgen import ( + "errors" "net" "strings" "syscall" @@ -10,7 +11,6 @@ import ( "github.com/containers/image/v5/manifest" "github.com/containers/storage/types" spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" ) // LogConfig describes the logging characteristics for a container @@ -571,10 +571,10 @@ type Secret struct { var ( // ErrNoStaticIPRootless is used when a rootless user requests to assign a static IP address // to a pod or container - ErrNoStaticIPRootless error = errors.New("rootless containers and pods cannot be assigned static IP addresses") + ErrNoStaticIPRootless = errors.New("rootless containers and pods cannot be assigned static IP addresses") // ErrNoStaticMACRootless is used when a rootless user requests to assign a static MAC address // to a pod or container - ErrNoStaticMACRootless error = errors.New("rootless containers and pods cannot be assigned static MAC addresses") + ErrNoStaticMACRootless = errors.New("rootless containers and pods cannot be assigned static MAC addresses") ) // NewSpecGenerator returns a SpecGenerator struct given one of two mandatory inputs diff --git a/pkg/specgen/volumes.go b/pkg/specgen/volumes.go index c9f944abf..84de4fdd1 100644 --- a/pkg/specgen/volumes.go +++ b/pkg/specgen/volumes.go @@ -1,12 +1,13 @@ package specgen import ( + "errors" + "fmt" "path/filepath" "strings" "github.com/containers/common/pkg/parse" spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -50,13 +51,13 @@ type ImageVolume struct { // GenVolumeMounts parses user input into mounts, volumes and overlay volumes func GenVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*NamedVolume, map[string]*OverlayVolume, error) { - errDuplicateDest := errors.Errorf("duplicate mount destination") + errDuplicateDest := errors.New("duplicate mount destination") mounts := make(map[string]spec.Mount) volumes := make(map[string]*NamedVolume) overlayVolumes := make(map[string]*OverlayVolume) - volumeFormatErr := errors.Errorf("incorrect volume format, should be [host-dir:]ctr-dir[:option]") + volumeFormatErr := errors.New("incorrect volume format, should be [host-dir:]ctr-dir[:option]") for _, vol := range volumeFlag { var ( options []string @@ -67,7 +68,7 @@ func GenVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*Na splitVol := SplitVolumeString(vol) if len(splitVol) > 3 { - return nil, nil, nil, errors.Wrapf(volumeFormatErr, vol) + return nil, nil, nil, fmt.Errorf("%v: %w", vol, volumeFormatErr) } src = splitVol[0] @@ -143,13 +144,13 @@ func GenVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*Na // relative values as lowerdir for overlay mounts source, err := filepath.Abs(src) if err != nil { - return nil, nil, nil, errors.Wrapf(err, "failed while resolving absolute path for source %v for overlay mount", src) + return nil, nil, nil, fmt.Errorf("failed while resolving absolute path for source %v for overlay mount: %w", src, err) } newOverlayVol.Source = source newOverlayVol.Options = options if _, ok := overlayVolumes[newOverlayVol.Destination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, newOverlayVol.Destination) + return nil, nil, nil, fmt.Errorf("%v: %w", newOverlayVol.Destination, errDuplicateDest) } overlayVolumes[newOverlayVol.Destination] = newOverlayVol } else { @@ -160,7 +161,7 @@ func GenVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*Na Options: options, } if _, ok := mounts[newMount.Destination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, newMount.Destination) + return nil, nil, nil, fmt.Errorf("%v: %w", newMount.Destination, errDuplicateDest) } mounts[newMount.Destination] = newMount } @@ -172,7 +173,7 @@ func GenVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*Na newNamedVol.Options = options if _, ok := volumes[newNamedVol.Dest]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, newNamedVol.Dest) + return nil, nil, nil, fmt.Errorf("%v: %w", newNamedVol.Dest, errDuplicateDest) } volumes[newNamedVol.Dest] = newNamedVol } diff --git a/pkg/specgen/winpath.go b/pkg/specgen/winpath.go index 0df4ebdd7..5c19aeb4b 100644 --- a/pkg/specgen/winpath.go +++ b/pkg/specgen/winpath.go @@ -1,11 +1,10 @@ package specgen import ( + "errors" "fmt" "strings" "unicode" - - "github.com/pkg/errors" ) func isHostWinPath(path string) bool { diff --git a/pkg/specgenutil/createparse.go b/pkg/specgenutil/createparse.go index 132f93771..373fb6faa 100644 --- a/pkg/specgenutil/createparse.go +++ b/pkg/specgenutil/createparse.go @@ -1,9 +1,10 @@ package specgenutil import ( + "errors" + "github.com/containers/common/pkg/config" "github.com/containers/podman/v4/pkg/domain/entities" - "github.com/pkg/errors" ) // validate determines if the flags and values given by the user are valid. things checked @@ -11,7 +12,7 @@ import ( func validate(c *entities.ContainerCreateOptions) error { var () if c.Rm && (c.Restart != "" && c.Restart != "no" && c.Restart != "on-failure") { - return errors.Errorf(`the --rm option conflicts with --restart, when the restartPolicy is not "" and "no"`) + return errors.New(`the --rm option conflicts with --restart, when the restartPolicy is not "" and "no"`) } if _, err := config.ParsePullPolicy(c.Pull); err != nil { diff --git a/pkg/specgenutil/ports.go b/pkg/specgenutil/ports.go index 6cc4de1ed..23ee9b4d3 100644 --- a/pkg/specgenutil/ports.go +++ b/pkg/specgenutil/ports.go @@ -1,8 +1,9 @@ package specgenutil import ( + "fmt" + "github.com/docker/go-connections/nat" - "github.com/pkg/errors" ) func verifyExpose(expose []string) error { @@ -15,7 +16,7 @@ func verifyExpose(expose []string) error { // if expose a port, the start and end port are the same _, _, err := nat.ParsePortRange(port) if err != nil { - return errors.Wrapf(err, "invalid range format for --expose: %s", expose) + return fmt.Errorf("invalid range format for --expose: %s: %w", expose, err) } } return nil diff --git a/pkg/specgenutil/specgen.go b/pkg/specgenutil/specgen.go index 34350579d..9a7d50947 100644 --- a/pkg/specgenutil/specgen.go +++ b/pkg/specgenutil/specgen.go @@ -2,6 +2,7 @@ package specgenutil import ( "encoding/json" + "errors" "fmt" "os" "strconv" @@ -22,7 +23,6 @@ import ( "github.com/docker/docker/opts" "github.com/docker/go-units" "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" ) func getCPULimits(c *entities.ContainerCreateOptions) *specs.LinuxCPU { @@ -78,7 +78,7 @@ func getIOLimits(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions) ( if b := c.BlkIOWeight; len(b) > 0 { u, err := strconv.ParseUint(b, 10, 16) if err != nil { - return nil, errors.Wrapf(err, "invalid value for blkio-weight") + return nil, fmt.Errorf("invalid value for blkio-weight: %w", err) } nu := uint16(u) io.Weight = &nu @@ -143,7 +143,7 @@ func getMemoryLimits(c *entities.ContainerCreateOptions) (*specs.LinuxMemory, er if m := c.Memory; len(m) > 0 { ml, err := units.RAMInBytes(m) if err != nil { - return nil, errors.Wrapf(err, "invalid value for memory") + return nil, fmt.Errorf("invalid value for memory: %w", err) } LimitToSwap(memory, c.MemorySwap, ml) hasLimits = true @@ -151,7 +151,7 @@ func getMemoryLimits(c *entities.ContainerCreateOptions) (*specs.LinuxMemory, er if m := c.MemoryReservation; len(m) > 0 { mr, err := units.RAMInBytes(m) if err != nil { - return nil, errors.Wrapf(err, "invalid value for memory") + return nil, fmt.Errorf("invalid value for memory: %w", err) } memory.Reservation = &mr hasLimits = true @@ -164,7 +164,7 @@ func getMemoryLimits(c *entities.ContainerCreateOptions) (*specs.LinuxMemory, er ms, err = units.RAMInBytes(m) memory.Swap = &ms if err != nil { - return nil, errors.Wrapf(err, "invalid value for memory") + return nil, fmt.Errorf("invalid value for memory: %w", err) } hasLimits = true } @@ -248,7 +248,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions if len(c.HealthCmd) > 0 { if c.NoHealthCheck { - return errors.New("Cannot specify both --no-healthcheck and --health-cmd") + return errors.New("cannot specify both --no-healthcheck and --health-cmd") } s.HealthConfig, err = makeHealthCheckFromCli(c.HealthCmd, c.HealthInterval, c.HealthRetries, c.HealthTimeout, c.HealthStartPeriod) if err != nil { @@ -318,7 +318,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions if len(c.PodIDFile) > 0 { if len(s.Pod) > 0 { - return errors.New("Cannot specify both --pod and --pod-id-file") + return errors.New("cannot specify both --pod and --pod-id-file") } podID, err := ReadPodIDFile(c.PodIDFile) if err != nil { @@ -357,7 +357,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions // any case. osEnv, err := envLib.ParseSlice(os.Environ()) if err != nil { - return errors.Wrap(err, "error parsing host environment variables") + return fmt.Errorf("error parsing host environment variables: %w", err) } if !s.EnvHost { @@ -390,7 +390,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions // LABEL VARIABLES labels, err := parse.GetAllLabels(c.LabelFile, c.Label) if err != nil { - return errors.Wrapf(err, "unable to process labels") + return fmt.Errorf("unable to process labels: %w", err) } if systemdUnit, exists := osEnv[systemdDefine.EnvVariable]; exists { @@ -414,7 +414,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions for _, annotation := range c.Annotation { splitAnnotation := strings.SplitN(annotation, "=", 2) if len(splitAnnotation) < 2 { - return errors.Errorf("Annotations must be formatted KEY=VALUE") + return errors.New("annotations must be formatted KEY=VALUE") } annotations[splitAnnotation[0]] = splitAnnotation[1] } @@ -427,7 +427,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions for _, opt := range c.StorageOpts { split := strings.SplitN(opt, "=", 2) if len(split) != 2 { - return errors.Errorf("storage-opt must be formatted KEY=VALUE") + return errors.New("storage-opt must be formatted KEY=VALUE") } opts[split[0]] = split[1] } @@ -459,7 +459,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions if c.ShmSize != "" { var m opts.MemBytes if err := m.Set(c.ShmSize); err != nil { - return errors.Wrapf(err, "unable to translate --shm-size") + return fmt.Errorf("unable to translate --shm-size: %w", err) } val := m.Value() s.ShmSize = &val @@ -531,7 +531,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions for _, unified := range c.CgroupConf { splitUnified := strings.SplitN(unified, "=", 2) if len(splitUnified) < 2 { - return errors.Errorf("--cgroup-conf must be formatted KEY=VALUE") + return errors.New("--cgroup-conf must be formatted KEY=VALUE") } unifieds[splitUnified[0]] = splitUnified[1] } @@ -608,7 +608,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions for _, ctl := range c.Sysctl { splitCtl := strings.SplitN(ctl, "=", 2) if len(splitCtl) < 2 { - return errors.Errorf("invalid sysctl value %q", ctl) + return fmt.Errorf("invalid sysctl value %q", ctl) } sysmap[splitCtl[0]] = splitCtl[1] } @@ -731,7 +731,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions } ul, err := units.ParseUlimit(u) if err != nil { - return errors.Wrapf(err, "ulimit option %q requires name=SOFT:HARD, failed to be parsed", u) + return fmt.Errorf("ulimit option %q requires name=SOFT:HARD, failed to be parsed: %w", u, err) } rl := specs.POSIXRlimit{ Type: ul.Name, @@ -745,7 +745,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions for _, o := range c.LogOptions { split := strings.SplitN(o, "=", 2) if len(split) < 2 { - return errors.Errorf("invalid log option %q", o) + return fmt.Errorf("invalid log option %q", o) } switch strings.ToLower(split[0]) { case "driver": @@ -782,19 +782,19 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions // No retries specified case 2: if strings.ToLower(splitRestart[0]) != "on-failure" { - return errors.Errorf("restart policy retries can only be specified with on-failure restart policy") + return errors.New("restart policy retries can only be specified with on-failure restart policy") } retries, err := strconv.Atoi(splitRestart[1]) if err != nil { - return errors.Wrapf(err, "error parsing restart policy retry count") + return fmt.Errorf("error parsing restart policy retry count: %w", err) } if retries < 0 { - return errors.Errorf("must specify restart policy retry count as a number greater than 0") + return errors.New("must specify restart policy retry count as a number greater than 0") } var retriesUint = uint(retries) s.RestartRetries = &retriesUint default: - return errors.Errorf("invalid restart policy: may specify retries at most once") + return errors.New("invalid restart policy: may specify retries at most once") } s.RestartPolicy = splitRestart[0] } @@ -869,7 +869,7 @@ func makeHealthCheckFromCli(inCmd, interval string, retries uint, timeout, start } // Every healthcheck requires a command if len(cmdArr) == 0 { - return nil, errors.New("Must define a healthcheck command for all healthchecks") + return nil, errors.New("must define a healthcheck command for all healthchecks") } var concat string @@ -902,7 +902,7 @@ func makeHealthCheckFromCli(inCmd, interval string, retries uint, timeout, start } intervalDuration, err := time.ParseDuration(interval) if err != nil { - return nil, errors.Wrapf(err, "invalid healthcheck-interval") + return nil, fmt.Errorf("invalid healthcheck-interval: %w", err) } hc.Interval = intervalDuration @@ -913,7 +913,7 @@ func makeHealthCheckFromCli(inCmd, interval string, retries uint, timeout, start hc.Retries = int(retries) timeoutDuration, err := time.ParseDuration(timeout) if err != nil { - return nil, errors.Wrapf(err, "invalid healthcheck-timeout") + return nil, fmt.Errorf("invalid healthcheck-timeout: %w", err) } if timeoutDuration < time.Duration(1) { return nil, errors.New("healthcheck-timeout must be at least 1 second") @@ -922,7 +922,7 @@ func makeHealthCheckFromCli(inCmd, interval string, retries uint, timeout, start startPeriodDuration, err := time.ParseDuration(startPeriod) if err != nil { - return nil, errors.Wrapf(err, "invalid healthcheck-start-period") + return nil, fmt.Errorf("invalid healthcheck-start-period: %w", err) } if startPeriodDuration < time.Duration(0) { return nil, errors.New("healthcheck-start-period must be 0 seconds or greater") @@ -1035,17 +1035,17 @@ func parseSecrets(secrets []string) ([]specgen.Secret, map[string]string, error) for _, val := range split { kv := strings.SplitN(val, "=", 2) if len(kv) < 2 { - return nil, nil, errors.Wrapf(secretParseError, "option %s must be in form option=value", val) + return nil, nil, fmt.Errorf("option %s must be in form option=value: %w", val, secretParseError) } switch kv[0] { case "source": source = kv[1] case "type": if secretType != "" { - return nil, nil, errors.Wrap(secretParseError, "cannot set more than one secret type") + return nil, nil, fmt.Errorf("cannot set more than one secret type: %w", secretParseError) } if kv[1] != "mount" && kv[1] != "env" { - return nil, nil, errors.Wrapf(secretParseError, "type %s is invalid", kv[1]) + return nil, nil, fmt.Errorf("type %s is invalid: %w", kv[1], secretParseError) } secretType = kv[1] case "target": @@ -1054,26 +1054,26 @@ func parseSecrets(secrets []string) ([]specgen.Secret, map[string]string, error) mountOnly = true mode64, err := strconv.ParseUint(kv[1], 8, 32) if err != nil { - return nil, nil, errors.Wrapf(secretParseError, "mode %s invalid", kv[1]) + return nil, nil, fmt.Errorf("mode %s invalid: %w", kv[1], secretParseError) } mode = uint32(mode64) case "uid", "UID": mountOnly = true uid64, err := strconv.ParseUint(kv[1], 10, 32) if err != nil { - return nil, nil, errors.Wrapf(secretParseError, "UID %s invalid", kv[1]) + return nil, nil, fmt.Errorf("UID %s invalid: %w", kv[1], secretParseError) } uid = uint32(uid64) case "gid", "GID": mountOnly = true gid64, err := strconv.ParseUint(kv[1], 10, 32) if err != nil { - return nil, nil, errors.Wrapf(secretParseError, "GID %s invalid", kv[1]) + return nil, nil, fmt.Errorf("GID %s invalid: %w", kv[1], secretParseError) } gid = uint32(gid64) default: - return nil, nil, errors.Wrapf(secretParseError, "option %s invalid", val) + return nil, nil, fmt.Errorf("option %s invalid: %w", val, secretParseError) } } @@ -1081,7 +1081,7 @@ func parseSecrets(secrets []string) ([]specgen.Secret, map[string]string, error) secretType = "mount" } if source == "" { - return nil, nil, errors.Wrapf(secretParseError, "no source found %s", val) + return nil, nil, fmt.Errorf("no source found %s: %w", val, secretParseError) } if secretType == "mount" { mountSecret := specgen.Secret{ @@ -1095,7 +1095,7 @@ func parseSecrets(secrets []string) ([]specgen.Secret, map[string]string, error) } if secretType == "env" { if mountOnly { - return nil, nil, errors.Wrap(secretParseError, "UID, GID, Mode options cannot be set with secret type env") + return nil, nil, fmt.Errorf("UID, GID, Mode options cannot be set with secret type env: %w", secretParseError) } if target == "" { target = source diff --git a/pkg/specgenutil/util.go b/pkg/specgenutil/util.go index fa2e90457..66d148672 100644 --- a/pkg/specgenutil/util.go +++ b/pkg/specgenutil/util.go @@ -1,6 +1,8 @@ package specgenutil import ( + "errors" + "fmt" "io/ioutil" "net" "os" @@ -10,7 +12,6 @@ import ( "github.com/containers/common/libnetwork/types" "github.com/containers/common/pkg/config" storageTypes "github.com/containers/storage/types" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -19,7 +20,7 @@ import ( func ReadPodIDFile(path string) (string, error) { content, err := ioutil.ReadFile(path) if err != nil { - return "", errors.Wrap(err, "error reading pod ID file") + return "", fmt.Errorf("error reading pod ID file: %w", err) } return strings.Split(string(content), "\n")[0], nil } @@ -50,7 +51,7 @@ func CreateExpose(expose []string) (map[uint16]string, error) { proto := "tcp" splitProto := strings.Split(e, "/") if len(splitProto) > 2 { - return nil, errors.Errorf("invalid expose format - protocol can only be specified once") + return nil, errors.New("invalid expose format - protocol can only be specified once") } else if len(splitProto) == 2 { proto = splitProto[1] } @@ -96,7 +97,7 @@ func CreatePortBindings(ports []string) ([]types.PortMapping, error) { case 2: proto = &(splitProto[1]) default: - return nil, errors.Errorf("invalid port format - protocol can only be specified once") + return nil, errors.New("invalid port format - protocol can only be specified once") } remainder := splitProto[0] @@ -111,23 +112,23 @@ func CreatePortBindings(ports []string) ([]types.PortMapping, error) { // We potentially have an IPv6 address haveV6 = true if !strings.HasPrefix(splitV6[0], "[") { - return nil, errors.Errorf("invalid port format - IPv6 addresses must be enclosed by []") + return nil, errors.New("invalid port format - IPv6 addresses must be enclosed by []") } if !strings.HasPrefix(splitV6[1], ":") { - return nil, errors.Errorf("invalid port format - IPv6 address must be followed by a colon (':')") + return nil, errors.New("invalid port format - IPv6 address must be followed by a colon (':')") } ipNoPrefix := strings.TrimPrefix(splitV6[0], "[") hostIP = &ipNoPrefix remainder = strings.TrimPrefix(splitV6[1], ":") default: - return nil, errors.Errorf("invalid port format - at most one IPv6 address can be specified in a --publish") + return nil, errors.New("invalid port format - at most one IPv6 address can be specified in a --publish") } splitPort := strings.Split(remainder, ":") switch len(splitPort) { case 1: if haveV6 { - return nil, errors.Errorf("invalid port format - must provide host and destination port if specifying an IP") + return nil, errors.New("invalid port format - must provide host and destination port if specifying an IP") } ctrPort = splitPort[0] case 2: @@ -135,13 +136,13 @@ func CreatePortBindings(ports []string) ([]types.PortMapping, error) { ctrPort = splitPort[1] case 3: if haveV6 { - return nil, errors.Errorf("invalid port format - when v6 address specified, must be [ipv6]:hostPort:ctrPort") + return nil, errors.New("invalid port format - when v6 address specified, must be [ipv6]:hostPort:ctrPort") } hostIP = &(splitPort[0]) hostPort = &(splitPort[1]) ctrPort = splitPort[2] default: - return nil, errors.Errorf("invalid port format - format is [[hostIP:]hostPort:]containerPort") + return nil, errors.New("invalid port format - format is [[hostIP:]hostPort:]containerPort") } newPort, err := parseSplitPort(hostIP, hostPort, ctrPort, proto) @@ -160,30 +161,30 @@ func CreatePortBindings(ports []string) ([]types.PortMapping, error) { func parseSplitPort(hostIP, hostPort *string, ctrPort string, protocol *string) (types.PortMapping, error) { newPort := types.PortMapping{} if ctrPort == "" { - return newPort, errors.Errorf("must provide a non-empty container port to publish") + return newPort, errors.New("must provide a non-empty container port to publish") } ctrStart, ctrLen, err := parseAndValidateRange(ctrPort) if err != nil { - return newPort, errors.Wrapf(err, "error parsing container port") + return newPort, fmt.Errorf("error parsing container port: %w", err) } newPort.ContainerPort = ctrStart newPort.Range = ctrLen if protocol != nil { if *protocol == "" { - return newPort, errors.Errorf("must provide a non-empty protocol to publish") + return newPort, errors.New("must provide a non-empty protocol to publish") } newPort.Protocol = *protocol } if hostIP != nil { if *hostIP == "" { - return newPort, errors.Errorf("must provide a non-empty container host IP to publish") + return newPort, errors.New("must provide a non-empty container host IP to publish") } else if *hostIP != "0.0.0.0" { // If hostIP is 0.0.0.0, leave it unset - CNI treats // 0.0.0.0 and empty differently, Docker does not. testIP := net.ParseIP(*hostIP) if testIP == nil { - return newPort, errors.Errorf("cannot parse %q as an IP address", *hostIP) + return newPort, fmt.Errorf("cannot parse %q as an IP address", *hostIP) } newPort.HostIP = testIP.String() } @@ -196,10 +197,10 @@ func parseSplitPort(hostIP, hostPort *string, ctrPort string, protocol *string) } else { hostStart, hostLen, err := parseAndValidateRange(*hostPort) if err != nil { - return newPort, errors.Wrapf(err, "error parsing host port") + return newPort, fmt.Errorf("error parsing host port: %w", err) } if hostLen != ctrLen { - return newPort, errors.Errorf("host and container port ranges have different lengths: %d vs %d", hostLen, ctrLen) + return newPort, fmt.Errorf("host and container port ranges have different lengths: %d vs %d", hostLen, ctrLen) } newPort.HostPort = hostStart } @@ -216,11 +217,11 @@ func parseSplitPort(hostIP, hostPort *string, ctrPort string, protocol *string) func parseAndValidateRange(portRange string) (uint16, uint16, error) { splitRange := strings.Split(portRange, "-") if len(splitRange) > 2 { - return 0, 0, errors.Errorf("invalid port format - port ranges are formatted as startPort-stopPort") + return 0, 0, errors.New("invalid port format - port ranges are formatted as startPort-stopPort") } if splitRange[0] == "" { - return 0, 0, errors.Errorf("port numbers cannot be negative") + return 0, 0, errors.New("port numbers cannot be negative") } startPort, err := parseAndValidatePort(splitRange[0]) @@ -231,14 +232,14 @@ func parseAndValidateRange(portRange string) (uint16, uint16, error) { var rangeLen uint16 = 1 if len(splitRange) == 2 { if splitRange[1] == "" { - return 0, 0, errors.Errorf("must provide ending number for port range") + return 0, 0, errors.New("must provide ending number for port range") } endPort, err := parseAndValidatePort(splitRange[1]) if err != nil { return 0, 0, err } if endPort <= startPort { - return 0, 0, errors.Errorf("the end port of a range must be higher than the start port - %d is not higher than %d", endPort, startPort) + return 0, 0, fmt.Errorf("the end port of a range must be higher than the start port - %d is not higher than %d", endPort, startPort) } // Our range is the total number of ports // involved, so we need to add 1 (8080:8081 is @@ -253,10 +254,10 @@ func parseAndValidateRange(portRange string) (uint16, uint16, error) { func parseAndValidatePort(port string) (uint16, error) { num, err := strconv.Atoi(port) if err != nil { - return 0, errors.Wrapf(err, "invalid port number") + return 0, fmt.Errorf("invalid port number: %w", err) } if num < 1 || num > 65535 { - return 0, errors.Errorf("port numbers must be between 1 and 65535 (inclusive), got %d", num) + return 0, fmt.Errorf("port numbers must be between 1 and 65535 (inclusive), got %d", num) } return uint16(num), nil } diff --git a/pkg/specgenutil/volumes.go b/pkg/specgenutil/volumes.go index 016166a20..e9fffdca9 100644 --- a/pkg/specgenutil/volumes.go +++ b/pkg/specgenutil/volumes.go @@ -2,6 +2,7 @@ package specgenutil import ( "encoding/csv" + "errors" "fmt" "path" "strings" @@ -11,14 +12,13 @@ import ( "github.com/containers/podman/v4/pkg/specgen" "github.com/containers/podman/v4/pkg/util" spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" ) var ( - errDuplicateDest = errors.Errorf("duplicate mount destination") - optionArgError = errors.Errorf("must provide an argument for option") - noDestError = errors.Errorf("must set volume destination") - errInvalidSyntax = errors.Errorf("incorrect mount format: should be --mount type=<bind|tmpfs|volume>,[src=<host-dir|volume-name>,]target=<ctr-dir>[,options]") + errDuplicateDest = errors.New("duplicate mount destination") + errOptionArg = errors.New("must provide an argument for option") + errNoDest = errors.New("must set volume destination") + errInvalidSyntax = errors.New("incorrect mount format: should be --mount type=<bind|tmpfs|volume>,[src=<host-dir|volume-name>,]target=<ctr-dir>[,options]") ) // Parse all volume-related options in the create config into a set of mounts @@ -50,20 +50,20 @@ func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bo // Start with --volume. for dest, mount := range volumeMounts { if _, ok := unifiedMounts[dest]; ok { - return nil, nil, nil, nil, errors.Wrapf(errDuplicateDest, dest) + return nil, nil, nil, nil, fmt.Errorf("%v: %w", dest, errDuplicateDest) } unifiedMounts[dest] = mount } for dest, volume := range volumeVolumes { if _, ok := unifiedVolumes[dest]; ok { - return nil, nil, nil, nil, errors.Wrapf(errDuplicateDest, dest) + return nil, nil, nil, nil, fmt.Errorf("%v: %w", dest, errDuplicateDest) } unifiedVolumes[dest] = volume } // Now --tmpfs for dest, tmpfs := range tmpfsMounts { if _, ok := unifiedMounts[dest]; ok { - return nil, nil, nil, nil, errors.Wrapf(errDuplicateDest, dest) + return nil, nil, nil, nil, fmt.Errorf("%v: %w", dest, errDuplicateDest) } unifiedMounts[dest] = tmpfs } @@ -93,7 +93,7 @@ func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bo allMounts := make(map[string]bool) testAndSet := func(dest string) error { if _, ok := allMounts[dest]; ok { - return errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) + return fmt.Errorf("conflict at mount destination %v: %w", dest, errDuplicateDest) } allMounts[dest] = true return nil @@ -125,7 +125,7 @@ func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bo if mount.Type == define.TypeBind { absSrc, err := specgen.ConvertWinMountPath(mount.Source) if err != nil { - return nil, nil, nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source) + return nil, nil, nil, nil, fmt.Errorf("error getting absolute path of %s: %w", mount.Source, err) } mount.Source = absSrc } @@ -199,7 +199,7 @@ func Mounts(mountFlag []string) (map[string]spec.Mount, map[string]*specgen.Name return nil, nil, nil, err } if _, ok := finalMounts[mount.Destination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination) + return nil, nil, nil, fmt.Errorf("%v: %w", mount.Destination, errDuplicateDest) } finalMounts[mount.Destination] = mount case define.TypeTmpfs: @@ -208,7 +208,7 @@ func Mounts(mountFlag []string) (map[string]spec.Mount, map[string]*specgen.Name return nil, nil, nil, err } if _, ok := finalMounts[mount.Destination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination) + return nil, nil, nil, fmt.Errorf("%v: %w", mount.Destination, errDuplicateDest) } finalMounts[mount.Destination] = mount case define.TypeDevpts: @@ -217,7 +217,7 @@ func Mounts(mountFlag []string) (map[string]spec.Mount, map[string]*specgen.Name return nil, nil, nil, err } if _, ok := finalMounts[mount.Destination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination) + return nil, nil, nil, fmt.Errorf("%v: %w", mount.Destination, errDuplicateDest) } finalMounts[mount.Destination] = mount case "image": @@ -226,7 +226,7 @@ func Mounts(mountFlag []string) (map[string]spec.Mount, map[string]*specgen.Name return nil, nil, nil, err } if _, ok := finalImageVolumes[volume.Destination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, volume.Destination) + return nil, nil, nil, fmt.Errorf("%v: %w", volume.Destination, errDuplicateDest) } finalImageVolumes[volume.Destination] = volume case "volume": @@ -235,11 +235,11 @@ func Mounts(mountFlag []string) (map[string]spec.Mount, map[string]*specgen.Name return nil, nil, nil, err } if _, ok := finalNamedVolumes[volume.Dest]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, volume.Dest) + return nil, nil, nil, fmt.Errorf("%v: %w", volume.Dest, errDuplicateDest) } finalNamedVolumes[volume.Dest] = volume default: - return nil, nil, nil, errors.Errorf("invalid filesystem type %q", mountType) + return nil, nil, nil, fmt.Errorf("invalid filesystem type %q", mountType) } } @@ -261,7 +261,7 @@ func getBindMount(args []string) (spec.Mount, error) { newMount.Options = append(newMount.Options, "bind") case "readonly", "ro", "rw": if setRORW { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'readonly', 'ro', or 'rw' options more than once") + return newMount, fmt.Errorf("cannot pass 'readonly', 'ro', or 'rw' options more than once: %w", errOptionArg) } setRORW = true // Can be formatted as one of: @@ -288,26 +288,26 @@ func getBindMount(args []string) (spec.Mount, error) { newMount.Options = append(newMount.Options, "ro") } default: - return newMount, errors.Wrapf(optionArgError, "'readonly', 'ro', or 'rw' must be set to true or false, instead received %q", kv[1]) + return newMount, fmt.Errorf("'readonly', 'ro', or 'rw' must be set to true or false, instead received %q: %w", kv[1], errOptionArg) } default: - return newMount, errors.Wrapf(optionArgError, "badly formatted option %q", val) + return newMount, fmt.Errorf("badly formatted option %q: %w", val, errOptionArg) } case "nosuid", "suid": if setSuid { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once") + return newMount, fmt.Errorf("cannot pass 'nosuid' and 'suid' options more than once: %w", errOptionArg) } setSuid = true newMount.Options = append(newMount.Options, kv[0]) case "nodev", "dev": if setDev { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once") + return newMount, fmt.Errorf("cannot pass 'nodev' and 'dev' options more than once: %w", errOptionArg) } setDev = true newMount.Options = append(newMount.Options, kv[0]) case "noexec", "exec": if setExec { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once") + return newMount, fmt.Errorf("cannot pass 'noexec' and 'exec' options more than once: %w", errOptionArg) } setExec = true newMount.Options = append(newMount.Options, kv[0]) @@ -315,21 +315,21 @@ func getBindMount(args []string) (spec.Mount, error) { newMount.Options = append(newMount.Options, kv[0]) case "bind-propagation": if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) + return newMount, fmt.Errorf("%v: %w", kv[0], errOptionArg) } newMount.Options = append(newMount.Options, kv[1]) case "src", "source": if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) + return newMount, fmt.Errorf("%v: %w", kv[0], errOptionArg) } if len(kv[1]) == 0 { - return newMount, errors.Wrapf(optionArgError, "host directory cannot be empty") + return newMount, fmt.Errorf("host directory cannot be empty: %w", errOptionArg) } newMount.Source = kv[1] setSource = true case "target", "dst", "destination": if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) + return newMount, fmt.Errorf("%v: %w", kv[0], errOptionArg) } if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { return newMount, err @@ -338,11 +338,11 @@ func getBindMount(args []string) (spec.Mount, error) { setDest = true case "relabel": if setRelabel { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'relabel' option more than once") + return newMount, fmt.Errorf("cannot pass 'relabel' option more than once: %w", errOptionArg) } setRelabel = true if len(kv) != 2 { - return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0]) + return newMount, fmt.Errorf("%s mount option must be 'private' or 'shared': %w", kv[0], util.ErrBadMntOption) } switch kv[1] { case "private": @@ -350,11 +350,11 @@ func getBindMount(args []string) (spec.Mount, error) { case "shared": newMount.Options = append(newMount.Options, "z") default: - return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0]) + return newMount, fmt.Errorf("%s mount option must be 'private' or 'shared': %w", kv[0], util.ErrBadMntOption) } case "U", "chown": if setOwnership { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'U' or 'chown' option more than once") + return newMount, fmt.Errorf("cannot pass 'U' or 'chown' option more than once: %w", errOptionArg) } ok, err := validChownFlag(val) if err != nil { @@ -375,12 +375,12 @@ func getBindMount(args []string) (spec.Mount, error) { // Since Docker ignores this option so shall we. continue default: - return newMount, errors.Wrapf(util.ErrBadMntOption, "%s", kv[0]) + return newMount, fmt.Errorf("%s: %w", kv[0], util.ErrBadMntOption) } } if !setDest { - return newMount, noDestError + return newMount, errNoDest } if !setSource { @@ -409,49 +409,49 @@ func getTmpfsMount(args []string) (spec.Mount, error) { switch kv[0] { case "tmpcopyup", "notmpcopyup": if setTmpcopyup { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'tmpcopyup' and 'notmpcopyup' options more than once") + return newMount, fmt.Errorf("cannot pass 'tmpcopyup' and 'notmpcopyup' options more than once: %w", errOptionArg) } setTmpcopyup = true newMount.Options = append(newMount.Options, kv[0]) case "ro", "rw": if setRORW { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'ro' and 'rw' options more than once") + return newMount, fmt.Errorf("cannot pass 'ro' and 'rw' options more than once: %w", errOptionArg) } setRORW = true newMount.Options = append(newMount.Options, kv[0]) case "nosuid", "suid": if setSuid { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once") + return newMount, fmt.Errorf("cannot pass 'nosuid' and 'suid' options more than once: %w", errOptionArg) } setSuid = true newMount.Options = append(newMount.Options, kv[0]) case "nodev", "dev": if setDev { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once") + return newMount, fmt.Errorf("cannot pass 'nodev' and 'dev' options more than once: %w", errOptionArg) } setDev = true newMount.Options = append(newMount.Options, kv[0]) case "noexec", "exec": if setExec { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once") + return newMount, fmt.Errorf("cannot pass 'noexec' and 'exec' options more than once: %w", errOptionArg) } setExec = true newMount.Options = append(newMount.Options, kv[0]) case "tmpfs-mode": if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) + return newMount, fmt.Errorf("%v: %w", kv[0], errOptionArg) } newMount.Options = append(newMount.Options, fmt.Sprintf("mode=%s", kv[1])) case "tmpfs-size": if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) + return newMount, fmt.Errorf("%v: %w", kv[0], errOptionArg) } newMount.Options = append(newMount.Options, fmt.Sprintf("size=%s", kv[1])) case "src", "source": - return newMount, errors.Errorf("source is not supported with tmpfs mounts") + return newMount, fmt.Errorf("source is not supported with tmpfs mounts") case "target", "dst", "destination": if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) + return newMount, fmt.Errorf("%v: %w", kv[0], errOptionArg) } if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { return newMount, err @@ -460,7 +460,7 @@ func getTmpfsMount(args []string) (spec.Mount, error) { setDest = true case "U", "chown": if setOwnership { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'U' or 'chown' option more than once") + return newMount, fmt.Errorf("cannot pass 'U' or 'chown' option more than once: %w", errOptionArg) } ok, err := validChownFlag(val) if err != nil { @@ -475,12 +475,12 @@ func getTmpfsMount(args []string) (spec.Mount, error) { // Since Docker ignores this option so shall we. continue default: - return newMount, errors.Wrapf(util.ErrBadMntOption, "%s", kv[0]) + return newMount, fmt.Errorf("%s: %w", kv[0], util.ErrBadMntOption) } } if !setDest { - return newMount, noDestError + return newMount, errNoDest } return newMount, nil @@ -502,7 +502,7 @@ func getDevptsMount(args []string) (spec.Mount, error) { newMount.Options = append(newMount.Options, val) case "target", "dst", "destination": if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) + return newMount, fmt.Errorf("%v: %w", kv[0], errOptionArg) } if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { return newMount, err @@ -510,12 +510,12 @@ func getDevptsMount(args []string) (spec.Mount, error) { newMount.Destination = unixPathClean(kv[1]) setDest = true default: - return newMount, errors.Wrapf(util.ErrBadMntOption, "%s", kv[0]) + return newMount, fmt.Errorf("%s: %w", kv[0], util.ErrBadMntOption) } } if !setDest { - return newMount, noDestError + return newMount, errNoDest } return newMount, nil @@ -536,38 +536,38 @@ func getNamedVolume(args []string) (*specgen.NamedVolume, error) { newVolume.Options = append(newVolume.Options, val) case "ro", "rw": if setRORW { - return nil, errors.Wrapf(optionArgError, "cannot pass 'ro' and 'rw' options more than once") + return nil, fmt.Errorf("cannot pass 'ro' and 'rw' options more than once: %w", errOptionArg) } setRORW = true newVolume.Options = append(newVolume.Options, kv[0]) case "nosuid", "suid": if setSuid { - return nil, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once") + return nil, fmt.Errorf("cannot pass 'nosuid' and 'suid' options more than once: %w", errOptionArg) } setSuid = true newVolume.Options = append(newVolume.Options, kv[0]) case "nodev", "dev": if setDev { - return nil, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once") + return nil, fmt.Errorf("cannot pass 'nodev' and 'dev' options more than once: %w", errOptionArg) } setDev = true newVolume.Options = append(newVolume.Options, kv[0]) case "noexec", "exec": if setExec { - return nil, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once") + return nil, fmt.Errorf("cannot pass 'noexec' and 'exec' options more than once: %w", errOptionArg) } setExec = true newVolume.Options = append(newVolume.Options, kv[0]) case "volume-label": - return nil, errors.Errorf("the --volume-label option is not presently implemented") + return nil, fmt.Errorf("the --volume-label option is not presently implemented") case "src", "source": if len(kv) == 1 { - return nil, errors.Wrapf(optionArgError, kv[0]) + return nil, fmt.Errorf("%v: %w", kv[0], errOptionArg) } newVolume.Name = kv[1] case "target", "dst", "destination": if len(kv) == 1 { - return nil, errors.Wrapf(optionArgError, kv[0]) + return nil, fmt.Errorf("%v: %w", kv[0], errOptionArg) } if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { return nil, err @@ -576,7 +576,7 @@ func getNamedVolume(args []string) (*specgen.NamedVolume, error) { setDest = true case "U", "chown": if setOwnership { - return newVolume, errors.Wrapf(optionArgError, "cannot pass 'U' or 'chown' option more than once") + return newVolume, fmt.Errorf("cannot pass 'U' or 'chown' option more than once: %w", errOptionArg) } ok, err := validChownFlag(val) if err != nil { @@ -591,12 +591,12 @@ func getNamedVolume(args []string) (*specgen.NamedVolume, error) { // Since Docker ignores this option so shall we. continue default: - return nil, errors.Wrapf(util.ErrBadMntOption, "%s", kv[0]) + return nil, fmt.Errorf("%s: %w", kv[0], util.ErrBadMntOption) } } if !setDest { - return nil, noDestError + return nil, errNoDest } return newVolume, nil @@ -614,12 +614,12 @@ func getImageVolume(args []string) (*specgen.ImageVolume, error) { switch kv[0] { case "src", "source": if len(kv) == 1 { - return nil, errors.Wrapf(optionArgError, kv[0]) + return nil, fmt.Errorf("%v: %w", kv[0], errOptionArg) } newVolume.Source = kv[1] case "target", "dst", "destination": if len(kv) == 1 { - return nil, errors.Wrapf(optionArgError, kv[0]) + return nil, fmt.Errorf("%v: %w", kv[0], errOptionArg) } if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { return nil, err @@ -632,19 +632,19 @@ func getImageVolume(args []string) (*specgen.ImageVolume, error) { case "false": // Nothing to do. RO is default. default: - return nil, errors.Wrapf(util.ErrBadMntOption, "invalid rw value %q", kv[1]) + return nil, fmt.Errorf("invalid rw value %q: %w", kv[1], util.ErrBadMntOption) } case "consistency": // Often used on MACs and mistakenly on Linux platforms. // Since Docker ignores this option so shall we. continue default: - return nil, errors.Wrapf(util.ErrBadMntOption, "%s", kv[0]) + return nil, fmt.Errorf("%s: %w", kv[0], util.ErrBadMntOption) } } if len(newVolume.Source)*len(newVolume.Destination) == 0 { - return nil, errors.Errorf("must set source and destination for image volume") + return nil, errors.New("must set source and destination for image volume") } return newVolume, nil @@ -666,7 +666,7 @@ func getTmpfsMounts(tmpfsFlag []string) (map[string]spec.Mount, error) { } if _, ok := m[destPath]; ok { - return nil, errors.Wrapf(errDuplicateDest, destPath) + return nil, fmt.Errorf("%v: %w", destPath, errDuplicateDest) } mount := spec.Mount{ @@ -692,10 +692,10 @@ func validChownFlag(flag string) (bool, error) { case "false": return false, nil default: - return false, errors.Wrapf(optionArgError, "'U' or 'chown' must be set to true or false, instead received %q", kv[1]) + return false, fmt.Errorf("'U' or 'chown' must be set to true or false, instead received %q: %w", kv[1], errOptionArg) } default: - return false, errors.Wrapf(optionArgError, "badly formatted option %q", flag) + return false, fmt.Errorf("badly formatted option %q: %w", flag, errOptionArg) } return true, nil diff --git a/pkg/systemd/generate/common.go b/pkg/systemd/generate/common.go index e53d37897..60b0c4b52 100644 --- a/pkg/systemd/generate/common.go +++ b/pkg/systemd/generate/common.go @@ -1,11 +1,11 @@ package generate import ( + "fmt" "strconv" "strings" "github.com/containers/podman/v4/pkg/systemd/define" - "github.com/pkg/errors" ) // minTimeoutStopSec is the minimal stop timeout for generated systemd units. @@ -20,7 +20,7 @@ func validateRestartPolicy(restart string) error { return nil } } - return errors.Errorf("%s is not a valid restart policy", restart) + return fmt.Errorf("%s is not a valid restart policy", restart) } const headerTemplate = `# {{{{.ServiceName}}}}{{{{- if (eq .IdentifySpecifier true) }}}}@{{{{- end}}}}.service diff --git a/pkg/systemd/generate/containers.go b/pkg/systemd/generate/containers.go index e953a1f1f..6596ef73b 100644 --- a/pkg/systemd/generate/containers.go +++ b/pkg/systemd/generate/containers.go @@ -2,6 +2,7 @@ package generate import ( "bytes" + "errors" "fmt" "os" "sort" @@ -14,7 +15,6 @@ import ( "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/systemd/define" "github.com/containers/podman/v4/version" - "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/pflag" ) @@ -186,14 +186,14 @@ func generateContainerInfo(ctr *libpod.Container, options entities.GenerateSyste config := ctr.Config() conmonPidFile := config.ConmonPidFile if conmonPidFile == "" { - return nil, errors.Errorf("conmon PID file path is empty, try to recreate the container with --conmon-pidfile flag") + return nil, errors.New("conmon PID file path is empty, try to recreate the container with --conmon-pidfile flag") } createCommand := []string{} if config.CreateCommand != nil { createCommand = config.CreateCommand } else if options.New { - return nil, errors.Errorf("cannot use --new on container %q: no create command found: only works on containers created directly with podman but not via REST API", ctr.ID()) + return nil, fmt.Errorf("cannot use --new on container %q: no create command found: only works on containers created directly with podman but not via REST API", ctr.ID()) } nameOrID, serviceName := containerServiceName(ctr, options) @@ -204,7 +204,7 @@ func generateContainerInfo(ctr *libpod.Container, options entities.GenerateSyste } else { runRoot = ctr.Runtime().RunRoot() if runRoot == "" { - return nil, errors.Errorf("could not look up container's runroot: got empty string") + return nil, errors.New("could not look up container's runroot: got empty string") } } @@ -350,7 +350,7 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst } } if index == 0 { - return "", errors.Errorf("container's create command is too short or invalid: %v", info.CreateCommand) + return "", fmt.Errorf("container's create command is too short or invalid: %v", info.CreateCommand) } // We're hard-coding the first five arguments and append the // CreateCommand with a stripped command and subcommand. @@ -520,7 +520,7 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst // template execution. templ, err := template.New("container_template").Delims("{{{{", "}}}}").Parse(containerTemplate) if err != nil { - return "", errors.Wrap(err, "error parsing systemd service template") + return "", fmt.Errorf("error parsing systemd service template: %w", err) } var buf bytes.Buffer @@ -531,7 +531,7 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst // Now parse the generated template (i.e., buf) and execute it. templ, err = template.New("container_template").Delims("{{{{", "}}}}").Parse(buf.String()) if err != nil { - return "", errors.Wrap(err, "error parsing systemd service template") + return "", fmt.Errorf("error parsing systemd service template: %w", err) } buf = bytes.Buffer{} diff --git a/pkg/systemd/generate/pods.go b/pkg/systemd/generate/pods.go index 4043fbfcb..8640418a7 100644 --- a/pkg/systemd/generate/pods.go +++ b/pkg/systemd/generate/pods.go @@ -2,6 +2,7 @@ package generate import ( "bytes" + "errors" "fmt" "os" "sort" @@ -13,7 +14,6 @@ import ( "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/systemd/define" "github.com/containers/podman/v4/version" - "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/pflag" ) @@ -141,7 +141,7 @@ func PodUnits(pod *libpod.Pod, options entities.GenerateSystemdOptions) (map[str // Error out if the pod has no infra container, which we require to be the // main service. if !pod.HasInfraContainer() { - return nil, errors.Errorf("generating systemd unit files: Pod %q has no infra container", pod.Name()) + return nil, fmt.Errorf("generating systemd unit files: Pod %q has no infra container", pod.Name()) } podInfo, err := generatePodInfo(pod, options) @@ -160,7 +160,7 @@ func PodUnits(pod *libpod.Pod, options entities.GenerateSystemdOptions) (map[str return nil, err } if len(containers) == 0 { - return nil, errors.Errorf("generating systemd unit files: Pod %q has no containers", pod.Name()) + return nil, fmt.Errorf("generating systemd unit files: Pod %q has no containers", pod.Name()) } graph, err := libpod.BuildContainerGraph(containers) if err != nil { @@ -217,7 +217,7 @@ func generatePodInfo(pod *libpod.Pod, options entities.GenerateSystemdOptions) ( // containerInfo acts as the main service of the pod. infraCtr, err := pod.InfraContainer() if err != nil { - return nil, errors.Wrap(err, "could not find infra container") + return nil, fmt.Errorf("could not find infra container: %w", err) } stopTimeout := infraCtr.StopTimeout() @@ -228,12 +228,12 @@ func generatePodInfo(pod *libpod.Pod, options entities.GenerateSystemdOptions) ( config := infraCtr.Config() conmonPidFile := config.ConmonPidFile if conmonPidFile == "" { - return nil, errors.Errorf("conmon PID file path is empty, try to recreate the container with --conmon-pidfile flag") + return nil, errors.New("conmon PID file path is empty, try to recreate the container with --conmon-pidfile flag") } createCommand := pod.CreateCommand() if options.New && len(createCommand) == 0 { - return nil, errors.Errorf("cannot use --new on pod %q: no create command found", pod.ID()) + return nil, fmt.Errorf("cannot use --new on pod %q: no create command found", pod.ID()) } nameOrID := pod.ID() @@ -312,7 +312,7 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions) var podRootArgs, podCreateArgs []string switch len(info.CreateCommand) { case 0, 1, 2: - return "", errors.Errorf("pod does not appear to be created via `podman pod create`: %v", info.CreateCommand) + return "", fmt.Errorf("pod does not appear to be created via `podman pod create`: %v", info.CreateCommand) default: // Make sure that pod was created with `pod create` and // not something else, such as `run --pod new`. @@ -323,7 +323,7 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions) } } if podCreateIndex == 0 { - return "", errors.Errorf("pod does not appear to be created via `podman pod create`: %v", info.CreateCommand) + return "", fmt.Errorf("pod does not appear to be created via `podman pod create`: %v", info.CreateCommand) } podRootArgs = info.CreateCommand[1 : podCreateIndex-1] info.RootFlags = strings.Join(escapeSystemdArguments(podRootArgs), " ") @@ -402,7 +402,7 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions) // template execution. templ, err := template.New("pod_template").Delims("{{{{", "}}}}").Parse(podTemplate) if err != nil { - return "", errors.Wrap(err, "error parsing systemd service template") + return "", fmt.Errorf("error parsing systemd service template: %w", err) } var buf bytes.Buffer @@ -413,7 +413,7 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions) // Now parse the generated template (i.e., buf) and execute it. templ, err = template.New("pod_template").Delims("{{{{", "}}}}").Parse(buf.String()) if err != nil { - return "", errors.Wrap(err, "error parsing systemd service template") + return "", fmt.Errorf("error parsing systemd service template: %w", err) } buf = bytes.Buffer{} diff --git a/pkg/trust/trust.go b/pkg/trust/trust.go index 1d0cc61ba..663a1b5e2 100644 --- a/pkg/trust/trust.go +++ b/pkg/trust/trust.go @@ -5,6 +5,7 @@ import ( "bytes" "encoding/base64" "encoding/json" + "fmt" "io/ioutil" "os" "os/exec" @@ -14,7 +15,6 @@ import ( "github.com/containers/image/v5/types" "github.com/docker/docker/pkg/homedir" "github.com/ghodss/yaml" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -126,11 +126,11 @@ func LoadAndMergeConfig(dirPath string) (*RegistryConfiguration, error) { var config RegistryConfiguration err = yaml.Unmarshal(configBytes, &config) if err != nil { - return nil, errors.Wrapf(err, "error parsing %s", configPath) + return nil, fmt.Errorf("error parsing %s: %w", configPath, err) } if config.DefaultDocker != nil { if mergedConfig.DefaultDocker != nil { - return nil, errors.Errorf(`Error parsing signature storage configuration: "default-docker" defined both in "%s" and "%s"`, + return nil, fmt.Errorf(`error parsing signature storage configuration: "default-docker" defined both in "%s" and "%s"`, dockerDefaultMergedFrom, configPath) } mergedConfig.DefaultDocker = config.DefaultDocker @@ -138,7 +138,7 @@ func LoadAndMergeConfig(dirPath string) (*RegistryConfiguration, error) { } for nsName, nsConfig := range config.Docker { // includes config.Docker == nil if _, ok := mergedConfig.Docker[nsName]; ok { - return nil, errors.Errorf(`Error parsing signature storage configuration: "docker" namespace "%s" defined both in "%s" and "%s"`, + return nil, fmt.Errorf(`error parsing signature storage configuration: "docker" namespace "%s" defined both in "%s" and "%s"`, nsName, nsMergedFrom[nsName], configPath) } mergedConfig.Docker[nsName] = nsConfig @@ -234,10 +234,10 @@ func GetPolicy(policyPath string) (PolicyContent, error) { var policyContentStruct PolicyContent policyContent, err := ioutil.ReadFile(policyPath) if err != nil { - return policyContentStruct, errors.Wrap(err, "unable to read policy file") + return policyContentStruct, fmt.Errorf("unable to read policy file: %w", err) } if err := json.Unmarshal(policyContent, &policyContentStruct); err != nil { - return policyContentStruct, errors.Wrapf(err, "could not parse trust policies from %s", policyPath) + return policyContentStruct, fmt.Errorf("could not parse trust policies from %s: %w", policyPath, err) } return policyContentStruct, nil } diff --git a/pkg/util/filters.go b/pkg/util/filters.go index 05ba4f82c..08148806f 100644 --- a/pkg/util/filters.go +++ b/pkg/util/filters.go @@ -2,6 +2,7 @@ package util import ( "encoding/json" + "errors" "fmt" "net/http" "path/filepath" @@ -9,14 +10,13 @@ import ( "time" "github.com/containers/podman/v4/pkg/timetype" - "github.com/pkg/errors" ) // ComputeUntilTimestamp extracts until timestamp from filters func ComputeUntilTimestamp(filterValues []string) (time.Time, error) { invalid := time.Time{} if len(filterValues) != 1 { - return invalid, errors.Errorf("specify exactly one timestamp for until") + return invalid, errors.New("specify exactly one timestamp for until") } ts, err := timetype.GetTimestamp(filterValues[0], time.Now()) if err != nil { diff --git a/pkg/util/mountOpts.go b/pkg/util/mountOpts.go index d1dd75a82..49fd5b467 100644 --- a/pkg/util/mountOpts.go +++ b/pkg/util/mountOpts.go @@ -1,16 +1,16 @@ package util import ( + "errors" + "fmt" "strings" - - "github.com/pkg/errors" ) var ( // ErrBadMntOption indicates that an invalid mount option was passed. - ErrBadMntOption = errors.Errorf("invalid mount option") + ErrBadMntOption = errors.New("invalid mount option") // ErrDupeMntOption indicates that a duplicate mount option was passed. - ErrDupeMntOption = errors.Errorf("duplicate mount option passed") + ErrDupeMntOption = errors.New("duplicate mount option passed") ) type defaultMountOptions struct { @@ -47,7 +47,7 @@ func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string if strings.HasPrefix(splitOpt[0], "idmap") { if foundIdmap { - return nil, errors.Wrapf(ErrDupeMntOption, "the 'idmap' option can only be set once") + return nil, fmt.Errorf("the 'idmap' option can only be set once: %w", ErrDupeMntOption) } foundIdmap = true newOptions = append(newOptions, opt) @@ -57,7 +57,7 @@ func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string switch splitOpt[0] { case "copy", "nocopy": if foundCopy { - return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'nocopy' and 'copy' can be used") + return nil, fmt.Errorf("only one of 'nocopy' and 'copy' can be used: %w", ErrDupeMntOption) } foundCopy = true case "O": @@ -67,51 +67,51 @@ func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string newOptions = append(newOptions, opt) case "exec", "noexec": if foundExec { - return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'noexec' and 'exec' can be used") + return nil, fmt.Errorf("only one of 'noexec' and 'exec' can be used: %w", ErrDupeMntOption) } foundExec = true case "suid", "nosuid": if foundSuid { - return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'nosuid' and 'suid' can be used") + return nil, fmt.Errorf("only one of 'nosuid' and 'suid' can be used: %w", ErrDupeMntOption) } foundSuid = true case "nodev", "dev": if foundDev { - return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'nodev' and 'dev' can be used") + return nil, fmt.Errorf("only one of 'nodev' and 'dev' can be used: %w", ErrDupeMntOption) } foundDev = true case "rw", "ro": if foundWrite { - return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'rw' and 'ro' can be used") + return nil, fmt.Errorf("only one of 'rw' and 'ro' can be used: %w", ErrDupeMntOption) } foundWrite = true case "private", "rprivate", "slave", "rslave", "shared", "rshared", "unbindable", "runbindable": if foundProp { - return nil, errors.Wrapf(ErrDupeMntOption, "only one root propagation mode can be used") + return nil, fmt.Errorf("only one root propagation mode can be used: %w", ErrDupeMntOption) } foundProp = true case "size": if !isTmpfs { - return nil, errors.Wrapf(ErrBadMntOption, "the 'size' option is only allowed with tmpfs mounts") + return nil, fmt.Errorf("the 'size' option is only allowed with tmpfs mounts: %w", ErrBadMntOption) } if foundSize { - return nil, errors.Wrapf(ErrDupeMntOption, "only one tmpfs size can be specified") + return nil, fmt.Errorf("only one tmpfs size can be specified: %w", ErrDupeMntOption) } foundSize = true case "mode": if !isTmpfs { - return nil, errors.Wrapf(ErrBadMntOption, "the 'mode' option is only allowed with tmpfs mounts") + return nil, fmt.Errorf("the 'mode' option is only allowed with tmpfs mounts: %w", ErrBadMntOption) } if foundMode { - return nil, errors.Wrapf(ErrDupeMntOption, "only one tmpfs mode can be specified") + return nil, fmt.Errorf("only one tmpfs mode can be specified: %w", ErrDupeMntOption) } foundMode = true case "tmpcopyup": if !isTmpfs { - return nil, errors.Wrapf(ErrBadMntOption, "the 'tmpcopyup' option is only allowed with tmpfs mounts") + return nil, fmt.Errorf("the 'tmpcopyup' option is only allowed with tmpfs mounts: %w", ErrBadMntOption) } if foundCopyUp { - return nil, errors.Wrapf(ErrDupeMntOption, "the 'tmpcopyup' or 'notmpcopyup' option can only be set once") + return nil, fmt.Errorf("the 'tmpcopyup' or 'notmpcopyup' option can only be set once: %w", ErrDupeMntOption) } foundCopyUp = true case "consistency": @@ -120,37 +120,37 @@ func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string continue case "notmpcopyup": if !isTmpfs { - return nil, errors.Wrapf(ErrBadMntOption, "the 'notmpcopyup' option is only allowed with tmpfs mounts") + return nil, fmt.Errorf("the 'notmpcopyup' option is only allowed with tmpfs mounts: %w", ErrBadMntOption) } if foundCopyUp { - return nil, errors.Wrapf(ErrDupeMntOption, "the 'tmpcopyup' or 'notmpcopyup' option can only be set once") + return nil, fmt.Errorf("the 'tmpcopyup' or 'notmpcopyup' option can only be set once: %w", ErrDupeMntOption) } foundCopyUp = true // do not propagate notmpcopyup to the OCI runtime continue case "bind", "rbind": if isTmpfs { - return nil, errors.Wrapf(ErrBadMntOption, "the 'bind' and 'rbind' options are not allowed with tmpfs mounts") + return nil, fmt.Errorf("the 'bind' and 'rbind' options are not allowed with tmpfs mounts: %w", ErrBadMntOption) } if foundBind { - return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'rbind' and 'bind' can be used") + return nil, fmt.Errorf("only one of 'rbind' and 'bind' can be used: %w", ErrDupeMntOption) } foundBind = true case "z", "Z": if isTmpfs { - return nil, errors.Wrapf(ErrBadMntOption, "the 'z' and 'Z' options are not allowed with tmpfs mounts") + return nil, fmt.Errorf("the 'z' and 'Z' options are not allowed with tmpfs mounts: %w", ErrBadMntOption) } if foundZ { - return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'z' and 'Z' can be used") + return nil, fmt.Errorf("only one of 'z' and 'Z' can be used: %w", ErrDupeMntOption) } foundZ = true case "U": if foundU { - return nil, errors.Wrapf(ErrDupeMntOption, "the 'U' option can only be set once") + return nil, fmt.Errorf("the 'U' option can only be set once: %w", ErrDupeMntOption) } foundU = true default: - return nil, errors.Wrapf(ErrBadMntOption, "unknown mount option %q", opt) + return nil, fmt.Errorf("unknown mount option %q: %w", opt, ErrBadMntOption) } newOptions = append(newOptions, opt) } @@ -187,11 +187,11 @@ func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string func ParseDriverOpts(option string) (string, string, error) { token := strings.SplitN(option, "=", 2) if len(token) != 2 { - return "", "", errors.Wrapf(ErrBadMntOption, "cannot parse driver opts") + return "", "", fmt.Errorf("cannot parse driver opts: %w", ErrBadMntOption) } opt := strings.SplitN(token[1], "=", 2) if len(opt) != 2 { - return "", "", errors.Wrapf(ErrBadMntOption, "cannot parse driver opts") + return "", "", fmt.Errorf("cannot parse driver opts: %w", ErrBadMntOption) } return opt[0], opt[1], nil } diff --git a/pkg/util/utils.go b/pkg/util/utils.go index ad5db9a1a..33c11d611 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -2,6 +2,7 @@ package util import ( "encoding/json" + "errors" "fmt" "io/fs" "math" @@ -27,7 +28,6 @@ import ( stypes "github.com/containers/storage/types" v1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/term" ) @@ -68,7 +68,7 @@ func ParseRegistryCreds(creds string) (*types.DockerAuthConfig, error) { fmt.Print("Password: ") termPassword, err := term.ReadPassword(0) if err != nil { - return nil, errors.Wrapf(err, "could not read password from terminal") + return nil, fmt.Errorf("could not read password from terminal: %w", err) } password = string(termPassword) } @@ -129,7 +129,7 @@ func GetImageConfig(changes []string) (ImageConfig, error) { if len(split) != 2 { split = strings.SplitN(change, "=", 2) if len(split) != 2 { - return ImageConfig{}, errors.Errorf("invalid change %q - must be formatted as KEY VALUE", change) + return ImageConfig{}, fmt.Errorf("invalid change %q - must be formatted as KEY VALUE", change) } } @@ -139,7 +139,7 @@ func GetImageConfig(changes []string) (ImageConfig, error) { case "USER": // Assume literal contents are the user. if value == "" { - return ImageConfig{}, errors.Errorf("invalid change %q - must provide a value to USER", change) + return ImageConfig{}, fmt.Errorf("invalid change %q - must provide a value to USER", change) } config.User = value case "EXPOSE": @@ -148,14 +148,14 @@ func GetImageConfig(changes []string) (ImageConfig, error) { // Protocol must be "tcp" or "udp" splitPort := strings.Split(value, "/") if len(splitPort) > 2 { - return ImageConfig{}, errors.Errorf("invalid change %q - EXPOSE port must be formatted as PORT[/PROTO]", change) + return ImageConfig{}, fmt.Errorf("invalid change %q - EXPOSE port must be formatted as PORT[/PROTO]", change) } portNum, err := strconv.Atoi(splitPort[0]) if err != nil { - return ImageConfig{}, errors.Wrapf(err, "invalid change %q - EXPOSE port must be an integer", change) + return ImageConfig{}, fmt.Errorf("invalid change %q - EXPOSE port must be an integer: %w", change, err) } if portNum > 65535 || portNum <= 0 { - return ImageConfig{}, errors.Errorf("invalid change %q - EXPOSE port must be a valid port number", change) + return ImageConfig{}, fmt.Errorf("invalid change %q - EXPOSE port must be a valid port number", change) } proto := "tcp" if len(splitPort) > 1 { @@ -164,7 +164,7 @@ func GetImageConfig(changes []string) (ImageConfig, error) { case "tcp", "udp": proto = testProto default: - return ImageConfig{}, errors.Errorf("invalid change %q - EXPOSE protocol must be TCP or UDP", change) + return ImageConfig{}, fmt.Errorf("invalid change %q - EXPOSE protocol must be TCP or UDP", change) } } if config.ExposedPorts == nil { @@ -188,7 +188,7 @@ func GetImageConfig(changes []string) (ImageConfig, error) { key = splitEnv[0] // We do need a key if key == "" { - return ImageConfig{}, errors.Errorf("invalid change %q - ENV must have at least one argument", change) + return ImageConfig{}, fmt.Errorf("invalid change %q - ENV must have at least one argument", change) } // Perfectly valid to not have a value if len(splitEnv) == 2 { @@ -250,11 +250,11 @@ func GetImageConfig(changes []string) (ImageConfig, error) { testUnmarshal = strings.Split(value, " ") } if len(testUnmarshal) == 0 { - return ImageConfig{}, errors.Errorf("invalid change %q - must provide at least one argument to VOLUME", change) + return ImageConfig{}, fmt.Errorf("invalid change %q - must provide at least one argument to VOLUME", change) } for _, vol := range testUnmarshal { if vol == "" { - return ImageConfig{}, errors.Errorf("invalid change %q - VOLUME paths must not be empty", change) + return ImageConfig{}, fmt.Errorf("invalid change %q - VOLUME paths must not be empty", change) } if config.Volumes == nil { config.Volumes = make(map[string]struct{}) @@ -268,7 +268,7 @@ func GetImageConfig(changes []string) (ImageConfig, error) { // WORKDIR c results in /A/b/c // Just need to check it's not empty... if value == "" { - return ImageConfig{}, errors.Errorf("invalid change %q - must provide a non-empty WORKDIR", change) + return ImageConfig{}, fmt.Errorf("invalid change %q - must provide a non-empty WORKDIR", change) } config.WorkingDir = filepath.Join(config.WorkingDir, value) case "LABEL": @@ -285,7 +285,7 @@ func GetImageConfig(changes []string) (ImageConfig, error) { splitLabel := strings.SplitN(value, "=", 2) // Unlike ENV, LABEL must have a value if len(splitLabel) != 2 { - return ImageConfig{}, errors.Errorf("invalid change %q - LABEL must be formatted key=value", change) + return ImageConfig{}, fmt.Errorf("invalid change %q - LABEL must be formatted key=value", change) } key = splitLabel[0] val = splitLabel[1] @@ -298,7 +298,7 @@ func GetImageConfig(changes []string) (ImageConfig, error) { } // Check key after we strip quotations if key == "" { - return ImageConfig{}, errors.Errorf("invalid change %q - LABEL must have a non-empty key", change) + return ImageConfig{}, fmt.Errorf("invalid change %q - LABEL must have a non-empty key", change) } if config.Labels == nil { config.Labels = make(map[string]string) @@ -308,17 +308,17 @@ func GetImageConfig(changes []string) (ImageConfig, error) { // Check the provided signal for validity. killSignal, err := ParseSignal(value) if err != nil { - return ImageConfig{}, errors.Wrapf(err, "invalid change %q - KILLSIGNAL must be given a valid signal", change) + return ImageConfig{}, fmt.Errorf("invalid change %q - KILLSIGNAL must be given a valid signal: %w", change, err) } config.StopSignal = fmt.Sprintf("%d", killSignal) case "ONBUILD": // Onbuild always appends. if value == "" { - return ImageConfig{}, errors.Errorf("invalid change %q - ONBUILD must be given an argument", change) + return ImageConfig{}, fmt.Errorf("invalid change %q - ONBUILD must be given an argument", change) } config.OnBuild = append(config.OnBuild, value) default: - return ImageConfig{}, errors.Errorf("invalid change %q - invalid instruction %s", change, outerKey) + return ImageConfig{}, fmt.Errorf("invalid change %q - invalid instruction %s", change, outerKey) } } @@ -336,7 +336,7 @@ func ParseSignal(rawSignal string) (syscall.Signal, error) { } // 64 is SIGRTMAX; wish we could get this from a standard Go library if sig < 1 || sig > 64 { - return -1, errors.Errorf("valid signals are 1 through 64") + return -1, errors.New("valid signals are 1 through 64") } return sig, nil } @@ -362,10 +362,10 @@ func GetKeepIDMapping() (*stypes.IDMappingOptions, int, int, error) { uids, gids, err := rootless.GetConfiguredMappings() if err != nil { - return nil, -1, -1, errors.Wrapf(err, "cannot read mappings") + return nil, -1, -1, fmt.Errorf("cannot read mappings: %w", err) } if len(uids) == 0 || len(gids) == 0 { - return nil, -1, -1, errors.Wrapf(err, "keep-id requires additional UIDs or GIDs defined in /etc/subuid and /etc/subgid to function correctly") + return nil, -1, -1, fmt.Errorf("keep-id requires additional UIDs or GIDs defined in /etc/subuid and /etc/subgid to function correctly: %w", err) } maxUID, maxGID := 0, 0 for _, u := range uids { @@ -403,10 +403,10 @@ func GetNoMapMapping() (*stypes.IDMappingOptions, int, int, error) { } uids, gids, err := rootless.GetConfiguredMappings() if err != nil { - return nil, -1, -1, errors.Wrapf(err, "cannot read mappings") + return nil, -1, -1, fmt.Errorf("cannot read mappings: %w", err) } if len(uids) == 0 || len(gids) == 0 { - return nil, -1, -1, errors.Wrapf(err, "nomap requires additional UIDs or GIDs defined in /etc/subuid and /etc/subgid to function correctly") + return nil, -1, -1, fmt.Errorf("nomap requires additional UIDs or GIDs defined in /etc/subuid and /etc/subgid to function correctly: %w", err) } options.UIDMap, options.GIDMap = nil, nil uid, gid := 0, 0 @@ -566,7 +566,7 @@ func ParseInputTime(inputTime string, since bool) (time.Time, error) { // input might be a duration duration, err := time.ParseDuration(inputTime) if err != nil { - return time.Time{}, errors.Errorf("unable to interpret time value") + return time.Time{}, errors.New("unable to interpret time value") } if since { return time.Now().Add(-duration), nil @@ -607,7 +607,7 @@ func HomeDir() (string, error) { if home == "" { usr, err := user.LookupId(fmt.Sprintf("%d", rootless.GetRootlessUID())) if err != nil { - return "", errors.Wrapf(err, "unable to resolve HOME directory") + return "", fmt.Errorf("unable to resolve HOME directory: %w", err) } home = usr.HomeDir } @@ -645,12 +645,12 @@ func ValidateSysctls(strSlice []string) (map[string]string, error) { foundMatch := false arr := strings.Split(val, "=") if len(arr) < 2 { - return nil, errors.Errorf("%s is invalid, sysctl values must be in the form of KEY=VALUE", val) + return nil, fmt.Errorf("%s is invalid, sysctl values must be in the form of KEY=VALUE", val) } trimmed := fmt.Sprintf("%s=%s", strings.TrimSpace(arr[0]), strings.TrimSpace(arr[1])) if trimmed != val { - return nil, errors.Errorf("'%s' is invalid, extra spaces found", val) + return nil, fmt.Errorf("'%s' is invalid, extra spaces found", val) } if validSysctlMap[arr[0]] { @@ -666,7 +666,7 @@ func ValidateSysctls(strSlice []string) (map[string]string, error) { } } if !foundMatch { - return nil, errors.Errorf("sysctl '%s' is not allowed", arr[0]) + return nil, fmt.Errorf("sysctl '%s' is not allowed", arr[0]) } } return sysctl, nil @@ -680,9 +680,9 @@ func CreateCidFile(cidfile string, id string) error { cidFile, err := OpenExclusiveFile(cidfile) if err != nil { if os.IsExist(err) { - return errors.Errorf("container id file exists. Ensure another container is not using it or delete %s", cidfile) + return fmt.Errorf("container id file exists. Ensure another container is not using it or delete %s", cidfile) } - return errors.Errorf("opening cidfile %s", cidfile) + return fmt.Errorf("opening cidfile %s", cidfile) } if _, err = cidFile.WriteString(id); err != nil { logrus.Error(err) diff --git a/pkg/util/utils_darwin.go b/pkg/util/utils_darwin.go index 66ae85e9c..3a2e587df 100644 --- a/pkg/util/utils_darwin.go +++ b/pkg/util/utils_darwin.go @@ -4,7 +4,7 @@ package util import ( - "github.com/pkg/errors" + "errors" ) func GetContainerPidInformationDescriptors() ([]string, error) { diff --git a/pkg/util/utils_linux.go b/pkg/util/utils_linux.go index bc522361f..e2d9e3e89 100644 --- a/pkg/util/utils_linux.go +++ b/pkg/util/utils_linux.go @@ -1,6 +1,7 @@ package util import ( + "errors" "fmt" "io/fs" "io/ioutil" @@ -14,7 +15,6 @@ import ( "github.com/containers/psgo" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" - "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) @@ -53,7 +53,7 @@ func FindDeviceNodes() (map[string]string, error) { // We are a device node. Get major/minor. sysstat, ok := info.Sys().(*syscall.Stat_t) if !ok { - return errors.Errorf("Could not convert stat output for use") + return errors.New("could not convert stat output for use") } // We must typeconvert sysstat.Rdev from uint64->int to avoid constant overflow rdev := int(sysstat.Rdev) diff --git a/pkg/util/utils_supported.go b/pkg/util/utils_supported.go index 50e4b1b7b..b3d690158 100644 --- a/pkg/util/utils_supported.go +++ b/pkg/util/utils_supported.go @@ -7,13 +7,13 @@ package util // should work to take darwin from this import ( + "errors" "fmt" "os" "path/filepath" "syscall" "github.com/containers/podman/v4/pkg/rootless" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -51,12 +51,12 @@ func GetRuntimeDir() (string, error) { if runtimeDir == "" { home := os.Getenv("HOME") if home == "" { - rootlessRuntimeDirError = fmt.Errorf("neither XDG_RUNTIME_DIR nor HOME was set non-empty") + rootlessRuntimeDirError = errors.New("neither XDG_RUNTIME_DIR nor HOME was set non-empty") return } resolvedHome, err := filepath.EvalSymlinks(home) if err != nil { - rootlessRuntimeDirError = errors.Wrapf(err, "cannot resolve %s", home) + rootlessRuntimeDirError = fmt.Errorf("cannot resolve %s: %w", home, err) return } runtimeDir = filepath.Join(resolvedHome, "rundir") @@ -80,7 +80,7 @@ func GetRootlessConfigHomeDir() (string, error) { home := os.Getenv("HOME") resolvedHome, err := filepath.EvalSymlinks(home) if err != nil { - rootlessConfigHomeDirError = errors.Wrapf(err, "cannot resolve %s", home) + rootlessConfigHomeDirError = fmt.Errorf("cannot resolve %s: %w", home, err) return } tmpDir := filepath.Join(resolvedHome, ".config") @@ -115,7 +115,7 @@ func GetRootlessPauseProcessPidPath() (string, error) { // files. func GetRootlessPauseProcessPidPathGivenDir(libpodTmpDir string) (string, error) { if libpodTmpDir == "" { - return "", errors.Errorf("must provide non-empty temporary directory") + return "", errors.New("must provide non-empty temporary directory") } return filepath.Join(libpodTmpDir, "pause.pid"), nil } diff --git a/pkg/util/utils_unsupported.go b/pkg/util/utils_unsupported.go index 896346493..3a0f8646b 100644 --- a/pkg/util/utils_unsupported.go +++ b/pkg/util/utils_unsupported.go @@ -3,11 +3,9 @@ package util -import ( - "github.com/pkg/errors" -) +import "errors" // FindDeviceNodes is not implemented anywhere except Linux. func FindDeviceNodes() (map[string]string, error) { - return nil, errors.Errorf("not supported on non-Linux OSes") + return nil, errors.New("not supported on non-Linux OSes") } diff --git a/pkg/util/utils_windows.go b/pkg/util/utils_windows.go index b91680f7a..703e5472a 100644 --- a/pkg/util/utils_windows.go +++ b/pkg/util/utils_windows.go @@ -4,35 +4,36 @@ package util import ( + "errors" + "fmt" "path/filepath" "github.com/containers/storage/pkg/homedir" - "github.com/pkg/errors" ) var errNotImplemented = errors.New("not yet implemented") // IsCgroup2UnifiedMode returns whether we are running in cgroup 2 unified mode. func IsCgroup2UnifiedMode() (bool, error) { - return false, errors.Wrap(errNotImplemented, "IsCgroup2Unified") + return false, fmt.Errorf("IsCgroup2Unified: %w", errNotImplemented) } // GetContainerPidInformationDescriptors returns a string slice of all supported // format descriptors of GetContainerPidInformation. func GetContainerPidInformationDescriptors() ([]string, error) { - return nil, errors.Wrap(errNotImplemented, "GetContainerPidInformationDescriptors") + return nil, fmt.Errorf("GetContainerPidInformationDescriptors: %w", errNotImplemented) } // GetRootlessPauseProcessPidPath returns the path to the file that holds the pid for // the pause process func GetRootlessPauseProcessPidPath() (string, error) { - return "", errors.Wrap(errNotImplemented, "GetRootlessPauseProcessPidPath") + return "", fmt.Errorf("GetRootlessPauseProcessPidPath: %w", errNotImplemented) } // GetRootlessPauseProcessPidPath returns the path to the file that holds the pid for // the pause process func GetRootlessPauseProcessPidPathGivenDir(unused string) (string, error) { - return "", errors.Wrap(errNotImplemented, "GetRootlessPauseProcessPidPath") + return "", fmt.Errorf("GetRootlessPauseProcessPidPath: %w", errNotImplemented) } // GetRuntimeDir returns the runtime directory diff --git a/test/apiv2/01-basic.at b/test/apiv2/01-basic.at index e4348a9a7..c4183eda9 100644 --- a/test/apiv2/01-basic.at +++ b/test/apiv2/01-basic.at @@ -21,7 +21,7 @@ for i in /version version; do .Components[0].Details.APIVersion~4[0-9.-]\\+ \ .Components[0].Details.MinAPIVersion=4.0.0 \ .Components[0].Details.Os=linux \ - .ApiVersion=1.40 \ + .ApiVersion=1.41 \ .MinAPIVersion=1.24 \ .Os=linux done diff --git a/test/apiv2/40-pods.at b/test/apiv2/40-pods.at index 0a5201213..80724a8d9 100644 --- a/test/apiv2/40-pods.at +++ b/test/apiv2/40-pods.at @@ -134,4 +134,6 @@ t GET libpod/pods/json?filters='{"label":["testl' 400 \ t DELETE libpod/pods/foo 200 t DELETE "libpod/pods/foo (pod has already been deleted)" 404 +t_timeout 5 GET "libpod/pods/stats?stream=true&delay=1" 200 + # vim: filetype=sh diff --git a/test/apiv2/python/rest_api/fixtures/api_testcase.py b/test/apiv2/python/rest_api/fixtures/api_testcase.py index 155e93928..f47136555 100644 --- a/test/apiv2/python/rest_api/fixtures/api_testcase.py +++ b/test/apiv2/python/rest_api/fixtures/api_testcase.py @@ -64,6 +64,10 @@ class APITestCase(unittest.TestCase): def uri(path): return APITestCase.PODMAN_URL + "/v2.0.0/libpod" + path + @staticmethod + def compat_uri(path): + return APITestCase.PODMAN_URL + "/v3.0.0/" + path + def resolve_container(self, path): """Find 'first' container and return 'Id' formatted into given URI path.""" diff --git a/test/apiv2/python/rest_api/test_v2_0_0_container.py b/test/apiv2/python/rest_api/test_v2_0_0_container.py index a44786c0d..a6cd93a1a 100644 --- a/test/apiv2/python/rest_api/test_v2_0_0_container.py +++ b/test/apiv2/python/rest_api/test_v2_0_0_container.py @@ -1,10 +1,12 @@ import multiprocessing import queue import random +import subprocess import threading import unittest import requests +import os import time from dateutil.parser import parse @@ -358,5 +360,50 @@ class ContainerTestCase(APITestCase): self.assertEqual(1000, out["HostConfig"]["Memory"]) + +def execute_process(cmd): + return subprocess.run( + cmd, + shell=True, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + +def create_named_network_ns(network_ns_name): + execute_process(f"ip netns add {network_ns_name}") + execute_process(f"ip netns exec {network_ns_name} ip link add enp2s0 type veth peer name eth0") + execute_process(f"ip netns exec {network_ns_name} ip addr add 10.0.1.0/24 dev eth0") + execute_process(f"ip netns exec {network_ns_name} ip link set eth0 up") + execute_process(f"ip netns exec {network_ns_name} ip link add enp2s1 type veth peer name eth1") + execute_process(f"ip netns exec {network_ns_name} ip addr add 10.0.2.0/24 dev eth1") + execute_process(f"ip netns exec {network_ns_name} ip link set eth1 up") + +def delete_named_network_ns(network_ns_name): + execute_process(f"ip netns delete {network_ns_name}") + +class ContainerCompatibleAPITestCase(APITestCase): + def test_inspect_network(self): + if os.getuid() != 0: + self.skipTest("test needs to be executed as root!") + try: + network_ns_name = "test-compat-api" + create_named_network_ns(network_ns_name) + self.podman.run("rm", "--all", "--force", check=True) + self.podman.run("run", "--net", f"ns:/run/netns/{network_ns_name}", "-d", "alpine", "top", check=True) + + r = requests.post(self.uri(self.resolve_container("/containers/{}/start"))) + self.assertIn(r.status_code, (204, 304), r.text) + + r = requests.get(self.compat_uri(self.resolve_container("/containers/{}/json"))) + self.assertEqual(r.status_code, 200, r.text) + self.assertId(r.content) + out = r.json() + + self.assertEqual("10.0.2.0", out["NetworkSettings"]["SecondaryIPAddresses"][0]["Addr"]) + self.assertEqual(24, out["NetworkSettings"]["SecondaryIPAddresses"][0]["PrefixLen"]) + finally: + delete_named_network_ns(network_ns_name) + if __name__ == "__main__": unittest.main() diff --git a/test/apiv2/test-apiv2 b/test/apiv2/test-apiv2 index 8548d84e5..0fd282854 100755 --- a/test/apiv2/test-apiv2 +++ b/test/apiv2/test-apiv2 @@ -56,6 +56,9 @@ fi # Path to podman binary PODMAN_BIN=${PODMAN:-${CONTAINERS_HELPER_BINARY_DIR}/podman} +# Timeout for streamed responses +CURL_TIMEOUT=0 + # Cleanup handlers clean_up_server() { if [ -n "$service_pid" ]; then @@ -217,6 +220,21 @@ function jsonify() { } ####### +# t_timeout # Timeout wrapper for test helper +####### +function t_timeout() { + CURL_TIMEOUT=$1; shift + local min_runtime=$((CURL_TIMEOUT - 1)) + start=`date +%s` + t $@ + local end=`date +%s` + local runtime=$((end-start)) + if ! [[ "$runtime" -ge "$min_runtime" ]]; then + die "Error: Streaming time should be greater or equal to '$min_runtime'" + fi +} + +####### # t # Main test helper ####### function t() { @@ -226,6 +244,12 @@ function t() { local content_type="application/json" local testname="$method $path" + + if [[ $CURL_TIMEOUT != 0 ]]; then + local c_timeout=$CURL_TIMEOUT + curl_args+=("-m $CURL_TIMEOUT") + CURL_TIMEOUT=0 # 'consume' timeout + fi # POST and PUT requests may be followed by one or more key=value pairs. # Slurp the command line until we see a 3-digit status code. if [[ $method = "POST" || $method == "PUT" ]]; then @@ -291,7 +315,7 @@ function t() { -o $WORKDIR/curl.result.out "$url"); rc=$?; } || : # Any error from curl is instant bad news, from which we can't recover - if [[ $rc -ne 0 ]]; then + if [[ $rc -ne 0 ]] && [[ $c_timeout -eq 0 ]]; then die "curl failure ($rc) on $url - cannot continue" fi diff --git a/test/e2e/libpod_suite_test.go b/test/e2e/libpod_suite_test.go index a633bd3d7..ecb7a2278 100644 --- a/test/e2e/libpod_suite_test.go +++ b/test/e2e/libpod_suite_test.go @@ -76,3 +76,8 @@ func (p *PodmanTestIntegration) StopRemoteService() {} // We don't support running API service when local func (p *PodmanTestIntegration) StartRemoteService() { } + +// Just a stub for compiling with `!remote`. +func getRemoteOptions(p *PodmanTestIntegration, args []string) []string { + return nil +} diff --git a/test/e2e/manifest_test.go b/test/e2e/manifest_test.go index 2f8b47e25..1f58419a1 100644 --- a/test/e2e/manifest_test.go +++ b/test/e2e/manifest_test.go @@ -42,7 +42,6 @@ var _ = Describe("Podman manifest", func() { podmanTest.Cleanup() f := CurrentGinkgoTestDescription() processTestResult(f) - }) It("create w/o image", func() { session := podmanTest.Podman([]string{"manifest", "create", "foo"}) @@ -297,8 +296,21 @@ var _ = Describe("Podman manifest", func() { registryOptions := &podmanRegistry.Options{ Image: "docker-archive:" + imageTarPath(REGISTRY_IMAGE), } + + // registry script invokes $PODMAN; make sure we define that + // so it can use our same networking options. + opts := strings.Join(podmanTest.MakeOptions(nil, false, false), " ") + if IsRemote() { + opts = strings.Join(getRemoteOptions(podmanTest, nil), " ") + } + os.Setenv("PODMAN", podmanTest.PodmanBinary+" "+opts) registry, err := podmanRegistry.StartWithOptions(registryOptions) Expect(err).To(BeNil()) + defer func() { + err := registry.Stop() + Expect(err).To(BeNil()) + os.Unsetenv("PODMAN") + }() session := podmanTest.Podman([]string{"manifest", "create", "foo"}) session.WaitWithDefaultTimeout() @@ -327,9 +339,6 @@ var _ = Describe("Podman manifest", func() { push = podmanTest.Podman([]string{"manifest", "push", "--tls-verify=false", "--creds=podmantest:wrongpasswd", "foo", "localhost:" + registry.Port + "/credstest"}) push.WaitWithDefaultTimeout() Expect(push).To(ExitWithError()) - - err = registry.Stop() - Expect(err).To(BeNil()) }) It("push with error", func() { diff --git a/test/e2e/pod_clone_test.go b/test/e2e/pod_clone_test.go index b90bf10da..9c7abe7a8 100644 --- a/test/e2e/pod_clone_test.go +++ b/test/e2e/pod_clone_test.go @@ -11,9 +11,10 @@ import ( var _ = Describe("Podman pod clone", func() { var ( - tempdir string - err error - podmanTest *PodmanTestIntegration + tempdir string + err error + podmanTest *PodmanTestIntegration + hostname, _ = os.Hostname() ) BeforeEach(func() { @@ -155,4 +156,39 @@ var _ = Describe("Podman pod clone", func() { Expect(strings[0]).Should(ContainSubstring("size=10240k")) }) + It("podman pod clone --uts test", func() { + SkipIfRemote("hostname for the custom NS test is not as expected on the remote client") + + session := podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"pod", "clone", "--uts", "host", session.OutputToString()}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"run", "-it", "--pod", session.OutputToString(), ALPINE, "printenv", "HOSTNAME"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring(hostname)) + + podName := "utsPod" + ns := "ns:/proc/self/ns/" + + session = podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + // just share uts with a custom path + podCreate := podmanTest.Podman([]string{"pod", "clone", "--uts", ns, "--name", podName, session.OutputToString()}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate).Should(Exit(0)) + + podInspect := podmanTest.Podman([]string{"pod", "inspect", podName}) + podInspect.WaitWithDefaultTimeout() + Expect(podInspect).Should(Exit(0)) + podJSON := podInspect.InspectPodToJSON() + Expect(podJSON.InfraConfig).To(HaveField("UtsNS", ns)) + }) + }) diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index 0e363c1fb..3caae2bd5 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -23,9 +23,10 @@ import ( var _ = Describe("Podman pod create", func() { var ( - tempdir string - err error - podmanTest *PodmanTestIntegration + tempdir string + err error + podmanTest *PodmanTestIntegration + hostname, _ = os.Hostname() ) BeforeEach(func() { @@ -1136,4 +1137,28 @@ ENTRYPOINT ["sleep","99999"] Expect(run).ShouldNot(Exit(0)) }) + It("podman pod create --uts test", func() { + session := podmanTest.Podman([]string{"pod", "create", "--uts", "host"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"run", "-it", "--pod", session.OutputToString(), ALPINE, "printenv", "HOSTNAME"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring(hostname)) + + podName := "utsPod" + ns := "ns:/proc/self/ns/" + + // just share uts with a custom path + podCreate := podmanTest.Podman([]string{"pod", "create", "--uts", ns, "--name", podName, "--share", "uts"}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate).Should(Exit(0)) + + podInspect := podmanTest.Podman([]string{"pod", "inspect", podName}) + podInspect.WaitWithDefaultTimeout() + Expect(podInspect).Should(Exit(0)) + podJSON := podInspect.InspectPodToJSON() + Expect(podJSON.InfraConfig).To(HaveField("UtsNS", ns)) + }) }) diff --git a/test/e2e/prune_test.go b/test/e2e/prune_test.go index 119c8d41e..89cc65540 100644 --- a/test/e2e/prune_test.go +++ b/test/e2e/prune_test.go @@ -259,11 +259,12 @@ var _ = Describe("Podman prune", func() { }) It("podman system prune networks", func() { - // About netavark network backend test. + // Create new network. session := podmanTest.Podman([]string{"network", "create", "test"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) + // Remove all unused networks. session = podmanTest.Podman([]string{"system", "prune", "-f"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) @@ -274,7 +275,7 @@ var _ = Describe("Podman prune", func() { Expect(session).Should(Exit(0)) Expect(session.OutputToStringArray()).To(HaveLen(1)) - // Remove all unused networks. + // Unused networks removed. session = podmanTest.Podman([]string{"network", "ls", "-q", "--filter", "name=^test$"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) diff --git a/test/system/035-logs.bats b/test/system/035-logs.bats index 97d984ef1..6b8d5fbc5 100644 --- a/test/system/035-logs.bats +++ b/test/system/035-logs.bats @@ -124,7 +124,7 @@ function _log_test_restarted() { # FIXME: #9597 # run/start is flaking for remote so let's wait for the container condition # to stop wasting energy until the root cause gets fixed. - run_podman container wait --condition=exited logtest + run_podman container wait --condition=exited --condition=stopped logtest run_podman ${events_backend} start -a logtest logfile=$(mktemp -p ${PODMAN_TMPDIR} logfileXXXXXXXX) $PODMAN $_PODMAN_TEST_OPTS ${events_backend} logs -f logtest > $logfile diff --git a/test/system/065-cp.bats b/test/system/065-cp.bats index 12c6e1a01..8f5abd228 100644 --- a/test/system/065-cp.bats +++ b/test/system/065-cp.bats @@ -66,7 +66,7 @@ load helpers # Container (parent) path does not exist. run_podman 125 cp $srcdir/hostfile0 cpcontainer:/IdoNotExist/ - is "$output" 'Error: "/IdoNotExist/" could not be found on container cpcontainer: No such file or directory' \ + is "$output" 'Error: "/IdoNotExist/" could not be found on container cpcontainer: no such file or directory' \ "copy into nonexistent path in container" run_podman kill cpcontainer @@ -794,7 +794,7 @@ ${randomcontent[1]}" "$description" is "$output" "" "output from podman cp 1" run_podman 125 cp --pause=false $srcdir/$rand_filename2 cpcontainer:/tmp/d2/x/ - is "$output" 'Error: "/tmp/d2/x/" could not be found on container cpcontainer: No such file or directory' "cp will not create nonexistent destination directory" + is "$output" 'Error: "/tmp/d2/x/" could not be found on container cpcontainer: no such file or directory' "cp will not create nonexistent destination directory" run_podman cp --pause=false $srcdir/$rand_filename3 cpcontainer:/tmp/d3/x is "$output" "" "output from podman cp 3" diff --git a/test/system/130-kill.bats b/test/system/130-kill.bats index 96b633a42..7522c475d 100644 --- a/test/system/130-kill.bats +++ b/test/system/130-kill.bats @@ -133,11 +133,22 @@ load helpers @test "podman kill - concurrent stop" { # 14761 - concurrent kill/stop must record the exit code random_name=$(random_string 10) - run_podman run -d --replace --name=$random_name alpine sh -c "trap 'echo Received SIGTERM, ignoring' SIGTERM; echo READY; while :; do sleep 0.2; done" + run_podman run -d --replace --name=$random_name $IMAGE sh -c "trap 'echo Received SIGTERM, ignoring' SIGTERM; echo READY; while :; do sleep 0.2; done" $PODMAN stop -t 1 $random_name & run_podman kill $random_name run_podman wait $random_name run_podman rm -f $random_name } +@test "podman wait - exit codes" { + random_name=$(random_string 10) + run_podman create --name=$random_name $IMAGE /no/such/command + # Container never ran -> exit code == 0 + run_podman wait $random_name + # Container did not start successfully -> exit code != 0 + run_podman 125 start $random_name + # FIXME(#14873): while older Podmans return 0 on wait, Docker does not. + run_podman wait $random_name +} + # vim: filetype=sh diff --git a/test/system/200-pod.bats b/test/system/200-pod.bats index 0e522b34d..b93f3f92f 100644 --- a/test/system/200-pod.bats +++ b/test/system/200-pod.bats @@ -332,7 +332,7 @@ EOF @test "podman pod create --share" { local pod_name="$(random_string 10 | tr A-Z a-z)" run_podman 125 pod create --share bogus --name $pod_name - is "$output" ".*Invalid kernel namespace to share: bogus. Options are: cgroup, ipc, net, pid, uts or none" \ + is "$output" ".*invalid kernel namespace to share: bogus. Options are: cgroup, ipc, net, pid, uts or none" \ "pod test for bogus --share option" run_podman pod create --share ipc --name $pod_name run_podman pod inspect $pod_name --format "{{.SharedNamespaces}}" diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats index 0d724985e..50eb15216 100644 --- a/test/system/500-networking.bats +++ b/test/system/500-networking.bats @@ -677,16 +677,20 @@ EOF @test "podman run port forward range" { for netmode in bridge slirp4netns:port_handler=slirp4netns slirp4netns:port_handler=rootlesskit; do local range=$(random_free_port_range 3) - local port="${test%-*}" - local end_port="${test#-*}" + # die() inside $(...) does not actually stop us. + assert "$range" != "" "Could not find free port range" + + local port="${range%-*}" + local end_port="${range#*-}" local random=$(random_string) run_podman run --network $netmode -p "$range:$range" -d $IMAGE sleep inf cid="$output" for port in $(seq $port $end_port); do run_podman exec -d $cid nc -l -p $port -e /bin/cat - # -w 1 adds a 1 second timeout, for some reason ubuntus ncat doesn't close the connection on EOF, - # other options to change this are not portable across distros but -w seems to work + # -w 1 adds a 1 second timeout. For some reason, ubuntu's ncat + # doesn't close the connection on EOF, and other options to + # change this are not portable across distros. -w seems to work. run nc -w 1 127.0.0.1 $port <<<$random is "$output" "$random" "ncat got data back (netmode=$netmode port=$port)" done diff --git a/test/system/helpers.bash b/test/system/helpers.bash index 273e8d2f5..ceac48036 100644 --- a/test/system/helpers.bash +++ b/test/system/helpers.bash @@ -299,15 +299,17 @@ function random_free_port_range() { local maxtries=10 while [[ $maxtries -gt 0 ]]; do local firstport=$(random_free_port) - local all_ports_free=1 - for i in $(seq 2 $size); do - if ! port_is_free $((firstport + $i)); then - all_ports_free= + local lastport= + for i in $(seq 1 $((size - 1))); do + lastport=$((firstport + i)) + if ! port_is_free $lastport; then + echo "# port $lastport is in use; trying another." >&3 + lastport= break fi done - if [[ -n "$all_ports_free" ]]; then - echo "$firstport-$((firstport + $size - 1))" + if [[ -n "$lastport" ]]; then + echo "$firstport-$lastport" return fi diff --git a/utils/utils.go b/utils/utils.go index a20181b0a..997de150d 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -13,7 +13,6 @@ import ( "sync" "github.com/containers/common/pkg/cgroups" - "github.com/containers/podman/v4/libpod/define" "github.com/containers/storage/pkg/archive" "github.com/godbus/dbus/v5" "github.com/sirupsen/logrus" @@ -52,57 +51,6 @@ func ExecCmdWithStdStreams(stdin io.Reader, stdout, stderr io.Writer, env []stri return nil } -// ErrDetach is an error indicating that the user manually detached from the -// container. -var ErrDetach = define.ErrDetach - -// CopyDetachable is similar to io.Copy but support a detach key sequence to break out. -func CopyDetachable(dst io.Writer, src io.Reader, keys []byte) (written int64, err error) { - buf := make([]byte, 32*1024) - for { - nr, er := src.Read(buf) - if nr > 0 { - preservBuf := []byte{} - for i, key := range keys { - preservBuf = append(preservBuf, buf[0:nr]...) - if nr != 1 || buf[0] != key { - break - } - if i == len(keys)-1 { - return 0, ErrDetach - } - nr, er = src.Read(buf) - } - var nw int - var ew error - if len(preservBuf) > 0 { - nw, ew = dst.Write(preservBuf) - nr = len(preservBuf) - } else { - nw, ew = dst.Write(buf[0:nr]) - } - if nw > 0 { - written += int64(nw) - } - if ew != nil { - err = ew - break - } - if nr != nw { - err = io.ErrShortWrite - break - } - } - if er != nil { - if er != io.EOF { - err = er - } - break - } - } - return written, err -} - // UntarToFileSystem untars an os.file of a tarball to a destination in the filesystem func UntarToFileSystem(dest string, tarball *os.File, options *archive.TarOptions) error { logrus.Debugf("untarring %s", tarball.Name()) diff --git a/pkg/kubeutils/resize.go b/vendor/github.com/containers/common/pkg/resize/resize.go index a744c66cc..9a2afcf73 100644 --- a/pkg/kubeutils/resize.go +++ b/vendor/github.com/containers/common/pkg/resize/resize.go @@ -14,16 +14,18 @@ See the License for the specific language governing permissions and limitations under the License. */ -package kubeutils +package resize -import ( - "github.com/containers/podman/v4/libpod/define" -) +// TerminalSize represents the width and height of a terminal. +type TerminalSize struct { + Width uint16 + Height uint16 +} // HandleResizing spawns a goroutine that processes the resize channel, calling resizeFunc for each -// remotecommand.TerminalSize received from the channel. The resize channel must be closed elsewhere to stop the +// TerminalSize received from the channel. The resize channel must be closed elsewhere to stop the // goroutine. -func HandleResizing(resize <-chan define.TerminalSize, resizeFunc func(size define.TerminalSize)) { +func HandleResizing(resize <-chan TerminalSize, resizeFunc func(size TerminalSize)) { if resize == nil { return } diff --git a/vendor/github.com/containers/common/pkg/util/copy.go b/vendor/github.com/containers/common/pkg/util/copy.go new file mode 100644 index 000000000..a45b82fc9 --- /dev/null +++ b/vendor/github.com/containers/common/pkg/util/copy.go @@ -0,0 +1,57 @@ +package util + +import ( + "errors" + "io" +) + +// ErrDetach indicates that an attach session was manually detached by +// the user. +var ErrDetach = errors.New("detached from container") + +// CopyDetachable is similar to io.Copy but support a detach key sequence to break out. +func CopyDetachable(dst io.Writer, src io.Reader, keys []byte) (written int64, err error) { + buf := make([]byte, 32*1024) + for { + nr, er := src.Read(buf) + if nr > 0 { + preservBuf := []byte{} + for i, key := range keys { + preservBuf = append(preservBuf, buf[0:nr]...) + if nr != 1 || buf[0] != key { + break + } + if i == len(keys)-1 { + return 0, ErrDetach + } + nr, er = src.Read(buf) + } + var nw int + var ew error + if len(preservBuf) > 0 { + nw, ew = dst.Write(preservBuf) + nr = len(preservBuf) + } else { + nw, ew = dst.Write(buf[0:nr]) + } + if nw > 0 { + written += int64(nw) + } + if ew != nil { + err = ew + break + } + if nr != nw { + err = io.ErrShortWrite + break + } + } + if er != nil { + if er != io.EOF { + err = er + } + break + } + } + return written, err +} diff --git a/vendor/modules.txt b/vendor/modules.txt index aaddcaeb0..195c40405 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -111,7 +111,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.48.1-0.20220630172158-178929cf063e +# github.com/containers/common v0.48.1-0.20220705175712-dd1c331887b9 ## explicit github.com/containers/common/libimage github.com/containers/common/libimage/define @@ -142,6 +142,7 @@ github.com/containers/common/pkg/netns github.com/containers/common/pkg/parse github.com/containers/common/pkg/report github.com/containers/common/pkg/report/camelcase +github.com/containers/common/pkg/resize github.com/containers/common/pkg/retry github.com/containers/common/pkg/seccomp github.com/containers/common/pkg/secrets @@ -594,7 +595,6 @@ github.com/openshift/imagebuilder/strslice github.com/ostreedev/ostree-go/pkg/glibobject github.com/ostreedev/ostree-go/pkg/otbuiltin # github.com/pkg/errors v0.9.1 -## explicit github.com/pkg/errors # github.com/pmezard/go-difflib v1.0.0 ## explicit diff --git a/version/version.go b/version/version.go index e180c7cb0..57403b42c 100644 --- a/version/version.go +++ b/version/version.go @@ -41,7 +41,7 @@ var APIVersion = map[Tree]map[Level]semver.Version{ MinimalAPI: semver.MustParse("4.0.0"), }, Compat: { - CurrentAPI: semver.MustParse("1.40.0"), + CurrentAPI: semver.MustParse("1.41.0"), MinimalAPI: semver.MustParse("1.24.0"), }, } |