diff options
136 files changed, 2229 insertions, 1180 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 3dfbff11b..c33ed9c0c 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -29,7 +29,7 @@ env: UBUNTU_NAME: "ubuntu-2110" # Google-cloud VM Images - IMAGE_SUFFIX: "c6015869890330624" + IMAGE_SUFFIX: "c6454758209748992" FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}" PRIOR_FEDORA_CACHE_IMAGE_NAME: "prior-fedora-${IMAGE_SUFFIX}" UBUNTU_CACHE_IMAGE_NAME: "ubuntu-${IMAGE_SUFFIX}" @@ -612,10 +612,10 @@ rootless_gitlab_test_task: alias: rootless_gitlab_test skip: *tags only_if: *not_docs - # Test temporarily ignored due to failure on/around 7-10th Dec. 2021 - # Appears related to https://gitlab.com/gitlab-org/gitlab-runner/-/issues/28732 - # Log: https://cirrus-ci.com/task/5708221852680192?logs=setup#L433 - allow_failures: $CI == $CI + # Community-maintained downstream test may fail unexpectedly. + # Ref. repository: https://gitlab.com/gitlab-org/gitlab-runner + # If necessary, uncomment the next line and file issue(s) with details. + # allow_failures: $CI == $CI depends_on: - rootless_integration_test gce_instance: *standardvm diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index 9610c29dc..694b97fe5 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -388,10 +388,7 @@ func createPodIfNecessary(cmd *cobra.Command, s *specgen.SpecGenerator, netOpts if err != nil { return nil, err } - imageName := config.DefaultInfraImage - podGen.InfraImage = imageName - podGen.InfraContainerSpec = specgen.NewSpecGenerator(imageName, false) - podGen.InfraContainerSpec.RawImageName = imageName + podGen.InfraContainerSpec = specgen.NewSpecGenerator("", false) podGen.InfraContainerSpec.NetworkOptions = podGen.NetworkOptions err = specgenutil.FillOutSpecGen(podGen.InfraContainerSpec, &infraOpts, []string{}) if err != nil { diff --git a/cmd/podman/containers/rm.go b/cmd/podman/containers/rm.go index 70cb76693..cede0ba14 100644 --- a/cmd/podman/containers/rm.go +++ b/cmd/podman/containers/rm.go @@ -61,7 +61,8 @@ func rmFlags(cmd *cobra.Command) { flags.BoolVarP(&rmOptions.All, "all", "a", false, "Remove all containers") flags.BoolVarP(&rmOptions.Ignore, "ignore", "i", false, "Ignore errors when a specified container is missing") - flags.BoolVarP(&rmOptions.Force, "force", "f", false, "Force removal of a running or unusable container. The default is false") + flags.BoolVarP(&rmOptions.Force, "force", "f", false, "Force removal of a running or unusable container") + flags.BoolVar(&rmOptions.Depend, "depend", false, "Remove container and all containers that depend on the selected container") timeFlagName := "time" flags.UintVarP(&stopTimeout, timeFlagName, "t", containerConfig.Engine.StopTimeout, "Seconds to wait for stop before killing the container") _ = cmd.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone) diff --git a/cmd/podman/images/scp.go b/cmd/podman/images/scp.go index f02a3c15e..fb20d9417 100644 --- a/cmd/podman/images/scp.go +++ b/cmd/podman/images/scp.go @@ -46,6 +46,7 @@ var ( var ( parentFlags []string + quiet bool source entities.ImageScpOptions dest entities.ImageScpOptions sshInfo entities.ImageScpConnections @@ -61,7 +62,7 @@ func init() { func scpFlags(cmd *cobra.Command) { flags := cmd.Flags() - flags.BoolVarP(&source.Quiet, "quiet", "q", false, "Suppress the output") + flags.BoolVarP(&quiet, "quiet", "q", false, "Suppress the output") } func scp(cmd *cobra.Command, args []string) (finalErr error) { @@ -139,6 +140,7 @@ func scp(cmd *cobra.Command, args []string) (finalErr error) { } } + source.Quiet = quiet source.File = f.Name() // after parsing the arguments, set the file for the save/load dest.File = source.File if err = os.Remove(source.File); err != nil { // remove the file and simply use its name so podman creates the file upon save. avoids umask errors @@ -203,15 +205,6 @@ func scp(cmd *cobra.Command, args []string) (finalErr error) { } } - src, err := json.MarshalIndent(source, "", " ") - if err != nil { - return err - } - dst, err := json.MarshalIndent(dest, "", " ") - if err != nil { - return err - } - fmt.Printf("SOURCE: %s\nDEST: %s\n", string(src), string(dst)) return nil } diff --git a/cmd/podman/machine/list.go b/cmd/podman/machine/list.go index 858d87401..3d8def0db 100644 --- a/cmd/podman/machine/list.go +++ b/cmd/podman/machine/list.go @@ -32,6 +32,7 @@ var ( Args: validate.NoArgs, ValidArgsFunction: completion.AutocompleteNone, Example: `podman machine list, + podman machine list --format json podman machine ls`, } listFlag = listFlagType{} @@ -43,16 +44,19 @@ type listFlagType struct { } type machineReporter struct { - Name string - Default bool - Created string - Running bool - LastUp string - Stream string - VMType string - CPUs uint64 - Memory string - DiskSize string + Name string + Default bool + Created string + Running bool + LastUp string + Stream string + VMType string + CPUs uint64 + Memory string + DiskSize string + Port int + RemoteUsername string + IdentityPath string } func init() { @@ -96,7 +100,7 @@ func list(cmd *cobra.Command, args []string) error { return err } - b, err := json.Marshal(machineReporter) + b, err := json.MarshalIndent(machineReporter, "", " ") if err != nil { return err } @@ -189,6 +193,9 @@ func toMachineFormat(vms []*machine.ListResponse) ([]*machineReporter, error) { response.CPUs = vm.CPUs response.Memory = strUint(vm.Memory) response.DiskSize = strUint(vm.DiskSize) + response.Port = vm.Port + response.RemoteUsername = vm.RemoteUsername + response.IdentityPath = vm.IdentityPath machineResponses = append(machineResponses, response) } diff --git a/cmd/podman/machine/start.go b/cmd/podman/machine/start.go index 9c9c24f64..a3770d61a 100644 --- a/cmd/podman/machine/start.go +++ b/cmd/podman/machine/start.go @@ -60,9 +60,9 @@ func start(cmd *cobra.Command, args []string) error { if err != nil { return err } + fmt.Printf("Starting machine %q\n", vmName) if err := vm.Start(vmName, machine.StartOptions{}); err != nil { return err } - fmt.Printf("Machine %q started successfully\n", vmName) return nil } diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go index f844812c2..6a28f7a79 100644 --- a/cmd/podman/pods/create.go +++ b/cmd/podman/pods/create.go @@ -71,7 +71,11 @@ func init() { _ = createCommand.RegisterFlagCompletionFunc(nameFlagName, completion.AutocompleteNone) infraImageFlagName := "infra-image" - flags.StringVar(&infraImage, infraImageFlagName, containerConfig.Engine.InfraImage, "The image of the infra container to associate with the pod") + var defInfraImage string + if !registry.IsRemote() { + defInfraImage = containerConfig.Engine.InfraImage + } + flags.StringVar(&infraImage, infraImageFlagName, defInfraImage, "The image of the infra container to associate with the pod") _ = createCommand.RegisterFlagCompletionFunc(infraImageFlagName, common.AutocompleteImages) podIDFileFlagName := "pod-id-file" @@ -109,7 +113,9 @@ func create(cmd *cobra.Command, args []string) error { return errors.Wrapf(err, "unable to process labels") } - imageName = infraImage + if cmd.Flag("infra-image").Changed { + imageName = infraImage + } img := imageName if !createOptions.Infra { if cmd.Flag("no-hosts").Changed { diff --git a/cmd/podman/root.go b/cmd/podman/root.go index bccc559ce..1de937ca5 100644 --- a/cmd/podman/root.go +++ b/cmd/podman/root.go @@ -114,6 +114,10 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error { } cfg := registry.PodmanConfig() + if cfg.NoOut { + null, _ := os.Open(os.DevNull) + os.Stdout = null + } // Currently it is only possible to restore a container with the same runtime // as used for checkpointing. It should be possible to make crun and runc @@ -137,7 +141,7 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error { runtimeFlag := cmd.Root().Flags().Lookup("runtime") if runtimeFlag == nil { return errors.Errorf( - "Unexcpected error setting runtime to '%s' for restore", + "setting runtime to '%s' for restore", *runtime, ) } @@ -217,7 +221,7 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error { context := cmd.Root().LocalFlags().Lookup("context") if context.Value.String() != "default" { - return errors.New("Podman does not support swarm, the only --context value allowed is \"default\"") + return errors.New("podman does not support swarm, the only --context value allowed is \"default\"") } if !registry.IsRemote() { if cmd.Flag("cpu-profile").Changed { @@ -343,6 +347,7 @@ func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) { lFlags.StringVar(&opts.Identity, identityFlagName, ident, "path to SSH identity file, (CONTAINER_SSHKEY)") _ = cmd.RegisterFlagCompletionFunc(identityFlagName, completion.AutocompleteDefault) + lFlags.BoolVar(&opts.NoOut, "noout", false, "do not output to stdout") lFlags.BoolVarP(&opts.Remote, "remote", "r", registry.IsRemote(), "Access remote Podman service") pFlags := cmd.PersistentFlags() if registry.IsRemote() { diff --git a/cmd/podman/utils/error.go b/cmd/podman/utils/error.go index aab1da675..b3b54876f 100644 --- a/cmd/podman/utils/error.go +++ b/cmd/podman/utils/error.go @@ -27,7 +27,7 @@ func (o OutputErrors) PrintErrors() (lastError error) { instead returns a message and we cast it to a new error. Following function performs parsing on build error and returns - exit status which was exepected for this current build + exit status which was expected for this current build */ func ExitCodeFromBuildError(errorMsg string) (int, error) { if strings.Contains(errorMsg, "exit status") { diff --git a/cmd/winpath/main.go b/cmd/winpath/main.go index 494d1cf3c..6fbe72837 100644 --- a/cmd/winpath/main.go +++ b/cmd/winpath/main.go @@ -114,7 +114,7 @@ func addPathToRegistry(dir string) error { return err } -// Removes all occurences of a directory path from the Windows path stored in the registry +// Removes all occurrences of a directory path from the Windows path stored in the registry func removePathFromRegistry(path string) error { k, err := registry.OpenKey(registry.CURRENT_USER, Environment, registry.READ|registry.WRITE) if err != nil { @@ -155,7 +155,7 @@ func removePathFromRegistry(path string) error { return err } -// Sends a notification message to all top level windows informing them the environmental setings have changed. +// Sends a notification message to all top level windows informing them the environmental settings have changed. // Applications such as the Windows command prompt and powershell will know to stop caching stale values on // subsequent restarts. Since applications block the sender when receiving a message, we set a 3 second timeout func broadcastEnvironmentChange() { diff --git a/contrib/cirrus/setup_environment.sh b/contrib/cirrus/setup_environment.sh index ee80a209c..8f535c7e7 100755 --- a/contrib/cirrus/setup_environment.sh +++ b/contrib/cirrus/setup_environment.sh @@ -118,12 +118,6 @@ fi case "$OS_RELEASE_ID" in ubuntu) ;; fedora) - # Force a crun version that has this fix: https://github.com/containers/crun/pull/819 - # FIXME: Remove once a fixed crun made its way into Fedora - if test "$OS_RELEASE_VER" == "35"; then - yum upgrade -y crun - fi - if ((CONTAINER==0)); then # All SELinux distros need this for systemd-in-a-container msg "Enabling container_manage_cgroup" diff --git a/docs/source/markdown/podman-machine-list.1.md b/docs/source/markdown/podman-machine-list.1.md index accdd760c..d68b8b1ca 100644 --- a/docs/source/markdown/podman-machine-list.1.md +++ b/docs/source/markdown/podman-machine-list.1.md @@ -20,16 +20,26 @@ tied to the Linux kernel. #### **--format**=*format* -Format list output using a Go template. - +Change the default output format. This can be of a supported type like 'json' +or a Go template. Valid placeholders for the Go template are listed below: | **Placeholder** | **Description** | | --------------- | ------------------------------- | -| .Name | VM name | +| .CPUs | Number of CPUs | | .Created | Time since VM creation | +| .Default | Is default machine | +| .DiskSize | Disk size of machine | +| .LastUp | Time machine was last up | | .LastUp | Time since the VM was last run | -| .VMType | VM type | +| .Memory | Allocated memeory for machine | +| .Name | VM name | +| .Running | Is machine running | +| .Stream | Stream name | +| .VMType | VM type | +| .Port | SSH Port to use to connect to VM| +| .RemoteUsername | VM Username for rootless Podman | +| .IdentityPath | Path to ssh identify file | #### **--help** @@ -43,8 +53,28 @@ Omit the table headings from the listing of pods. ``` $ podman machine list - -$ podman machine ls --format {{.Name}}\t{{.VMType}}\t{{.Created}}\t{{.LastUp}}\n +NAME VM TYPE CREATED LAST UP CPUS MEMORY DISK SIZE +podman-machine-default qemu 2 weeks ago 2 weeks ago 1 2.147GB 10.74GB + +$ podman machine ls --format "table {{.Name}}\t{{.VMType}}\t{{.Created}}\t{{.LastUp}}" +NAME VM TYPE CREATED LAST UP +podman-machine-default qemu 2 weeks ago 2 weeks ago + +$ podman machine ls --format json +[ + { + "Name": "podman-machine-default", + "Default": false, + "Created": "2021-12-27T10:36:14.373347492-05:00", + "Running": false, + "LastUp": "2021-12-27T11:22:50.17333371-05:00", + "Stream": "default", + "VMType": "qemu", + "CPUs": 1, + "Memory": "2147483648", + "DiskSize": "10737418240" + } +] ``` ## SEE ALSO diff --git a/docs/source/markdown/podman-rm.1.md b/docs/source/markdown/podman-rm.1.md index c599c8687..f3807d2f7 100644 --- a/docs/source/markdown/podman-rm.1.md +++ b/docs/source/markdown/podman-rm.1.md @@ -18,6 +18,10 @@ Running or unusable containers will not be removed without the **-f** option. Remove all containers. Can be used in conjunction with **-f** as well. +#### **--depend** + +Remove selected container and recursively remove all containers that depend on it. + #### **--cidfile** Read container ID from the specified file and remove the container. Can be specified multiple times. @@ -56,6 +60,11 @@ Remove a container by its name *mywebserver* $ podman rm mywebserver ``` +Remove a *mywebserver* container and all of the containers that depend on it +``` +$ podman rm --depend mywebserver +``` + Remove several containers by name and container id. ``` $ podman rm mywebserver myflaskserver 860a4b23 diff --git a/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md index daa8212c5..9f85ebda3 100644 --- a/docs/source/markdown/podman.1.md +++ b/docs/source/markdown/podman.1.md @@ -92,6 +92,11 @@ When namespace is set, created containers and pods will join the given namespace #### **--network-cmd-path**=*path* Path to the command binary to use for setting up a network. It is currently only used for setting up a slirp4netns network. If "" is used then the binary is looked up using the $PATH environment variable. +#### **--noout** + +Redirect stdout to /dev/null. This command will prevent all stdout from the Podman command. The **--noout** option will not block stderr or stdout from containers. + + #### **--remote**, **-r** When true, access to the Podman service will be remote. Defaults to false. Settings can be modified in the containers.conf file. If the CONTAINER_HOST @@ -3,7 +3,7 @@ module github.com/containers/podman/v3 go 1.16 require ( - github.com/BurntSushi/toml v0.4.1 + github.com/BurntSushi/toml v1.0.0 github.com/blang/semver v3.5.1+incompatible github.com/buger/goterm v0.0.0-20181115115552-c206103e1f37 github.com/checkpoint-restore/checkpointctl v0.0.0-20211204171957-54b4ebfdb681 @@ -12,9 +12,9 @@ require ( github.com/containernetworking/cni v1.0.1 github.com/containernetworking/plugins v1.0.1 github.com/containers/buildah v1.23.1 - github.com/containers/common v0.46.1-0.20211209220542-24f363480347 + github.com/containers/common v0.46.1-0.20220112112017-31e8cc4aeeab github.com/containers/conmon v2.0.20+incompatible - github.com/containers/image/v5 v5.17.1-0.20211207161909-6f3c8453e1a7 + github.com/containers/image/v5 v5.17.1-0.20220106205022-73f80d60f0e1 github.com/containers/ocicrypt v1.1.2 github.com/containers/psgo v1.7.1 github.com/containers/storage v1.37.1-0.20211213220314-73a749e4fec5 @@ -46,7 +46,7 @@ require ( github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.17.0 github.com/opencontainers/go-digest v1.0.0 - github.com/opencontainers/image-spec v1.0.2-0.20211123152302-43a7dee1ec31 + github.com/opencontainers/image-spec v1.0.3-0.20211202193544-a5463b7f9c84 github.com/opencontainers/runc v1.0.3 github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 github.com/opencontainers/runtime-tools v0.9.1-0.20211020193359-09d837bf40a7 @@ -66,7 +66,7 @@ require ( go.etcd.io/bbolt v1.3.6 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20211205182925-97ca703d548d + golang.org/x/sys v0.0.0-20211214234402-4825e8c3871d golang.org/x/text v0.3.7 google.golang.org/protobuf v1.27.1 gopkg.in/fsnotify.v1 v1.4.7 // indirect @@ -65,8 +65,9 @@ github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935 github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU= +github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= @@ -221,8 +222,8 @@ github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoT github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= github.com/containerd/containerd v1.5.5/go.mod h1:oSTh0QpT1w6jYcGmbiSbxv9OSQYaa88mPyWIuU79zyo= github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= -github.com/containerd/containerd v1.5.8 h1:NmkCC1/QxyZFBny8JogwLpOy2f+VEbO/f6bV2Mqtwuw= -github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s= +github.com/containerd/containerd v1.5.9 h1:rs6Xg1gtIxaeyG+Smsb/0xaSDu1VgFhOCKBXxMxbsF4= +github.com/containerd/containerd v1.5.9/go.mod h1:fvQqCfadDGga5HZyn3j4+dx56qj2I9YwBrlSdalvJYQ= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= @@ -283,14 +284,13 @@ github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNB github.com/containers/buildah v1.23.1 h1:Tpc9DsRuU+0Oofewpxb6OJVNQjCu7yloN/obUqzfDTY= github.com/containers/buildah v1.23.1/go.mod h1:4WnrN0yrA7ab0ppgunixu2WM1rlD2rG8QLJAKbEkZlQ= github.com/containers/common v0.44.2/go.mod h1:7sdP4vmI5Bm6FPFxb3lvAh1Iktb6tiO1MzjUzhxdoGo= -github.com/containers/common v0.46.1-0.20211209220542-24f363480347 h1:6CS7RroQLJu/SgUJXGZ3bOs2vnh9rxEnxczDcGjStBw= -github.com/containers/common v0.46.1-0.20211209220542-24f363480347/go.mod h1:SoHWZESBD7dbqIOkvKrIg5D8EuVIQgL6vkOvv0Yebws= +github.com/containers/common v0.46.1-0.20220112112017-31e8cc4aeeab h1:Pf1kwI8sZPiPMuen619noeltwtB2cIFC1iY42fE87AY= +github.com/containers/common v0.46.1-0.20220112112017-31e8cc4aeeab/go.mod h1:hXUU9gtA8V9dSLHhizp/k/s0ZXBzrnUSScUfrsw8z2Y= github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg= github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= github.com/containers/image/v5 v5.16.0/go.mod h1:XgTpfAPLRGOd1XYyCU5cISFr777bLmOerCSpt/v7+Q4= -github.com/containers/image/v5 v5.17.1-0.20211201214147-603ec1341d58/go.mod h1:iUA6fv9NnqIhEaP3+dqo22nKMNkSWCj8d5o8Dju0j1Q= -github.com/containers/image/v5 v5.17.1-0.20211207161909-6f3c8453e1a7 h1:iDxYJ+bqh626ui+jdSd17Bg7mCfqOJrLngnDCodxCY0= -github.com/containers/image/v5 v5.17.1-0.20211207161909-6f3c8453e1a7/go.mod h1:WbE0J9s8mDldjRSP1S07tgVX1R7rZGnvU70bYZq0aBg= +github.com/containers/image/v5 v5.17.1-0.20220106205022-73f80d60f0e1 h1:IIxEBQaYuj6w15h2q6mc8gB4MF6oSeDnOaq3pbVk3mg= +github.com/containers/image/v5 v5.17.1-0.20220106205022-73f80d60f0e1/go.mod h1:daAiRXgcGIf/7eD7B2EkuHHw084/8M8Kh35rzOu56y0= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= @@ -362,7 +362,6 @@ github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BU github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.8+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.11+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.12+incompatible h1:CEeNmFM0QZIsJCZKMkZx0ZcahTiewkrgiwfYD+dfl1U= github.com/docker/docker v20.10.12+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= @@ -818,8 +817,9 @@ github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3I github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.2-0.20210819154149-5ad6f50d6283/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2-0.20211123152302-43a7dee1ec31 h1:Wh4aR2I6JFwySre9m3iHJYuMnvUFE/HT6qAXozRWi/E= -github.com/opencontainers/image-spec v1.0.2-0.20211123152302-43a7dee1ec31/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.3-0.20211202193544-a5463b7f9c84 h1:g47eG1u/gw0JB7mZ88TcHKCmsy7sWUNZD8ZS9Jhi0O8= +github.com/opencontainers/image-spec v1.0.3-0.20211202193544-a5463b7f9c84/go.mod h1:Qnt1q4cjDNQI9bT832ziho5Iw2BhK8o1KwLOwW56VP4= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= @@ -913,6 +913,7 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rootless-containers/rootlesskit v0.14.6 h1:5kvJK6eeUtWZz1mYegu5S7DHOahq93K+jbc/mz+hbFQ= github.com/rootless-containers/rootlesskit v0.14.6/go.mod h1:uHPTRoPO6ZdOl2q99ZKOK14PJAwepfNKh6hV57AOZYQ= +github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -1010,9 +1011,8 @@ github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaW github.com/vbauerster/mpb/v6 v6.0.4 h1:h6J5zM/2wimP5Hj00unQuV8qbo5EPcj6wbkCqgj7KcY= github.com/vbauerster/mpb/v6 v6.0.4/go.mod h1:a/+JT57gqh6Du0Ay5jSR+uBMfXGdlR7VQlGP52fJxLM= github.com/vbauerster/mpb/v7 v7.1.3/go.mod h1:X5GlohZw2fIpypMXWaKart+HGSAjpz49skxkDk+ZL7c= -github.com/vbauerster/mpb/v7 v7.1.5/go.mod h1:4M8+qAoQqV60WDNktBM5k05i1iTrXE7rjKOHEVkVlec= -github.com/vbauerster/mpb/v7 v7.2.0 h1:WfCkVMNQfDZy8ZZ2r7YFXP7bi5+mnCiSecakBDYGW+E= -github.com/vbauerster/mpb/v7 v7.2.0/go.mod h1:WxlpgTrbnxiIjm5xn7IGYOopqP57hs0MhzBoGFC5ek4= +github.com/vbauerster/mpb/v7 v7.3.0 h1:WwRtHHT26gjVln0yJypDEEpTWyX9sk4QcUxM6tQjdEc= +github.com/vbauerster/mpb/v7 v7.3.0/go.mod h1:KERDXx9bfuStUwTH2FbsrJhJhVu1q+xmjjoCZMZrin4= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= @@ -1327,15 +1327,14 @@ golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211004093028-2c5d950f24ef/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211107104306-e0b2ad06fe42/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211205182925-97ca703d548d h1:FjkYO/PPp4Wi0EAUOVLxePm7qVW4r4ctbWpURyuOD0E= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211214234402-4825e8c3871d h1:1oIt9o40TWWI9FUaveVpUvBe13FNqBNVXy3ue2fcfkw= +golang.org/x/sys v0.0.0-20211214234402-4825e8c3871d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 7745646b6..28d961e4b 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -2221,33 +2221,50 @@ func (c *Container) getHosts() string { depCtr = c } + // getLocalIP returns the non loopback local IP of the host + getLocalIP := func() string { + addrs, err := net.InterfaceAddrs() + if err != nil { + return "" + } + for _, address := range addrs { + // check the address type and if it is not a loopback the display it + if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + if ipnet.IP.To4() != nil { + return ipnet.IP.String() + } + } + } + return "" + } + if depCtr != nil { - for _, status := range depCtr.getNetworkStatus() { + host := "" + outer: + for net, status := range depCtr.getNetworkStatus() { + network, err := c.runtime.network.NetworkInspect(net) + // only add the host entry for bridge networks + // ip/macvlan gateway is normally not on the host + if err != nil || network.Driver != types.BridgeNetworkDriver { + continue + } for _, netInt := range status.Interfaces { for _, netAddress := range netInt.Subnets { if netAddress.Gateway != nil { - hosts += fmt.Sprintf("%s host.containers.internal\n", netAddress.Gateway.String()) + host = fmt.Sprintf("%s host.containers.internal\n", netAddress.Gateway.String()) + break outer } } } } - } else if c.config.NetMode.IsSlirp4netns() { - // getLocalIP returns the non loopback local IP of the host - getLocalIP := func() string { - addrs, err := net.InterfaceAddrs() - if err != nil { - return "" - } - for _, address := range addrs { - // check the address type and if it is not a loopback the display it - if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { - if ipnet.IP.To4() != nil { - return ipnet.IP.String() - } - } + // if no bridge gw was found try to use a local ip + if host == "" { + if ip := getLocalIP(); ip != "" { + host = fmt.Sprintf("%s\t%s\n", ip, "host.containers.internal") } - return "" } + hosts += host + } else if c.config.NetMode.IsSlirp4netns() { if ip := getLocalIP(); ip != "" { hosts += fmt.Sprintf("%s\t%s\n", ip, "host.containers.internal") } diff --git a/libpod/kube.go b/libpod/kube.go index d667616d0..f465fc776 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -595,7 +595,7 @@ func containerToV1Container(ctx context.Context, c *Container) (v1.Container, [] // pause one and make sure it's in the storage by pulling it down if // missing. if image == "" && c.IsInfra() { - image = config.DefaultInfraImage + image = c.runtime.config.Engine.InfraImage if _, err := c.runtime.libimageRuntime.Pull(ctx, image, config.PullPolicyMissing, nil); err != nil { return kubeContainer, nil, nil, nil, err } diff --git a/libpod/options.go b/libpod/options.go index 6edb9972b..630fe809d 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -115,19 +115,6 @@ func WithStorageConfig(config storage.StoreOptions) RuntimeOption { } } -// WithDefaultTransport sets the default transport for retrieving images. -func WithDefaultTransport(defaultTransport string) RuntimeOption { - return func(rt *Runtime) error { - if rt.valid { - return define.ErrRuntimeFinalized - } - - rt.config.Engine.ImageDefaultTransport = defaultTransport - - return nil - } -} - // WithSignaturePolicy specifies the path of a file which decides how trust is // managed for images we've pulled. // If this is not specified, the system default configuration will be used @@ -144,26 +131,6 @@ func WithSignaturePolicy(path string) RuntimeOption { } } -// WithStateType sets the backing state implementation for libpod. -// Please note that information is not portable between backing states. -// As such, if this differs between two libpods running on the same system, -// they will not share containers, and unspecified behavior may occur. -func WithStateType(storeType config.RuntimeStateStore) RuntimeOption { - return func(rt *Runtime) error { - if rt.valid { - return define.ErrRuntimeFinalized - } - - if storeType == config.InvalidStateStore { - return errors.Wrapf(define.ErrInvalidArg, "must provide a valid state store type") - } - - rt.config.Engine.StateType = storeType - - return nil - } -} - // WithOCIRuntime specifies an OCI runtime to use for running containers. func WithOCIRuntime(runtime string) RuntimeOption { return func(rt *Runtime) error { @@ -452,23 +419,6 @@ func WithVolumePath(volPath string) RuntimeOption { } } -// WithDefaultInfraImage sets the infra image for libpod. -// An infra image is used for inter-container kernel -// namespace sharing within a pod. Typically, an infra -// container is lightweight and is there to reap -// zombie processes within its pid namespace. -func WithDefaultInfraImage(img string) RuntimeOption { - return func(rt *Runtime) error { - if rt.valid { - return define.ErrRuntimeFinalized - } - - rt.config.Engine.InfraImage = img - - return nil - } -} - // WithDefaultInfraCommand sets the command to // run on pause container start up. func WithDefaultInfraCommand(cmd string) RuntimeOption { @@ -483,19 +433,6 @@ func WithDefaultInfraCommand(cmd string) RuntimeOption { } } -// WithDefaultInfraName sets the infra container name for a single pod. -func WithDefaultInfraName(name string) RuntimeOption { - return func(rt *Runtime) error { - if rt.valid { - return define.ErrRuntimeFinalized - } - - rt.config.Engine.InfraImage = name - - return nil - } -} - // WithRenumber instructs libpod to perform a lock renumbering while // initializing. This will handle migrations from early versions of libpod with // file locks to newer versions with SHM locking, as well as changes in the @@ -563,8 +500,6 @@ func WithEventsLogger(logger string) RuntimeOption { } rt.config.Engine.EventsLogger = logger - rt.config.Engine.EventsLogFilePath = filepath.Join(rt.config.Engine.TmpDir, "events", "events.log") - return nil } } diff --git a/libpod/runtime.go b/libpod/runtime.go index 9794b3605..90cd8ffe0 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -170,7 +170,6 @@ func NewRuntime(ctx context.Context, options ...RuntimeOption) (*Runtime, error) if err != nil { return nil, err } - conf.CheckCgroupsAndAdjustConfig() return newRuntimeFromConfig(ctx, conf, options...) } @@ -228,6 +227,8 @@ func newRuntimeFromConfig(ctx context.Context, conf *config.Config, options ...R return nil, err } + runtime.config.CheckCgroupsAndAdjustConfig() + return runtime, nil } @@ -1113,7 +1114,9 @@ func (r *Runtime) mergeDBConfig(dbConfig *DBConfig) { logrus.Debugf("Overriding tmp dir %q with %q from database", c.TmpDir, dbConfig.LibpodTmp) } c.TmpDir = dbConfig.LibpodTmp - c.EventsLogFilePath = filepath.Join(dbConfig.LibpodTmp, "events", "events.log") + if c.EventsLogFilePath == "" { + c.EventsLogFilePath = filepath.Join(dbConfig.LibpodTmp, "events", "events.log") + } } if !r.storageSet.VolumePathSet && dbConfig.VolumePath != "" { diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index 59a1fd153..53ccb9139 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -429,7 +429,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai }() ctr.config.SecretsPath = filepath.Join(ctr.config.StaticDir, "secrets") - err = os.MkdirAll(ctr.config.SecretsPath, 0644) + err = os.MkdirAll(ctr.config.SecretsPath, 0755) if err != nil { return nil, err } @@ -915,6 +915,37 @@ func (r *Runtime) evictContainer(ctx context.Context, idOrName string, removeVol return id, cleanupErr } +// RemoveDepend removes all dependencies for a container +func (r *Runtime) RemoveDepend(ctx context.Context, rmCtr *Container, force bool, removeVolume bool, timeout *uint) ([]*reports.RmReport, error) { + rmReports := make([]*reports.RmReport, 0) + deps, err := r.state.ContainerInUse(rmCtr) + if err != nil { + if err == define.ErrCtrRemoved { + return rmReports, nil + } + return rmReports, err + } + for _, cid := range deps { + ctr, err := r.state.Container(cid) + if err != nil { + if err == define.ErrNoSuchCtr { + continue + } + return rmReports, err + } + + reports, err := r.RemoveDepend(ctx, ctr, force, removeVolume, timeout) + if err != nil { + return rmReports, err + } + rmReports = append(rmReports, reports...) + } + report := reports.RmReport{Id: rmCtr.ID()} + report.Err = r.removeContainer(ctx, rmCtr, force, removeVolume, false, timeout) + + return append(rmReports, &report), nil +} + // GetContainer retrieves a container by its ID func (r *Runtime) GetContainer(id string) (*Container, error) { r.lock.RLock() diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index ad341c3ab..4539199d3 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -36,6 +36,7 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request) { query := struct { Force bool `schema:"force"` Ignore bool `schema:"ignore"` + Depend bool `schema:"depend"` Link bool `schema:"link"` Timeout *uint `schema:"timeout"` DockerVolumes bool `schema:"v"` @@ -57,6 +58,7 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request) { if utils.IsLibpodRequest(r) { options.Volumes = query.LibpodVolumes options.Timeout = query.Timeout + options.Depend = query.Depend } else { if query.Link { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, @@ -71,7 +73,7 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request) { // code. containerEngine := abi.ContainerEngine{Libpod: runtime} name := utils.GetName(r) - report, err := containerEngine.ContainerRm(r.Context(), []string{name}, options) + reports, err := containerEngine.ContainerRm(r.Context(), []string{name}, options) if err != nil { if errors.Cause(err) == define.ErrNoSuchCtr { utils.ContainerNotFound(w, name, err) @@ -81,8 +83,8 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request) { utils.InternalServerError(w, err) return } - if len(report) > 0 && report[0].Err != nil { - err = report[0].Err + if len(reports) > 0 && reports[0].Err != nil { + err = reports[0].Err if errors.Cause(err) == define.ErrNoSuchCtr { utils.ContainerNotFound(w, name, err) return @@ -90,7 +92,10 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request) { utils.InternalServerError(w, err) return } - + if utils.IsLibpodRequest(r) { + utils.WriteResponse(w, http.StatusOK, reports) + return + } utils.WriteResponse(w, http.StatusNoContent, nil) } diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go index 0fcac5330..2d296b5ce 100644 --- a/pkg/api/handlers/compat/images_build.go +++ b/pkg/api/handlers/compat/images_build.go @@ -138,7 +138,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { // if layers field not set assume its not from a valid podman-client // could be a docker client, set `layers=true` since that is the default - // expected behviour + // expected behaviour if !utils.IsLibpodRequest(r) { if _, found := r.URL.Query()["layers"]; !found { query.Layers = true diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go index 1b29831b4..43b39060b 100644 --- a/pkg/api/handlers/libpod/pods.go +++ b/pkg/api/handlers/libpod/pods.go @@ -7,7 +7,6 @@ import ( "strings" "time" - "github.com/containers/common/pkg/config" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/api/handlers" @@ -62,15 +61,8 @@ func PodCreate(w http.ResponseWriter, r *http.Request) { psg.InfraContainerSpec.Name = psg.InfraName psg.InfraContainerSpec.ConmonPidFile = psg.InfraConmonPidFile psg.InfraContainerSpec.ContainerCreateCommand = psg.InfraCommand - imageName := psg.InfraImage - rawImageName := psg.InfraImage - if imageName == "" { - imageName = config.DefaultInfraImage - rawImageName = config.DefaultInfraImage - } - psg.InfraImage = imageName - psg.InfraContainerSpec.Image = imageName - psg.InfraContainerSpec.RawImageName = rawImageName + psg.InfraContainerSpec.Image = psg.InfraImage + psg.InfraContainerSpec.RawImageName = psg.InfraImage } podSpecComplete := entities.PodSpec{PodSpecGen: psg} pod, err := generate.MakePod(&podSpecComplete, runtime) diff --git a/pkg/api/handlers/swagger/swagger.go b/pkg/api/handlers/swagger/swagger.go index 9844839b7..7868ff206 100644 --- a/pkg/api/handlers/swagger/swagger.go +++ b/pkg/api/handlers/swagger/swagger.go @@ -111,6 +111,13 @@ type swagLibpodInspectImageResponse struct { } } +// Rm containers +// swagger:response DocsLibpodContainerRmReport +type swagLibpodContainerRmReport struct { + // in: body + Body []handlers.LibpodContainersRmReport +} + // Prune containers // swagger:response DocsContainerPruneReport type swagContainerPruneReport struct { diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go index f850db3d8..588758b2c 100644 --- a/pkg/api/handlers/types.go +++ b/pkg/api/handlers/types.go @@ -53,6 +53,17 @@ type LibpodContainersPruneReport struct { PruneError string `json:"Err,omitempty"` } +type LibpodContainersRmReport struct { + ID string `json:"Id"` + // Error which occurred during Rm operation (if any). + // This field is optional and may be omitted if no error occurred. + // + // Extensions: + // x-omitempty: true + // x-nullable: true + RmError string `json:"Err,omitempty"` +} + type Info struct { docker.Info BuildahVersion string diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go index 601e1251b..4d19c04d4 100644 --- a/pkg/api/server/register_containers.go +++ b/pkg/api/server/register_containers.go @@ -817,9 +817,22 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // required: true // description: the name or ID of the container // - in: query + // name: depend + // type: boolean + // description: additionally remove containers that depend on the container to be removed + // - in: query // name: force // type: boolean - // description: need something + // description: force stop container if running + // - in: query + // name: ignore + // type: boolean + // description: ignore errors when the container to be removed does not existxo + // - in: query + // name: timeout + // type: integer + // default: 10 + // description: number of seconds to wait before killing container when force removing // - in: query // name: v // type: boolean @@ -827,6 +840,8 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // produces: // - application/json // responses: + // 200: + // $ref: "#/responses/DocsLibpodContainerRmReport" // 204: // description: no error // 400: diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go index 14a173025..0148e62cb 100644 --- a/pkg/bindings/containers/containers.go +++ b/pkg/bindings/containers/containers.go @@ -78,25 +78,26 @@ func Prune(ctx context.Context, options *PruneOptions) ([]*reports.PruneReport, // The volumes bool dictates that a container's volumes should also be removed. // The All option indicates that all containers should be removed // The Ignore option indicates that if a container did not exist, ignore the error -func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) error { +func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) ([]*reports.RmReport, error) { if options == nil { options = new(RemoveOptions) } + var reports []*reports.RmReport conn, err := bindings.GetClient(ctx) if err != nil { - return err + return reports, err } params, err := options.ToParams() if err != nil { - return err + return reports, err } response, err := conn.DoRequest(ctx, nil, http.MethodDelete, "/containers/%s", params, nil, nameOrID) if err != nil { - return err + return reports, err } defer response.Body.Close() - return response.Process(nil) + return reports, response.Process(&reports) } // Inspect returns low level information about a Container. The nameOrID can be a container name diff --git a/pkg/bindings/containers/types.go b/pkg/bindings/containers/types.go index 81a53a549..db3eb3e1b 100644 --- a/pkg/bindings/containers/types.go +++ b/pkg/bindings/containers/types.go @@ -138,6 +138,7 @@ type PruneOptions struct { //go:generate go run ../generator/generator.go RemoveOptions // RemoveOptions are optional options for removing containers type RemoveOptions struct { + Depend *bool Ignore *bool Force *bool Volumes *bool diff --git a/pkg/bindings/containers/types_remove_options.go b/pkg/bindings/containers/types_remove_options.go index 1e52e819d..7fa198d2f 100644 --- a/pkg/bindings/containers/types_remove_options.go +++ b/pkg/bindings/containers/types_remove_options.go @@ -17,6 +17,21 @@ func (o *RemoveOptions) ToParams() (url.Values, error) { return util.ToParams(o) } +// WithDepend set field Depend to given value +func (o *RemoveOptions) WithDepend(value bool) *RemoveOptions { + o.Depend = &value + return o +} + +// GetDepend returns value of field Depend +func (o *RemoveOptions) GetDepend() bool { + if o.Depend == nil { + var z bool + return z + } + return *o.Depend +} + // WithIgnore set field Ignore to given value func (o *RemoveOptions) WithIgnore(value bool) *RemoveOptions { o.Ignore = &value diff --git a/pkg/bindings/test/containers_test.go b/pkg/bindings/test/containers_test.go index b6c06756b..cab032a40 100644 --- a/pkg/bindings/test/containers_test.go +++ b/pkg/bindings/test/containers_test.go @@ -175,7 +175,7 @@ var _ = Describe("Podman containers ", func() { Expect(err).To(BeNil()) err = containers.Pause(bt.conn, cid, nil) Expect(err).To(BeNil()) - err = containers.Remove(bt.conn, cid, nil) + _, err = containers.Remove(bt.conn, cid, nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -188,8 +188,10 @@ var _ = Describe("Podman containers ", func() { Expect(err).To(BeNil()) err = containers.Pause(bt.conn, cid, nil) Expect(err).To(BeNil()) - err = containers.Remove(bt.conn, cid, new(containers.RemoveOptions).WithForce(true)) + rmResponse, err := containers.Remove(bt.conn, cid, new(containers.RemoveOptions).WithForce(true)) Expect(err).To(BeNil()) + Expect(len(reports.RmReportsErrs(rmResponse))).To(Equal(0)) + Expect(len(reports.RmReportsIds(rmResponse))).To(Equal(1)) }) It("podman stop a paused container by name", func() { @@ -669,7 +671,8 @@ var _ = Describe("Podman containers ", func() { }) It("podman remove bogus container", func() { - err = containers.Remove(bt.conn, "foobar", nil) + _, err := containers.Remove(bt.conn, "foobar", nil) + Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) }) @@ -679,7 +682,7 @@ var _ = Describe("Podman containers ", func() { _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, name, nil) + _, err = containers.Remove(bt.conn, name, nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -690,7 +693,7 @@ var _ = Describe("Podman containers ", func() { cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, cid, nil) + _, err = containers.Remove(bt.conn, cid, nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -700,22 +703,22 @@ var _ = Describe("Podman containers ", func() { var name = "top" _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) - // Removing running container should fail - err = containers.Remove(bt.conn, name, new(containers.RemoveOptions).WithForce(true)) + // Removing running container should succeed + rmResponse, err := containers.Remove(bt.conn, name, new(containers.RemoveOptions).WithForce(true)) Expect(err).To(BeNil()) - //code, _ := bindings.CheckResponseCode(err) - //Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) + Expect(len(reports.RmReportsErrs(rmResponse))).To(Equal(0)) + Expect(len(reports.RmReportsIds(rmResponse))).To(Equal(1)) }) It("podman forcibly remove running container by ID", func() { var name = "top" cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) - // Removing running container should fail - err = containers.Remove(bt.conn, cid, new(containers.RemoveOptions).WithForce(true)) + // Forcably Removing running container should succeed + rmResponse, err := containers.Remove(bt.conn, cid, new(containers.RemoveOptions).WithForce(true)) Expect(err).To(BeNil()) - //code, _ := bindings.CheckResponseCode(err) - //Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) + Expect(len(reports.RmReportsErrs(rmResponse))).To(Equal(0)) + Expect(len(reports.RmReportsIds(rmResponse))).To(Equal(1)) }) It("podman remove running container and volume by name", func() { @@ -723,7 +726,7 @@ var _ = Describe("Podman containers ", func() { _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, name, new(containers.RemoveOptions).WithVolumes(true)) + _, err = containers.Remove(bt.conn, name, new(containers.RemoveOptions).WithVolumes(true)) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -734,7 +737,7 @@ var _ = Describe("Podman containers ", func() { cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, cid, new(containers.RemoveOptions).WithVolumes(true)) + _, err = containers.Remove(bt.conn, cid, new(containers.RemoveOptions).WithVolumes(true)) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -744,11 +747,11 @@ var _ = Describe("Podman containers ", func() { var name = "top" _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) - // Removing running container should fail - err = containers.Remove(bt.conn, name, new(containers.RemoveOptions).WithVolumes(true).WithForce(true)) + // Forcibly Removing running container should succeed + rmResponse, err := containers.Remove(bt.conn, name, new(containers.RemoveOptions).WithVolumes(true).WithForce(true)) Expect(err).To(BeNil()) - //code, _ := bindings.CheckResponseCode(err) - //Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) + Expect(len(reports.RmReportsErrs(rmResponse))).To(Equal(0)) + Expect(len(reports.RmReportsIds(rmResponse))).To(Equal(1)) }) It("podman forcibly remove running container and volume by ID", func() { @@ -756,10 +759,10 @@ var _ = Describe("Podman containers ", func() { cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, cid, new(containers.RemoveOptions).WithForce(true).WithVolumes(true)) + rmResponse, err := containers.Remove(bt.conn, cid, new(containers.RemoveOptions).WithForce(true).WithVolumes(true)) Expect(err).To(BeNil()) - //code, _ := bindings.CheckResponseCode(err) - //Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) + Expect(len(reports.RmReportsErrs(rmResponse))).To(Equal(0)) + Expect(len(reports.RmReportsIds(rmResponse))).To(Equal(1)) }) It("List containers with filters", func() { diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index ae441b7f3..e3f8f1b7c 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -129,6 +129,7 @@ type RestartReport struct { type RmOptions struct { All bool + Depend bool Force bool Ignore bool Latest bool @@ -136,11 +137,6 @@ type RmOptions struct { Volumes bool } -type RmReport struct { - Err error - Id string //nolint -} - type ContainerInspectReport struct { *define.InspectContainerData } diff --git a/pkg/domain/entities/engine.go b/pkg/domain/entities/engine.go index a8023f7cf..055af7ff9 100644 --- a/pkg/domain/entities/engine.go +++ b/pkg/domain/entities/engine.go @@ -40,6 +40,7 @@ type PodmanConfig struct { Identity string // ssh identity for connecting to server MaxWorks int // maximum number of parallel threads MemoryProfile string // Hidden: Should memory profile be taken + NoOut bool // Don't output to stdout RegistriesConf string // allows for specifying a custom registries.conf Remote bool // Connection to Podman API Service will use RESTful API RuntimePath string // --runtime flag will set Engine.RuntimePath diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 383e42098..7ce4dd0f6 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -40,7 +40,7 @@ type ContainerEngine interface { ContainerRename(ctr context.Context, nameOrID string, options ContainerRenameOptions) error ContainerRestart(ctx context.Context, namesOrIds []string, options RestartOptions) ([]*RestartReport, error) ContainerRestore(ctx context.Context, namesOrIds []string, options RestoreOptions) ([]*RestoreReport, error) - ContainerRm(ctx context.Context, namesOrIds []string, options RmOptions) ([]*RmReport, error) + ContainerRm(ctx context.Context, namesOrIds []string, options RmOptions) ([]*reports.RmReport, error) ContainerRun(ctx context.Context, opts ContainerRunOptions) (*ContainerRunReport, error) ContainerRunlabel(ctx context.Context, label string, image string, args []string, opts ContainerRunlabelOptions) error ContainerStart(ctx context.Context, namesOrIds []string, options ContainerStartOptions) ([]*ContainerStartReport, error) diff --git a/pkg/domain/entities/events.go b/pkg/domain/entities/events.go index 73a375b94..fa815d7b9 100644 --- a/pkg/domain/entities/events.go +++ b/pkg/domain/entities/events.go @@ -42,7 +42,7 @@ func ConvertToLibpodEvent(e Event) *libpodEvents.Event { Image: image, Name: name, Status: status, - Time: time.Unix(e.Time, e.TimeNano), + Time: time.Unix(0, e.TimeNano), Type: t, Details: libpodEvents.Details{ Attributes: details, diff --git a/pkg/domain/entities/reports/containers.go b/pkg/domain/entities/reports/containers.go new file mode 100644 index 000000000..54bcd092b --- /dev/null +++ b/pkg/domain/entities/reports/containers.go @@ -0,0 +1,28 @@ +package reports + +type RmReport struct { + Id string `json:"Id"` //nolint + Err error `json:"Err,omitempty"` +} + +func RmReportsIds(r []*RmReport) []string { + ids := make([]string, 0, len(r)) + for _, v := range r { + if v == nil || v.Id == "" { + continue + } + ids = append(ids, v.Id) + } + return ids +} + +func RmReportsErrs(r []*RmReport) []error { + errs := make([]error, 0, len(r)) + for _, v := range r { + if v == nil || v.Err == nil { + continue + } + errs = append(errs, v.Err) + } + return errs +} diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index bf4dcff62..a4522698e 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -301,27 +301,27 @@ func (ic *ContainerEngine) removeContainer(ctx context.Context, ctr *libpod.Cont return err } -func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, options entities.RmOptions) ([]*entities.RmReport, error) { - reports := []*entities.RmReport{} +func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, options entities.RmOptions) ([]*reports.RmReport, error) { + rmReports := []*reports.RmReport{} names := namesOrIds // Attempt to remove named containers directly from storage, if container is defined in libpod // this will fail and code will fall through to removing the container from libpod.` tmpNames := []string{} for _, ctr := range names { - report := entities.RmReport{Id: ctr} + report := reports.RmReport{Id: ctr} report.Err = ic.Libpod.RemoveStorageContainer(ctr, options.Force) switch errors.Cause(report.Err) { case nil: // remove container names that we successfully deleted - reports = append(reports, &report) + rmReports = append(rmReports, &report) case define.ErrNoSuchCtr, define.ErrCtrExists: // There is still a potential this is a libpod container tmpNames = append(tmpNames, ctr) default: if _, err := ic.Libpod.LookupContainer(ctr); errors.Cause(err) == define.ErrNoSuchCtr { // remove container failed, but not a libpod container - reports = append(reports, &report) + rmReports = append(rmReports, &report) continue } // attempt to remove as a libpod container @@ -340,23 +340,34 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, for _, ctr := range names { logrus.Debugf("Evicting container %q", ctr) - report := entities.RmReport{Id: ctr} + report := reports.RmReport{Id: ctr} _, err := ic.Libpod.EvictContainer(ctx, ctr, options.Volumes) if err != nil { if options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr { logrus.Debugf("Ignoring error (--allow-missing): %v", err) - reports = append(reports, &report) + rmReports = append(rmReports, &report) continue } report.Err = err - reports = append(reports, &report) + rmReports = append(rmReports, &report) continue } - reports = append(reports, &report) + rmReports = append(rmReports, &report) } - return reports, nil + return rmReports, nil } + if !options.All && options.Depend { + // Add additional containers based on dependencies to container map + for _, ctr := range ctrs { + reports, err := ic.Libpod.RemoveDepend(ctx, ctr, options.Force, options.Volumes, options.Timeout) + if err != nil { + return rmReports, err + } + rmReports = append(rmReports, reports...) + } + return rmReports, nil + } errMap, err := parallelctr.ContainerOp(ctx, ctrs, func(c *libpod.Container) error { return ic.removeContainer(ctx, c, options) }) @@ -364,12 +375,12 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, return nil, err } for ctr, err := range errMap { - report := new(entities.RmReport) + report := new(reports.RmReport) report.Id = ctr.ID() report.Err = err - reports = append(reports, report) + rmReports = append(rmReports, report) } - return reports, nil + return rmReports, nil } func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]*entities.ContainerInspectReport, []error, error) { diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 84c83ea8e..592e0f4e3 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -745,11 +745,17 @@ func putSignature(manifestBlob []byte, mech signature.SigningMechanism, sigStore // TransferRootless creates new podman processes using exec.Command and sudo, transferring images between the given source and destination users func transferRootless(source entities.ImageScpOptions, dest entities.ImageScpOptions, podman string, parentFlags []string) error { var cmdSave *exec.Cmd - saveCommand := parentFlags - saveCommand = append(saveCommand, []string{"save", "--output", source.File, source.Image}...) + saveCommand, loadCommand := parentFlags, parentFlags + saveCommand = append(saveCommand, []string{"save"}...) + loadCommand = append(loadCommand, []string{"load"}...) + if source.Quiet { + saveCommand = append(saveCommand, "-q") + loadCommand = append(loadCommand, "-q") + } + + saveCommand = append(saveCommand, []string{"--output", source.File, source.Image}...) - loadCommand := parentFlags - loadCommand = append(loadCommand, []string{"load", "--input", dest.File}...) + loadCommand = append(loadCommand, []string{"--input", dest.File}...) if source.User == "root" { cmdSave = exec.Command("sudo", podman) @@ -757,7 +763,7 @@ func transferRootless(source entities.ImageScpOptions, dest entities.ImageScpOpt cmdSave = exec.Command(podman) } cmdSave = utils.CreateSCPCommand(cmdSave, saveCommand) - logrus.Debug("Executing save command") + logrus.Debugf("Executing save command: %q", cmdSave) err := cmdSave.Run() if err != nil { return err @@ -770,20 +776,22 @@ func transferRootless(source entities.ImageScpOptions, dest entities.ImageScpOpt cmdLoad = exec.Command(podman) } cmdLoad = utils.CreateSCPCommand(cmdLoad, loadCommand) - logrus.Debug("Executing load command") - err = cmdLoad.Run() - if err != nil { - return err - } - return nil + logrus.Debugf("Executing load command: %q", cmdLoad) + return cmdLoad.Run() } -// TransferRootless creates new podman processes using exec.Command and su/machinectl, transferring images between the given source and destination users +// TransferRootful creates new podman processes using exec.Command and su/machinectl, transferring images between the given source and destination users func transferRootful(source entities.ImageScpOptions, dest entities.ImageScpOptions, podman string, parentFlags []string) error { basicCommand := []string{podman} basicCommand = append(basicCommand, parentFlags...) - saveCommand := append(basicCommand, []string{"save", "--output", source.File, source.Image}...) - loadCommand := append(basicCommand, []string{"load", "--input", dest.File}...) + saveCommand := append(basicCommand, "save") + loadCommand := append(basicCommand, "load") + if source.Quiet { + saveCommand = append(saveCommand, "-q") + loadCommand = append(loadCommand, "-q") + } + saveCommand = append(saveCommand, []string{"--output", source.File, source.Image}...) + loadCommand = append(loadCommand, []string{"--input", dest.File}...) save := []string{strings.Join(saveCommand, " ")} load := []string{strings.Join(loadCommand, " ")} @@ -846,18 +854,18 @@ func lookupUser(u string) (*user.User, error) { func execSu(execUser *user.User, command []string) error { cmd := exec.Command("su", "-l", execUser.Username, "--command") cmd = utils.CreateSCPCommand(cmd, command) - logrus.Debug("Executing command su") + logrus.Debugf("Executing via su: %q", cmd) return cmd.Run() } func execMachine(execUser *user.User, command []string, machinectl string) error { - var cmd *exec.Cmd + verb := machinectl + args := []string{"shell", "-q", execUser.Username + "@.host"} if execUser.Uid == "0" { - cmd = exec.Command("sudo", machinectl, "shell", "-q", execUser.Username+"@.host") - } else { - cmd = exec.Command(machinectl, "shell", "-q", execUser.Username+"@.host") + args = append([]string{verb}, args...) + verb = "sudo" } - cmd = utils.CreateSCPCommand(cmd, command) - logrus.Debug("Executing command machinectl") + cmd := utils.CreateSCPCommand(exec.Command(verb, args...), command) + logrus.Debugf("Executing via machinectl: %q", cmd) return cmd.Run() } diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 2127f8749..4f72eab96 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -182,9 +182,9 @@ func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []st return reports, nil } -func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, opts entities.RmOptions) ([]*entities.RmReport, error) { +func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, opts entities.RmOptions) ([]*reports.RmReport, error) { // TODO there is no endpoint for container eviction. Need to discuss - options := new(containers.RemoveOptions).WithForce(opts.Force).WithVolumes(opts.Volumes).WithIgnore(opts.Ignore) + options := new(containers.RemoveOptions).WithForce(opts.Force).WithVolumes(opts.Volumes).WithIgnore(opts.Ignore).WithDepend(opts.Depend) if opts.Timeout != nil { options = options.WithTimeout(*opts.Timeout) } @@ -193,25 +193,31 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, if err != nil { return nil, err } - reports := make([]*entities.RmReport, 0, len(ctrs)) + rmReports := make([]*reports.RmReport, 0, len(ctrs)) for _, c := range ctrs { - reports = append(reports, &entities.RmReport{ - Id: c.ID, - Err: containers.Remove(ic.ClientCtx, c.ID, options), - }) + report, err := containers.Remove(ic.ClientCtx, c.ID, options) + if err != nil { + return rmReports, err + } + rmReports = append(rmReports, report...) } - return reports, nil + return rmReports, nil } - reports := make([]*entities.RmReport, 0, len(namesOrIds)) + rmReports := make([]*reports.RmReport, 0, len(namesOrIds)) for _, name := range namesOrIds { - reports = append(reports, &entities.RmReport{ - Id: name, - Err: containers.Remove(ic.ClientCtx, name, options), - }) + report, err := containers.Remove(ic.ClientCtx, name, options) + if err != nil { + rmReports = append(rmReports, &reports.RmReport{ + Id: name, + Err: err, + }) + continue + } + rmReports = append(rmReports, report...) } - return reports, nil + return rmReports, nil } func (ic *ContainerEngine) ContainerPrune(ctx context.Context, opts entities.ContainerPruneOptions) ([]*reports.PruneReport, error) { @@ -552,6 +558,27 @@ func startAndAttach(ic *ContainerEngine, name string, detachKeys *string, input, return <-attachErr } +func logIfRmError(id string, err error, reports []*reports.RmReport) { + logError := func(id string, err error) { + if errorhandling.Contains(err, define.ErrNoSuchCtr) || + errorhandling.Contains(err, define.ErrCtrRemoved) || + errorhandling.Contains(err, types.ErrLayerUnknown) { + logrus.Debugf("Container %s does not exist: %v", id, err) + } else { + logrus.Errorf("Removing container %s: %v", id, err) + } + } + if err != nil { + logError(id, err) + } else { + for _, report := range reports { + if report.Err != nil { + logError(report.Id, report.Err) + } + } + } +} + func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) { reports := []*entities.ContainerStartReport{} var exitCode = define.ExecErrorCodeGeneric @@ -590,14 +617,8 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri } removeOptions := new(containers.RemoveOptions).WithVolumes(true).WithForce(false) removeContainer := func(id string) { - if err := containers.Remove(ic.ClientCtx, id, removeOptions); err != nil { - if errorhandling.Contains(err, define.ErrNoSuchCtr) || - errorhandling.Contains(err, define.ErrCtrRemoved) { - logrus.Debugf("Container %s does not exist: %v", id, err) - } else { - logrus.Errorf("Removing container %s: %v", id, err) - } - } + reports, err := containers.Remove(ic.ClientCtx, id, removeOptions) + logIfRmError(id, err, reports) } // There can only be one container if attach was used @@ -674,15 +695,8 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri if err != nil { if ctr.AutoRemove { rmOptions := new(containers.RemoveOptions).WithForce(false).WithVolumes(true) - if err := containers.Remove(ic.ClientCtx, ctr.ID, rmOptions); err != nil { - if errorhandling.Contains(err, define.ErrNoSuchCtr) || - errorhandling.Contains(err, define.ErrCtrRemoved) || - errorhandling.Contains(err, types.ErrLayerUnknown) { - logrus.Debugf("Container %s does not exist: %v", ctr.ID, err) - } else { - logrus.Errorf("Removing container %s: %v", ctr.ID, err) - } - } + reports, err := containers.Remove(ic.ClientCtx, ctr.ID, rmOptions) + logIfRmError(ctr.ID, err, reports) } report.Err = errors.Wrapf(err, "unable to start container %q", name) report.ExitCode = define.ExitCode(err) @@ -741,7 +755,8 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta report.ExitCode = define.ExitCode(err) if opts.Rm { - if rmErr := containers.Remove(ic.ClientCtx, con.ID, new(containers.RemoveOptions).WithForce(false).WithVolumes(true)); rmErr != nil { + reports, rmErr := containers.Remove(ic.ClientCtx, con.ID, new(containers.RemoveOptions).WithForce(false).WithVolumes(true)) + if rmErr != nil || reports[0].Err != nil { logrus.Debugf("unable to remove container %s after failing to start and attach to it", con.ID) } } @@ -759,15 +774,8 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta } if !shouldRestart { - if err := containers.Remove(ic.ClientCtx, con.ID, new(containers.RemoveOptions).WithForce(false).WithVolumes(true)); err != nil { - if errorhandling.Contains(err, define.ErrNoSuchCtr) || - errorhandling.Contains(err, define.ErrCtrRemoved) || - errorhandling.Contains(err, types.ErrLayerUnknown) { - logrus.Debugf("Container %s does not exist: %v", con.ID, err) - } else { - logrus.Errorf("Removing container %s: %v", con.ID, err) - } - } + reports, err := containers.Remove(ic.ClientCtx, con.ID, new(containers.RemoveOptions).WithForce(false).WithVolumes(true)) + logIfRmError(con.ID, err, reports) } }() } diff --git a/pkg/machine/config.go b/pkg/machine/config.go index 33a352898..97237f5e5 100644 --- a/pkg/machine/config.go +++ b/pkg/machine/config.go @@ -29,6 +29,15 @@ type InitOptions struct { ReExec bool } +type QemuMachineStatus = string + +const ( + // Running indicates the qemu vm is running + Running QemuMachineStatus = "running" + // Stopped indicates the vm has stopped + Stopped QemuMachineStatus = "stopped" +) + type Provider interface { NewMachine(opts InitOptions) (VM, error) LoadVMByName(name string) (VM, error) @@ -66,15 +75,18 @@ type Download struct { type ListOptions struct{} type ListResponse struct { - Name string - CreatedAt time.Time - LastUp time.Time - Running bool - Stream string - VMType string - CPUs uint64 - Memory uint64 - DiskSize uint64 + Name string + CreatedAt time.Time + LastUp time.Time + Running bool + Stream string + VMType string + CPUs uint64 + Memory uint64 + DiskSize uint64 + Port int + RemoteUsername string + IdentityPath string } type SSHOptions struct { diff --git a/pkg/machine/ignition.go b/pkg/machine/ignition.go index 84d3be296..ac2cf71cf 100644 --- a/pkg/machine/ignition.go +++ b/pkg/machine/ignition.go @@ -10,6 +10,7 @@ import ( "os" "path/filepath" + "github.com/containers/common/pkg/config" "github.com/sirupsen/logrus" ) @@ -340,6 +341,24 @@ machine_enabled=true }, }) + setProxyOpts := getProxyVariables() + if setProxyOpts != "" { + files = append(files, File{ + Node: Node{ + Group: getNodeGrp("root"), + Path: "/etc/profile.d/proxy-opts.sh", + User: getNodeUsr("root"), + }, + FileEmbedded1: FileEmbedded1{ + Append: nil, + Contents: Resource{ + Source: encodeDataURLPtr(setProxyOpts), + }, + Mode: intToPtr(0644), + }, + }) + } + setDockerHost := `export DOCKER_HOST="unix://$(podman info -f "{{.Host.RemoteSocket.Path}}")" ` @@ -365,52 +384,110 @@ machine_enabled=true return files } - certFiles := getCerts(filepath.Join(userHome, ".config/containers/certs.d")) + certFiles := getCerts(filepath.Join(userHome, ".config/containers/certs.d"), true) files = append(files, certFiles...) - certFiles = getCerts(filepath.Join(userHome, ".config/docker/certs.d")) + certFiles = getCerts(filepath.Join(userHome, ".config/docker/certs.d"), true) files = append(files, certFiles...) + if sslCertFile, ok := os.LookupEnv("SSL_CERT_FILE"); ok { + if _, err := os.Stat(sslCertFile); err == nil { + certFiles = getCerts(sslCertFile, false) + files = append(files, certFiles...) + + if len(certFiles) > 0 { + setSSLCertFile := fmt.Sprintf("export %s=%s", "SSL_CERT_FILE", filepath.Join("/etc/containers/certs.d", filepath.Base(sslCertFile))) + files = append(files, File{ + Node: Node{ + Group: getNodeGrp("root"), + Path: "/etc/profile.d/ssl_cert_file.sh", + User: getNodeUsr("root"), + }, + FileEmbedded1: FileEmbedded1{ + Append: nil, + Contents: Resource{ + Source: encodeDataURLPtr(setSSLCertFile), + }, + Mode: intToPtr(0644), + }, + }) + } + } + } + return files } -func getCerts(certsDir string) []File { +func getCerts(certsDir string, isDir bool) []File { var ( files []File ) certs, err := ioutil.ReadDir(certsDir) - if err == nil { - for _, cert := range certs { - b, err := ioutil.ReadFile(filepath.Join(certsDir, cert.Name())) - if err != nil { - logrus.Warnf("Unable to read cert file %s", err.Error()) - continue - } - files = append(files, File{ - Node: Node{ - Group: getNodeGrp("root"), - Path: filepath.Join("/etc/containers/certs.d/", cert.Name()), - User: getNodeUsr("root"), - }, - FileEmbedded1: FileEmbedded1{ - Append: nil, - Contents: Resource{ - Source: encodeDataURLPtr(string(b)), + if isDir { + if err == nil { + for _, cert := range certs { + b, err := ioutil.ReadFile(filepath.Join(certsDir, cert.Name())) + if err != nil { + logrus.Warnf("Unable to read cert file %s", err.Error()) + continue + } + files = append(files, File{ + Node: Node{ + Group: getNodeGrp("root"), + Path: filepath.Join("/etc/containers/certs.d/", cert.Name()), + User: getNodeUsr("root"), }, - Mode: intToPtr(0644), - }, - }) + FileEmbedded1: FileEmbedded1{ + Append: nil, + Contents: Resource{ + Source: encodeDataURLPtr(string(b)), + }, + Mode: intToPtr(0644), + }, + }) + } + } else { + if !os.IsNotExist(err) { + logrus.Warnf("Unable to copy certs via ignition, error while reading certs from %s: %s", certsDir, err.Error()) + } } } else { - if !os.IsNotExist(err) { - logrus.Warnf("Unable to copy certs via ignition, error while reading certs from %s: %s", certsDir, err.Error()) + fileName := filepath.Base(certsDir) + b, err := ioutil.ReadFile(certsDir) + if err != nil { + logrus.Warnf("Unable to read cert file %s", err.Error()) + return files } + files = append(files, File{ + Node: Node{ + Group: getNodeGrp("root"), + Path: filepath.Join("/etc/containers/certs.d/", fileName), + User: getNodeUsr("root"), + }, + FileEmbedded1: FileEmbedded1{ + Append: nil, + Contents: Resource{ + Source: encodeDataURLPtr(string(b)), + }, + Mode: intToPtr(0644), + }, + }) } return files } +func getProxyVariables() string { + proxyOpts := "" + for _, variable := range config.ProxyEnv { + if value, ok := os.LookupEnv(variable); ok { + proxyOpts += fmt.Sprintf("\n export %s=%s", variable, value) + } + } + return proxyOpts +} + func getLinks(usrName string) []Link { return []Link{{ Node: Node{ diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index f09107c71..560037542 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -386,8 +386,16 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { } if len(v.Mounts) > 0 { - for !v.isRunning() || !v.isListening() { + running, err := v.isRunning() + if err != nil { + return err + } + for running || !v.isListening() { time.Sleep(100 * time.Millisecond) + running, err = v.isRunning() + if err != nil { + return err + } } } for _, mount := range v.Mounts { @@ -416,8 +424,48 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { return nil } +func (v *MachineVM) checkStatus(monitor *qmp.SocketMonitor) (machine.QemuMachineStatus, error) { + // this is the format returned from the monitor + // {"return": {"status": "running", "singlestep": false, "running": true}} + + type statusDetails struct { + Status string `json:"status"` + Step bool `json:"singlestep"` + Running bool `json:"running"` + } + type statusResponse struct { + Response statusDetails `json:"return"` + } + var response statusResponse + + checkCommand := struct { + Execute string `json:"execute"` + }{ + Execute: "query-status", + } + input, err := json.Marshal(checkCommand) + if err != nil { + return "", err + } + b, err := monitor.Run(input) + if err != nil { + if errors.Cause(err) == os.ErrNotExist { + return machine.Stopped, nil + } + return "", err + } + if err := json.Unmarshal(b, &response); err != nil { + return "", err + } + if response.Response.Status == machine.Running { + return machine.Running, nil + } + return machine.Stopped, nil +} + // Stop uses the qmp monitor to call a system_powerdown func (v *MachineVM) Stop(name string, _ machine.StopOptions) error { + var disconnected bool // check if the qmp socket is there. if not, qemu instance is gone if _, err := os.Stat(v.QMPMonitor.Address); os.IsNotExist(err) { // Right now it is NOT an error to stop a stopped machine @@ -442,19 +490,22 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error { return err } defer func() { - if err := qmpMonitor.Disconnect(); err != nil { - logrus.Error(err) + if !disconnected { + if err := qmpMonitor.Disconnect(); err != nil { + logrus.Error(err) + } } }() + if _, err = qmpMonitor.Run(input); err != nil { return err } + qemuSocketFile, pidFile, err := v.getSocketandPid() if err != nil { return err } if _, err := os.Stat(pidFile); os.IsNotExist(err) { - logrus.Info(err) return nil } pidString, err := ioutil.ReadFile(pidFile) @@ -483,6 +534,24 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error { return err } + if err := qmpMonitor.Disconnect(); err != nil { + return nil + } + + disconnected = true + waitInternal := 250 * time.Millisecond + for i := 0; i < 5; i++ { + running, err := v.isRunning() + if err != nil { + return err + } + if !running { + break + } + time.Sleep(waitInternal) + waitInternal = waitInternal * 2 + } + return nil } @@ -519,7 +588,11 @@ func (v *MachineVM) Remove(name string, opts machine.RemoveOptions) (string, fun ) // cannot remove a running vm - if v.isRunning() { + running, err := v.isRunning() + if err != nil { + return "", nil, err + } + if running { return "", nil, errors.Errorf("running vm %q cannot be destroyed", v.Name) } @@ -578,16 +651,33 @@ func (v *MachineVM) Remove(name string, opts machine.RemoveOptions) (string, fun }, nil } -func (v *MachineVM) isRunning() bool { +func (v *MachineVM) isRunning() (bool, error) { // Check if qmp socket path exists if _, err := os.Stat(v.QMPMonitor.Address); os.IsNotExist(err) { - return false + return false, nil } // Check if we can dial it - if _, err := qmp.NewSocketMonitor(v.QMPMonitor.Network, v.QMPMonitor.Address, v.QMPMonitor.Timeout); err != nil { - return false + monitor, err := qmp.NewSocketMonitor(v.QMPMonitor.Network, v.QMPMonitor.Address, v.QMPMonitor.Timeout) + if err != nil { + return false, nil } - return true + if err := monitor.Connect(); err != nil { + return false, err + } + defer func() { + if err := monitor.Disconnect(); err != nil { + logrus.Error(err) + } + }() + // If there is a monitor, lets see if we can query state + state, err := v.checkStatus(monitor) + if err != nil { + return false, err + } + if state == machine.Running { + return true, nil + } + return false, nil } func (v *MachineVM) isListening() bool { @@ -603,7 +693,11 @@ func (v *MachineVM) isListening() bool { // SSH opens an interactive SSH session to the vm specified. // 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() { + running, err := v.isRunning() + if err != nil { + return err + } + if !running { return errors.Errorf("vm %q is not running.", v.Name) } @@ -696,6 +790,9 @@ func GetVMInfos() ([]*machine.ListResponse, error) { listEntry.CPUs = vm.CPUs listEntry.Memory = vm.Memory * units.MiB listEntry.DiskSize = vm.DiskSize * units.GiB + listEntry.Port = vm.Port + listEntry.RemoteUsername = vm.RemoteUsername + listEntry.IdentityPath = vm.IdentityPath fi, err := os.Stat(fullPath) if err != nil { return err @@ -707,7 +804,11 @@ func GetVMInfos() ([]*machine.ListResponse, error) { return err } listEntry.LastUp = fi.ModTime() - if vm.isRunning() { + running, err := vm.isRunning() + if err != nil { + return err + } + if running { listEntry.Running = true } diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go index 5ec7c7b03..2c7b3c091 100644 --- a/pkg/specgen/generate/container.go +++ b/pkg/specgen/generate/container.go @@ -7,6 +7,7 @@ import ( "time" "github.com/containers/common/libimage" + "github.com/containers/common/pkg/config" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" ann "github.com/containers/podman/v3/pkg/annotations" @@ -126,16 +127,7 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat if s.EnvHost { defaultEnvs = envLib.Join(defaultEnvs, osEnv) } else if s.HTTPProxy { - for _, envSpec := range []string{ - "http_proxy", - "HTTP_PROXY", - "https_proxy", - "HTTPS_PROXY", - "ftp_proxy", - "FTP_PROXY", - "no_proxy", - "NO_PROXY", - } { + for _, envSpec := range config.ProxyEnv { if v, ok := osEnv[envSpec]; ok { defaultEnvs[envSpec] = v } diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index 6d9f598c9..b41ee8db0 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -291,7 +291,10 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener return nil, err } - envs[env.Name] = value + // Only set the env if the value is not "" + if value != "" { + envs[env.Name] = value + } } for _, envFrom := range opts.Container.EnvFrom { cmEnvs, err := envVarsFrom(envFrom, opts) diff --git a/pkg/specgen/generate/ports.go b/pkg/specgen/generate/ports.go index b60cc1e98..34b43a62e 100644 --- a/pkg/specgen/generate/ports.go +++ b/pkg/specgen/generate/ports.go @@ -206,7 +206,7 @@ func ParsePortMapping(portMappings []types.PortMapping, exposePorts map[uint16][ } // we do no longer need the original port mappings - // set it to 0 length so we can resuse it to populate + // set it to 0 length so we can reuse it to populate // the slice again while keeping the underlying capacity portMappings = portMappings[:0] diff --git a/pkg/specgen/podspecgen.go b/pkg/specgen/podspecgen.go index a18364882..31f96b933 100644 --- a/pkg/specgen/podspecgen.go +++ b/pkg/specgen/podspecgen.go @@ -97,7 +97,7 @@ type PodNetworkConfig struct { // Map of networks names ot ids the container should join to. // You can request additional settings for each network, you can // set network aliases, static ips, static mac address and the - // network interface name for this container on the specifc network. + // network interface name for this container on the specific network. // If the map is empty and the bridge network mode is set the container // will be joined to the default network. Networks map[string]types.PerNetworkOptions diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index 5989456c9..6c1011a78 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -426,7 +426,7 @@ type ContainerNetworkConfig struct { // Map of networks names ot ids the container should join to. // You can request additional settings for each network, you can // set network aliases, static ips, static mac address and the - // network interface name for this container on the specifc network. + // network interface name for this container on the specific network. // If the map is empty and the bridge network mode is set the container // will be joined to the default network. Networks map[string]nettypes.PerNetworkOptions diff --git a/test/apiv2/12-imagesMore.at b/test/apiv2/12-imagesMore.at index 3a5d5c096..96eba1db5 100644 --- a/test/apiv2/12-imagesMore.at +++ b/test/apiv2/12-imagesMore.at @@ -53,7 +53,7 @@ t GET libpod/images/$IMAGE/json 200 \ .RepoTags[-1]=$IMAGE # Remove the registry container -t DELETE libpod/containers/registry?force=true 204 +t DELETE libpod/containers/registry?force=true 200 # Remove images t DELETE libpod/images/$IMAGE 200 \ diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at index 554a905d4..936597f72 100644 --- a/test/apiv2/20-containers.at +++ b/test/apiv2/20-containers.at @@ -85,7 +85,7 @@ else fi fi -t DELETE libpod/containers/$cid 204 +t DELETE libpod/containers/$cid 200 .[0].Id=$cid # Issue #6799: it should be possible to start a container, even w/o args. t POST libpod/containers/create?name=test_noargs Image=${IMAGE} 201 \ @@ -100,7 +100,7 @@ t GET libpod/containers/${cid}/json 200 \ .State.Status~\\\(exited\\\|stopped\\\) \ .State.Running=false \ .State.ExitCode=0 -t DELETE libpod/containers/$cid 204 +t DELETE libpod/containers/$cid 200 .[0].Id=$cid CNAME=myfoo podman run -d --name $CNAME $IMAGE top @@ -190,8 +190,8 @@ t GET containers/myctr/json 200 \ t DELETE images/localhost/newrepo:latest?force=true 200 t DELETE images/localhost/newrepo:v1?force=true 200 t DELETE images/localhost/newrepo:v2?force=true 200 -t DELETE libpod/containers/$cid?force=true 204 -t DELETE libpod/containers/myctr 204 +t DELETE libpod/containers/$cid?force=true 200 .[0].Id=$cid +t DELETE libpod/containers/myctr 200 t DELETE libpod/containers/bogus 404 diff --git a/test/apiv2/22-stop.at b/test/apiv2/22-stop.at index 91bc9937d..bde534b72 100644 --- a/test/apiv2/22-stop.at +++ b/test/apiv2/22-stop.at @@ -11,7 +11,7 @@ podman run -dt --name mytop $IMAGE top &>/dev/null t GET libpod/containers/mytop/json 200 .State.Status=running t POST libpod/containers/mytop/stop 204 t GET libpod/containers/mytop/json 200 .State.Status~\\\(exited\\\|stopped\\\) -t DELETE libpod/containers/mytop 204 +t DELETE libpod/containers/mytop 200 # stop, by ID # Remember that podman() hides all output; we need to get our CID via inspect @@ -21,4 +21,4 @@ t GET libpod/containers/mytop/json 200 .State.Status=running cid=$(jq -r .Id <<<"$output") t POST libpod/containers/$cid/stop 204 t GET libpod/containers/mytop/json 200 .State.Status~\\\(exited\\\|stopped\\\) -t DELETE libpod/containers/mytop 204 +t DELETE libpod/containers/mytop 200 diff --git a/test/apiv2/25-containersMore.at b/test/apiv2/25-containersMore.at index 0a049d869..c9fda8c6f 100644 --- a/test/apiv2/25-containersMore.at +++ b/test/apiv2/25-containersMore.at @@ -51,7 +51,7 @@ like "$output" ".*merged" "Check container mount" # Unmount the container t POST libpod/containers/foo/unmount 204 -t DELETE libpod/containers/foo?force=true 204 +t DELETE libpod/containers/foo?force=true 200 podman run $IMAGE true @@ -79,7 +79,7 @@ like "$output" ".*metadata:.*" "Check generated kube yaml(service=true) - metada like "$output" ".*spec:.*" "Check generated kube yaml(service=true) - spec" like "$output" ".*kind:\\sService.*" "Check generated kube yaml(service=true) - kind: Service" -t DELETE libpod/containers/$cid 204 +t DELETE libpod/containers/$cid 200 .[0].Id=$cid # Create 3 stopped containers to test containers prune podman run $IMAGE true 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 101044bbb..1b4597cf8 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 @@ -99,7 +99,7 @@ class ContainerTestCase(APITestCase): def test_delete(self): r = requests.delete(self.uri(self.resolve_container("/containers/{}?force=true"))) - self.assertEqual(r.status_code, 204, r.text) + self.assertEqual(r.status_code, 200, r.text) def test_stop(self): r = requests.post(self.uri(self.resolve_container("/containers/{}/start"))) diff --git a/test/buildah-bud/apply-podman-deltas b/test/buildah-bud/apply-podman-deltas index 44a33b0b8..e42c8aa52 100755 --- a/test/buildah-bud/apply-podman-deltas +++ b/test/buildah-bud/apply-podman-deltas @@ -191,6 +191,16 @@ skip_if_remote "--stdin option will not be implemented in podman-remote" \ # BEGIN tests which are skipped due to actual podman-remote bugs. ############################################################################### +# BEGIN emergency handling of github git-protocol shutdown +# +# Please remove this as soon as we vendor buildah with #3701 + +skip "emergency workaround until buildah #3701 gets vendored in" \ + "bud-git-context" \ + "bud using gitrepo and branch" + +# END emergency handling of github git-protocol shutdown +############################################################################### # Done. exit $RC diff --git a/test/e2e/containers_conf_test.go b/test/e2e/containers_conf_test.go index 2a6f177f2..838221dd5 100644 --- a/test/e2e/containers_conf_test.go +++ b/test/e2e/containers_conf_test.go @@ -452,4 +452,36 @@ var _ = Describe("Podman run", func() { Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(ContainSubstring("(default 1234)")) }) + + It("podman bad infra_image name in containers.conf", func() { + infra1 := "i.do/not/exist:image" + infra2 := "i.still.do/not/exist:image" + errorString := "initializing source docker://" + infra1 + error2String := "initializing source docker://" + infra2 + configPath := filepath.Join(podmanTest.TempDir, "containers.conf") + os.Setenv("CONTAINERS_CONF", configPath) + + containersConf := []byte("[engine]\ninfra_image=\"" + infra1 + "\"") + err = ioutil.WriteFile(configPath, containersConf, os.ModePerm) + Expect(err).To(BeNil()) + + if IsRemote() { + podmanTest.RestartRemoteService() + } + + result := podmanTest.Podman([]string{"pod", "create", "--infra-image", infra2}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(125)) + Expect(result.ErrorToString()).To(ContainSubstring(error2String)) + + result = podmanTest.Podman([]string{"pod", "create"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(125)) + Expect(result.ErrorToString()).To(ContainSubstring(errorString)) + + result = podmanTest.Podman([]string{"create", "--pod", "new:pod1", ALPINE}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(125)) + Expect(result.ErrorToString()).To(ContainSubstring(errorString)) + }) }) diff --git a/test/e2e/events_test.go b/test/e2e/events_test.go index 39f495460..3b5b8ac6c 100644 --- a/test/e2e/events_test.go +++ b/test/e2e/events_test.go @@ -62,6 +62,8 @@ var _ = Describe("Podman events", func() { result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).To(BeNumerically(">=", 1), "Number of events") + date := time.Now().Format("2006-01-02") + Expect(result.OutputToStringArray()).To(ContainElement(HavePrefix(date)), "event log has correct timestamp") }) It("podman events with an event filter and container=cid", func() { diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 1c7eb09a4..71bfd1e99 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -1659,7 +1659,7 @@ var _ = Describe("Podman play kube", func() { inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ range .Config.Env }}[{{ . }}]{{end}}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) - Expect(inspect.OutputToString()).To(ContainSubstring(`[FOO=]`)) + Expect(inspect.OutputToString()).To(Not(ContainSubstring(`[FOO=]`))) }) It("podman play kube test optional env value from missing configmap", func() { @@ -1674,7 +1674,7 @@ var _ = Describe("Podman play kube", func() { inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ range .Config.Env }}[{{ . }}]{{end}}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) - Expect(inspect.OutputToString()).To(ContainSubstring(`[FOO=]`)) + Expect(inspect.OutputToString()).To(Not(ContainSubstring(`[FOO=]`))) }) It("podman play kube test get all key-value pairs from configmap as envs", func() { @@ -1768,7 +1768,7 @@ var _ = Describe("Podman play kube", func() { inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ range .Config.Env }}[{{ . }}]{{end}}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) - Expect(inspect.OutputToString()).To(ContainSubstring(`[FOO=]`)) + Expect(inspect.OutputToString()).To(Not(ContainSubstring(`[FOO=]`))) }) It("podman play kube test optional env value from secret with missing key", func() { @@ -1784,7 +1784,7 @@ var _ = Describe("Podman play kube", func() { inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ range .Config.Env }}[{{ . }}]{{end}}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) - Expect(inspect.OutputToString()).To(ContainSubstring(`[FOO=]`)) + Expect(inspect.OutputToString()).To(Not(ContainSubstring(`[FOO=]`))) }) It("podman play kube test get all key-value pairs from secret as envs", func() { @@ -3212,7 +3212,7 @@ ENV OPENJ9_JAVA_OPTIONS=%q inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ range .Config.Env }}[{{ . }}]{{end}}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) - Expect(inspect.OutputToString()).To(ContainSubstring(`[FOO=]`)) + Expect(inspect.OutputToString()).To(Not(ContainSubstring(`[FOO=]`))) }) It("podman play kube uses all key-value pairs as envs", func() { diff --git a/test/e2e/pull_test.go b/test/e2e/pull_test.go index f22acca6e..38afff9bd 100644 --- a/test/e2e/pull_test.go +++ b/test/e2e/pull_test.go @@ -354,13 +354,13 @@ var _ = Describe("Podman pull", func() { session = podmanTest.Podman([]string{"rmi", "cirros"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - session = podmanTest.Podman([]string{"pull", imgPath}) + session = podmanTest.Podman([]string{"run", imgPath, "ls"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - session = podmanTest.Podman([]string{"images"}) + // Note that reference is not preserved in dir. + session = podmanTest.Podman([]string{"image", "exists", "cirros"}) session.WaitWithDefaultTimeout() - Expect(session).Should(Exit(0)) - Expect(session.LineInOutputContainsTag(filepath.Join("localhost", dirpath), "latest")).To(BeTrue()) + Expect(session).Should(Exit(1)) }) It("podman pull from local OCI directory", func() { diff --git a/test/system/001-basic.bats b/test/system/001-basic.bats index 23489c1b5..9b0a71285 100644 --- a/test/system/001-basic.bats +++ b/test/system/001-basic.bats @@ -41,7 +41,7 @@ function setup() { # This one must fail run_podman 125 --context=swarm version is "$output" \ - "Error: Podman does not support swarm, the only --context value allowed is \"default\"" \ + "Error: podman does not support swarm, the only --context value allowed is \"default\"" \ "--context=default or fail" } diff --git a/test/system/010-images.bats b/test/system/010-images.bats index 201418620..ebd71450f 100644 --- a/test/system/010-images.bats +++ b/test/system/010-images.bats @@ -272,6 +272,10 @@ Deleted: $pauseID" "infra images gets removed as well" is "$output" "" run_podman create --pod new:$pname $IMAGE + # Clean up + run_podman rm "${lines[-1]}" + run_podman pod rm -a + run_podman rmi $pauseImage } @test "podman images - rmi -f can remove infra images" { diff --git a/test/system/030-run.bats b/test/system/030-run.bats index feca5370b..afcda3d3c 100644 --- a/test/system/030-run.bats +++ b/test/system/030-run.bats @@ -509,6 +509,21 @@ json-file | f rm -f $new_runtime } +@test "podman --noout run should print output" { + run_podman --noout run -d --name test $IMAGE echo hi + is "$output" "" "output should be empty" + run_podman wait test + run_podman --noout rm test + is "$output" "" "output should be empty" +} + +@test "podman --noout create should print output" { + run_podman --noout create --name test $IMAGE echo hi + is "$output" "" "output should be empty" + run_podman --noout rm test + is "$output" "" "output should be empty" +} + # Regression test for issue #8082 @test "podman run : look up correct image name" { # Create a 2nd tag for the local image. Force to lower case, and apply it. diff --git a/test/system/035-logs.bats b/test/system/035-logs.bats index 3caf97a22..db50c8f8c 100644 --- a/test/system/035-logs.bats +++ b/test/system/035-logs.bats @@ -90,14 +90,16 @@ ${cid[0]} d" "Sequential output from logs" } function _log_test_restarted() { - run_podman run --log-driver=$1 --name logtest $IMAGE sh -c 'start=0; if test -s log; then start=`tail -n 1 log`; fi; seq `expr $start + 1` `expr $start + 10` | tee -a log' + local driver=$1 + local events_backend=$(_additional_events_backend $driver) + run_podman run --log-driver=$driver ${events_backend} --name logtest $IMAGE sh -c 'start=0; if test -s log; then start=`tail -n 1 log`; fi; seq `expr $start + 1` `expr $start + 10` | tee -a log' # 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 start -a logtest + run_podman ${events_backend} start -a logtest logfile=$(mktemp -p ${PODMAN_TMPDIR} logfileXXXXXXXX) - $PODMAN $_PODMAN_TEST_OPTS logs -f logtest > $logfile + $PODMAN $_PODMAN_TEST_OPTS ${events_backend} logs -f logtest > $logfile expected=$(mktemp -p ${PODMAN_TMPDIR} expectedXXXXXXXX) seq 1 20 > $expected diff -u ${expected} ${logfile} diff --git a/test/system/050-stop.bats b/test/system/050-stop.bats index e049da518..7dd8f98e8 100644 --- a/test/system/050-stop.bats +++ b/test/system/050-stop.bats @@ -173,4 +173,9 @@ load helpers is "$output" ".*StopSignal SIGTERM failed to stop container stopme in 1 seconds, resorting to SIGKILL" "stopping container should print warning" } +@test "podman stop --noout" { + run_podman run --rm --name stopme -d $IMAGE top + run_podman --noout stop -t 0 stopme + is "$output" "" "output should be empty" +} # vim: filetype=sh diff --git a/test/system/055-rm.bats b/test/system/055-rm.bats index 7fe81c084..69663fafa 100644 --- a/test/system/055-rm.bats +++ b/test/system/055-rm.bats @@ -58,6 +58,18 @@ load helpers run_podman rm -af } +@test "podman rm --depend" { + run_podman create $IMAGE + dependCid=$output + run_podman create --net=container:$dependCid $IMAGE + cid=$output + run_podman 125 rm $dependCid + is "$output" "Error: container $dependCid has dependent containers which must be removed before it:.*" "Fail to remove because of dependencies" + run_podman rm --depend $dependCid + is "$output" ".*$cid" "Container should have been removed" + is "$output" ".*$dependCid" "Depend container should have been removed" +} + # I'm sorry! This test takes 13 seconds. There's not much I can do about it, # please know that I think it's justified: podman 1.5.0 had a strange bug # in with exit status was not preserved on some code paths with 'rm -f' diff --git a/test/system/080-pause.bats b/test/system/080-pause.bats index 857c8bbf4..57f390a74 100644 --- a/test/system/080-pause.bats +++ b/test/system/080-pause.bats @@ -21,7 +21,8 @@ load helpers # time to write a new post-restart time value. Pause by CID, unpause # by name, just to exercise code paths. While paused, check 'ps' # and 'inspect', then check again after restarting. - run_podman pause $cid + run_podman --noout pause $cid + is "$output" "" "output should be empty" run_podman inspect --format '{{.State.Status}}' $cid is "$output" "paused" "podman inspect .State.Status" sleep 3 diff --git a/test/system/090-events.bats b/test/system/090-events.bats index 5af6a3793..a0b0380a2 100644 --- a/test/system/090-events.bats +++ b/test/system/090-events.bats @@ -116,3 +116,16 @@ function _events_disjunctive_filters() { @test "events with disjunctive filters - default" { _events_disjunctive_filters "" } + +@test "events with events_logfile_path in containers.conf" { + skip_if_remote "remote does not support --events-backend" + events_file=$PODMAN_TMPDIR/events.log + containersconf=$PODMAN_TMPDIR/containers.conf + cat >$containersconf <<EOF +[engine] +events_logfile_path="$events_file" +EOF + CONTAINERS_CONF="$containersconf" run_podman --events-backend=file pull $IMAGE + run cat $events_file + is "$output" ".*\"Name\":\"$IMAGE" "test" +} diff --git a/test/system/170-run-userns.bats b/test/system/170-run-userns.bats index a5be591ef..c020a73ab 100644 --- a/test/system/170-run-userns.bats +++ b/test/system/170-run-userns.bats @@ -78,3 +78,19 @@ EOF # Then check that the main user is not mapped into the user namespace CONTAINERS_CONF=$PODMAN_TMPDIR/userns_auto.conf run_podman 0 run --rm $IMAGE awk '{if($2 == "0"){exit 1}}' /proc/self/uid_map /proc/self/gid_map } + +@test "podman userns=auto and secrets" { + ns_user="containers" + if is_rootless; then + ns_user=$(id -un) + fi + egrep -q "${ns_user}:" /etc/subuid || skip "no IDs allocated for user ${ns_user}" + test_name="test_$(random_string 12)" + secret_file=$PODMAN_TMPDIR/secret$(random_string 12) + secret_content=$(random_string) + echo ${secret_content} > ${secret_file} + run_podman secret create ${test_name} ${secret_file} + run_podman run --rm --secret=${test_name} --userns=auto:size=1000 $IMAGE cat /run/secrets/${test_name} + is ${output} ${secret_content} "Secrets should work with user namespace" + run_podman secret rm ${test_name} +} diff --git a/test/system/200-pod.bats b/test/system/200-pod.bats index 57d052ce2..4a3337e57 100644 --- a/test/system/200-pod.bats +++ b/test/system/200-pod.bats @@ -57,13 +57,14 @@ function teardown() { fi # Clean up - run_podman pod rm -f -t 0 $podid + run_podman --noout pod rm -f -t 0 $podid + is "$output" "" "output should be empty" } @test "podman pod create - custom infra image" { + skip_if_remote "CONTAINERS_CONF only effects server side" image="i.do/not/exist:image" - tmpdir=$PODMAN_TMPDIR/pod-test run mkdir -p $tmpdir containersconf=$tmpdir/containers.conf @@ -77,6 +78,9 @@ EOF CONTAINERS_CONF=$containersconf run_podman 125 pod create is "$output" ".*initializing source docker://$image:.*" + + CONTAINERS_CONF=$containersconf run_podman 125 create --pod new:test $IMAGE + is "$output" ".*initializing source docker://$image:.*" } @test "podman pod - communicating between pods" { @@ -327,7 +331,8 @@ EOF # Note that the internal pause image is built even when --infra-image is # set to the K8s one. - run_podman pod create --name $pod_name --infra-name "$infra_name" --infra-image "k8s.gcr.io/pause:3.5" + run_podman --noout pod create --name $pod_name --infra-name "$infra_name" --infra-image "k8s.gcr.io/pause:3.5" + is "$output" "" "output should be empty" run_podman '?' pod create --infra-name "$infra_name" if [ $status -eq 0 ]; then die "Podman should fail when user try to create two pods with the same infra-name value" diff --git a/test/system/250-systemd.bats b/test/system/250-systemd.bats index c49727cc9..c47679904 100644 --- a/test/system/250-systemd.bats +++ b/test/system/250-systemd.bats @@ -276,4 +276,9 @@ LISTEN_FDNAMES=listen_fdnames" | sort) is "$output" ".*--template cannot be set" "Error message should be '--template requires --new'" } +@test "podman --cgroup=cgroupfs doesn't show systemd warning" { + DBUS_SESSION_BUS_ADDRESS= run_podman --log-level warning --cgroup-manager=cgroupfs info -f '' + is "$output" "" "output should be empty" +} + # vim: filetype=sh diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats index 2b5ad44dc..5a721c965 100644 --- a/test/system/500-networking.bats +++ b/test/system/500-networking.bats @@ -332,7 +332,8 @@ load helpers is_rootless || skip "only meaningful for rootless" local mynetname=testnet-$(random_string 10) - run_podman network create $mynetname + run_podman --noout network create $mynetname + is "$output" "" "output should be empty" # Test that rootless cni adds /usr/sbin to $PATH # iptables is located under /usr/sbin and is needed for the CNI plugins. @@ -340,7 +341,8 @@ load helpers PATH=/usr/local/bin:/usr/bin run_podman run --rm --network $mynetname $IMAGE ip addr is "$output" ".*eth0.*" "Interface eth0 not found in ip addr output" - run_podman network rm -t 0 -f $mynetname + run_podman --noout network rm -t 0 -f $mynetname + is "$output" "" "output should be empty" } @test "podman ipv6 in /etc/resolv.conf" { diff --git a/vendor/github.com/BurntSushi/toml/README.md b/vendor/github.com/BurntSushi/toml/README.md index 64410cf75..cc13f8667 100644 --- a/vendor/github.com/BurntSushi/toml/README.md +++ b/vendor/github.com/BurntSushi/toml/README.md @@ -1,10 +1,6 @@ -## TOML parser and encoder for Go with reflection - TOML stands for Tom's Obvious, Minimal Language. This Go package provides a reflection interface similar to Go's standard library `json` and `xml` -packages. This package also supports the `encoding.TextUnmarshaler` and -`encoding.TextMarshaler` interfaces so that you can define custom data -representations. (There is an example of this below.) +packages. Compatible with TOML version [v1.0.0](https://toml.io/en/v1.0.0). @@ -16,26 +12,25 @@ v0.4.0`). This library requires Go 1.13 or newer; install it with: - $ go get github.com/BurntSushi/toml + % go get github.com/BurntSushi/toml@latest It also comes with a TOML validator CLI tool: - $ go get github.com/BurntSushi/toml/cmd/tomlv - $ tomlv some-toml-file.toml + % go install github.com/BurntSushi/toml/cmd/tomlv@latest + % tomlv some-toml-file.toml ### Testing +This package passes all tests in [toml-test] for both the decoder and the +encoder. -This package passes all tests in -[toml-test](https://github.com/BurntSushi/toml-test) for both the decoder -and the encoder. +[toml-test]: https://github.com/BurntSushi/toml-test ### Examples +This package works similar to how the Go standard library handles XML and JSON. +Namely, data is loaded into Go values via reflection. -This package works similarly to how the Go standard library handles XML and -JSON. Namely, data is loaded into Go values via reflection. - -For the simplest example, consider some TOML file as just a list of keys -and values: +For the simplest example, consider some TOML file as just a list of keys and +values: ```toml Age = 25 @@ -61,9 +56,8 @@ And then decoded with: ```go var conf Config -if _, err := toml.Decode(tomlData, &conf); err != nil { - // handle error -} +err := toml.Decode(tomlData, &conf) +// handle error ``` You can also use struct tags if your struct field name doesn't map to a TOML @@ -75,15 +69,14 @@ some_key_NAME = "wat" ```go type TOML struct { - ObscureKey string `toml:"some_key_NAME"` + ObscureKey string `toml:"some_key_NAME"` } ``` Beware that like other most other decoders **only exported fields** are considered when encoding and decoding; private fields are silently ignored. -### Using the `encoding.TextUnmarshaler` interface - +### Using the `Marshaler` and `encoding.TextUnmarshaler` interfaces Here's an example that automatically parses duration strings into `time.Duration` values: @@ -136,7 +129,6 @@ To target TOML specifically you can implement `UnmarshalTOML` TOML interface in a similar way. ### More complex usage - Here's an example of how to load the example from the official spec page: ```toml @@ -216,5 +208,4 @@ type clients struct { Note that a case insensitive match will be tried if an exact match can't be found. -A working example of the above can be found in `_examples/example.{go,toml}`. - +A working example of the above can be found in `_example/example.{go,toml}`. diff --git a/vendor/github.com/BurntSushi/toml/decode.go b/vendor/github.com/BurntSushi/toml/decode.go index d3d3b8397..e24f0c5d5 100644 --- a/vendor/github.com/BurntSushi/toml/decode.go +++ b/vendor/github.com/BurntSushi/toml/decode.go @@ -9,7 +9,6 @@ import ( "os" "reflect" "strings" - "time" ) // Unmarshaler is the interface implemented by objects that can unmarshal a @@ -40,6 +39,13 @@ type Primitive struct { context Key } +// The significand precision for float32 and float64 is 24 and 53 bits; this is +// the range a natural number can be stored in a float without loss of data. +const ( + maxSafeFloat32Int = 16777215 // 2^24-1 + maxSafeFloat64Int = 9007199254740991 // 2^53-1 +) + // PrimitiveDecode is just like the other `Decode*` functions, except it // decodes a TOML value that has already been parsed. Valid primitive values // can *only* be obtained from values filled by the decoder functions, @@ -100,18 +106,38 @@ func NewDecoder(r io.Reader) *Decoder { return &Decoder{r: r} } +var ( + unmarshalToml = reflect.TypeOf((*Unmarshaler)(nil)).Elem() + unmarshalText = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() +) + // Decode TOML data in to the pointer `v`. func (dec *Decoder) Decode(v interface{}) (MetaData, error) { rv := reflect.ValueOf(v) if rv.Kind() != reflect.Ptr { - return MetaData{}, e("Decode of non-pointer %s", reflect.TypeOf(v)) + s := "%q" + if reflect.TypeOf(v) == nil { + s = "%v" + } + + return MetaData{}, e("cannot decode to non-pointer "+s, reflect.TypeOf(v)) } if rv.IsNil() { - return MetaData{}, e("Decode of nil %s", reflect.TypeOf(v)) + return MetaData{}, e("cannot decode to nil value of %q", reflect.TypeOf(v)) + } + + // Check if this is a supported type: struct, map, interface{}, or something + // that implements UnmarshalTOML or UnmarshalText. + rv = indirect(rv) + rt := rv.Type() + if rv.Kind() != reflect.Struct && rv.Kind() != reflect.Map && + !(rv.Kind() == reflect.Interface && rv.NumMethod() == 0) && + !rt.Implements(unmarshalToml) && !rt.Implements(unmarshalText) { + return MetaData{}, e("cannot decode to type %s", rt) } - // TODO: have parser should read from io.Reader? Or at the very least, make - // it read from []byte rather than string + // TODO: parser should read from io.Reader? Or at the very least, make it + // read from []byte rather than string data, err := ioutil.ReadAll(dec.r) if err != nil { return MetaData{}, err @@ -121,11 +147,15 @@ func (dec *Decoder) Decode(v interface{}) (MetaData, error) { if err != nil { return MetaData{}, err } + md := MetaData{ - p.mapping, p.types, p.ordered, - make(map[string]bool, len(p.ordered)), nil, + mapping: p.mapping, + types: p.types, + keys: p.ordered, + decoded: make(map[string]struct{}, len(p.ordered)), + context: nil, } - return md, md.unify(p.mapping, indirect(rv)) + return md, md.unify(p.mapping, rv) } // Decode the TOML data in to the pointer v. @@ -218,9 +248,7 @@ func (md *MetaData) unify(data interface{}, rv reflect.Value) error { return e("unsupported type %s", rv.Type()) } return md.unifyAnything(data, rv) - case reflect.Float32: - fallthrough - case reflect.Float64: + case reflect.Float32, reflect.Float64: return md.unifyFloat64(data, rv) } return e("unsupported type %s", rv.Kind()) @@ -254,17 +282,17 @@ func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error { for _, i := range f.index { subv = indirect(subv.Field(i)) } + if isUnifiable(subv) { - md.decoded[md.context.add(key).String()] = true + md.decoded[md.context.add(key).String()] = struct{}{} md.context = append(md.context, key) - if err := md.unify(datum, subv); err != nil { + err := md.unify(datum, subv) + if err != nil { return err } md.context = md.context[0 : len(md.context)-1] } else if f.name != "" { - // Bad user! No soup for you! - return e("cannot write unexported field %s.%s", - rv.Type().String(), f.name) + return e("cannot write unexported field %s.%s", rv.Type().String(), f.name) } } } @@ -283,22 +311,22 @@ func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error { if tmap == nil { return nil } - return badtype("map", mapping) + return md.badtype("map", mapping) } if rv.IsNil() { rv.Set(reflect.MakeMap(rv.Type())) } for k, v := range tmap { - md.decoded[md.context.add(k).String()] = true + md.decoded[md.context.add(k).String()] = struct{}{} md.context = append(md.context, k) - rvkey := indirect(reflect.New(rv.Type().Key())) rvval := reflect.Indirect(reflect.New(rv.Type().Elem())) if err := md.unify(v, rvval); err != nil { return err } md.context = md.context[0 : len(md.context)-1] + rvkey := indirect(reflect.New(rv.Type().Key())) rvkey.SetString(k) rv.SetMapIndex(rvkey, rvval) } @@ -311,7 +339,7 @@ func (md *MetaData) unifyArray(data interface{}, rv reflect.Value) error { if !datav.IsValid() { return nil } - return badtype("slice", data) + return md.badtype("slice", data) } if l := datav.Len(); l != rv.Len() { return e("expected array length %d; got TOML array of length %d", rv.Len(), l) @@ -325,7 +353,7 @@ func (md *MetaData) unifySlice(data interface{}, rv reflect.Value) error { if !datav.IsValid() { return nil } - return badtype("slice", data) + return md.badtype("slice", data) } n := datav.Len() if rv.IsNil() || rv.Cap() < n { @@ -346,26 +374,21 @@ func (md *MetaData) unifySliceArray(data, rv reflect.Value) error { return nil } -func (md *MetaData) unifyDatetime(data interface{}, rv reflect.Value) error { - if _, ok := data.(time.Time); ok { - rv.Set(reflect.ValueOf(data)) - return nil - } - return badtype("time.Time", data) -} - func (md *MetaData) unifyString(data interface{}, rv reflect.Value) error { if s, ok := data.(string); ok { rv.SetString(s) return nil } - return badtype("string", data) + return md.badtype("string", data) } func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error { if num, ok := data.(float64); ok { switch rv.Kind() { case reflect.Float32: + if num < -math.MaxFloat32 || num > math.MaxFloat32 { + return e("value %f is out of range for float32", num) + } fallthrough case reflect.Float64: rv.SetFloat(num) @@ -374,7 +397,26 @@ func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error { } return nil } - return badtype("float", data) + + if num, ok := data.(int64); ok { + switch rv.Kind() { + case reflect.Float32: + if num < -maxSafeFloat32Int || num > maxSafeFloat32Int { + return e("value %d is out of range for float32", num) + } + fallthrough + case reflect.Float64: + if num < -maxSafeFloat64Int || num > maxSafeFloat64Int { + return e("value %d is out of range for float64", num) + } + rv.SetFloat(float64(num)) + default: + panic("bug") + } + return nil + } + + return md.badtype("float", data) } func (md *MetaData) unifyInt(data interface{}, rv reflect.Value) error { @@ -421,7 +463,7 @@ func (md *MetaData) unifyInt(data interface{}, rv reflect.Value) error { } return nil } - return badtype("integer", data) + return md.badtype("integer", data) } func (md *MetaData) unifyBool(data interface{}, rv reflect.Value) error { @@ -429,7 +471,7 @@ func (md *MetaData) unifyBool(data interface{}, rv reflect.Value) error { rv.SetBool(b) return nil } - return badtype("boolean", data) + return md.badtype("boolean", data) } func (md *MetaData) unifyAnything(data interface{}, rv reflect.Value) error { @@ -440,6 +482,12 @@ func (md *MetaData) unifyAnything(data interface{}, rv reflect.Value) error { func (md *MetaData) unifyText(data interface{}, v encoding.TextUnmarshaler) error { var s string switch sdata := data.(type) { + case Marshaler: + text, err := sdata.MarshalTOML() + if err != nil { + return err + } + s = string(text) case TextMarshaler: text, err := sdata.MarshalText() if err != nil { @@ -457,7 +505,7 @@ func (md *MetaData) unifyText(data interface{}, v encoding.TextUnmarshaler) erro case float64: s = fmt.Sprintf("%f", sdata) default: - return badtype("primitive (string-like)", data) + return md.badtype("primitive (string-like)", data) } if err := v.UnmarshalText([]byte(s)); err != nil { return err @@ -465,17 +513,22 @@ func (md *MetaData) unifyText(data interface{}, v encoding.TextUnmarshaler) erro return nil } +func (md *MetaData) badtype(dst string, data interface{}) error { + return e("incompatible types: TOML key %q has type %T; destination has type %s", md.context, data, dst) +} + // rvalue returns a reflect.Value of `v`. All pointers are resolved. func rvalue(v interface{}) reflect.Value { return indirect(reflect.ValueOf(v)) } // indirect returns the value pointed to by a pointer. -// Pointers are followed until the value is not a pointer. -// New values are allocated for each nil pointer. // -// An exception to this rule is if the value satisfies an interface of -// interest to us (like encoding.TextUnmarshaler). +// Pointers are followed until the value is not a pointer. New values are +// allocated for each nil pointer. +// +// An exception to this rule is if the value satisfies an interface of interest +// to us (like encoding.TextUnmarshaler). func indirect(v reflect.Value) reflect.Value { if v.Kind() != reflect.Ptr { if v.CanSet() { @@ -505,7 +558,3 @@ func isUnifiable(rv reflect.Value) bool { func e(format string, args ...interface{}) error { return fmt.Errorf("toml: "+format, args...) } - -func badtype(expected string, data interface{}) error { - return e("cannot load TOML value of type %T into a Go %s", data, expected) -} diff --git a/vendor/github.com/BurntSushi/toml/decode_go116.go b/vendor/github.com/BurntSushi/toml/decode_go116.go index 38aa75fdc..eddfb641b 100644 --- a/vendor/github.com/BurntSushi/toml/decode_go116.go +++ b/vendor/github.com/BurntSushi/toml/decode_go116.go @@ -1,3 +1,4 @@ +//go:build go1.16 // +build go1.16 package toml diff --git a/vendor/github.com/BurntSushi/toml/deprecated.go b/vendor/github.com/BurntSushi/toml/deprecated.go index db89eac1d..c6af3f239 100644 --- a/vendor/github.com/BurntSushi/toml/deprecated.go +++ b/vendor/github.com/BurntSushi/toml/deprecated.go @@ -5,29 +5,17 @@ import ( "io" ) -// DEPRECATED! -// -// Use the identical encoding.TextMarshaler instead. It is defined here to -// support Go 1.1 and older. +// Deprecated: use encoding.TextMarshaler type TextMarshaler encoding.TextMarshaler -// DEPRECATED! -// -// Use the identical encoding.TextUnmarshaler instead. It is defined here to -// support Go 1.1 and older. +// Deprecated: use encoding.TextUnmarshaler type TextUnmarshaler encoding.TextUnmarshaler -// DEPRECATED! -// -// Use MetaData.PrimitiveDecode instead. +// Deprecated: use MetaData.PrimitiveDecode. func PrimitiveDecode(primValue Primitive, v interface{}) error { - md := MetaData{decoded: make(map[string]bool)} + md := MetaData{decoded: make(map[string]struct{})} return md.unify(primValue.undecoded, rvalue(v)) } -// DEPRECATED! -// -// Use NewDecoder(reader).Decode(&v) instead. -func DecodeReader(r io.Reader, v interface{}) (MetaData, error) { - return NewDecoder(r).Decode(v) -} +// Deprecated: use NewDecoder(reader).Decode(&value). +func DecodeReader(r io.Reader, v interface{}) (MetaData, error) { return NewDecoder(r).Decode(v) } diff --git a/vendor/github.com/BurntSushi/toml/encode.go b/vendor/github.com/BurntSushi/toml/encode.go index 10d88ac63..dee4e6d31 100644 --- a/vendor/github.com/BurntSushi/toml/encode.go +++ b/vendor/github.com/BurntSushi/toml/encode.go @@ -21,12 +21,11 @@ type tomlEncodeError struct{ error } var ( errArrayNilElement = errors.New("toml: cannot encode array with nil element") errNonString = errors.New("toml: cannot encode a map with non-string key type") - errAnonNonStruct = errors.New("toml: cannot encode an anonymous field that is not a struct") errNoKey = errors.New("toml: top-level values must be Go maps or structs") errAnything = errors.New("") // used in testing ) -var quotedReplacer = strings.NewReplacer( +var dblQuotedReplacer = strings.NewReplacer( "\"", "\\\"", "\\", "\\\\", "\x00", `\u0000`, @@ -64,13 +63,22 @@ var quotedReplacer = strings.NewReplacer( "\x7f", `\u007f`, ) +// Marshaler is the interface implemented by types that can marshal themselves +// into valid TOML. +type Marshaler interface { + MarshalTOML() ([]byte, error) +} + // Encoder encodes a Go to a TOML document. // // The mapping between Go values and TOML values should be precisely the same as -// for the Decode* functions. Similarly, the TextMarshaler interface is -// supported by encoding the resulting bytes as strings. If you want to write -// arbitrary binary data then you will need to use something like base64 since -// TOML does not have any binary types. +// for the Decode* functions. +// +// The toml.Marshaler and encoder.TextMarshaler interfaces are supported to +// encoding the value as custom TOML. +// +// If you want to write arbitrary binary data then you will need to use +// something like base64 since TOML does not have any binary types. // // When encoding TOML hashes (Go maps or structs), keys without any sub-hashes // are encoded first. @@ -83,16 +91,14 @@ var quotedReplacer = strings.NewReplacer( // structs. (e.g. [][]map[string]string is not allowed but []map[string]string // is okay, as is []map[string][]string). // -// NOTE: Only exported keys are encoded due to the use of reflection. Unexported +// NOTE: only exported keys are encoded due to the use of reflection. Unexported // keys are silently discarded. type Encoder struct { - // The string to use for a single indentation level. The default is two - // spaces. + // String to use for a single indentation level; default is two spaces. Indent string - // hasWritten is whether we have written any output to w yet. - hasWritten bool w *bufio.Writer + hasWritten bool // written any output to w yet? } // NewEncoder create a new Encoder. @@ -130,12 +136,13 @@ func (enc *Encoder) safeEncode(key Key, rv reflect.Value) (err error) { } func (enc *Encoder) encode(key Key, rv reflect.Value) { - // Special case. Time needs to be in ISO8601 format. - // Special case. If we can marshal the type to text, then we used that. - // Basically, this prevents the encoder for handling these types as - // generic structs (or whatever the underlying type of a TextMarshaler is). + // Special case: time needs to be in ISO8601 format. + // + // Special case: if we can marshal the type to text, then we used that. This + // prevents the encoder for handling these types as generic structs (or + // whatever the underlying type of a TextMarshaler is). switch t := rv.Interface().(type) { - case time.Time, encoding.TextMarshaler: + case time.Time, encoding.TextMarshaler, Marshaler: enc.writeKeyValue(key, rv, false) return // TODO: #76 would make this superfluous after implemented. @@ -200,13 +207,19 @@ func (enc *Encoder) eElement(rv reflect.Value) { enc.wf(v.In(time.UTC).Format(format)) } return + case Marshaler: + s, err := v.MarshalTOML() + if err != nil { + encPanic(err) + } + enc.writeQuoted(string(s)) + return case encoding.TextMarshaler: - // Use text marshaler if it's available for this value. - if s, err := v.MarshalText(); err != nil { + s, err := v.MarshalText() + if err != nil { encPanic(err) - } else { - enc.writeQuoted(string(s)) } + enc.writeQuoted(string(s)) return } @@ -260,7 +273,7 @@ func floatAddDecimal(fstr string) string { } func (enc *Encoder) writeQuoted(s string) { - enc.wf("\"%s\"", quotedReplacer.Replace(s)) + enc.wf("\"%s\"", dblQuotedReplacer.Replace(s)) } func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) { @@ -286,7 +299,7 @@ func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) { continue } enc.newline() - enc.wf("%s[[%s]]", enc.indentStr(key), key.maybeQuotedAll()) + enc.wf("%s[[%s]]", enc.indentStr(key), key) enc.newline() enc.eMapOrStruct(key, trv, false) } @@ -299,7 +312,7 @@ func (enc *Encoder) eTable(key Key, rv reflect.Value) { enc.newline() } if len(key) > 0 { - enc.wf("%s[%s]", enc.indentStr(key), key.maybeQuotedAll()) + enc.wf("%s[%s]", enc.indentStr(key), key) enc.newline() } enc.eMapOrStruct(key, rv, false) @@ -328,7 +341,7 @@ func (enc *Encoder) eMap(key Key, rv reflect.Value, inline bool) { var mapKeysDirect, mapKeysSub []string for _, mapKey := range rv.MapKeys() { k := mapKey.String() - if typeIsHash(tomlTypeOfGo(rv.MapIndex(mapKey))) { + if typeIsTable(tomlTypeOfGo(rv.MapIndex(mapKey))) { mapKeysSub = append(mapKeysSub, k) } else { mapKeysDirect = append(mapKeysDirect, k) @@ -364,6 +377,8 @@ func (enc *Encoder) eMap(key Key, rv reflect.Value, inline bool) { } } +const is32Bit = (32 << (^uint(0) >> 63)) == 32 + func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) { // Write keys for fields directly under this key first, because if we write // a field that creates a new table then all keys under it will be in that @@ -408,10 +423,20 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) { } } - if typeIsHash(tomlTypeOfGo(frv)) { + if typeIsTable(tomlTypeOfGo(frv)) { fieldsSub = append(fieldsSub, append(start, f.Index...)) } else { - fieldsDirect = append(fieldsDirect, append(start, f.Index...)) + // Copy so it works correct on 32bit archs; not clear why this + // is needed. See #314, and https://www.reddit.com/r/golang/comments/pnx8v4 + // This also works fine on 64bit, but 32bit archs are somewhat + // rare and this is a wee bit faster. + if is32Bit { + copyStart := make([]int, len(start)) + copy(copyStart, start) + fieldsDirect = append(fieldsDirect, append(copyStart, f.Index...)) + } else { + fieldsDirect = append(fieldsDirect, append(start, f.Index...)) + } } } } @@ -462,13 +487,13 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) { } } -// tomlTypeName returns the TOML type name of the Go value's type. It is -// used to determine whether the types of array elements are mixed (which is -// forbidden). If the Go value is nil, then it is illegal for it to be an array -// element, and valueIsNil is returned as true. - -// Returns the TOML type of a Go value. The type may be `nil`, which means -// no concrete TOML type could be found. +// tomlTypeOfGo returns the TOML type name of the Go value's type. +// +// It is used to determine whether the types of array elements are mixed (which +// is forbidden). If the Go value is nil, then it is illegal for it to be an +// array element, and valueIsNil is returned as true. +// +// The type may be `nil`, which means no concrete TOML type could be found. func tomlTypeOfGo(rv reflect.Value) tomlType { if isNil(rv) || !rv.IsValid() { return nil @@ -495,32 +520,43 @@ func tomlTypeOfGo(rv reflect.Value) tomlType { case reflect.Map: return tomlHash case reflect.Struct: - switch rv.Interface().(type) { - case time.Time: + if _, ok := rv.Interface().(time.Time); ok { return tomlDatetime - case encoding.TextMarshaler: + } + if isMarshaler(rv) { return tomlString - default: - // Someone used a pointer receiver: we can make it work for pointer - // values. - if rv.CanAddr() { - _, ok := rv.Addr().Interface().(encoding.TextMarshaler) - if ok { - return tomlString - } - } - return tomlHash } + return tomlHash default: - _, ok := rv.Interface().(encoding.TextMarshaler) - if ok { + if isMarshaler(rv) { return tomlString } + encPanic(errors.New("unsupported type: " + rv.Kind().String())) - panic("") // Need *some* return value + panic("unreachable") } } +func isMarshaler(rv reflect.Value) bool { + switch rv.Interface().(type) { + case encoding.TextMarshaler: + return true + case Marshaler: + return true + } + + // Someone used a pointer receiver: we can make it work for pointer values. + if rv.CanAddr() { + if _, ok := rv.Addr().Interface().(encoding.TextMarshaler); ok { + return true + } + if _, ok := rv.Addr().Interface().(Marshaler); ok { + return true + } + } + return false +} + // tomlArrayType returns the element type of a TOML array. The type returned // may be nil if it cannot be determined (e.g., a nil slice or a zero length // slize). This function may also panic if it finds a type that cannot be @@ -604,7 +640,14 @@ func (enc *Encoder) newline() { // // key = <any value> // -// If inline is true it won't add a newline at the end. +// This is also used for "k = v" in inline tables; so something like this will +// be written in three calls: +// +// ┌────────────────────┐ +// │ ┌───┐ ┌─────┐│ +// v v v v vv +// key = {k = v, k2 = v2} +// func (enc *Encoder) writeKeyValue(key Key, val reflect.Value, inline bool) { if len(key) == 0 { encPanic(errNoKey) @@ -617,7 +660,8 @@ func (enc *Encoder) writeKeyValue(key Key, val reflect.Value, inline bool) { } func (enc *Encoder) wf(format string, v ...interface{}) { - if _, err := fmt.Fprintf(enc.w, format, v...); err != nil { + _, err := fmt.Fprintf(enc.w, format, v...) + if err != nil { encPanic(err) } enc.hasWritten = true diff --git a/vendor/github.com/BurntSushi/toml/error.go b/vendor/github.com/BurntSushi/toml/error.go new file mode 100644 index 000000000..36edc4655 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/error.go @@ -0,0 +1,229 @@ +package toml + +import ( + "fmt" + "strings" +) + +// ParseError is returned when there is an error parsing the TOML syntax. +// +// For example invalid syntax, duplicate keys, etc. +// +// In addition to the error message itself, you can also print detailed location +// information with context by using ErrorWithLocation(): +// +// toml: error: Key 'fruit' was already created and cannot be used as an array. +// +// At line 4, column 2-7: +// +// 2 | fruit = [] +// 3 | +// 4 | [[fruit]] # Not allowed +// ^^^^^ +// +// Furthermore, the ErrorWithUsage() can be used to print the above with some +// more detailed usage guidance: +// +// toml: error: newlines not allowed within inline tables +// +// At line 1, column 18: +// +// 1 | x = [{ key = 42 # +// ^ +// +// Error help: +// +// Inline tables must always be on a single line: +// +// table = {key = 42, second = 43} +// +// It is invalid to split them over multiple lines like so: +// +// # INVALID +// table = { +// key = 42, +// second = 43 +// } +// +// Use regular for this: +// +// [table] +// key = 42 +// second = 43 +type ParseError struct { + Message string // Short technical message. + Usage string // Longer message with usage guidance; may be blank. + Position Position // Position of the error + LastKey string // Last parsed key, may be blank. + Line int // Line the error occurred. Deprecated: use Position. + + err error + input string +} + +// Position of an error. +type Position struct { + Line int // Line number, starting at 1. + Start int // Start of error, as byte offset starting at 0. + Len int // Lenght in bytes. +} + +func (pe ParseError) Error() string { + msg := pe.Message + if msg == "" { // Error from errorf() + msg = pe.err.Error() + } + + if pe.LastKey == "" { + return fmt.Sprintf("toml: line %d: %s", pe.Position.Line, msg) + } + return fmt.Sprintf("toml: line %d (last key %q): %s", + pe.Position.Line, pe.LastKey, msg) +} + +// ErrorWithUsage() returns the error with detailed location context. +// +// See the documentation on ParseError. +func (pe ParseError) ErrorWithPosition() string { + if pe.input == "" { // Should never happen, but just in case. + return pe.Error() + } + + var ( + lines = strings.Split(pe.input, "\n") + col = pe.column(lines) + b = new(strings.Builder) + ) + + msg := pe.Message + if msg == "" { + msg = pe.err.Error() + } + + // TODO: don't show control characters as literals? This may not show up + // well everywhere. + + if pe.Position.Len == 1 { + fmt.Fprintf(b, "toml: error: %s\n\nAt line %d, column %d:\n\n", + msg, pe.Position.Line, col+1) + } else { + fmt.Fprintf(b, "toml: error: %s\n\nAt line %d, column %d-%d:\n\n", + msg, pe.Position.Line, col, col+pe.Position.Len) + } + if pe.Position.Line > 2 { + fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line-2, lines[pe.Position.Line-3]) + } + if pe.Position.Line > 1 { + fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line-1, lines[pe.Position.Line-2]) + } + fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line, lines[pe.Position.Line-1]) + fmt.Fprintf(b, "% 10s%s%s\n", "", strings.Repeat(" ", col), strings.Repeat("^", pe.Position.Len)) + return b.String() +} + +// ErrorWithUsage() returns the error with detailed location context and usage +// guidance. +// +// See the documentation on ParseError. +func (pe ParseError) ErrorWithUsage() string { + m := pe.ErrorWithPosition() + if u, ok := pe.err.(interface{ Usage() string }); ok && u.Usage() != "" { + return m + "Error help:\n\n " + + strings.ReplaceAll(strings.TrimSpace(u.Usage()), "\n", "\n ") + + "\n" + } + return m +} + +func (pe ParseError) column(lines []string) int { + var pos, col int + for i := range lines { + ll := len(lines[i]) + 1 // +1 for the removed newline + if pos+ll >= pe.Position.Start { + col = pe.Position.Start - pos + if col < 0 { // Should never happen, but just in case. + col = 0 + } + break + } + pos += ll + } + + return col +} + +type ( + errLexControl struct{ r rune } + errLexEscape struct{ r rune } + errLexUTF8 struct{ b byte } + errLexInvalidNum struct{ v string } + errLexInvalidDate struct{ v string } + errLexInlineTableNL struct{} + errLexStringNL struct{} +) + +func (e errLexControl) Error() string { + return fmt.Sprintf("TOML files cannot contain control characters: '0x%02x'", e.r) +} +func (e errLexControl) Usage() string { return "" } + +func (e errLexEscape) Error() string { return fmt.Sprintf(`invalid escape in string '\%c'`, e.r) } +func (e errLexEscape) Usage() string { return usageEscape } +func (e errLexUTF8) Error() string { return fmt.Sprintf("invalid UTF-8 byte: 0x%02x", e.b) } +func (e errLexUTF8) Usage() string { return "" } +func (e errLexInvalidNum) Error() string { return fmt.Sprintf("invalid number: %q", e.v) } +func (e errLexInvalidNum) Usage() string { return "" } +func (e errLexInvalidDate) Error() string { return fmt.Sprintf("invalid date: %q", e.v) } +func (e errLexInvalidDate) Usage() string { return "" } +func (e errLexInlineTableNL) Error() string { return "newlines not allowed within inline tables" } +func (e errLexInlineTableNL) Usage() string { return usageInlineNewline } +func (e errLexStringNL) Error() string { return "strings cannot contain newlines" } +func (e errLexStringNL) Usage() string { return usageStringNewline } + +const usageEscape = ` +A '\' inside a "-delimited string is interpreted as an escape character. + +The following escape sequences are supported: +\b, \t, \n, \f, \r, \", \\, \uXXXX, and \UXXXXXXXX + +To prevent a '\' from being recognized as an escape character, use either: + +- a ' or '''-delimited string; escape characters aren't processed in them; or +- write two backslashes to get a single backslash: '\\'. + +If you're trying to add a Windows path (e.g. "C:\Users\martin") then using '/' +instead of '\' will usually also work: "C:/Users/martin". +` + +const usageInlineNewline = ` +Inline tables must always be on a single line: + + table = {key = 42, second = 43} + +It is invalid to split them over multiple lines like so: + + # INVALID + table = { + key = 42, + second = 43 + } + +Use regular for this: + + [table] + key = 42 + second = 43 +` + +const usageStringNewline = ` +Strings must always be on a single line, and cannot span more than one line: + + # INVALID + string = "Hello, + world!" + +Instead use """ or ''' to split strings over multiple lines: + + string = """Hello, + world!""" +` diff --git a/vendor/github.com/BurntSushi/toml/go.sum b/vendor/github.com/BurntSushi/toml/go.sum deleted file mode 100644 index e69de29bb..000000000 --- a/vendor/github.com/BurntSushi/toml/go.sum +++ /dev/null diff --git a/vendor/github.com/BurntSushi/toml/lex.go b/vendor/github.com/BurntSushi/toml/lex.go index adc4eb5d5..63ef20f47 100644 --- a/vendor/github.com/BurntSushi/toml/lex.go +++ b/vendor/github.com/BurntSushi/toml/lex.go @@ -37,28 +37,14 @@ const ( itemInlineTableEnd ) -const ( - eof = 0 - comma = ',' - tableStart = '[' - tableEnd = ']' - arrayTableStart = '[' - arrayTableEnd = ']' - tableSep = '.' - keySep = '=' - arrayStart = '[' - arrayEnd = ']' - commentStart = '#' - stringStart = '"' - stringEnd = '"' - rawStringStart = '\'' - rawStringEnd = '\'' - inlineTableStart = '{' - inlineTableEnd = '}' -) +const eof = 0 type stateFn func(lx *lexer) stateFn +func (p Position) String() string { + return fmt.Sprintf("at line %d; start %d; length %d", p.Line, p.Start, p.Len) +} + type lexer struct { input string start int @@ -67,26 +53,26 @@ type lexer struct { state stateFn items chan item - // Allow for backing up up to four runes. - // This is necessary because TOML contains 3-rune tokens (""" and '''). + // Allow for backing up up to 4 runes. This is necessary because TOML + // contains 3-rune tokens (""" and '''). prevWidths [4]int - nprev int // how many of prevWidths are in use - // If we emit an eof, we can still back up, but it is not OK to call - // next again. - atEOF bool + nprev int // how many of prevWidths are in use + atEOF bool // If we emit an eof, we can still back up, but it is not OK to call next again. // A stack of state functions used to maintain context. - // The idea is to reuse parts of the state machine in various places. - // For example, values can appear at the top level or within arbitrarily - // nested arrays. The last state on the stack is used after a value has - // been lexed. Similarly for comments. + // + // The idea is to reuse parts of the state machine in various places. For + // example, values can appear at the top level or within arbitrarily nested + // arrays. The last state on the stack is used after a value has been lexed. + // Similarly for comments. stack []stateFn } type item struct { - typ itemType - val string - line int + typ itemType + val string + err error + pos Position } func (lx *lexer) nextItem() item { @@ -96,7 +82,7 @@ func (lx *lexer) nextItem() item { return item default: lx.state = lx.state(lx) - //fmt.Printf(" STATE %-24s current: %-10q stack: %s\n", lx.state, lx.current(), lx.stack) + //fmt.Printf(" STATE %-24s current: %-10q stack: %s\n", lx.state, lx.current(), lx.stack) } } } @@ -105,9 +91,9 @@ func lex(input string) *lexer { lx := &lexer{ input: input, state: lexTop, - line: 1, items: make(chan item, 10), stack: make([]stateFn, 0, 10), + line: 1, } return lx } @@ -129,13 +115,25 @@ func (lx *lexer) current() string { return lx.input[lx.start:lx.pos] } +func (lx lexer) getPos() Position { + p := Position{ + Line: lx.line, + Start: lx.start, + Len: lx.pos - lx.start, + } + if p.Len <= 0 { + p.Len = 1 + } + return p +} + func (lx *lexer) emit(typ itemType) { - lx.items <- item{typ, lx.current(), lx.line} + lx.items <- item{typ: typ, pos: lx.getPos(), val: lx.current()} lx.start = lx.pos } func (lx *lexer) emitTrim(typ itemType) { - lx.items <- item{typ, strings.TrimSpace(lx.current()), lx.line} + lx.items <- item{typ: typ, pos: lx.getPos(), val: strings.TrimSpace(lx.current())} lx.start = lx.pos } @@ -160,7 +158,13 @@ func (lx *lexer) next() (r rune) { r, w := utf8.DecodeRuneInString(lx.input[lx.pos:]) if r == utf8.RuneError { - lx.errorf("invalid UTF-8 byte at position %d (line %d): 0x%02x", lx.pos, lx.line, lx.input[lx.pos]) + lx.error(errLexUTF8{lx.input[lx.pos]}) + return utf8.RuneError + } + + // Note: don't use peek() here, as this calls next(). + if isControl(r) || (r == '\r' && (len(lx.input)-1 == lx.pos || lx.input[lx.pos+1] != '\n')) { + lx.errorControlChar(r) return utf8.RuneError } @@ -188,6 +192,7 @@ func (lx *lexer) backup() { lx.prevWidths[1] = lx.prevWidths[2] lx.prevWidths[2] = lx.prevWidths[3] lx.nprev-- + lx.pos -= w if lx.pos < len(lx.input) && lx.input[lx.pos] == '\n' { lx.line-- @@ -223,18 +228,58 @@ func (lx *lexer) skip(pred func(rune) bool) { } } -// errorf stops all lexing by emitting an error and returning `nil`. +// error stops all lexing by emitting an error and returning `nil`. +// // Note that any value that is a character is escaped if it's a special // character (newlines, tabs, etc.). +func (lx *lexer) error(err error) stateFn { + if lx.atEOF { + return lx.errorPrevLine(err) + } + lx.items <- item{typ: itemError, pos: lx.getPos(), err: err} + return nil +} + +// errorfPrevline is like error(), but sets the position to the last column of +// the previous line. +// +// This is so that unexpected EOF or NL errors don't show on a new blank line. +func (lx *lexer) errorPrevLine(err error) stateFn { + pos := lx.getPos() + pos.Line-- + pos.Len = 1 + pos.Start = lx.pos - 1 + lx.items <- item{typ: itemError, pos: pos, err: err} + return nil +} + +// errorPos is like error(), but allows explicitly setting the position. +func (lx *lexer) errorPos(start, length int, err error) stateFn { + pos := lx.getPos() + pos.Start = start + pos.Len = length + lx.items <- item{typ: itemError, pos: pos, err: err} + return nil +} + +// errorf is like error, and creates a new error. func (lx *lexer) errorf(format string, values ...interface{}) stateFn { - lx.items <- item{ - itemError, - fmt.Sprintf(format, values...), - lx.line, + if lx.atEOF { + pos := lx.getPos() + pos.Line-- + pos.Len = 1 + pos.Start = lx.pos - 1 + lx.items <- item{typ: itemError, pos: pos, err: fmt.Errorf(format, values...)} + return nil } + lx.items <- item{typ: itemError, pos: lx.getPos(), err: fmt.Errorf(format, values...)} return nil } +func (lx *lexer) errorControlChar(cc rune) stateFn { + return lx.errorPos(lx.pos-1, 1, errLexControl{cc}) +} + // lexTop consumes elements at the top level of TOML data. func lexTop(lx *lexer) stateFn { r := lx.next() @@ -242,10 +287,10 @@ func lexTop(lx *lexer) stateFn { return lexSkip(lx, lexTop) } switch r { - case commentStart: + case '#': lx.push(lexTop) return lexCommentStart - case tableStart: + case '[': return lexTableStart case eof: if lx.pos > lx.start { @@ -268,7 +313,7 @@ func lexTop(lx *lexer) stateFn { func lexTopEnd(lx *lexer) stateFn { r := lx.next() switch { - case r == commentStart: + case r == '#': // a comment will read to a newline for us. lx.push(lexTop) return lexCommentStart @@ -292,7 +337,7 @@ func lexTopEnd(lx *lexer) stateFn { // It also handles the case that this is an item in an array of tables. // e.g., '[[name]]'. func lexTableStart(lx *lexer) stateFn { - if lx.peek() == arrayTableStart { + if lx.peek() == '[' { lx.next() lx.emit(itemArrayTableStart) lx.push(lexArrayTableEnd) @@ -309,10 +354,8 @@ func lexTableEnd(lx *lexer) stateFn { } func lexArrayTableEnd(lx *lexer) stateFn { - if r := lx.next(); r != arrayTableEnd { - return lx.errorf( - "expected end of table array name delimiter %q, but got %q instead", - arrayTableEnd, r) + if r := lx.next(); r != ']' { + return lx.errorf("expected end of table array name delimiter ']', but got %q instead", r) } lx.emit(itemArrayTableEnd) return lexTopEnd @@ -321,11 +364,11 @@ func lexArrayTableEnd(lx *lexer) stateFn { func lexTableNameStart(lx *lexer) stateFn { lx.skip(isWhitespace) switch r := lx.peek(); { - case r == tableEnd || r == eof: + case r == ']' || r == eof: return lx.errorf("unexpected end of table name (table names cannot be empty)") - case r == tableSep: + case r == '.': return lx.errorf("unexpected table separator (table names cannot be empty)") - case r == stringStart || r == rawStringStart: + case r == '"' || r == '\'': lx.ignore() lx.push(lexTableNameEnd) return lexQuotedName @@ -342,10 +385,10 @@ func lexTableNameEnd(lx *lexer) stateFn { switch r := lx.next(); { case isWhitespace(r): return lexTableNameEnd - case r == tableSep: + case r == '.': lx.ignore() return lexTableNameStart - case r == tableEnd: + case r == ']': return lx.pop() default: return lx.errorf("expected '.' or ']' to end table name, but got %q instead", r) @@ -379,10 +422,10 @@ func lexQuotedName(lx *lexer) stateFn { switch { case isWhitespace(r): return lexSkip(lx, lexValue) - case r == stringStart: + case r == '"': lx.ignore() // ignore the '"' return lexString - case r == rawStringStart: + case r == '\'': lx.ignore() // ignore the "'" return lexRawString case r == eof: @@ -400,7 +443,7 @@ func lexKeyStart(lx *lexer) stateFn { return lx.errorf("unexpected '=': key name appears blank") case r == '.': return lx.errorf("unexpected '.': keys cannot start with a '.'") - case r == stringStart || r == rawStringStart: + case r == '"' || r == '\'': lx.ignore() fallthrough default: // Bare key @@ -416,7 +459,7 @@ func lexKeyNameStart(lx *lexer) stateFn { return lx.errorf("unexpected '='") case r == '.': return lx.errorf("unexpected '.'") - case r == stringStart || r == rawStringStart: + case r == '"' || r == '\'': lx.ignore() lx.push(lexKeyEnd) return lexQuotedName @@ -434,7 +477,7 @@ func lexKeyEnd(lx *lexer) stateFn { case isWhitespace(r): return lexSkip(lx, lexKeyEnd) case r == eof: - return lx.errorf("unexpected EOF; expected key separator %q", keySep) + return lx.errorf("unexpected EOF; expected key separator '='") case r == '.': lx.ignore() return lexKeyNameStart @@ -461,17 +504,17 @@ func lexValue(lx *lexer) stateFn { return lexNumberOrDateStart } switch r { - case arrayStart: + case '[': lx.ignore() lx.emit(itemArray) return lexArrayValue - case inlineTableStart: + case '{': lx.ignore() lx.emit(itemInlineTableStart) return lexInlineTableValue - case stringStart: - if lx.accept(stringStart) { - if lx.accept(stringStart) { + case '"': + if lx.accept('"') { + if lx.accept('"') { lx.ignore() // Ignore """ return lexMultilineString } @@ -479,9 +522,9 @@ func lexValue(lx *lexer) stateFn { } lx.ignore() // ignore the '"' return lexString - case rawStringStart: - if lx.accept(rawStringStart) { - if lx.accept(rawStringStart) { + case '\'': + if lx.accept('\'') { + if lx.accept('\'') { lx.ignore() // Ignore """ return lexMultilineRawString } @@ -520,14 +563,12 @@ func lexArrayValue(lx *lexer) stateFn { switch { case isWhitespace(r) || isNL(r): return lexSkip(lx, lexArrayValue) - case r == commentStart: + case r == '#': lx.push(lexArrayValue) return lexCommentStart - case r == comma: + case r == ',': return lx.errorf("unexpected comma") - case r == arrayEnd: - // NOTE(caleb): The spec isn't clear about whether you can have - // a trailing comma or not, so we'll allow it. + case r == ']': return lexArrayEnd } @@ -540,22 +581,20 @@ func lexArrayValue(lx *lexer) stateFn { // the next value (or the end of the array): it ignores whitespace and newlines // and expects either a ',' or a ']'. func lexArrayValueEnd(lx *lexer) stateFn { - r := lx.next() - switch { + switch r := lx.next(); { case isWhitespace(r) || isNL(r): return lexSkip(lx, lexArrayValueEnd) - case r == commentStart: + case r == '#': lx.push(lexArrayValueEnd) return lexCommentStart - case r == comma: + case r == ',': lx.ignore() return lexArrayValue // move on to the next value - case r == arrayEnd: + case r == ']': return lexArrayEnd + default: + return lx.errorf("expected a comma (',') or array terminator (']'), but got %s", runeOrEOF(r)) } - return lx.errorf( - "expected a comma or array terminator %q, but got %s instead", - arrayEnd, runeOrEOF(r)) } // lexArrayEnd finishes the lexing of an array. @@ -574,13 +613,13 @@ func lexInlineTableValue(lx *lexer) stateFn { case isWhitespace(r): return lexSkip(lx, lexInlineTableValue) case isNL(r): - return lx.errorf("newlines not allowed within inline tables") - case r == commentStart: + return lx.errorPrevLine(errLexInlineTableNL{}) + case r == '#': lx.push(lexInlineTableValue) return lexCommentStart - case r == comma: + case r == ',': return lx.errorf("unexpected comma") - case r == inlineTableEnd: + case r == '}': return lexInlineTableEnd } lx.backup() @@ -596,23 +635,21 @@ func lexInlineTableValueEnd(lx *lexer) stateFn { case isWhitespace(r): return lexSkip(lx, lexInlineTableValueEnd) case isNL(r): - return lx.errorf("newlines not allowed within inline tables") - case r == commentStart: + return lx.errorPrevLine(errLexInlineTableNL{}) + case r == '#': lx.push(lexInlineTableValueEnd) return lexCommentStart - case r == comma: + case r == ',': lx.ignore() lx.skip(isWhitespace) if lx.peek() == '}' { return lx.errorf("trailing comma not allowed in inline tables") } return lexInlineTableValue - case r == inlineTableEnd: + case r == '}': return lexInlineTableEnd default: - return lx.errorf( - "expected a comma or an inline table terminator %q, but got %s instead", - inlineTableEnd, runeOrEOF(r)) + return lx.errorf("expected a comma or an inline table terminator '}', but got %s instead", runeOrEOF(r)) } } @@ -638,14 +675,12 @@ func lexString(lx *lexer) stateFn { switch { case r == eof: return lx.errorf(`unexpected EOF; expected '"'`) - case isControl(r) || r == '\r': - return lx.errorf("control characters are not allowed inside strings: '0x%02x'", r) case isNL(r): - return lx.errorf("strings cannot contain newlines") + return lx.errorPrevLine(errLexStringNL{}) case r == '\\': lx.push(lexString) return lexStringEscape - case r == stringEnd: + case r == '"': lx.backup() lx.emit(itemString) lx.next() @@ -660,23 +695,20 @@ func lexString(lx *lexer) stateFn { func lexMultilineString(lx *lexer) stateFn { r := lx.next() switch r { + default: + return lexMultilineString case eof: return lx.errorf(`unexpected EOF; expected '"""'`) - case '\r': - if lx.peek() != '\n' { - return lx.errorf("control characters are not allowed inside strings: '0x%02x'", r) - } - return lexMultilineString case '\\': return lexMultilineStringEscape - case stringEnd: + case '"': /// Found " → try to read two more "". - if lx.accept(stringEnd) { - if lx.accept(stringEnd) { + if lx.accept('"') { + if lx.accept('"') { /// Peek ahead: the string can contain " and "", including at the /// end: """str""""" /// 6 or more at the end, however, is an error. - if lx.peek() == stringEnd { + if lx.peek() == '"' { /// Check if we already lexed 5 's; if so we have 6 now, and /// that's just too many man! if strings.HasSuffix(lx.current(), `"""""`) { @@ -699,12 +731,8 @@ func lexMultilineString(lx *lexer) stateFn { } lx.backup() } + return lexMultilineString } - - if isControl(r) { - return lx.errorf("control characters are not allowed inside strings: '0x%02x'", r) - } - return lexMultilineString } // lexRawString consumes a raw string. Nothing can be escaped in such a string. @@ -712,20 +740,19 @@ func lexMultilineString(lx *lexer) stateFn { func lexRawString(lx *lexer) stateFn { r := lx.next() switch { + default: + return lexRawString case r == eof: return lx.errorf(`unexpected EOF; expected "'"`) - case isControl(r) || r == '\r': - return lx.errorf("control characters are not allowed inside strings: '0x%02x'", r) case isNL(r): - return lx.errorf("strings cannot contain newlines") - case r == rawStringEnd: + return lx.errorPrevLine(errLexStringNL{}) + case r == '\'': lx.backup() lx.emit(itemRawString) lx.next() lx.ignore() return lx.pop() } - return lexRawString } // lexMultilineRawString consumes a raw string. Nothing can be escaped in such @@ -734,21 +761,18 @@ func lexRawString(lx *lexer) stateFn { func lexMultilineRawString(lx *lexer) stateFn { r := lx.next() switch r { + default: + return lexMultilineRawString case eof: return lx.errorf(`unexpected EOF; expected "'''"`) - case '\r': - if lx.peek() != '\n' { - return lx.errorf("control characters are not allowed inside strings: '0x%02x'", r) - } - return lexMultilineRawString - case rawStringEnd: + case '\'': /// Found ' → try to read two more ''. - if lx.accept(rawStringEnd) { - if lx.accept(rawStringEnd) { + if lx.accept('\'') { + if lx.accept('\'') { /// Peek ahead: the string can contain ' and '', including at the /// end: '''str''''' /// 6 or more at the end, however, is an error. - if lx.peek() == rawStringEnd { + if lx.peek() == '\'' { /// Check if we already lexed 5 's; if so we have 6 now, and /// that's just too many man! if strings.HasSuffix(lx.current(), "'''''") { @@ -771,12 +795,8 @@ func lexMultilineRawString(lx *lexer) stateFn { } lx.backup() } + return lexMultilineRawString } - - if isControl(r) { - return lx.errorf("control characters are not allowed inside strings: '0x%02x'", r) - } - return lexMultilineRawString } // lexMultilineStringEscape consumes an escaped character. It assumes that the @@ -817,8 +837,7 @@ func lexStringEscape(lx *lexer) stateFn { case 'U': return lexLongUnicodeEscape } - return lx.errorf("invalid escape character %q; only the following escape characters are allowed: "+ - `\b, \t, \n, \f, \r, \", \\, \uXXXX, and \UXXXXXXXX`, r) + return lx.error(errLexEscape{r}) } func lexShortUnicodeEscape(lx *lexer) stateFn { @@ -1108,8 +1127,6 @@ func lexComment(lx *lexer) stateFn { lx.backup() lx.emit(itemText) return lx.pop() - case isControl(r): - return lx.errorf("control characters are not allowed inside comments: '0x%02x'", r) default: return lexComment } @@ -1121,52 +1138,6 @@ func lexSkip(lx *lexer, nextState stateFn) stateFn { return nextState } -// isWhitespace returns true if `r` is a whitespace character according -// to the spec. -func isWhitespace(r rune) bool { - return r == '\t' || r == ' ' -} - -func isNL(r rune) bool { - return r == '\n' || r == '\r' -} - -// Control characters except \n, \t -func isControl(r rune) bool { - switch r { - case '\t', '\r', '\n': - return false - default: - return (r >= 0x00 && r <= 0x1f) || r == 0x7f - } -} - -func isDigit(r rune) bool { - return r >= '0' && r <= '9' -} - -func isHexadecimal(r rune) bool { - return (r >= '0' && r <= '9') || - (r >= 'a' && r <= 'f') || - (r >= 'A' && r <= 'F') -} - -func isOctal(r rune) bool { - return r >= '0' && r <= '7' -} - -func isBinary(r rune) bool { - return r == '0' || r == '1' -} - -func isBareKeyChar(r rune) bool { - return (r >= 'A' && r <= 'Z') || - (r >= 'a' && r <= 'z') || - (r >= '0' && r <= '9') || - r == '_' || - r == '-' -} - func (s stateFn) String() string { name := runtime.FuncForPC(reflect.ValueOf(s).Pointer()).Name() if i := strings.LastIndexByte(name, '.'); i > -1 { @@ -1223,3 +1194,26 @@ func (itype itemType) String() string { func (item item) String() string { return fmt.Sprintf("(%s, %s)", item.typ.String(), item.val) } + +func isWhitespace(r rune) bool { return r == '\t' || r == ' ' } +func isNL(r rune) bool { return r == '\n' || r == '\r' } +func isControl(r rune) bool { // Control characters except \t, \r, \n + switch r { + case '\t', '\r', '\n': + return false + default: + return (r >= 0x00 && r <= 0x1f) || r == 0x7f + } +} +func isDigit(r rune) bool { return r >= '0' && r <= '9' } +func isBinary(r rune) bool { return r == '0' || r == '1' } +func isOctal(r rune) bool { return r >= '0' && r <= '7' } +func isHexadecimal(r rune) bool { + return (r >= '0' && r <= '9') || (r >= 'a' && r <= 'f') || (r >= 'A' && r <= 'F') +} +func isBareKeyChar(r rune) bool { + return (r >= 'A' && r <= 'Z') || + (r >= 'a' && r <= 'z') || + (r >= '0' && r <= '9') || + r == '_' || r == '-' +} diff --git a/vendor/github.com/BurntSushi/toml/decode_meta.go b/vendor/github.com/BurntSushi/toml/meta.go index ad8899c6c..868619fb9 100644 --- a/vendor/github.com/BurntSushi/toml/decode_meta.go +++ b/vendor/github.com/BurntSushi/toml/meta.go @@ -1,34 +1,39 @@ package toml -import "strings" +import ( + "strings" +) -// MetaData allows access to meta information about TOML data that may not be -// inferable via reflection. In particular, whether a key has been defined and -// the TOML type of a key. +// MetaData allows access to meta information about TOML data that's not +// accessible otherwise. +// +// It allows checking if a key is defined in the TOML data, whether any keys +// were undecoded, and the TOML type of a key. type MetaData struct { + context Key // Used only during decoding. + mapping map[string]interface{} types map[string]tomlType keys []Key - decoded map[string]bool - context Key // Used only during decoding. + decoded map[string]struct{} } // IsDefined reports if the key exists in the TOML data. // // The key should be specified hierarchically, for example to access the TOML -// key "a.b.c" you would use: +// key "a.b.c" you would use IsDefined("a", "b", "c"). Keys are case sensitive. // -// IsDefined("a", "b", "c") -// -// IsDefined will return false if an empty key given. Keys are case sensitive. +// Returns false for an empty key. func (md *MetaData) IsDefined(key ...string) bool { if len(key) == 0 { return false } - var hash map[string]interface{} - var ok bool - var hashOrVal interface{} = md.mapping + var ( + hash map[string]interface{} + ok bool + hashOrVal interface{} = md.mapping + ) for _, k := range key { if hash, ok = hashOrVal.(map[string]interface{}); !ok { return false @@ -45,51 +50,12 @@ func (md *MetaData) IsDefined(key ...string) bool { // Type will return the empty string if given an empty key or a key that does // not exist. Keys are case sensitive. func (md *MetaData) Type(key ...string) string { - fullkey := strings.Join(key, ".") - if typ, ok := md.types[fullkey]; ok { + if typ, ok := md.types[Key(key).String()]; ok { return typ.typeString() } return "" } -// Key represents any TOML key, including key groups. Use (MetaData).Keys to get -// values of this type. -type Key []string - -func (k Key) String() string { return strings.Join(k, ".") } - -func (k Key) maybeQuotedAll() string { - var ss []string - for i := range k { - ss = append(ss, k.maybeQuoted(i)) - } - return strings.Join(ss, ".") -} - -func (k Key) maybeQuoted(i int) string { - if k[i] == "" { - return `""` - } - quote := false - for _, c := range k[i] { - if !isBareKeyChar(c) { - quote = true - break - } - } - if quote { - return `"` + quotedReplacer.Replace(k[i]) + `"` - } - return k[i] -} - -func (k Key) add(piece string) Key { - newKey := make(Key, len(k)+1) - copy(newKey, k) - newKey[len(k)] = piece - return newKey -} - // Keys returns a slice of every key in the TOML data, including key groups. // // Each key is itself a slice, where the first element is the top of the @@ -115,9 +81,40 @@ func (md *MetaData) Keys() []Key { func (md *MetaData) Undecoded() []Key { undecoded := make([]Key, 0, len(md.keys)) for _, key := range md.keys { - if !md.decoded[key.String()] { + if _, ok := md.decoded[key.String()]; !ok { undecoded = append(undecoded, key) } } return undecoded } + +// Key represents any TOML key, including key groups. Use (MetaData).Keys to get +// values of this type. +type Key []string + +func (k Key) String() string { + ss := make([]string, len(k)) + for i := range k { + ss[i] = k.maybeQuoted(i) + } + return strings.Join(ss, ".") +} + +func (k Key) maybeQuoted(i int) string { + if k[i] == "" { + return `""` + } + for _, c := range k[i] { + if !isBareKeyChar(c) { + return `"` + dblQuotedReplacer.Replace(k[i]) + `"` + } + } + return k[i] +} + +func (k Key) add(piece string) Key { + newKey := make(Key, len(k)+1) + copy(newKey, k) + newKey[len(k)] = piece + return newKey +} diff --git a/vendor/github.com/BurntSushi/toml/parse.go b/vendor/github.com/BurntSushi/toml/parse.go index d9ae5db94..8269cca17 100644 --- a/vendor/github.com/BurntSushi/toml/parse.go +++ b/vendor/github.com/BurntSushi/toml/parse.go @@ -1,7 +1,6 @@ package toml import ( - "errors" "fmt" "strconv" "strings" @@ -12,35 +11,23 @@ import ( ) type parser struct { - mapping map[string]interface{} - types map[string]tomlType - lx *lexer - - ordered []Key // List of keys in the order that they appear in the TOML data. - context Key // Full key for the current hash in scope. - currentKey string // Base key name for everything except hashes. - approxLine int // Rough approximation of line number - implicits map[string]bool // Record implied keys (e.g. 'key.group.names'). -} - -// ParseError is used when a file can't be parsed: for example invalid integer -// literals, duplicate keys, etc. -type ParseError struct { - Message string - Line int - LastKey string -} - -func (pe ParseError) Error() string { - return fmt.Sprintf("Near line %d (last key parsed '%s'): %s", - pe.Line, pe.LastKey, pe.Message) + lx *lexer + context Key // Full key for the current hash in scope. + currentKey string // Base key name for everything except hashes. + pos Position // Current position in the TOML file. + + ordered []Key // List of keys in the order that they appear in the TOML data. + mapping map[string]interface{} // Map keyname → key value. + types map[string]tomlType // Map keyname → TOML type. + implicits map[string]struct{} // Record implicit keys (e.g. "key.group.names"). } func parse(data string) (p *parser, err error) { defer func() { if r := recover(); r != nil { - var ok bool - if err, ok = r.(ParseError); ok { + if pErr, ok := r.(ParseError); ok { + pErr.input = data + err = pErr return } panic(r) @@ -60,8 +47,13 @@ func parse(data string) (p *parser, err error) { if len(data) < 6 { ex = len(data) } - if strings.ContainsRune(data[:ex], 0) { - return nil, errors.New("files cannot contain NULL bytes; probably using UTF-16; TOML files must be UTF-8") + if i := strings.IndexRune(data[:ex], 0); i > -1 { + return nil, ParseError{ + Message: "files cannot contain NULL bytes; probably using UTF-16; TOML files must be UTF-8", + Position: Position{Line: 1, Start: i, Len: 1}, + Line: 1, + input: data, + } } p = &parser{ @@ -69,7 +61,7 @@ func parse(data string) (p *parser, err error) { types: make(map[string]tomlType), lx: lex(data), ordered: make([]Key, 0), - implicits: make(map[string]bool), + implicits: make(map[string]struct{}), } for { item := p.next() @@ -82,12 +74,21 @@ func parse(data string) (p *parser, err error) { return p, nil } +func (p *parser) panicItemf(it item, format string, v ...interface{}) { + panic(ParseError{ + Message: fmt.Sprintf(format, v...), + Position: it.pos, + Line: it.pos.Len, + LastKey: p.current(), + }) +} + func (p *parser) panicf(format string, v ...interface{}) { - msg := fmt.Sprintf(format, v...) panic(ParseError{ - Message: msg, - Line: p.approxLine, - LastKey: p.current(), + Message: fmt.Sprintf(format, v...), + Position: p.pos, + Line: p.pos.Line, + LastKey: p.current(), }) } @@ -95,11 +96,26 @@ func (p *parser) next() item { it := p.lx.nextItem() //fmt.Printf("ITEM %-18s line %-3d │ %q\n", it.typ, it.line, it.val) if it.typ == itemError { - p.panicf("%s", it.val) + if it.err != nil { + panic(ParseError{ + Position: it.pos, + Line: it.pos.Line, + LastKey: p.current(), + err: it.err, + }) + } + + p.panicItemf(it, "%s", it.val) } return it } +func (p *parser) nextPos() item { + it := p.next() + p.pos = it.pos + return it +} + func (p *parser) bug(format string, v ...interface{}) { panic(fmt.Sprintf("BUG: "+format+"\n\n", v...)) } @@ -119,11 +135,9 @@ func (p *parser) assertEqual(expected, got itemType) { func (p *parser) topLevel(item item) { switch item.typ { case itemCommentStart: // # .. - p.approxLine = item.line p.expect(itemText) case itemTableStart: // [ .. ] - name := p.next() - p.approxLine = name.line + name := p.nextPos() var key Key for ; name.typ != itemTableEnd && name.typ != itemEOF; name = p.next() { @@ -135,8 +149,7 @@ func (p *parser) topLevel(item item) { p.setType("", tomlHash) p.ordered = append(p.ordered, key) case itemArrayTableStart: // [[ .. ]] - name := p.next() - p.approxLine = name.line + name := p.nextPos() var key Key for ; name.typ != itemArrayTableEnd && name.typ != itemEOF; name = p.next() { @@ -150,8 +163,7 @@ func (p *parser) topLevel(item item) { case itemKeyStart: // key = .. outerContext := p.context /// Read all the key parts (e.g. 'a' and 'b' in 'a.b') - k := p.next() - p.approxLine = k.line + k := p.nextPos() var key Key for ; k.typ != itemKeyEnd && k.typ != itemEOF; k = p.next() { key = append(key, p.keyString(k)) @@ -206,9 +218,9 @@ var datetimeRepl = strings.NewReplacer( func (p *parser) value(it item, parentIsArray bool) (interface{}, tomlType) { switch it.typ { case itemString: - return p.replaceEscapes(it.val), p.typeOfPrimitive(it) + return p.replaceEscapes(it, it.val), p.typeOfPrimitive(it) case itemMultilineString: - return p.replaceEscapes(stripFirstNewline(stripEscapedNewlines(it.val))), p.typeOfPrimitive(it) + return p.replaceEscapes(it, stripFirstNewline(stripEscapedNewlines(it.val))), p.typeOfPrimitive(it) case itemRawString: return it.val, p.typeOfPrimitive(it) case itemRawMultilineString: @@ -240,10 +252,10 @@ func (p *parser) value(it item, parentIsArray bool) (interface{}, tomlType) { func (p *parser) valueInteger(it item) (interface{}, tomlType) { if !numUnderscoresOK(it.val) { - p.panicf("Invalid integer %q: underscores must be surrounded by digits", it.val) + p.panicItemf(it, "Invalid integer %q: underscores must be surrounded by digits", it.val) } if numHasLeadingZero(it.val) { - p.panicf("Invalid integer %q: cannot have leading zeroes", it.val) + p.panicItemf(it, "Invalid integer %q: cannot have leading zeroes", it.val) } num, err := strconv.ParseInt(it.val, 0, 64) @@ -254,7 +266,7 @@ func (p *parser) valueInteger(it item) (interface{}, tomlType) { // So mark the former as a bug but the latter as a legitimate user // error. if e, ok := err.(*strconv.NumError); ok && e.Err == strconv.ErrRange { - p.panicf("Integer '%s' is out of the range of 64-bit signed integers.", it.val) + p.panicItemf(it, "Integer '%s' is out of the range of 64-bit signed integers.", it.val) } else { p.bug("Expected integer value, but got '%s'.", it.val) } @@ -272,18 +284,18 @@ func (p *parser) valueFloat(it item) (interface{}, tomlType) { }) for _, part := range parts { if !numUnderscoresOK(part) { - p.panicf("Invalid float %q: underscores must be surrounded by digits", it.val) + p.panicItemf(it, "Invalid float %q: underscores must be surrounded by digits", it.val) } } if len(parts) > 0 && numHasLeadingZero(parts[0]) { - p.panicf("Invalid float %q: cannot have leading zeroes", it.val) + p.panicItemf(it, "Invalid float %q: cannot have leading zeroes", it.val) } if !numPeriodsOK(it.val) { // As a special case, numbers like '123.' or '1.e2', // which are valid as far as Go/strconv are concerned, // must be rejected because TOML says that a fractional // part consists of '.' followed by 1+ digits. - p.panicf("Invalid float %q: '.' must be followed by one or more digits", it.val) + p.panicItemf(it, "Invalid float %q: '.' must be followed by one or more digits", it.val) } val := strings.Replace(it.val, "_", "", -1) if val == "+nan" || val == "-nan" { // Go doesn't support this, but TOML spec does. @@ -292,9 +304,9 @@ func (p *parser) valueFloat(it item) (interface{}, tomlType) { num, err := strconv.ParseFloat(val, 64) if err != nil { if e, ok := err.(*strconv.NumError); ok && e.Err == strconv.ErrRange { - p.panicf("Float '%s' is out of the range of 64-bit IEEE-754 floating-point numbers.", it.val) + p.panicItemf(it, "Float '%s' is out of the range of 64-bit IEEE-754 floating-point numbers.", it.val) } else { - p.panicf("Invalid float value: %q", it.val) + p.panicItemf(it, "Invalid float value: %q", it.val) } } return num, p.typeOfPrimitive(it) @@ -325,7 +337,7 @@ func (p *parser) valueDatetime(it item) (interface{}, tomlType) { } } if !ok { - p.panicf("Invalid TOML Datetime: %q.", it.val) + p.panicItemf(it, "Invalid TOML Datetime: %q.", it.val) } return t, p.typeOfPrimitive(it) } @@ -335,8 +347,12 @@ func (p *parser) valueArray(it item) (interface{}, tomlType) { // p.setType(p.currentKey, typ) var ( - array []interface{} types []tomlType + + // Initialize to a non-nil empty slice. This makes it consistent with + // how S = [] decodes into a non-nil slice inside something like struct + // { S []string }. See #338 + array = []interface{}{} ) for it = p.next(); it.typ != itemArrayEnd; it = p.next() { if it.typ == itemCommentStart { @@ -347,6 +363,12 @@ func (p *parser) valueArray(it item) (interface{}, tomlType) { val, typ := p.value(it, true) array = append(array, val) types = append(types, typ) + + // XXX: types isn't used here, we need it to record the accurate type + // information. + // + // Not entirely sure how to best store this; could use "key[0]", + // "key[1]" notation, or maybe store it on the Array type? } return array, tomlArray } @@ -373,8 +395,7 @@ func (p *parser) valueInlineTable(it item, parentIsArray bool) (interface{}, tom } /// Read all key parts. - k := p.next() - p.approxLine = k.line + k := p.nextPos() var key Key for ; k.typ != itemKeyEnd && k.typ != itemEOF; k = p.next() { key = append(key, p.keyString(k)) @@ -408,7 +429,7 @@ func (p *parser) valueInlineTable(it item, parentIsArray bool) (interface{}, tom // numHasLeadingZero checks if this number has leading zeroes, allowing for '0', // +/- signs, and base prefixes. func numHasLeadingZero(s string) bool { - if len(s) > 1 && s[0] == '0' && isDigit(rune(s[1])) { // >1 to allow "0" and isDigit to allow 0x + if len(s) > 1 && s[0] == '0' && !(s[1] == 'b' || s[1] == 'o' || s[1] == 'x') { // Allow 0b, 0o, 0x return true } if len(s) > 2 && (s[0] == '-' || s[0] == '+') && s[1] == '0' { @@ -503,7 +524,7 @@ func (p *parser) addContext(key Key, array bool) { if hash, ok := hashContext[k].([]map[string]interface{}); ok { hashContext[k] = append(hash, make(map[string]interface{})) } else { - p.panicf("Key '%s' was already created and cannot be used as an array.", keyContext) + p.panicf("Key '%s' was already created and cannot be used as an array.", key) } } else { p.setValue(key[len(key)-1], make(map[string]interface{})) @@ -513,8 +534,8 @@ func (p *parser) addContext(key Key, array bool) { // set calls setValue and setType. func (p *parser) set(key string, val interface{}, typ tomlType) { - p.setValue(p.currentKey, val) - p.setType(p.currentKey, typ) + p.setValue(key, val) + p.setType(key, typ) } // setValue sets the given key to the given value in the current context. @@ -573,27 +594,31 @@ func (p *parser) setValue(key string, value interface{}) { hash[key] = value } -// setType sets the type of a particular value at a given key. -// It should be called immediately AFTER setValue. +// setType sets the type of a particular value at a given key. It should be +// called immediately AFTER setValue. // // Note that if `key` is empty, then the type given will be applied to the // current context (which is either a table or an array of tables). func (p *parser) setType(key string, typ tomlType) { keyContext := make(Key, 0, len(p.context)+1) - for _, k := range p.context { - keyContext = append(keyContext, k) - } + keyContext = append(keyContext, p.context...) if len(key) > 0 { // allow type setting for hashes keyContext = append(keyContext, key) } + // Special case to make empty keys ("" = 1) work. + // Without it it will set "" rather than `""`. + // TODO: why is this needed? And why is this only needed here? + if len(keyContext) == 0 { + keyContext = Key{""} + } p.types[keyContext.String()] = typ } // Implicit keys need to be created when tables are implied in "a.b.c.d = 1" and // "[a.b.c]" (the "a", "b", and "c" hashes are never created explicitly). -func (p *parser) addImplicit(key Key) { p.implicits[key.String()] = true } -func (p *parser) removeImplicit(key Key) { p.implicits[key.String()] = false } -func (p *parser) isImplicit(key Key) bool { return p.implicits[key.String()] } +func (p *parser) addImplicit(key Key) { p.implicits[key.String()] = struct{}{} } +func (p *parser) removeImplicit(key Key) { delete(p.implicits, key.String()) } +func (p *parser) isImplicit(key Key) bool { _, ok := p.implicits[key.String()]; return ok } func (p *parser) isArray(key Key) bool { return p.types[key.String()] == tomlArray } func (p *parser) addImplicitContext(key Key) { p.addImplicit(key) @@ -662,8 +687,8 @@ func stripEscapedNewlines(s string) string { return strings.Join(split, "") } -func (p *parser) replaceEscapes(str string) string { - var replaced []rune +func (p *parser) replaceEscapes(it item, str string) string { + replaced := make([]rune, 0, len(str)) s := []byte(str) r := 0 for r < len(s) { @@ -683,7 +708,7 @@ func (p *parser) replaceEscapes(str string) string { p.bug("Expected valid escape code after \\, but got %q.", s[r]) return "" case ' ', '\t': - p.panicf("invalid escape: '\\%c'", s[r]) + p.panicItemf(it, "invalid escape: '\\%c'", s[r]) return "" case 'b': replaced = append(replaced, rune(0x0008)) @@ -710,14 +735,14 @@ func (p *parser) replaceEscapes(str string) string { // At this point, we know we have a Unicode escape of the form // `uXXXX` at [r, r+5). (Because the lexer guarantees this // for us.) - escaped := p.asciiEscapeToUnicode(s[r+1 : r+5]) + escaped := p.asciiEscapeToUnicode(it, s[r+1:r+5]) replaced = append(replaced, escaped) r += 5 case 'U': // At this point, we know we have a Unicode escape of the form // `uXXXX` at [r, r+9). (Because the lexer guarantees this // for us.) - escaped := p.asciiEscapeToUnicode(s[r+1 : r+9]) + escaped := p.asciiEscapeToUnicode(it, s[r+1:r+9]) replaced = append(replaced, escaped) r += 9 } @@ -725,15 +750,14 @@ func (p *parser) replaceEscapes(str string) string { return string(replaced) } -func (p *parser) asciiEscapeToUnicode(bs []byte) rune { +func (p *parser) asciiEscapeToUnicode(it item, bs []byte) rune { s := string(bs) hex, err := strconv.ParseUint(strings.ToLower(s), 16, 32) if err != nil { - p.bug("Could not parse '%s' as a hexadecimal number, but the "+ - "lexer claims it's OK: %s", s, err) + p.bug("Could not parse '%s' as a hexadecimal number, but the lexer claims it's OK: %s", s, err) } if !utf8.ValidRune(rune(hex)) { - p.panicf("Escaped character '\\u%s' is not valid UTF-8.", s) + p.panicItemf(it, "Escaped character '\\u%s' is not valid UTF-8.", s) } return rune(hex) } diff --git a/vendor/github.com/BurntSushi/toml/type_fields.go b/vendor/github.com/BurntSushi/toml/type_fields.go index 608997c22..254ca82e5 100644 --- a/vendor/github.com/BurntSushi/toml/type_fields.go +++ b/vendor/github.com/BurntSushi/toml/type_fields.go @@ -70,8 +70,8 @@ func typeFields(t reflect.Type) []field { next := []field{{typ: t}} // Count of queued names for current level and the next. - count := map[reflect.Type]int{} - nextCount := map[reflect.Type]int{} + var count map[reflect.Type]int + var nextCount map[reflect.Type]int // Types already visited at an earlier level. visited := map[reflect.Type]bool{} diff --git a/vendor/github.com/BurntSushi/toml/type_check.go b/vendor/github.com/BurntSushi/toml/type_toml.go index d56aa80fa..4e90d7737 100644 --- a/vendor/github.com/BurntSushi/toml/type_check.go +++ b/vendor/github.com/BurntSushi/toml/type_toml.go @@ -16,7 +16,7 @@ func typeEqual(t1, t2 tomlType) bool { return t1.typeString() == t2.typeString() } -func typeIsHash(t tomlType) bool { +func typeIsTable(t tomlType) bool { return typeEqual(t, tomlHash) || typeEqual(t, tomlArrayHash) } diff --git a/vendor/github.com/containers/common/libimage/copier.go b/vendor/github.com/containers/common/libimage/copier.go index 4f5c7d0a1..459989579 100644 --- a/vendor/github.com/containers/common/libimage/copier.go +++ b/vendor/github.com/containers/common/libimage/copier.go @@ -218,15 +218,7 @@ func (r *Runtime) newCopier(options *CopyOptions) (*copier, error) { c.systemContext.DockerArchiveAdditionalTags = options.dockerArchiveAdditionalTags - if options.Architecture != "" { - c.systemContext.ArchitectureChoice = options.Architecture - } - if options.OS != "" { - c.systemContext.OSChoice = options.OS - } - if options.Variant != "" { - c.systemContext.VariantChoice = options.Variant - } + c.systemContext.OSChoice, c.systemContext.ArchitectureChoice, c.systemContext.VariantChoice = NormalizePlatform(options.OS, options.Architecture, options.Variant) if options.SignaturePolicyPath != "" { c.systemContext.SignaturePolicyPath = options.SignaturePolicyPath diff --git a/vendor/github.com/containers/common/libimage/filters.go b/vendor/github.com/containers/common/libimage/filters.go index dfa34dd44..063f07149 100644 --- a/vendor/github.com/containers/common/libimage/filters.go +++ b/vendor/github.com/containers/common/libimage/filters.go @@ -19,26 +19,53 @@ import ( // indicates that the image matches the criteria. type filterFunc func(*Image) (bool, error) +// Apply the specified filters. At least one filter of each key must apply. +func (i *Image) applyFilters(filters map[string][]filterFunc) (bool, error) { + matches := false + for key := range filters { // and + matches = false + for _, filter := range filters[key] { // or + var err error + matches, err = filter(i) + if err != nil { + // Some images may have been corrupted in the + // meantime, so do an extra check and make the + // error non-fatal (see containers/podman/issues/12582). + if errCorrupted := i.isCorrupted(""); errCorrupted != nil { + logrus.Errorf(errCorrupted.Error()) + return false, nil + } + return false, err + } + if matches { + break + } + } + if !matches { + return false, nil + } + } + return matches, nil +} + // filterImages returns a slice of images which are passing all specified // filters. -func filterImages(images []*Image, filters []filterFunc) ([]*Image, error) { - if len(filters) == 0 { +func (r *Runtime) filterImages(ctx context.Context, images []*Image, options *ListImagesOptions) ([]*Image, error) { + if len(options.Filters) == 0 || len(images) == 0 { return images, nil } + + filters, err := r.compileImageFilters(ctx, options) + if err != nil { + return nil, err + } result := []*Image{} for i := range images { - include := true - var err error - for _, filter := range filters { - include, err = filter(images[i]) - if err != nil { - return nil, err - } - if !include { - break - } + match, err := images[i].applyFilters(filters) + if err != nil { + return nil, err } - if include { + if match { result = append(result, images[i]) } } @@ -48,7 +75,7 @@ func filterImages(images []*Image, filters []filterFunc) ([]*Image, error) { // compileImageFilters creates `filterFunc`s for the specified filters. The // required format is `key=value` with the following supported keys: // after, since, before, containers, dangling, id, label, readonly, reference, intermediate -func (r *Runtime) compileImageFilters(ctx context.Context, options *ListImagesOptions) ([]filterFunc, error) { +func (r *Runtime) compileImageFilters(ctx context.Context, options *ListImagesOptions) (map[string][]filterFunc, error) { logrus.Tracef("Parsing image filters %s", options.Filters) var tree *layerTree @@ -63,12 +90,14 @@ func (r *Runtime) compileImageFilters(ctx context.Context, options *ListImagesOp return tree, nil } - filterFuncs := []filterFunc{} - for _, filter := range options.Filters { + filters := map[string][]filterFunc{} + duplicate := map[string]string{} + for _, f := range options.Filters { var key, value string - split := strings.SplitN(filter, "=", 2) + var filter filterFunc + split := strings.SplitN(f, "=", 2) if len(split) != 2 { - return nil, errors.Errorf("invalid image filter %q: must be in the format %q", filter, "filter=value") + return nil, errors.Errorf("invalid image filter %q: must be in the format %q", f, "filter=value") } key = split[0] @@ -76,87 +105,148 @@ func (r *Runtime) compileImageFilters(ctx context.Context, options *ListImagesOp switch key { case "after", "since": - img, _, err := r.LookupImage(value, nil) + img, err := r.time(key, value) if err != nil { - return nil, errors.Wrapf(err, "could not find local image for filter %q", filter) + return nil, err } - filterFuncs = append(filterFuncs, filterAfter(img.Created())) + key = "since" + filter = filterAfter(img.Created()) case "before": - img, _, err := r.LookupImage(value, nil) + img, err := r.time(key, value) if err != nil { - return nil, errors.Wrapf(err, "could not find local image for filter %q", filter) + return nil, err } - filterFuncs = append(filterFuncs, filterBefore(img.Created())) + filter = filterBefore(img.Created()) case "containers": - switch value { - case "false", "true": - case "external": - if options.IsExternalContainerFunc == nil { - return nil, fmt.Errorf("libimage error: external containers filter without callback") - } - default: - return nil, fmt.Errorf("unsupported value %q for containers filter", value) + if err := r.containers(duplicate, key, value, options.IsExternalContainerFunc); err != nil { + return nil, err } - filterFuncs = append(filterFuncs, filterContainers(value, options.IsExternalContainerFunc)) + filter = filterContainers(value, options.IsExternalContainerFunc) case "dangling": - dangling, err := strconv.ParseBool(value) + dangling, err := r.bool(duplicate, key, value) if err != nil { - return nil, errors.Wrapf(err, "non-boolean value %q for dangling filter", value) + return nil, err } t, err := getTree() if err != nil { return nil, err } - filterFuncs = append(filterFuncs, filterDangling(ctx, dangling, t)) + + filter = filterDangling(ctx, dangling, t) case "id": - filterFuncs = append(filterFuncs, filterID(value)) + filter = filterID(value) case "intermediate": - intermediate, err := strconv.ParseBool(value) + intermediate, err := r.bool(duplicate, key, value) if err != nil { - return nil, errors.Wrapf(err, "non-boolean value %q for intermediate filter", value) + return nil, err } t, err := getTree() if err != nil { return nil, err } - filterFuncs = append(filterFuncs, filterIntermediate(ctx, intermediate, t)) + + filter = filterIntermediate(ctx, intermediate, t) case "label": - filterFuncs = append(filterFuncs, filterLabel(ctx, value)) + filter = filterLabel(ctx, value) case "readonly": - readOnly, err := strconv.ParseBool(value) + readOnly, err := r.bool(duplicate, key, value) if err != nil { - return nil, errors.Wrapf(err, "non-boolean value %q for readonly filter", value) + return nil, err } - filterFuncs = append(filterFuncs, filterReadOnly(readOnly)) - - case "reference": - filterFuncs = append(filterFuncs, filterReferences(value)) + filter = filterReadOnly(readOnly) - case "until": - ts, err := timetype.GetTimestamp(value, time.Now()) + case "manifest": + manifest, err := r.bool(duplicate, key, value) if err != nil { return nil, err } - seconds, nanoseconds, err := timetype.ParseTimestamps(ts, 0) + filter = filterManifest(ctx, manifest) + + case "reference": + filter = filterReferences(value) + + case "until": + until, err := r.until(value) if err != nil { return nil, err } - until := time.Unix(seconds, nanoseconds) - filterFuncs = append(filterFuncs, filterBefore(until)) + filter = filterBefore(until) default: return nil, errors.Errorf("unsupported image filter %q", key) } + filters[key] = append(filters[key], filter) + } + + return filters, nil +} + +func (r *Runtime) containers(duplicate map[string]string, key, value string, externalFunc IsExternalContainerFunc) error { + if exists, ok := duplicate[key]; ok && exists != value { + return errors.Errorf("specifying %q filter more than once with different values is not supported", key) + } + duplicate[key] = value + switch value { + case "false", "true": + case "external": + if externalFunc == nil { + return fmt.Errorf("libimage error: external containers filter without callback") + } + default: + return fmt.Errorf("unsupported value %q for containers filter", value) + } + return nil +} + +func (r *Runtime) until(value string) (time.Time, error) { + var until time.Time + ts, err := timetype.GetTimestamp(value, time.Now()) + if err != nil { + return until, err } + seconds, nanoseconds, err := timetype.ParseTimestamps(ts, 0) + if err != nil { + return until, err + } + return time.Unix(seconds, nanoseconds), nil +} + +func (r *Runtime) time(key, value string) (*Image, error) { + img, _, err := r.LookupImage(value, nil) + if err != nil { + return nil, errors.Wrapf(err, "could not find local image for filter filter %q=%q", key, value) + } + return img, nil +} + +func (r *Runtime) bool(duplicate map[string]string, key, value string) (bool, error) { + if exists, ok := duplicate[key]; ok && exists != value { + return false, errors.Errorf("specifying %q filter more than once with different values is not supported", key) + } + duplicate[key] = value + set, err := strconv.ParseBool(value) + if err != nil { + return false, errors.Wrapf(err, "non-boolean value %q for %s filter", key, value) + } + return set, nil +} - return filterFuncs, nil +// filterManifest filters whether or not the image is a manifest list +func filterManifest(ctx context.Context, value bool) filterFunc { + return func(img *Image) (bool, error) { + isManifestList, err := img.IsManifestList(ctx) + if err != nil { + return false, err + } + return isManifestList == value, nil + } } // filterReferences creates a reference filter for matching the specified value. diff --git a/vendor/github.com/containers/common/libimage/load.go b/vendor/github.com/containers/common/libimage/load.go index 74a1870e0..4dfac7106 100644 --- a/vendor/github.com/containers/common/libimage/load.go +++ b/vendor/github.com/containers/common/libimage/load.go @@ -35,17 +35,6 @@ func (r *Runtime) Load(ctx context.Context, path string, options *LoadOptions) ( var loadErrors []error for _, f := range []func() ([]string, string, error){ - // DOCKER-ARCHIVE - must be first (see containers/podman/issues/10809) - func() ([]string, string, error) { - logrus.Debugf("-> Attempting to load %q as a Docker archive", path) - ref, err := dockerArchiveTransport.ParseReference(path) - if err != nil { - return nil, dockerArchiveTransport.Transport.Name(), err - } - images, err := r.loadMultiImageDockerArchive(ctx, ref, &options.CopyOptions) - return images, dockerArchiveTransport.Transport.Name(), err - }, - // OCI func() ([]string, string, error) { logrus.Debugf("-> Attempting to load %q as an OCI directory", path) @@ -68,6 +57,17 @@ func (r *Runtime) Load(ctx context.Context, path string, options *LoadOptions) ( return images, ociArchiveTransport.Transport.Name(), err }, + // DOCKER-ARCHIVE + func() ([]string, string, error) { + logrus.Debugf("-> Attempting to load %q as a Docker archive", path) + ref, err := dockerArchiveTransport.ParseReference(path) + if err != nil { + return nil, dockerArchiveTransport.Transport.Name(), err + } + images, err := r.loadMultiImageDockerArchive(ctx, ref, &options.CopyOptions) + return images, dockerArchiveTransport.Transport.Name(), err + }, + // DIR func() ([]string, string, error) { logrus.Debugf("-> Attempting to load %q as a Docker dir", path) diff --git a/vendor/github.com/containers/common/libimage/normalize.go b/vendor/github.com/containers/common/libimage/normalize.go index bfea807c8..7ceb62830 100644 --- a/vendor/github.com/containers/common/libimage/normalize.go +++ b/vendor/github.com/containers/common/libimage/normalize.go @@ -1,13 +1,51 @@ package libimage import ( + "runtime" "strings" + "github.com/containerd/containerd/platforms" "github.com/containers/image/v5/docker/reference" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) +// NormalizePlatform normalizes (according to the OCI spec) the specified os, +// arch and variant. If left empty, the individual item will not be normalized. +func NormalizePlatform(rawOS, rawArch, rawVariant string) (os, arch, variant string) { + os, arch, variant = rawOS, rawArch, rawVariant + if os == "" { + os = runtime.GOOS + } + if arch == "" { + arch = runtime.GOARCH + } + rawPlatform := os + "/" + arch + if variant != "" { + rawPlatform += "/" + variant + } + + normalizedPlatform, err := platforms.Parse(rawPlatform) + if err != nil { + logrus.Debugf("Error normalizing platform: %v", err) + return rawOS, rawArch, rawVariant + } + logrus.Debugf("Normalized platform %s to %s", rawPlatform, normalizedPlatform) + os = rawOS + if rawOS != "" { + os = normalizedPlatform.OS + } + arch = rawArch + if rawArch != "" { + arch = normalizedPlatform.Architecture + } + variant = rawVariant + if rawVariant != "" { + variant = normalizedPlatform.Variant + } + return os, arch, variant +} + // NormalizeName normalizes the provided name according to the conventions by // Podman and Buildah. If tag and digest are missing, the "latest" tag will be // used. If it's a short name, it will be prefixed with "localhost/". diff --git a/vendor/github.com/containers/common/libimage/pull.go b/vendor/github.com/containers/common/libimage/pull.go index 59221d935..ff93b6ed8 100644 --- a/vendor/github.com/containers/common/libimage/pull.go +++ b/vendor/github.com/containers/common/libimage/pull.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + "runtime" "strings" "time" @@ -21,6 +22,7 @@ import ( "github.com/containers/image/v5/types" "github.com/containers/storage" digest "github.com/opencontainers/go-digest" + ociSpec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -169,6 +171,20 @@ func (r *Runtime) Pull(ctx context.Context, name string, pullPolicy config.PullP return localImages, pullError } +// nameFromAnnotations returns a reference string to be used as an image name, +// or an empty string. The annotations map may be nil. +func nameFromAnnotations(annotations map[string]string) string { + if annotations == nil { + return "" + } + // buildkit/containerd are using a custom annotation see + // containers/podman/issues/12560. + if annotations["io.containerd.image.name"] != "" { + return annotations["io.containerd.image.name"] + } + return annotations[ociSpec.AnnotationRefName] +} + // copyFromDefault is the default copier for a number of transports. Other // transports require some specific dancing, sometimes Yoga. func (r *Runtime) copyFromDefault(ctx context.Context, ref types.ImageReference, options *CopyOptions) ([]string, error) { @@ -201,15 +217,16 @@ func (r *Runtime) copyFromDefault(ctx context.Context, ref types.ImageReference, if err != nil { return nil, err } - // if index.json has no reference name, compute the image ID instead - if manifestDescriptor.Annotations == nil || manifestDescriptor.Annotations["org.opencontainers.image.ref.name"] == "" { + storageName = nameFromAnnotations(manifestDescriptor.Annotations) + switch len(storageName) { + case 0: + // If there's no reference name in the annotations, compute an ID. storageName, err = getImageID(ctx, ref, nil) if err != nil { return nil, err } imageName = "sha256:" + storageName[1:] - } else { - storageName = manifestDescriptor.Annotations["org.opencontainers.image.ref.name"] + default: named, err := NormalizeName(storageName) if err != nil { return nil, err @@ -227,8 +244,14 @@ func (r *Runtime) copyFromDefault(ctx context.Context, ref types.ImageReference, imageName = named.String() default: - storageName = toLocalImageName(ref.StringWithinTransport()) - imageName = storageName + // Path-based transports (e.g., dir) may include invalid + // characters, so we should pessimistically generate an ID + // instead of looking at the StringWithinTransport(). + storageName, err = getImageID(ctx, ref, nil) + if err != nil { + return nil, err + } + imageName = "sha256:" + storageName[1:] } // Create a storage reference. @@ -424,12 +447,18 @@ func (r *Runtime) copySingleImageFromRegistry(ctx context.Context, imageName str // If there's already a local image "localhost/foo", then we should // attempt pulling that instead of doing the full short-name dance. // - // NOTE: we must ignore the platform of a local image when doing - // lookups here, even if arch/os/variant is set. Some images set an - // incorrect or even invalid platform (see containers/podman/issues/10682). - // Doing the lookup while ignoring the platform checks prevents - // redundantly downloading the same image. - localImage, resolvedImageName, err = r.LookupImage(imageName, nil) + // NOTE that we only do platform checks if the specified values differ + // from the local platform. Unfortunately, there are many images used + // in the wild which don't set the correct value(s) in the config + // causing various issues such as containers/podman/issues/10682. + lookupImageOptions := &LookupImageOptions{Variant: options.Variant} + if options.Architecture != runtime.GOARCH { + lookupImageOptions.Architecture = options.Architecture + } + if options.OS != runtime.GOOS { + lookupImageOptions.OS = options.OS + } + localImage, resolvedImageName, err = r.LookupImage(imageName, lookupImageOptions) if err != nil && errors.Cause(err) != storage.ErrImageUnknown { logrus.Errorf("Looking up %s in local storage: %v", imageName, err) } @@ -442,37 +471,18 @@ func (r *Runtime) copySingleImageFromRegistry(ctx context.Context, imageName str } } - customPlatform := false - if len(options.Architecture)+len(options.OS)+len(options.Variant) > 0 { - customPlatform = true - // Unless the pull policy is "always", we must pessimistically assume - // that the local image has an invalid architecture (see - // containers/podman/issues/10682). Hence, whenever the user requests - // a custom platform, set the pull policy to "always" to make sure - // we're pulling down the image. + customPlatform := len(options.Architecture)+len(options.OS)+len(options.Variant) > 0 + if customPlatform && pullPolicy != config.PullPolicyAlways && pullPolicy != config.PullPolicyNever { + // Unless the pull policy is always/never, we must + // pessimistically assume that the local image has an invalid + // architecture (see containers/podman/issues/10682). Hence, + // whenever the user requests a custom platform, set the pull + // policy to "newer" to make sure we're pulling down the + // correct image. // - // NOTE that this is will even override --pull={false,never}. This is - // very likely a bug but a consistent one in Podman/Buildah and should - // be addressed at a later point. - if pullPolicy != config.PullPolicyAlways && pullPolicy != config.PullPolicyNever { - switch { - // User input clearly refer to a local image. - case strings.HasPrefix(imageName, "localhost/"): - logrus.Debugf("Enforcing pull policy to %q to support custom platform (arch: %q, os: %q, variant: %q)", "never", options.Architecture, options.OS, options.Variant) - pullPolicy = config.PullPolicyNever - - // Image resolved to a local one, so let's still have a - // look at the registries or aliases but use it - // otherwise. - case strings.HasPrefix(resolvedImageName, "localhost/"): - logrus.Debugf("Enforcing pull policy to %q to support custom platform (arch: %q, os: %q, variant: %q)", "newer", options.Architecture, options.OS, options.Variant) - pullPolicy = config.PullPolicyNewer - - default: - logrus.Debugf("Enforcing pull policy to %q to support custom platform (arch: %q, os: %q, variant: %q)", "always", options.Architecture, options.OS, options.Variant) - pullPolicy = config.PullPolicyAlways - } - } + // NOTE that this is will even override --pull={false,never}. + pullPolicy = config.PullPolicyNewer + logrus.Debugf("Enforcing pull policy to %q to pull custom platform (arch: %q, os: %q, variant: %q) - local image may mistakenly specify wrong platform", pullPolicy, options.Architecture, options.OS, options.Variant) } if pullPolicy == config.PullPolicyNever { @@ -518,6 +528,12 @@ func (r *Runtime) copySingleImageFromRegistry(ctx context.Context, imageName str sys := r.systemContextCopy() resolved, err := shortnames.Resolve(sys, imageName) if err != nil { + // TODO: that is a too big of a hammer since we should only + // ignore errors that indicate that there's no alias and no + // USRs. Must be addressed in c/image first. + if localImage != nil && pullPolicy == config.PullPolicyNewer { + return []string{resolvedImageName}, nil + } return nil, err } diff --git a/vendor/github.com/containers/common/libimage/runtime.go b/vendor/github.com/containers/common/libimage/runtime.go index c0eee4212..86e7eee56 100644 --- a/vendor/github.com/containers/common/libimage/runtime.go +++ b/vendor/github.com/containers/common/libimage/runtime.go @@ -253,6 +253,8 @@ func (r *Runtime) LookupImage(name string, options *LookupImageOptions) (*Image, if options.Variant == "" { options.Variant = r.systemContext.VariantChoice } + // Normalize platform to be OCI compatible (e.g., "aarch64" -> "arm64"). + options.OS, options.Architecture, options.Variant = NormalizePlatform(options.OS, options.Architecture, options.Variant) // First, check if we have an exact match in the storage. Maybe an ID // or a fully-qualified image name. @@ -404,9 +406,15 @@ func (r *Runtime) lookupImageInDigestsAndRepoTags(name string, options *LookupIm digest := digested.Digest() for _, image := range allImages { for _, d := range image.Digests() { - if d == digest { - return image, name, nil + if d != digest { + continue + } + // Also make sure that the matching image fits all criteria (e.g., manifest list). + if _, err := r.lookupImageInLocalStorage(name, image.ID(), options); err != nil { + return nil, "", err } + return image, name, nil + } } return nil, "", errors.Wrap(storage.ErrImageUnknown, name) @@ -489,13 +497,16 @@ func (r *Runtime) imageReferenceMatchesContext(ref types.ImageReference, options } if options.Architecture != "" && options.Architecture != data.Architecture { - return false, err + logrus.Debugf("architecture %q does not match architecture %q of image %s", options.Architecture, data.Architecture, ref) + return false, nil } if options.OS != "" && options.OS != data.Os { - return false, err + logrus.Debugf("OS %q does not match OS %q of image %s", options.OS, data.Os, ref) + return false, nil } if options.Variant != "" && options.Variant != data.Variant { - return false, err + logrus.Debugf("variant %q does not match variant %q of image %s", options.Variant, data.Variant, ref) + return false, nil } return true, nil @@ -551,16 +562,7 @@ func (r *Runtime) ListImages(ctx context.Context, names []string, options *ListI } } - var filters []filterFunc - if len(options.Filters) > 0 { - compiledFilters, err := r.compileImageFilters(ctx, options) - if err != nil { - return nil, err - } - filters = append(filters, compiledFilters...) - } - - return filterImages(images, filters) + return r.filterImages(ctx, images, options) } // RemoveImagesOptions allow for customizing image removal. diff --git a/vendor/github.com/containers/common/pkg/cgroups/cgroups.go b/vendor/github.com/containers/common/pkg/cgroups/cgroups.go index d0bcd8bfd..0bf275f38 100644 --- a/vendor/github.com/containers/common/pkg/cgroups/cgroups.go +++ b/vendor/github.com/containers/common/pkg/cgroups/cgroups.go @@ -365,6 +365,29 @@ func readFileAsUint64(path string) (uint64, error) { return ret, nil } +func readFileByKeyAsUint64(path, key string) (uint64, error) { + content, err := ioutil.ReadFile(path) + if err != nil { + return 0, err + } + for _, line := range strings.Split(string(content), "\n") { + fields := strings.SplitN(line, " ", 2) + if fields[0] == key { + v := cleanString(string(fields[1])) + if v == "max" { + return math.MaxUint64, nil + } + ret, err := strconv.ParseUint(v, 10, 64) + if err != nil { + return ret, errors.Wrapf(err, "parse %s from %s", v, path) + } + return ret, nil + } + } + + return 0, fmt.Errorf("no key named %s from %s", key, path) +} + // New creates a new cgroup control func New(path string, resources *spec.LinuxResources) (*CgroupControl, error) { cgroup2, err := IsCgroup2UnifiedMode() @@ -509,32 +532,6 @@ func (c *CgroupControl) Delete() error { return c.DeleteByPath(c.path) } -// rmDirRecursively delete recursively a cgroup directory. -// It differs from os.RemoveAll as it doesn't attempt to unlink files. -// On cgroupfs we are allowed only to rmdir empty directories. -func rmDirRecursively(path string) error { - if err := os.Remove(path); err == nil || os.IsNotExist(err) { - return nil - } - entries, err := ioutil.ReadDir(path) - if err != nil { - return err - } - for _, i := range entries { - if i.IsDir() { - if err := rmDirRecursively(filepath.Join(path, i.Name())); err != nil { - return err - } - } - } - if err := os.Remove(path); err != nil { - if !os.IsNotExist(err) { - return errors.Wrapf(err, "remove %s", path) - } - } - return nil -} - // DeleteByPathConn deletes the specified cgroup path using the specified // dbus connection if needed. func (c *CgroupControl) DeleteByPathConn(path string, conn *systemdDbus.Conn) error { diff --git a/vendor/github.com/containers/common/pkg/cgroups/cgroups_supported.go b/vendor/github.com/containers/common/pkg/cgroups/cgroups_supported.go index fe17db7f7..c1fe194b2 100644 --- a/vendor/github.com/containers/common/pkg/cgroups/cgroups_supported.go +++ b/vendor/github.com/containers/common/pkg/cgroups/cgroups_supported.go @@ -5,11 +5,13 @@ package cgroups import ( "bufio" "fmt" + "io/ioutil" "os" "path/filepath" "strings" "sync" "syscall" + "time" "github.com/pkg/errors" "golang.org/x/sys/unix" @@ -88,3 +90,40 @@ func UserOwnsCurrentSystemdCgroup() (bool, error) { } return true, nil } + +// rmDirRecursively delete recursively a cgroup directory. +// It differs from os.RemoveAll as it doesn't attempt to unlink files. +// On cgroupfs we are allowed only to rmdir empty directories. +func rmDirRecursively(path string) error { + if err := os.Remove(path); err == nil || os.IsNotExist(err) { + return nil + } + entries, err := ioutil.ReadDir(path) + if err != nil { + return err + } + for _, i := range entries { + if i.IsDir() { + if err := rmDirRecursively(filepath.Join(path, i.Name())); err != nil { + return err + } + } + } + + attempts := 0 + for { + err := os.Remove(path) + if err == nil || os.IsNotExist(err) { + return nil + } + if errors.Is(err, unix.EBUSY) { + // attempt up to 5 seconds if the cgroup is busy + if attempts < 500 { + time.Sleep(time.Millisecond * 10) + attempts++ + continue + } + } + return errors.Wrapf(err, "remove %s", path) + } +} diff --git a/vendor/github.com/containers/common/pkg/cgroups/cgroups_unsupported.go b/vendor/github.com/containers/common/pkg/cgroups/cgroups_unsupported.go index cd140fbf3..95d424170 100644 --- a/vendor/github.com/containers/common/pkg/cgroups/cgroups_unsupported.go +++ b/vendor/github.com/containers/common/pkg/cgroups/cgroups_unsupported.go @@ -2,6 +2,10 @@ package cgroups +import ( + "os" +) + // IsCgroup2UnifiedMode returns whether we are running in cgroup 2 cgroup2 mode. func IsCgroup2UnifiedMode() (bool, error) { return false, nil @@ -12,3 +16,7 @@ func IsCgroup2UnifiedMode() (bool, error) { func UserOwnsCurrentSystemdCgroup() (bool, error) { return false, nil } + +func rmDirRecursively(path string) error { + return os.RemoveAll(path) +} diff --git a/vendor/github.com/containers/common/pkg/cgroups/memory.go b/vendor/github.com/containers/common/pkg/cgroups/memory.go index b3991f7e3..10d65893c 100644 --- a/vendor/github.com/containers/common/pkg/cgroups/memory.go +++ b/vendor/github.com/containers/common/pkg/cgroups/memory.go @@ -7,8 +7,7 @@ import ( spec "github.com/opencontainers/runtime-spec/specs-go" ) -type memHandler struct { -} +type memHandler struct{} func getMemoryHandler() *memHandler { return &memHandler{} @@ -41,22 +40,23 @@ func (c *memHandler) Stat(ctr *CgroupControl, m *Metrics) error { usage := MemoryUsage{} var memoryRoot string - filenames := map[string]string{} + var limitFilename string if ctr.cgroup2 { memoryRoot = filepath.Join(cgroupRoot, ctr.path) - filenames["usage"] = "memory.current" - filenames["limit"] = "memory.max" + limitFilename = "memory.max" + if usage.Usage, err = readFileByKeyAsUint64(filepath.Join(memoryRoot, "memory.stat"), "anon"); err != nil { + return err + } } else { memoryRoot = ctr.getCgroupv1Path(Memory) - filenames["usage"] = "memory.usage_in_bytes" - filenames["limit"] = "memory.limit_in_bytes" + limitFilename = "memory.limit_in_bytes" + if usage.Usage, err = readFileAsUint64(filepath.Join(memoryRoot, "memory.usage_in_bytes")); err != nil { + return err + } } - usage.Usage, err = readFileAsUint64(filepath.Join(memoryRoot, filenames["usage"])) - if err != nil { - return err - } - usage.Limit, err = readFileAsUint64(filepath.Join(memoryRoot, filenames["limit"])) + + usage.Limit, err = readFileAsUint64(filepath.Join(memoryRoot, limitFilename)) if err != nil { return err } diff --git a/vendor/github.com/containers/common/pkg/cgroups/pids.go b/vendor/github.com/containers/common/pkg/cgroups/pids.go index b2bfebe4d..58cb32b3b 100644 --- a/vendor/github.com/containers/common/pkg/cgroups/pids.go +++ b/vendor/github.com/containers/common/pkg/cgroups/pids.go @@ -34,6 +34,9 @@ func (c *pidHandler) Apply(ctr *CgroupControl, res *spec.LinuxResources) error { // Create the cgroup func (c *pidHandler) Create(ctr *CgroupControl) (bool, error) { + if ctr.cgroup2 { + return false, nil + } return ctr.createCgroupDirectory(Pids) } diff --git a/vendor/github.com/containers/common/pkg/config/config.go b/vendor/github.com/containers/common/pkg/config/config.go index f419601e9..7f89b9252 100644 --- a/vendor/github.com/containers/common/pkg/config/config.go +++ b/vendor/github.com/containers/common/pkg/config/config.go @@ -48,6 +48,18 @@ const ( BoltDBStateStore RuntimeStateStore = iota ) +// ProxyEnv is a list of Proxy Environment variables +var ProxyEnv = []string{ + "http_proxy", + "https_proxy", + "ftp_proxy", + "no_proxy", + "HTTP_PROXY", + "HTTPS_PROXY", + "FTP_PROXY", + "NO_PROXY", +} + // Config contains configuration options for container tools type Config struct { // Containers specify settings that configure how containers will run ont the system @@ -897,8 +909,7 @@ func (c *Config) GetDefaultEnvEx(envHost, httpProxy bool) []string { if envHost { env = append(env, os.Environ()...) } else if httpProxy { - proxy := []string{"http_proxy", "https_proxy", "ftp_proxy", "no_proxy", "HTTP_PROXY", "HTTPS_PROXY", "FTP_PROXY", "NO_PROXY"} - for _, p := range proxy { + for _, p := range ProxyEnv { if val, ok := os.LookupEnv(p); ok { env = append(env, fmt.Sprintf("%s=%s", p, val)) } diff --git a/vendor/github.com/containers/common/pkg/config/default.go b/vendor/github.com/containers/common/pkg/config/default.go index cd7fea4a1..561158b12 100644 --- a/vendor/github.com/containers/common/pkg/config/default.go +++ b/vendor/github.com/containers/common/pkg/config/default.go @@ -11,6 +11,7 @@ import ( "github.com/containers/common/pkg/apparmor" "github.com/containers/common/pkg/cgroupv2" + "github.com/containers/common/pkg/util" "github.com/containers/storage/pkg/homedir" "github.com/containers/storage/pkg/unshare" "github.com/containers/storage/types" @@ -202,7 +203,6 @@ func DefaultConfig() (*Config, error) { UserNSSize: DefaultUserNSSize, }, Network: NetworkConfig{ - NetworkBackend: "cni", DefaultNetwork: "podman", DefaultSubnet: DefaultSubnet, NetworkConfigDir: cniConfig, @@ -371,7 +371,7 @@ func defaultTmpDir() (string, error) { return "/run/libpod", nil } - runtimeDir, err := getRuntimeDir() + runtimeDir, err := util.GetRuntimeDir() if err != nil { return "", err } diff --git a/vendor/github.com/containers/common/pkg/seccomp/errno_list.go b/vendor/github.com/containers/common/pkg/seccomp/errno_list.go index 55b92ecc8..a1009012d 100644 --- a/vendor/github.com/containers/common/pkg/seccomp/errno_list.go +++ b/vendor/github.com/containers/common/pkg/seccomp/errno_list.go @@ -1,3 +1,5 @@ +// +build linux,seccomp + package seccomp import ( diff --git a/vendor/github.com/containers/common/pkg/util/util.go b/vendor/github.com/containers/common/pkg/util/util.go new file mode 100644 index 000000000..98890a686 --- /dev/null +++ b/vendor/github.com/containers/common/pkg/util/util.go @@ -0,0 +1,24 @@ +package util + +import "regexp" + +// StringInSlice determines if a string is in a string slice, returns bool +func StringInSlice(s string, sl []string) bool { + for _, i := range sl { + if i == s { + return true + } + } + return false +} + +// StringMatchRegexSlice determines if a given string matches one of the given regexes, returns bool +func StringMatchRegexSlice(s string, re []string) bool { + for _, r := range re { + m, err := regexp.MatchString(r, s) + if err == nil && m { + return true + } + } + return false +} diff --git a/vendor/github.com/containers/common/pkg/config/util_supported.go b/vendor/github.com/containers/common/pkg/util/util_supported.go index 33e4a9e8f..422e28742 100644 --- a/vendor/github.com/containers/common/pkg/config/util_supported.go +++ b/vendor/github.com/containers/common/pkg/util/util_supported.go @@ -1,6 +1,6 @@ // +build linux darwin -package config +package util import ( "fmt" @@ -19,8 +19,8 @@ var ( rootlessRuntimeDir string ) -// getRuntimeDir returns the runtime directory -func getRuntimeDir() (string, error) { +// GetRuntimeDir returns the runtime directory +func GetRuntimeDir() (string, error) { var rootlessRuntimeDirError error rootlessRuntimeDirOnce.Do(func() { diff --git a/vendor/github.com/containers/common/pkg/config/util_windows.go b/vendor/github.com/containers/common/pkg/util/util_windows.go index 995301f5d..2add712f1 100644 --- a/vendor/github.com/containers/common/pkg/config/util_windows.go +++ b/vendor/github.com/containers/common/pkg/util/util_windows.go @@ -1,12 +1,12 @@ // +build windows -package config +package util import ( "github.com/pkg/errors" ) // getRuntimeDir returns the runtime directory -func getRuntimeDir() (string, error) { +func GetRuntimeDir() (string, error) { return "", errors.New("this function is not implemented for windows") } diff --git a/vendor/github.com/containers/image/v5/copy/copy.go b/vendor/github.com/containers/image/v5/copy/copy.go index 8b785668f..383215182 100644 --- a/vendor/github.com/containers/image/v5/copy/copy.go +++ b/vendor/github.com/containers/image/v5/copy/copy.go @@ -1073,20 +1073,15 @@ func (c *copier) newProgressPool() *mpb.Progress { return mpb.New(mpb.WithWidth(40), mpb.WithOutput(c.progressOutput)) } -// customPartialBlobCounter provides a decorator function for the partial blobs retrieval progress bar -func customPartialBlobCounter(filler interface{}, wcc ...decor.WC) decor.Decorator { - producer := func(filler interface{}) decor.DecorFunc { - return func(s decor.Statistics) string { - if s.Total == 0 { - pairFmt := "%.1f / %.1f (skipped: %.1f)" - return fmt.Sprintf(pairFmt, decor.SizeB1024(s.Current), decor.SizeB1024(s.Total), decor.SizeB1024(s.Refill)) - } - pairFmt := "%.1f / %.1f (skipped: %.1f = %.2f%%)" - percentage := 100.0 * float64(s.Refill) / float64(s.Total) - return fmt.Sprintf(pairFmt, decor.SizeB1024(s.Current), decor.SizeB1024(s.Total), decor.SizeB1024(s.Refill), percentage) - } - } - return decor.Any(producer(filler), wcc...) +// customPartialBlobDecorFunc implements mpb.DecorFunc for the partial blobs retrieval progress bar +func customPartialBlobDecorFunc(s decor.Statistics) string { + if s.Total == 0 { + pairFmt := "%.1f / %.1f (skipped: %.1f)" + return fmt.Sprintf(pairFmt, decor.SizeB1024(s.Current), decor.SizeB1024(s.Total), decor.SizeB1024(s.Refill)) + } + pairFmt := "%.1f / %.1f (skipped: %.1f = %.2f%%)" + percentage := 100.0 * float64(s.Refill) / float64(s.Total) + return fmt.Sprintf(pairFmt, decor.SizeB1024(s.Current), decor.SizeB1024(s.Total), decor.SizeB1024(s.Refill), percentage) } // createProgressBar creates a mpb.Bar in pool. Note that if the copier's reportWriter @@ -1111,7 +1106,6 @@ func (c *copier) createProgressBar(pool *mpb.Progress, partial bool, info types. // Use a normal progress bar when we know the size (i.e., size > 0). // Otherwise, use a spinner to indicate that something's happening. var bar *mpb.Bar - sstyle := mpb.SpinnerStyle(".", "..", "...", "....", "").PositionLeft() if info.Size > 0 { if partial { bar = pool.AddBar(info.Size, @@ -1120,7 +1114,7 @@ func (c *copier) createProgressBar(pool *mpb.Progress, partial bool, info types. decor.OnComplete(decor.Name(prefix), onComplete), ), mpb.AppendDecorators( - customPartialBlobCounter(sstyle.Build()), + decor.Any(customPartialBlobDecorFunc), ), ) } else { @@ -1135,8 +1129,8 @@ func (c *copier) createProgressBar(pool *mpb.Progress, partial bool, info types. ) } } else { - bar = pool.Add(0, - sstyle.Build(), + bar = pool.New(0, + mpb.SpinnerStyle(".", "..", "...", "....", "").PositionLeft(), mpb.BarFillerClearOnComplete(), mpb.PrependDecorators( decor.OnComplete(decor.Name(prefix), onComplete), diff --git a/vendor/github.com/containers/image/v5/docker/docker_client.go b/vendor/github.com/containers/image/v5/docker/docker_client.go index 3fe9a11d0..833323b42 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_client.go +++ b/vendor/github.com/containers/image/v5/docker/docker_client.go @@ -711,7 +711,7 @@ func (c *dockerClient) getBearerToken(ctx context.Context, challenge challenge, return nil, err } defer res.Body.Close() - if err := httpResponseToError(res, "Requesting bear token"); err != nil { + if err := httpResponseToError(res, "Requesting bearer token"); err != nil { return nil, err } tokenBlob, err := iolimits.ReadAtMost(res.Body, iolimits.MaxAuthTokenBodySize) diff --git a/vendor/github.com/containers/image/v5/pkg/docker/config/config.go b/vendor/github.com/containers/image/v5/pkg/docker/config/config.go index 76362a66e..8fa530549 100644 --- a/vendor/github.com/containers/image/v5/pkg/docker/config/config.go +++ b/vendor/github.com/containers/image/v5/pkg/docker/config/config.go @@ -190,14 +190,12 @@ func GetAllCredentials(sys *types.SystemContext) (map[string]types.DockerAuthCon for key := range allKeys { authConf, err := GetCredentials(sys, key) if err != nil { - if credentials.IsErrCredentialsNotFoundMessage(err.Error()) { - // Ignore if the credentials could not be found (anymore). - continue - } // Note: we rely on the logging in `GetCredentials`. return nil, err } - authConfigs[key] = authConf + if authConf != (types.DockerAuthConfig{}) { + authConfigs[key] = authConf + } } return authConfigs, nil @@ -285,7 +283,7 @@ func getCredentialsWithHomeDir(sys *types.SystemContext, key, homeDir string) (t return types.DockerAuthConfig{}, "", err } - if (authConfig.Username != "" && authConfig.Password != "") || authConfig.IdentityToken != "" { + if authConfig != (types.DockerAuthConfig{}) { return authConfig, path.path, nil } } diff --git a/vendor/github.com/vbauerster/mpb/v7/README.md b/vendor/github.com/vbauerster/mpb/v7/README.md index ee1b21231..413f9e1db 100644 --- a/vendor/github.com/vbauerster/mpb/v7/README.md +++ b/vendor/github.com/vbauerster/mpb/v7/README.md @@ -36,10 +36,10 @@ func main() { total := 100 name := "Single Bar:" - // adding a single bar, which will inherit container's width - bar := p.Add(int64(total), - // progress bar filler with customized style - mpb.NewBarFiller(mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟")), + // create a single bar, which will inherit container's width + bar := p.New(int64(total), + // BarFillerBuilder with custom style + mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟"), mpb.PrependDecorators( // display our name with one space on the right decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}), @@ -65,7 +65,7 @@ func main() { ```go var wg sync.WaitGroup - // passed &wg will be accounted at p.Wait() call + // passed wg will be accounted at p.Wait() call p := mpb.New(mpb.WithWaitGroup(&wg)) total, numBars := 100, 3 wg.Add(numBars) @@ -103,7 +103,7 @@ func main() { } }() } - // Waiting for passed &wg and for all bars to complete and flush + // wait for passed wg and for all bars to complete and flush p.Wait() ``` diff --git a/vendor/github.com/vbauerster/mpb/v7/bar.go b/vendor/github.com/vbauerster/mpb/v7/bar.go index d07dc165c..35644a411 100644 --- a/vendor/github.com/vbauerster/mpb/v7/bar.go +++ b/vendor/github.com/vbauerster/mpb/v7/bar.go @@ -165,12 +165,12 @@ func (b *Bar) TraverseDecorators(cb func(decor.Decorator)) { } // SetTotal sets total dynamically. -// If total is less than or equal to zero it takes progress' current value. +// If total is negative it takes progress' current value. func (b *Bar) SetTotal(total int64, triggerComplete bool) { select { case b.operateState <- func(s *bState) { s.triggerComplete = triggerComplete - if total <= 0 { + if total < 0 { s.total = s.current } else { s.total = total diff --git a/vendor/github.com/vbauerster/mpb/v7/bar_filler.go b/vendor/github.com/vbauerster/mpb/v7/bar_filler.go index a69087c47..81177fc2e 100644 --- a/vendor/github.com/vbauerster/mpb/v7/bar_filler.go +++ b/vendor/github.com/vbauerster/mpb/v7/bar_filler.go @@ -9,31 +9,42 @@ import ( // BarFiller interface. // Bar (without decorators) renders itself by calling BarFiller's Fill method. // -// reqWidth is requested width, set by `func WithWidth(int) ContainerOption`. +// reqWidth is requested width set by `func WithWidth(int) ContainerOption`. // If not set, it defaults to terminal width. // -// Default implementations can be obtained via: -// -// func NewBarFiller(BarStyle()) BarFiller -// func NewBarFiller(SpinnerStyle()) BarFiller -// type BarFiller interface { Fill(w io.Writer, reqWidth int, stat decor.Statistics) } // BarFillerBuilder interface. +// Default implementations are: +// +// BarStyle() +// SpinnerStyle() +// NopStyle() +// type BarFillerBuilder interface { Build() BarFiller } -// BarFillerFunc is function type adapter to convert function into BarFiller. +// BarFillerFunc is function type adapter to convert compatible function +// into BarFiller interface. type BarFillerFunc func(w io.Writer, reqWidth int, stat decor.Statistics) func (f BarFillerFunc) Fill(w io.Writer, reqWidth int, stat decor.Statistics) { f(w, reqWidth, stat) } +// BarFillerBuilderFunc is function type adapter to convert compatible +// function into BarFillerBuilder interface. +type BarFillerBuilderFunc func() BarFiller + +func (f BarFillerBuilderFunc) Build() BarFiller { + return f() +} + // NewBarFiller constructs a BarFiller from provided BarFillerBuilder. +// Deprecated. Prefer using `*Progress.New(...)` directly. func NewBarFiller(b BarFillerBuilder) BarFiller { return b.Build() } diff --git a/vendor/github.com/vbauerster/mpb/v7/bar_filler_nop.go b/vendor/github.com/vbauerster/mpb/v7/bar_filler_nop.go new file mode 100644 index 000000000..1a7086fec --- /dev/null +++ b/vendor/github.com/vbauerster/mpb/v7/bar_filler_nop.go @@ -0,0 +1,14 @@ +package mpb + +import ( + "io" + + "github.com/vbauerster/mpb/v7/decor" +) + +// NopStyle provides BarFillerBuilder which builds NOP BarFiller. +func NopStyle() BarFillerBuilder { + return BarFillerBuilderFunc(func() BarFiller { + return BarFillerFunc(func(io.Writer, int, decor.Statistics) {}) + }) +} diff --git a/vendor/github.com/vbauerster/mpb/v7/bar_option.go b/vendor/github.com/vbauerster/mpb/v7/bar_option.go index 46b7de0bf..660e7c487 100644 --- a/vendor/github.com/vbauerster/mpb/v7/bar_option.go +++ b/vendor/github.com/vbauerster/mpb/v7/bar_option.go @@ -5,12 +5,20 @@ import ( "io" "github.com/vbauerster/mpb/v7/decor" - "github.com/vbauerster/mpb/v7/internal" ) // BarOption is a func option to alter default behavior of a bar. type BarOption func(*bState) +func skipNil(decorators []decor.Decorator) (filtered []decor.Decorator) { + for _, d := range decorators { + if d != nil { + filtered = append(filtered, d) + } + } + return +} + func (s *bState) addDecorators(dest *[]decor.Decorator, decorators ...decor.Decorator) { type mergeWrapper interface { MergeUnwrap() []decor.Decorator @@ -26,14 +34,14 @@ func (s *bState) addDecorators(dest *[]decor.Decorator, decorators ...decor.Deco // AppendDecorators let you inject decorators to the bar's right side. func AppendDecorators(decorators ...decor.Decorator) BarOption { return func(s *bState) { - s.addDecorators(&s.aDecorators, decorators...) + s.addDecorators(&s.aDecorators, skipNil(decorators)...) } } // PrependDecorators let you inject decorators to the bar's left side. func PrependDecorators(decorators ...decor.Decorator) BarOption { return func(s *bState) { - s.addDecorators(&s.pDecorators, decorators...) + s.addDecorators(&s.pDecorators, skipNil(decorators)...) } } @@ -138,9 +146,12 @@ func BarNoPop() BarOption { } } -// BarOptional will invoke provided option only when pick is true. -func BarOptional(option BarOption, pick bool) BarOption { - return BarOptOn(option, internal.Predicate(pick)) +// BarOptional will invoke provided option only when cond is true. +func BarOptional(option BarOption, cond bool) BarOption { + if cond { + return option + } + return nil } // BarOptOn will invoke provided option only when higher order predicate diff --git a/vendor/github.com/vbauerster/mpb/v7/container_option.go b/vendor/github.com/vbauerster/mpb/v7/container_option.go index a858c3c51..e523a1759 100644 --- a/vendor/github.com/vbauerster/mpb/v7/container_option.go +++ b/vendor/github.com/vbauerster/mpb/v7/container_option.go @@ -5,8 +5,6 @@ import ( "io/ioutil" "sync" "time" - - "github.com/vbauerster/mpb/v7/internal" ) // ContainerOption is a func option to alter default behavior of a bar @@ -101,9 +99,12 @@ func PopCompletedMode() ContainerOption { } } -// ContainerOptional will invoke provided option only when pick is true. -func ContainerOptional(option ContainerOption, pick bool) ContainerOption { - return ContainerOptOn(option, internal.Predicate(pick)) +// ContainerOptional will invoke provided option only when cond is true. +func ContainerOptional(option ContainerOption, cond bool) ContainerOption { + if cond { + return option + } + return nil } // ContainerOptOn will invoke provided option only when higher order diff --git a/vendor/github.com/vbauerster/mpb/v7/decor/merge.go b/vendor/github.com/vbauerster/mpb/v7/decor/merge.go index e41406a64..847671155 100644 --- a/vendor/github.com/vbauerster/mpb/v7/decor/merge.go +++ b/vendor/github.com/vbauerster/mpb/v7/decor/merge.go @@ -17,6 +17,9 @@ import ( // +----+--------+---------+--------+ // func Merge(decorator Decorator, placeholders ...WC) Decorator { + if decorator == nil { + return nil + } if _, ok := decorator.Sync(); !ok || len(placeholders) == 0 { return decorator } diff --git a/vendor/github.com/vbauerster/mpb/v7/decor/on_abort.go b/vendor/github.com/vbauerster/mpb/v7/decor/on_abort.go index 7ece120a2..10ff67009 100644 --- a/vendor/github.com/vbauerster/mpb/v7/decor/on_abort.go +++ b/vendor/github.com/vbauerster/mpb/v7/decor/on_abort.go @@ -9,6 +9,9 @@ package decor // `message` message to display on abort event // func OnAbort(decorator Decorator, message string) Decorator { + if decorator == nil { + return nil + } d := &onAbortWrapper{ Decorator: decorator, msg: message, diff --git a/vendor/github.com/vbauerster/mpb/v7/decor/on_complete.go b/vendor/github.com/vbauerster/mpb/v7/decor/on_complete.go index c7c9e4d31..2ada2b31b 100644 --- a/vendor/github.com/vbauerster/mpb/v7/decor/on_complete.go +++ b/vendor/github.com/vbauerster/mpb/v7/decor/on_complete.go @@ -8,6 +8,9 @@ package decor // `message` message to display on complete event // func OnComplete(decorator Decorator, message string) Decorator { + if decorator == nil { + return nil + } d := &onCompleteWrapper{ Decorator: decorator, msg: message, diff --git a/vendor/github.com/vbauerster/mpb/v7/decor/on_condition.go b/vendor/github.com/vbauerster/mpb/v7/decor/on_condition.go new file mode 100644 index 000000000..a9db0653a --- /dev/null +++ b/vendor/github.com/vbauerster/mpb/v7/decor/on_condition.go @@ -0,0 +1,27 @@ +package decor + +// OnPredicate returns decorator if predicate evaluates to true. +// +// `decorator` Decorator +// +// `predicate` func() bool +// +func OnPredicate(decorator Decorator, predicate func() bool) Decorator { + if predicate() { + return decorator + } + return nil +} + +// OnCondition returns decorator if condition is true. +// +// `decorator` Decorator +// +// `cond` bool +// +func OnCondition(decorator Decorator, cond bool) Decorator { + if cond { + return decorator + } + return nil +} diff --git a/vendor/github.com/vbauerster/mpb/v7/go.mod b/vendor/github.com/vbauerster/mpb/v7/go.mod index 32008c66c..19a6f8044 100644 --- a/vendor/github.com/vbauerster/mpb/v7/go.mod +++ b/vendor/github.com/vbauerster/mpb/v7/go.mod @@ -4,7 +4,7 @@ require ( github.com/VividCortex/ewma v1.2.0 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/mattn/go-runewidth v0.0.13 - golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 + golang.org/x/sys v0.0.0-20211214234402-4825e8c3871d ) go 1.14 diff --git a/vendor/github.com/vbauerster/mpb/v7/go.sum b/vendor/github.com/vbauerster/mpb/v7/go.sum index 6fed396bb..b4388d9ea 100644 --- a/vendor/github.com/vbauerster/mpb/v7/go.sum +++ b/vendor/github.com/vbauerster/mpb/v7/go.sum @@ -6,5 +6,5 @@ github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211214234402-4825e8c3871d h1:1oIt9o40TWWI9FUaveVpUvBe13FNqBNVXy3ue2fcfkw= +golang.org/x/sys v0.0.0-20211214234402-4825e8c3871d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/vendor/github.com/vbauerster/mpb/v7/internal/predicate.go b/vendor/github.com/vbauerster/mpb/v7/internal/predicate.go deleted file mode 100644 index 1e4dd24d9..000000000 --- a/vendor/github.com/vbauerster/mpb/v7/internal/predicate.go +++ /dev/null @@ -1,6 +0,0 @@ -package internal - -// Predicate helper for internal use. -func Predicate(pick bool) func() bool { - return func() bool { return pick } -} diff --git a/vendor/github.com/vbauerster/mpb/v7/progress.go b/vendor/github.com/vbauerster/mpb/v7/progress.go index c60c65694..46485f719 100644 --- a/vendor/github.com/vbauerster/mpb/v7/progress.go +++ b/vendor/github.com/vbauerster/mpb/v7/progress.go @@ -99,17 +99,19 @@ func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress { return p } -// AddBar creates a bar with default bar filler. Different filler can -// be chosen and applied via `*Progress.Add(...) *Bar` method. +// AddBar creates a bar with default bar filler. func (p *Progress) AddBar(total int64, options ...BarOption) *Bar { - return p.Add(total, NewBarFiller(BarStyle()), options...) + return p.New(total, BarStyle(), options...) } -// AddSpinner creates a bar with default spinner filler. Different -// filler can be chosen and applied via `*Progress.Add(...) *Bar` -// method. +// AddSpinner creates a bar with default spinner filler. func (p *Progress) AddSpinner(total int64, options ...BarOption) *Bar { - return p.Add(total, NewBarFiller(SpinnerStyle()), options...) + return p.New(total, SpinnerStyle(), options...) +} + +// New creates a bar with provided BarFillerBuilder. +func (p *Progress) New(total int64, builder BarFillerBuilder, options ...BarOption) *Bar { + return p.Add(total, builder.Build(), options...) } // Add creates a bar which renders itself by provided filler. @@ -117,7 +119,7 @@ func (p *Progress) AddSpinner(total int64, options ...BarOption) *Bar { // Panics if *Progress instance is done, i.e. called after *Progress.Wait(). func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) *Bar { if filler == nil { - filler = BarFillerFunc(func(io.Writer, int, decor.Statistics) {}) + filler = NopStyle().Build() } p.bwg.Add(1) result := make(chan *Bar) diff --git a/vendor/github.com/vbauerster/mpb/v7/proxyreader.go b/vendor/github.com/vbauerster/mpb/v7/proxyreader.go index 2f20053bd..a16f5ec8a 100644 --- a/vendor/github.com/vbauerster/mpb/v7/proxyreader.go +++ b/vendor/github.com/vbauerster/mpb/v7/proxyreader.go @@ -15,7 +15,7 @@ func (x *proxyReader) Read(p []byte) (int, error) { n, err := x.ReadCloser.Read(p) x.bar.IncrBy(n) if err == io.EOF { - go x.bar.SetTotal(0, true) + go x.bar.SetTotal(-1, true) } return n, err } @@ -30,7 +30,7 @@ func (x *proxyWriterTo) WriteTo(w io.Writer) (int64, error) { n, err := x.wt.WriteTo(w) x.bar.IncrInt64(n) if err == io.EOF { - go x.bar.SetTotal(0, true) + go x.bar.SetTotal(-1, true) } return n, err } diff --git a/vendor/golang.org/x/sys/plan9/syscall_plan9.go b/vendor/golang.org/x/sys/plan9/syscall_plan9.go index 84e147148..723b1f400 100644 --- a/vendor/golang.org/x/sys/plan9/syscall_plan9.go +++ b/vendor/golang.org/x/sys/plan9/syscall_plan9.go @@ -132,8 +132,10 @@ func Pipe(p []int) (err error) { } var pp [2]int32 err = pipe(&pp) - p[0] = int(pp[0]) - p[1] = int(pp[1]) + if err == nil { + p[0] = int(pp[0]) + p[1] = int(pp[1]) + } return } diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh index 4945739ea..a47b035f9 100644 --- a/vendor/golang.org/x/sys/unix/mkerrors.sh +++ b/vendor/golang.org/x/sys/unix/mkerrors.sh @@ -261,6 +261,7 @@ struct ltchars { #include <linux/vm_sockets.h> #include <linux/wait.h> #include <linux/watchdog.h> +#include <linux/wireguard.h> #include <mtd/ubi-user.h> #include <mtd/mtd-user.h> @@ -606,6 +607,7 @@ ccflags="$@" $2 ~ /^MTD/ || $2 ~ /^OTP/ || $2 ~ /^MEM/ || + $2 ~ /^WG/ || $2 ~ /^BLK[A-Z]*(GET$|SET$|BUF$|PART$|SIZE)/ {printf("\t%s = C.%s\n", $2, $2)} $2 ~ /^__WCOREFLAG$/ {next} $2 ~ /^__W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", substr($2,3), $2)} diff --git a/vendor/golang.org/x/sys/unix/syscall_aix.go b/vendor/golang.org/x/sys/unix/syscall_aix.go index 6192750ce..4f55c8d99 100644 --- a/vendor/golang.org/x/sys/unix/syscall_aix.go +++ b/vendor/golang.org/x/sys/unix/syscall_aix.go @@ -519,8 +519,10 @@ func Pipe(p []int) (err error) { } var pp [2]_C_int err = pipe(&pp) - p[0] = int(pp[0]) - p[1] = int(pp[1]) + if err == nil { + p[0] = int(pp[0]) + p[1] = int(pp[1]) + } return } diff --git a/vendor/golang.org/x/sys/unix/syscall_darwin.go b/vendor/golang.org/x/sys/unix/syscall_darwin.go index 8826f4143..0eaab9131 100644 --- a/vendor/golang.org/x/sys/unix/syscall_darwin.go +++ b/vendor/golang.org/x/sys/unix/syscall_darwin.go @@ -159,8 +159,10 @@ func Pipe(p []int) (err error) { } var x [2]int32 err = pipe(&x) - p[0] = int(x[0]) - p[1] = int(x[1]) + if err == nil { + p[0] = int(x[0]) + p[1] = int(x[1]) + } return } diff --git a/vendor/golang.org/x/sys/unix/syscall_dragonfly.go b/vendor/golang.org/x/sys/unix/syscall_dragonfly.go index 5af108a50..2e37c3167 100644 --- a/vendor/golang.org/x/sys/unix/syscall_dragonfly.go +++ b/vendor/golang.org/x/sys/unix/syscall_dragonfly.go @@ -101,7 +101,10 @@ func Pipe(p []int) (err error) { if len(p) != 2 { return EINVAL } - p[0], p[1], err = pipe() + r, w, err := pipe() + if err == nil { + p[0], p[1] = r, w + } return } @@ -114,7 +117,10 @@ func Pipe2(p []int, flags int) (err error) { var pp [2]_C_int // pipe2 on dragonfly takes an fds array as an argument, but still // returns the file descriptors. - p[0], p[1], err = pipe2(&pp, flags) + r, w, err := pipe2(&pp, flags) + if err == nil { + p[0], p[1] = r, w + } return err } diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd.go b/vendor/golang.org/x/sys/unix/syscall_freebsd.go index 18c392cf3..2f650ae66 100644 --- a/vendor/golang.org/x/sys/unix/syscall_freebsd.go +++ b/vendor/golang.org/x/sys/unix/syscall_freebsd.go @@ -110,8 +110,10 @@ func Pipe2(p []int, flags int) error { } var pp [2]_C_int err := pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) + if err == nil { + p[0] = int(pp[0]) + p[1] = int(pp[1]) + } return err } diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go index 4bc5baf77..f432b0684 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -131,8 +131,10 @@ func Pipe2(p []int, flags int) error { } var pp [2]_C_int err := pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) + if err == nil { + p[0] = int(pp[0]) + p[1] = int(pp[1]) + } return err } diff --git a/vendor/golang.org/x/sys/unix/syscall_netbsd.go b/vendor/golang.org/x/sys/unix/syscall_netbsd.go index 853d5f0f4..696fed496 100644 --- a/vendor/golang.org/x/sys/unix/syscall_netbsd.go +++ b/vendor/golang.org/x/sys/unix/syscall_netbsd.go @@ -110,14 +110,8 @@ func direntNamlen(buf []byte) (uint64, bool) { return readInt(buf, unsafe.Offsetof(Dirent{}.Namlen), unsafe.Sizeof(Dirent{}.Namlen)) } -//sysnb pipe() (fd1 int, fd2 int, err error) - func Pipe(p []int) (err error) { - if len(p) != 2 { - return EINVAL - } - p[0], p[1], err = pipe() - return + return Pipe2(p, 0) } //sysnb pipe2(p *[2]_C_int, flags int) (err error) @@ -128,8 +122,10 @@ func Pipe2(p []int, flags int) error { } var pp [2]_C_int err := pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) + if err == nil { + p[0] = int(pp[0]) + p[1] = int(pp[1]) + } return err } diff --git a/vendor/golang.org/x/sys/unix/syscall_openbsd.go b/vendor/golang.org/x/sys/unix/syscall_openbsd.go index 22b550385..11b1d419d 100644 --- a/vendor/golang.org/x/sys/unix/syscall_openbsd.go +++ b/vendor/golang.org/x/sys/unix/syscall_openbsd.go @@ -87,8 +87,10 @@ func Pipe2(p []int, flags int) error { } var pp [2]_C_int err := pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) + if err == nil { + p[0] = int(pp[0]) + p[1] = int(pp[1]) + } return err } diff --git a/vendor/golang.org/x/sys/unix/syscall_solaris.go b/vendor/golang.org/x/sys/unix/syscall_solaris.go index 8b88ac213..5c813921e 100644 --- a/vendor/golang.org/x/sys/unix/syscall_solaris.go +++ b/vendor/golang.org/x/sys/unix/syscall_solaris.go @@ -66,8 +66,10 @@ func Pipe(p []int) (err error) { if n != 0 { return err } - p[0] = int(pp[0]) - p[1] = int(pp[1]) + if err == nil { + p[0] = int(pp[0]) + p[1] = int(pp[1]) + } return nil } @@ -79,8 +81,10 @@ func Pipe2(p []int, flags int) error { } var pp [2]_C_int err := pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) + if err == nil { + p[0] = int(pp[0]) + p[1] = int(pp[1]) + } return err } diff --git a/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go b/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go index 5fb76a146..f8616f454 100644 --- a/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go +++ b/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go @@ -579,8 +579,10 @@ func Pipe(p []int) (err error) { } var pp [2]_C_int err = pipe(&pp) - p[0] = int(pp[0]) - p[1] = int(pp[1]) + if err == nil { + p[0] = int(pp[0]) + p[1] = int(pp[1]) + } return } diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux.go b/vendor/golang.org/x/sys/unix/zerrors_linux.go index d175aae89..bcc45d108 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux.go @@ -2826,6 +2826,13 @@ const ( WDIOS_TEMPPANIC = 0x4 WDIOS_UNKNOWN = -0x1 WEXITED = 0x4 + WGALLOWEDIP_A_MAX = 0x3 + WGDEVICE_A_MAX = 0x8 + WGPEER_A_MAX = 0xa + WG_CMD_MAX = 0x1 + WG_GENL_NAME = "wireguard" + WG_GENL_VERSION = 0x1 + WG_KEY_LEN = 0x20 WIN_ACKMEDIACHANGE = 0xdb WIN_CHECKPOWERMODE1 = 0xe5 WIN_CHECKPOWERMODE2 = 0x98 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go index 4726ab30a..51d0c0742 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go @@ -351,18 +351,6 @@ func Munlockall() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe() (fd1 int, fd2 int, err error) { - r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0) - fd1 = int(r0) - fd2 = int(r1) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func pipe2(p *[2]_C_int, flags int) (err error) { _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go index fe71456db..df2efb6db 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go @@ -351,18 +351,6 @@ func Munlockall() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe() (fd1 int, fd2 int, err error) { - r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0) - fd1 = int(r0) - fd2 = int(r1) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func pipe2(p *[2]_C_int, flags int) (err error) { _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go index 0b5b2f014..c8536c2c9 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go @@ -351,18 +351,6 @@ func Munlockall() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe() (fd1 int, fd2 int, err error) { - r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0) - fd1 = int(r0) - fd2 = int(r1) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func pipe2(p *[2]_C_int, flags int) (err error) { _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go index bfca28648..8b981bfc2 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go @@ -351,18 +351,6 @@ func Munlockall() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe() (fd1 int, fd2 int, err error) { - r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0) - fd1 = int(r0) - fd2 = int(r1) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func pipe2(p *[2]_C_int, flags int) (err error) { _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux.go b/vendor/golang.org/x/sys/unix/ztypes_linux.go index 37b521436..e76b7f6f9 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux.go @@ -867,6 +867,7 @@ const ( CTRL_CMD_NEWMCAST_GRP = 0x7 CTRL_CMD_DELMCAST_GRP = 0x8 CTRL_CMD_GETMCAST_GRP = 0x9 + CTRL_CMD_GETPOLICY = 0xa CTRL_ATTR_UNSPEC = 0x0 CTRL_ATTR_FAMILY_ID = 0x1 CTRL_ATTR_FAMILY_NAME = 0x2 @@ -875,12 +876,19 @@ const ( CTRL_ATTR_MAXATTR = 0x5 CTRL_ATTR_OPS = 0x6 CTRL_ATTR_MCAST_GROUPS = 0x7 + CTRL_ATTR_POLICY = 0x8 + CTRL_ATTR_OP_POLICY = 0x9 + CTRL_ATTR_OP = 0xa CTRL_ATTR_OP_UNSPEC = 0x0 CTRL_ATTR_OP_ID = 0x1 CTRL_ATTR_OP_FLAGS = 0x2 CTRL_ATTR_MCAST_GRP_UNSPEC = 0x0 CTRL_ATTR_MCAST_GRP_NAME = 0x1 CTRL_ATTR_MCAST_GRP_ID = 0x2 + CTRL_ATTR_POLICY_UNSPEC = 0x0 + CTRL_ATTR_POLICY_DO = 0x1 + CTRL_ATTR_POLICY_DUMP = 0x2 + CTRL_ATTR_POLICY_DUMP_MAX = 0x2 ) const ( @@ -3968,3 +3976,36 @@ type MountAttr struct { Propagation uint64 Userns_fd uint64 } + +const ( + WG_CMD_GET_DEVICE = 0x0 + WG_CMD_SET_DEVICE = 0x1 + WGDEVICE_F_REPLACE_PEERS = 0x1 + WGDEVICE_A_UNSPEC = 0x0 + WGDEVICE_A_IFINDEX = 0x1 + WGDEVICE_A_IFNAME = 0x2 + WGDEVICE_A_PRIVATE_KEY = 0x3 + WGDEVICE_A_PUBLIC_KEY = 0x4 + WGDEVICE_A_FLAGS = 0x5 + WGDEVICE_A_LISTEN_PORT = 0x6 + WGDEVICE_A_FWMARK = 0x7 + WGDEVICE_A_PEERS = 0x8 + WGPEER_F_REMOVE_ME = 0x1 + WGPEER_F_REPLACE_ALLOWEDIPS = 0x2 + WGPEER_F_UPDATE_ONLY = 0x4 + WGPEER_A_UNSPEC = 0x0 + WGPEER_A_PUBLIC_KEY = 0x1 + WGPEER_A_PRESHARED_KEY = 0x2 + WGPEER_A_FLAGS = 0x3 + WGPEER_A_ENDPOINT = 0x4 + WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL = 0x5 + WGPEER_A_LAST_HANDSHAKE_TIME = 0x6 + WGPEER_A_RX_BYTES = 0x7 + WGPEER_A_TX_BYTES = 0x8 + WGPEER_A_ALLOWEDIPS = 0x9 + WGPEER_A_PROTOCOL_VERSION = 0xa + WGALLOWEDIP_A_UNSPEC = 0x0 + WGALLOWEDIP_A_FAMILY = 0x1 + WGALLOWEDIP_A_IPADDR = 0x2 + WGALLOWEDIP_A_CIDR_MASK = 0x3 +) diff --git a/vendor/golang.org/x/sys/windows/exec_windows.go b/vendor/golang.org/x/sys/windows/exec_windows.go index 7a11e83b7..855698bb2 100644 --- a/vendor/golang.org/x/sys/windows/exec_windows.go +++ b/vendor/golang.org/x/sys/windows/exec_windows.go @@ -9,8 +9,6 @@ package windows import ( errorspkg "errors" "unsafe" - - "golang.org/x/sys/internal/unsafeheader" ) // EscapeArg rewrites command line argument s as prescribed @@ -147,8 +145,12 @@ func NewProcThreadAttributeList(maxAttrCount uint32) (*ProcThreadAttributeListCo } return nil, err } + alloc, err := LocalAlloc(LMEM_FIXED, uint32(size)) + if err != nil { + return nil, err + } // size is guaranteed to be ≥1 by InitializeProcThreadAttributeList. - al := &ProcThreadAttributeListContainer{data: (*ProcThreadAttributeList)(unsafe.Pointer(&make([]byte, size)[0]))} + al := &ProcThreadAttributeListContainer{data: (*ProcThreadAttributeList)(unsafe.Pointer(alloc))} err = initializeProcThreadAttributeList(al.data, maxAttrCount, 0, &size) if err != nil { return nil, err @@ -157,36 +159,17 @@ func NewProcThreadAttributeList(maxAttrCount uint32) (*ProcThreadAttributeListCo } // Update modifies the ProcThreadAttributeList using UpdateProcThreadAttribute. -// Note that the value passed to this function will be copied into memory -// allocated by LocalAlloc, the contents of which should not contain any -// Go-managed pointers, even if the passed value itself is a Go-managed -// pointer. func (al *ProcThreadAttributeListContainer) Update(attribute uintptr, value unsafe.Pointer, size uintptr) error { - alloc, err := LocalAlloc(LMEM_FIXED, uint32(size)) - if err != nil { - return err - } - var src, dst []byte - hdr := (*unsafeheader.Slice)(unsafe.Pointer(&src)) - hdr.Data = value - hdr.Cap = int(size) - hdr.Len = int(size) - hdr = (*unsafeheader.Slice)(unsafe.Pointer(&dst)) - hdr.Data = unsafe.Pointer(alloc) - hdr.Cap = int(size) - hdr.Len = int(size) - copy(dst, src) - al.heapAllocations = append(al.heapAllocations, alloc) - return updateProcThreadAttribute(al.data, 0, attribute, unsafe.Pointer(alloc), size, nil, nil) + al.pointers = append(al.pointers, value) + return updateProcThreadAttribute(al.data, 0, attribute, value, size, nil, nil) } // Delete frees ProcThreadAttributeList's resources. func (al *ProcThreadAttributeListContainer) Delete() { deleteProcThreadAttributeList(al.data) - for i := range al.heapAllocations { - LocalFree(Handle(al.heapAllocations[i])) - } - al.heapAllocations = nil + LocalFree(Handle(unsafe.Pointer(al.data))) + al.data = nil + al.pointers = nil } // List returns the actual ProcThreadAttributeList to be passed to StartupInfoEx. diff --git a/vendor/golang.org/x/sys/windows/types_windows.go b/vendor/golang.org/x/sys/windows/types_windows.go index 73087bf5e..655447e8f 100644 --- a/vendor/golang.org/x/sys/windows/types_windows.go +++ b/vendor/golang.org/x/sys/windows/types_windows.go @@ -938,8 +938,8 @@ type StartupInfoEx struct { type ProcThreadAttributeList struct{} type ProcThreadAttributeListContainer struct { - data *ProcThreadAttributeList - heapAllocations []uintptr + data *ProcThreadAttributeList + pointers []unsafe.Pointer } type ProcessInformation struct { diff --git a/vendor/modules.txt b/vendor/modules.txt index 68f9e5a99..54d49db42 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,7 +1,7 @@ # github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 github.com/Azure/go-ansiterm github.com/Azure/go-ansiterm/winterm -# github.com/BurntSushi/toml v0.4.1 +# github.com/BurntSushi/toml v1.0.0 ## explicit github.com/BurntSushi/toml github.com/BurntSushi/toml/internal @@ -63,7 +63,7 @@ github.com/container-orchestrated-devices/container-device-interface/pkg github.com/container-orchestrated-devices/container-device-interface/specs-go # github.com/containerd/cgroups v1.0.1 github.com/containerd/cgroups/stats/v1 -# github.com/containerd/containerd v1.5.8 +# github.com/containerd/containerd v1.5.9 github.com/containerd/containerd/errdefs github.com/containerd/containerd/log github.com/containerd/containerd/pkg/userns @@ -106,7 +106,7 @@ github.com/containers/buildah/pkg/rusage github.com/containers/buildah/pkg/sshagent github.com/containers/buildah/pkg/util github.com/containers/buildah/util -# github.com/containers/common v0.46.1-0.20211209220542-24f363480347 +# github.com/containers/common v0.46.1-0.20220112112017-31e8cc4aeeab ## explicit github.com/containers/common/libimage github.com/containers/common/libimage/manifests @@ -139,11 +139,12 @@ github.com/containers/common/pkg/supplemented github.com/containers/common/pkg/sysinfo github.com/containers/common/pkg/timetype github.com/containers/common/pkg/umask +github.com/containers/common/pkg/util github.com/containers/common/version # github.com/containers/conmon v2.0.20+incompatible ## explicit github.com/containers/conmon/runner/config -# github.com/containers/image/v5 v5.17.1-0.20211207161909-6f3c8453e1a7 +# github.com/containers/image/v5 v5.17.1-0.20220106205022-73f80d60f0e1 ## explicit github.com/containers/image/v5/copy github.com/containers/image/v5/directory @@ -546,7 +547,7 @@ github.com/onsi/gomega/types # github.com/opencontainers/go-digest v1.0.0 ## explicit github.com/opencontainers/go-digest -# github.com/opencontainers/image-spec v1.0.2-0.20211123152302-43a7dee1ec31 +# github.com/opencontainers/image-spec v1.0.3-0.20211202193544-a5463b7f9c84 ## explicit github.com/opencontainers/image-spec/specs-go github.com/opencontainers/image-spec/specs-go/v1 @@ -668,7 +669,7 @@ github.com/vbauerster/mpb/v6 github.com/vbauerster/mpb/v6/cwriter github.com/vbauerster/mpb/v6/decor github.com/vbauerster/mpb/v6/internal -# github.com/vbauerster/mpb/v7 v7.2.0 +# github.com/vbauerster/mpb/v7 v7.3.0 github.com/vbauerster/mpb/v7 github.com/vbauerster/mpb/v7/cwriter github.com/vbauerster/mpb/v7/decor @@ -736,7 +737,7 @@ golang.org/x/net/trace ## explicit golang.org/x/sync/errgroup golang.org/x/sync/semaphore -# golang.org/x/sys v0.0.0-20211205182925-97ca703d548d +# golang.org/x/sys v0.0.0-20211214234402-4825e8c3871d ## explicit golang.org/x/sys/cpu golang.org/x/sys/execabs |