diff options
171 files changed, 7766 insertions, 1888 deletions
@@ -226,7 +226,7 @@ bin/podman.cross.%: .gopathok # Update nix/nixpkgs.json its latest stable commit .PHONY: nixpkgs nixpkgs: - @nix run -f channel:nixos-20.03 nix-prefetch-git -c nix-prefetch-git \ + @nix run -f channel:nixos-20.09 nix-prefetch-git -c nix-prefetch-git \ --no-deepClone https://github.com/nixos/nixpkgs > nix/nixpkgs.json # Build statically linked binary diff --git a/cmd/podman/containers/cp.go b/cmd/podman/containers/cp.go index 9b0a01a2f..e0161824f 100644 --- a/cmd/podman/containers/cp.go +++ b/cmd/podman/containers/cp.go @@ -1,19 +1,34 @@ package containers import ( + "io" + "io/ioutil" + "os" + "os/user" + "path/filepath" + "strconv" + "strings" + + buildahCopiah "github.com/containers/buildah/copier" "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" + "github.com/containers/podman/v2/pkg/copy" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/errorhandling" + "github.com/containers/storage/pkg/archive" + "github.com/containers/storage/pkg/idtools" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) var ( - cpDescription = `Command copies the contents of SRC_PATH to the DEST_PATH. + cpDescription = `Copy the contents of SRC_PATH to the DEST_PATH. - You can copy from the container's file system to the local machine or the reverse, from the local filesystem to the container. If "-" is specified for either the SRC_PATH or DEST_PATH, you can also stream a tar archive from STDIN or to STDOUT. The CONTAINER can be a running or stopped container. The SRC_PATH or DEST_PATH can be a file or directory. + You can copy from the container's file system to the local machine or the reverse, from the local filesystem to the container. If "-" is specified for either the SRC_PATH or DEST_PATH, you can also stream a tar archive from STDIN or to STDOUT. The CONTAINER can be a running or stopped container. The SRC_PATH or DEST_PATH can be a file or a directory. ` cpCommand = &cobra.Command{ - Use: "cp [options] [CONTAINER:]SRC_PATH [CONTAINER:]DEST_PATH", + Use: "cp [CONTAINER:]SRC_PATH [CONTAINER:]DEST_PATH", Short: "Copy files/folders between a container and the local filesystem", Long: cpDescription, Args: cobra.ExactArgs(2), @@ -39,19 +54,21 @@ var ( func cpFlags(cmd *cobra.Command) { flags := cmd.Flags() - flags.BoolVar(&cpOpts.Extract, "extract", false, "Extract the tar file into the destination directory.") - flags.BoolVar(&cpOpts.Pause, "pause", true, "Pause the container while copying") + flags.BoolVar(&cpOpts.Extract, "extract", false, "Deprecated...") + flags.BoolVar(&cpOpts.Pause, "pause", true, "Deorecated") + _ = flags.MarkHidden("extract") + _ = flags.MarkHidden("pause") } func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode}, + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: cpCommand, }) cpFlags(cpCommand) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode}, + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerCpCommand, Parent: containerCmd, }) @@ -59,5 +76,290 @@ func init() { } func cp(cmd *cobra.Command, args []string) error { - return registry.ContainerEngine().ContainerCp(registry.GetContext(), args[0], args[1], cpOpts) + // Parse user input. + sourceContainerStr, sourcePath, destContainerStr, destPath, err := copy.ParseSourceAndDestination(args[0], args[1]) + if err != nil { + return err + } + + if len(sourceContainerStr) > 0 { + return copyFromContainer(sourceContainerStr, sourcePath, destPath) + } + + return copyToContainer(destContainerStr, destPath, sourcePath) +} + +// containerMustExist returns an error if the specified container does not +// exist. +func containerMustExist(container string) error { + exists, err := registry.ContainerEngine().ContainerExists(registry.GetContext(), container, entities.ContainerExistsOptions{}) + if err != nil { + return err + } + if !exists.Value { + return errors.Errorf("container %q does not exist", container) + } + return nil +} + +// doCopy executes the two functions in parallel to copy data from A to B and +// joins the errors if any. +func doCopy(funcA func() error, funcB func() error) error { + errChan := make(chan error) + go func() { + errChan <- funcA() + }() + var copyErrors []error + copyErrors = append(copyErrors, funcB()) + copyErrors = append(copyErrors, <-errChan) + return errorhandling.JoinErrors(copyErrors) +} + +// copyFromContainer copies from the containerPath on the container to hostPath. +func copyFromContainer(container string, containerPath string, hostPath string) error { + if err := containerMustExist(container); err != nil { + return err + } + + if hostPath == "-" { + hostPath = os.Stdout.Name() + } + + containerInfo, err := registry.ContainerEngine().ContainerStat(registry.GetContext(), container, containerPath) + if err != nil { + return errors.Wrapf(err, "%q could not be found on container %s", containerPath, container) + } + + var hostBaseName string + hostInfo, hostInfoErr := copy.ResolveHostPath(hostPath) + if hostInfoErr != nil { + if strings.HasSuffix(hostPath, "/") { + return errors.Wrapf(hostInfoErr, "%q could not be found on the host", hostPath) + } + // If it doesn't exist, then let's have a look at the parent dir. + parentDir := filepath.Dir(hostPath) + hostInfo, err = copy.ResolveHostPath(parentDir) + if err != nil { + return errors.Wrapf(hostInfoErr, "%q could not be found on the host", hostPath) + } + // If the specified path does not exist, we need to assume that + // it'll be created while copying. Hence, we use it as the + // base path. + hostBaseName = filepath.Base(hostPath) + } else { + // If the specified path exists on the host, we must use its + // base path as it may have changed due to symlink evaluations. + hostBaseName = filepath.Base(hostInfo.LinkTarget) + } + + reader, writer := io.Pipe() + hostCopy := func() error { + defer reader.Close() + if hostInfo.LinkTarget == os.Stdout.Name() { + _, err := io.Copy(os.Stdout, reader) + return err + } + + groot, err := user.Current() + if err != nil { + return err + } + + // Set the {G,U}ID. Let's be tolerant towards the different + // operating systems and only log the errors, so we can debug + // if necessary. + idPair := idtools.IDPair{} + if i, err := strconv.Atoi(groot.Uid); err == nil { + idPair.UID = i + } else { + logrus.Debugf("Error converting UID %q to int: %v", groot.Uid, err) + } + if i, err := strconv.Atoi(groot.Gid); err == nil { + idPair.GID = i + } else { + logrus.Debugf("Error converting GID %q to int: %v", groot.Gid, err) + } + + putOptions := buildahCopiah.PutOptions{ + ChownDirs: &idPair, + ChownFiles: &idPair, + } + if !containerInfo.IsDir && (!hostInfo.IsDir || hostInfoErr != nil) { + // If we're having a file-to-file copy, make sure to + // rename accordingly. + putOptions.Rename = map[string]string{filepath.Base(containerInfo.LinkTarget): hostBaseName} + } + dir := hostInfo.LinkTarget + if !hostInfo.IsDir { + dir = filepath.Dir(dir) + } + if err := buildahCopiah.Put(dir, "", putOptions, reader); err != nil { + return errors.Wrap(err, "error copying to host") + } + return nil + } + + containerCopy := func() error { + defer writer.Close() + copyFunc, err := registry.ContainerEngine().ContainerCopyToArchive(registry.GetContext(), container, containerInfo.LinkTarget, writer) + if err != nil { + return err + } + if err := copyFunc(); err != nil { + return errors.Wrap(err, "error copying from container") + } + return nil + } + return doCopy(containerCopy, hostCopy) +} + +// copyToContainer copies the hostPath to containerPath on the container. +func copyToContainer(container string, containerPath string, hostPath string) error { + if err := containerMustExist(container); err != nil { + return err + } + + isStdin := false + if hostPath == "-" { + hostPath = os.Stdin.Name() + isStdin = true + } else if hostPath == os.Stdin.Name() { + isStdin = true + } + + // Make sure that host path exists. + hostInfo, err := copy.ResolveHostPath(hostPath) + if err != nil { + return errors.Wrapf(err, "%q could not be found on the host", hostPath) + } + + // If the path on the container does not exist. We need to make sure + // that it's parent directory exists. The destination may be created + // while copying. + var containerBaseName string + containerInfo, containerInfoErr := registry.ContainerEngine().ContainerStat(registry.GetContext(), container, containerPath) + if containerInfoErr != nil { + if strings.HasSuffix(containerPath, "/") { + return errors.Wrapf(containerInfoErr, "%q could not be found on container %s", containerPath, container) + } + if isStdin { + return errors.New("destination must be a directory when copying from stdin") + } + // NOTE: containerInfo may actually be set. That happens when + // the container path is a symlink into nirvana. In that case, + // we must use the symlinked path instead. + path := containerPath + if containerInfo != nil { + containerBaseName = filepath.Base(containerInfo.LinkTarget) + path = containerInfo.LinkTarget + } else { + containerBaseName = filepath.Base(containerPath) + } + + parentDir, err := containerParentDir(container, path) + if err != nil { + return errors.Wrapf(err, "could not determine parent dir of %q on container %s", path, container) + } + containerInfo, err = registry.ContainerEngine().ContainerStat(registry.GetContext(), container, parentDir) + if err != nil { + return errors.Wrapf(err, "%q could not be found on container %s", containerPath, container) + } + } else { + // If the specified path exists on the container, we must use + // its base path as it may have changed due to symlink + // evaluations. + containerBaseName = filepath.Base(containerInfo.LinkTarget) + } + + var stdinFile string + if isStdin { + if !containerInfo.IsDir { + return errors.New("destination must be a directory when copying from stdin") + } + + // Copy from stdin to a temporary file *before* throwing it + // over the wire. This allows for proper client-side error + // reporting. + tmpFile, err := ioutil.TempFile("", "") + if err != nil { + return err + } + _, err = io.Copy(tmpFile, os.Stdin) + if err != nil { + return err + } + if err = tmpFile.Close(); err != nil { + return err + } + if !archive.IsArchivePath(tmpFile.Name()) { + return errors.New("source must be a (compressed) tar archive when copying from stdin") + } + stdinFile = tmpFile.Name() + } + + reader, writer := io.Pipe() + hostCopy := func() error { + defer writer.Close() + if isStdin { + stream, err := os.Open(stdinFile) + if err != nil { + return err + } + defer stream.Close() + _, err = io.Copy(writer, stream) + return err + } + + getOptions := buildahCopiah.GetOptions{ + // Unless the specified path ends with ".", we want to copy the base directory. + KeepDirectoryNames: !strings.HasSuffix(hostPath, "."), + } + if !hostInfo.IsDir && (!containerInfo.IsDir || containerInfoErr != nil) { + // If we're having a file-to-file copy, make sure to + // rename accordingly. + getOptions.Rename = map[string]string{filepath.Base(hostInfo.LinkTarget): containerBaseName} + } + if err := buildahCopiah.Get("/", "", getOptions, []string{hostInfo.LinkTarget}, writer); err != nil { + return errors.Wrap(err, "error copying from host") + } + return nil + } + + containerCopy := func() error { + defer reader.Close() + target := containerInfo.FileInfo.LinkTarget + if !containerInfo.IsDir { + target = filepath.Dir(target) + } + + copyFunc, err := registry.ContainerEngine().ContainerCopyFromArchive(registry.GetContext(), container, target, reader) + if err != nil { + return err + } + if err := copyFunc(); err != nil { + return errors.Wrap(err, "error copying to container") + } + return nil + } + + return doCopy(hostCopy, containerCopy) +} + +// containerParentDir returns the parent directory of the specified path on the +// container. If the path is relative, it will be resolved relative to the +// container's working directory (or "/" if the work dir isn't set). +func containerParentDir(container string, containerPath string) (string, error) { + if filepath.IsAbs(containerPath) { + return filepath.Dir(containerPath), nil + } + inspectData, _, err := registry.ContainerEngine().ContainerInspect(registry.GetContext(), []string{container}, entities.InspectOptions{}) + if err != nil { + return "", err + } + if len(inspectData) != 1 { + return "", errors.Errorf("inspecting container %q: expected 1 data item but got %d", container, len(inspectData)) + } + workDir := filepath.Join("/", inspectData[0].Config.WorkingDir) + workDir = filepath.Join(workDir, containerPath) + return filepath.Dir(workDir), nil } diff --git a/cmd/podman/system/prune.go b/cmd/podman/system/prune.go index ea47e271f..a74363684 100644 --- a/cmd/podman/system/prune.go +++ b/cmd/podman/system/prune.go @@ -4,7 +4,6 @@ import ( "bufio" "context" "fmt" - "net/url" "os" "strings" @@ -12,8 +11,8 @@ import ( "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" + lpfilters "github.com/containers/podman/v2/libpod/filters" "github.com/containers/podman/v2/pkg/domain/entities" - "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -55,6 +54,8 @@ func init() { } func prune(cmd *cobra.Command, args []string) error { + var err error + // Prompt for confirmation if --force is not set if !force { reader := bufio.NewReader(os.Stdin) @@ -79,16 +80,11 @@ Are you sure you want to continue? [y/N] `, volumeString) } } - pruneOptions.ContainerPruneOptions = entities.ContainerPruneOptions{} - for _, f := range filters { - t := strings.SplitN(f, "=", 2) - pruneOptions.ContainerPruneOptions.Filters = make(url.Values) - if len(t) < 2 { - return errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f) - } - pruneOptions.ContainerPruneOptions.Filters.Add(t[0], t[1]) + pruneOptions.Filters, err = lpfilters.ParseFilterArgumentsIntoFilters(filters) + if err != nil { + return err } - // TODO: support for filters in system prune + response, err := registry.ContainerEngine().SystemPrune(context.Background(), pruneOptions) if err != nil { return err diff --git a/contrib/spec/podman.spec.in b/contrib/spec/podman.spec.in index 3be44a3a3..db79ebede 100644 --- a/contrib/spec/podman.spec.in +++ b/contrib/spec/podman.spec.in @@ -90,8 +90,8 @@ Recommends: crun Recommends: container-selinux Recommends: slirp4netns Recommends: fuse-overlayfs -%endif Recommends: xz +%endif # vendored libraries # awk '{print "Provides: bundled(golang("$1")) = "$2}' vendor.conf | sort diff --git a/docs/source/markdown/podman-cp.1.md b/docs/source/markdown/podman-cp.1.md index 241a74c42..56511c244 100644 --- a/docs/source/markdown/podman-cp.1.md +++ b/docs/source/markdown/podman-cp.1.md @@ -4,9 +4,9 @@ podman\-cp - Copy files/folders between a container and the local filesystem ## SYNOPSIS -**podman cp** [*options*] [*container*:]*src_path* [*container*:]*dest_path* +**podman cp** [*container*:]*src_path* [*container*:]*dest_path* -**podman container cp** [*options*] [*container*:]*src_path* [*container*:]*dest_path* +**podman container cp** [*container*:]*src_path* [*container*:]*dest_path* ## DESCRIPTION Copy the contents of **src_path** to the **dest_path**. You can copy from the container's filesystem to the local machine or the reverse, from the local filesystem to the container. @@ -59,14 +59,6 @@ Using `-` as the *src_path* streams the contents of STDIN as a tar archive. The ## OPTIONS -#### **--extract** - -If the source is a tar archive, extract it to the provided destination (must be a directory). If the source is not a tar archive, follow the above rules. - -#### **--pause** - -Pause the container while copying into it to avoid potential security issues around symlinks. Defaults to *true*. On rootless containers with cgroups V1, defaults to false. - ## ALTERNATIVES Podman has much stronger capabilities than just `podman cp` to achieve copy files between host and container. @@ -112,8 +104,6 @@ podman cp containerID:/myapp/ /myapp/ podman cp containerID:/home/myuser/. /home/myuser/ -podman cp --extract /home/myuser/myfiles.tar.gz containerID:/myfiles - podman cp - containerID:/myfiles.tar.gz < myfiles.tar.gz ## SEE ALSO diff --git a/docs/source/markdown/podman-info.1.md b/docs/source/markdown/podman-info.1.md index dd01a0f49..4af51d3eb 100644 --- a/docs/source/markdown/podman-info.1.md +++ b/docs/source/markdown/podman-info.1.md @@ -31,17 +31,18 @@ Run podman info with plain text response: $ podman info host: arch: amd64 - buildahVersion: 1.15.0 - cgroupVersion: v1 + buildahVersion: 1.19.0-dev + cgroupManager: systemd + cgroupVersion: v2 conmon: - package: conmon-2.0.16-2.fc32.x86_64 + package: conmon-2.0.22-2.fc33.x86_64 path: /usr/bin/conmon - version: 'conmon version 2.0.16, commit: 1044176f7dd177c100779d1c63931d6022e419bd' + version: 'conmon version 2.0.22, commit: 1be6c73605006a85f7ed60b7f76a51e28eb67e01' cpus: 8 distribution: distribution: fedora - version: "32" - eventLogger: file + version: "33" + eventLogger: journald hostname: localhost.localdomain idMappings: gidmap: @@ -58,33 +59,41 @@ host: - container_id: 1 host_id: 100000 size: 65536 - kernel: 5.6.11-300.fc32.x86_64 + kernel: 5.9.11-200.fc33.x86_64 linkmode: dynamic - memFree: 1401929728 - memTotal: 16416161792 + memFree: 837505024 + memTotal: 16416481280 ociRuntime: - name: runc - package: containerd.io-1.2.10-3.2.fc31.x86_64 - path: /usr/bin/runc + name: crun + package: crun-0.16-1.fc33.x86_64 + path: /usr/bin/crun version: |- - runc version 1.0.0-rc8+dev - commit: 3e425f80a8c931f88e6d94a8c831b9d5aa481657 - spec: 1.0.1-dev + crun version 0.16 + commit: eb0145e5ad4d8207e84a327248af76663d4e50dd + spec: 1.0.0 + +SYSTEMD +SELINUX +APPARMOR +CAP +SECCOMP +EBPF +YAJL os: linux remoteSocket: - exists: false - path: /run/user/1000/podman/podman.sock - rootless: true + exists: true + path: /run/user/3267/podman/podman.sock + security: + apparmorEnabled: false + capabilities: CAP_CHOWN,CAP_DAC_OVERRIDE,CAP_FOWNER,CAP_FSETID,CAP_KILL,CAP_NET_BIND_SERVICE,CAP_SETFCAP,CAP_SETGID,CAP_SETPCAP,CAP_SETUID,CAP_SYS_CHROOT + rootless: true + seccompEnabled: true + selinuxEnabled: true slirp4netns: executable: /bin/slirp4netns - package: slirp4netns-1.0.0-1.fc32.x86_64 + package: slirp4netns-1.1.4-4.dev.giteecccdb.fc33.x86_64 version: |- - slirp4netns version 1.0.0 - commit: a3be729152a33e692cd28b52f664defbf2e7810a - libslirp: 4.2.0 - swapFree: 8291610624 - swapTotal: 8296329216 - uptime: 52h 29m 39.78s (Approximately 2.17 days) + slirp4netns version 1.1.4+dev + commit: eecccdb96f587b11d7764556ffacfeaffe4b6e11 + libslirp: 4.3.1 + SLIRP_CONFIG_VERSION_MAX: 3 + libseccomp: 2.5.0 + swapFree: 6509203456 + swapTotal: 12591292416 + uptime: 264h 14m 32.73s (Approximately 11.00 days) registries: search: - registry.fedoraproject.org @@ -94,19 +103,19 @@ registries: store: configFile: /home/dwalsh/.config/containers/storage.conf containerStore: - number: 2 + number: 3 paused: 0 running: 0 - stopped: 2 + stopped: 3 graphDriverName: overlay graphOptions: overlay.mount_program: Executable: /home/dwalsh/bin/fuse-overlayfs Package: Unknown Version: |- - fusermount3 version: 3.9.1 + fusermount3 version: 3.9.3 fuse-overlayfs: version 0.7.2 - FUSE library version 3.9.1 + FUSE library version 3.9.3 using FUSE kernel interface version 7.31 graphRoot: /home/dwalsh/.local/share/containers/storage graphStatus: @@ -115,36 +124,38 @@ store: Supports d_type: "true" Using metacopy: "false" imageStore: - number: 7 + number: 77 runRoot: /run/user/3267/containers volumePath: /home/dwalsh/.local/share/containers/storage/volumes version: - Built: 1589899246 - BuiltTime: Tue May 19 10:40:46 2020 - GitCommit: c3678ce3289f4195f3f16802411e795c6a587c9f-dirty - GoVersion: go1.14.2 + APIVersion: 3.0.0 + Built: 1608562922 + BuiltTime: Mon Dec 21 10:02:02 2020 + GitCommit: d6925182cdaf94225908a386d02eae8fd3e01123-dirty + GoVersion: go1.15.5 OsArch: linux/amd64 - APIVersion: 1 - Version: 2.0.0 + Version: 3.0.0-dev + ``` Run podman info with JSON formatted response: ``` { "host": { "arch": "amd64", - "buildahVersion": "1.15.0", - "cgroupVersion": "v1", + "buildahVersion": "1.19.0-dev", + "cgroupManager": "systemd", + "cgroupVersion": "v2", "conmon": { - "package": "conmon-2.0.16-2.fc32.x86_64", + "package": "conmon-2.0.22-2.fc33.x86_64", "path": "/usr/bin/conmon", - "version": "conmon version 2.0.16, commit: 1044176f7dd177c100779d1c63931d6022e419bd" + "version": "conmon version 2.0.22, commit: 1be6c73605006a85f7ed60b7f76a51e28eb67e01" }, "cpus": 8, "distribution": { "distribution": "fedora", - "version": "32" + "version": "33" }, - "eventLogger": "file", + "eventLogger": "journald", "hostname": "localhost.localdomain", "idMappings": { "gidmap": [ @@ -172,45 +183,51 @@ Run podman info with JSON formatted response: } ] }, - "kernel": "5.6.11-300.fc32.x86_64", - "memFree": 1380356096, - "memTotal": 16416161792, + "kernel": "5.9.11-200.fc33.x86_64", + "memFree": 894574592, + "memTotal": 16416481280, "ociRuntime": { - "name": "runc", - "package": "containerd.io-1.2.10-3.2.fc31.x86_64", - "path": "/usr/bin/runc", - "version": "runc version 1.0.0-rc8+dev\ncommit: 3e425f80a8c931f88e6d94a8c831b9d5aa481657\nspec: 1.0.1-dev" + "name": "crun", + "package": "crun-0.16-1.fc33.x86_64", + "path": "/usr/bin/crun", + "version": "crun version 0.16\ncommit: eb0145e5ad4d8207e84a327248af76663d4e50dd\nspec: 1.0.0\n+SYSTEMD +SELINUX +APPARMOR +CAP +SECCOMP +EBPF +YAJL" }, "os": "linux", "remoteSocket": { - "path": "/run/user/1000/podman/podman.sock", - "exists": false + "path": "/run/user/3267/podman/podman.sock", + "exists": true + }, + "security": { + "apparmorEnabled": false, + "capabilities": "CAP_CHOWN,CAP_DAC_OVERRIDE,CAP_FOWNER,CAP_FSETID,CAP_KILL,CAP_NET_BIND_SERVICE,CAP_SETFCAP,CAP_SETGID,CAP_SETPCAP,CAP_SETUID,CAP_SYS_CHROOT", + "rootless": true, + "seccompEnabled": true, + "selinuxEnabled": true }, - "rootless": true, "slirp4netns": { "executable": "/bin/slirp4netns", - "package": "slirp4netns-1.0.0-1.fc32.x86_64", - "version": "slirp4netns version 1.0.0\ncommit: a3be729152a33e692cd28b52f664defbf2e7810a\nlibslirp: 4.2.0" + "package": "slirp4netns-1.1.4-4.dev.giteecccdb.fc33.x86_64", + "version": "slirp4netns version 1.1.4+dev\ncommit: eecccdb96f587b11d7764556ffacfeaffe4b6e11\nlibslirp: 4.3.1\nSLIRP_CONFIG_VERSION_MAX: 3\nlibseccomp: 2.5.0" }, - "swapFree": 8291610624, - "swapTotal": 8296329216, - "uptime": "52h 27m 39.38s (Approximately 2.17 days)", + "swapFree": 6509203456, + "swapTotal": 12591292416, + "uptime": "264h 13m 12.39s (Approximately 11.00 days)", "linkmode": "dynamic" }, "store": { "configFile": "/home/dwalsh/.config/containers/storage.conf", "containerStore": { - "number": 2, + "number": 3, "paused": 0, "running": 0, - "stopped": 2 + "stopped": 3 }, "graphDriverName": "overlay", "graphOptions": { "overlay.mount_program": { "Executable": "/home/dwalsh/bin/fuse-overlayfs", "Package": "Unknown", - "Version": "fusermount3 version: 3.9.1\nfuse-overlayfs: version 0.7.2\nFUSE library version 3.9.1\nusing FUSE kernel interface version 7.31" + "Version": "fusermount3 version: 3.9.3\nfuse-overlayfs: version 0.7.2\nFUSE library version 3.9.3\nusing FUSE kernel interface version 7.31" } }, "graphRoot": "/home/dwalsh/.local/share/containers/storage", @@ -221,7 +238,7 @@ Run podman info with JSON formatted response: "Using metacopy": "false" }, "imageStore": { - "number": 7 + "number": 77 }, "runRoot": "/run/user/3267/containers", "volumePath": "/home/dwalsh/.local/share/containers/storage/volumes" @@ -235,12 +252,12 @@ Run podman info with JSON formatted response: ] }, "version": { - "APIVersion": 1, - "Version": "2.0.0", - "GoVersion": "go1.14.2", - "GitCommit": "c3678ce3289f4195f3f16802411e795c6a587c9f-dirty", - "BuiltTime": "Tue May 19 10:40:46 2020", - "Built": 1589899246, + "APIVersion": "3.0.0", + "Version": "3.0.0-dev", + "GoVersion": "go1.15.5", + "GitCommit": "d6925182cdaf94225908a386d02eae8fd3e01123-dirty", + "BuiltTime": "Mon Dec 21 10:02:02 2020", + "Built": 1608562922, "OsArch": "linux/amd64" } } diff --git a/docs/source/markdown/podman-ps.1.md b/docs/source/markdown/podman-ps.1.md index b94964f6c..28212b92c 100644 --- a/docs/source/markdown/podman-ps.1.md +++ b/docs/source/markdown/podman-ps.1.md @@ -57,6 +57,8 @@ Valid filters are listed below: | since | [ID] or [Name] Containers created since this container | | volume | [VolumeName] or [MountpointDestination] Volume mounted in container | | health | [Status] healthy or unhealthy | +| pod | [Pod] name or full or partial ID of pod | + #### **--format**=*format* @@ -10,8 +10,8 @@ require ( github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect github.com/containernetworking/cni v0.8.0 github.com/containernetworking/plugins v0.9.0 - github.com/containers/buildah v1.18.1-0.20201125084616-dd26b137459c - github.com/containers/common v0.31.1 + github.com/containers/buildah v1.18.1-0.20201217112226-67470615779c + github.com/containers/common v0.31.2 github.com/containers/conmon v2.0.20+incompatible github.com/containers/image/v5 v5.9.0 github.com/containers/psgo v1.5.1 @@ -68,6 +68,6 @@ require ( gopkg.in/square/go-jose.v2 v2.5.1 // indirect gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect k8s.io/api v0.0.0-20190620084959-7cf5895f2711 - k8s.io/apimachinery v0.20.0 + k8s.io/apimachinery v0.20.1 k8s.io/client-go v0.0.0-20190620085101-78d2af792bab ) @@ -93,11 +93,11 @@ github.com/containernetworking/plugins v0.8.7 h1:bU7QieuAp+sACI2vCzESJ3FoT860urY github.com/containernetworking/plugins v0.8.7/go.mod h1:R7lXeZaBzpfqapcAbHRW8/CYwm0dHzbz0XEjofx0uB0= github.com/containernetworking/plugins v0.9.0 h1:c+1gegKhR7+d0Caum9pEHugZlyhXPOG6v3V6xJgIGCI= github.com/containernetworking/plugins v0.9.0/go.mod h1:dbWv4dI0QrBGuVgj+TuVQ6wJRZVOhrCQj91YyC92sxg= -github.com/containers/buildah v1.18.1-0.20201125084616-dd26b137459c h1:vyc2iYz9b2vfDiigpLyhiXNqXITt/dmDk74HpHzlQow= -github.com/containers/buildah v1.18.1-0.20201125084616-dd26b137459c/go.mod h1:B+0OkXUogxdwsEy4ax3a5/vDtJjL6vCisiV6frQZJ4A= -github.com/containers/common v0.29.0/go.mod h1:yT4GTUHsKRmpaDb+mecXRnIMre7W3ZgwXqaYMywXlaA= -github.com/containers/common v0.31.1 h1:oBINnZpYZ2u90HPMnVCXOhm/TsTaTB7wU/56l05hq44= -github.com/containers/common v0.31.1/go.mod h1:Fehe82hQfJQvDspnRrV9rcdAWG3IalNHEt0F6QWNBHQ= +github.com/containers/buildah v1.18.1-0.20201217112226-67470615779c h1:DnJiPjBKeoZbzjkUA6YMf/r5ShYpNacK+EcQ/ui1Mxo= +github.com/containers/buildah v1.18.1-0.20201217112226-67470615779c/go.mod h1:hvIoL3urgYPL0zX8XlK05aWP6qfUnBNqTrsedsYw6OY= +github.com/containers/common v0.31.0/go.mod h1:yT4GTUHsKRmpaDb+mecXRnIMre7W3ZgwXqaYMywXlaA= +github.com/containers/common v0.31.2 h1:sNYwvLA4B7SpEiAWTUvkItPlCrUa2vcxh0FTKXKoC3Q= +github.com/containers/common v0.31.2/go.mod h1:Fehe82hQfJQvDspnRrV9rcdAWG3IalNHEt0F6QWNBHQ= github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg= github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= github.com/containers/image/v5 v5.8.1/go.mod h1:blOEFd/iFdeyh891ByhCVUc+xAcaI3gBegXECwz9UbQ= @@ -110,7 +110,6 @@ github.com/containers/ocicrypt v1.0.3/go.mod h1:CUBa+8MRNL/VkpxYIpaMtgn1WgXGyvPQ github.com/containers/psgo v1.5.1 h1:MQNb7FLbXqBdqz6u4lI2QWizVz4RSTzs1+Nk9XT1iVA= github.com/containers/psgo v1.5.1/go.mod h1:2ubh0SsreMZjSXW1Hif58JrEcFudQyIy9EzPUWfawVU= github.com/containers/storage v1.23.7/go.mod h1:cUT2zHjtx+WlVri30obWmM2gpqpi8jfPsmIzP1TVpEI= -github.com/containers/storage v1.24.1 h1:1+f8fy6ly35c8SLet5jzZ8t0WJJs5+xSpfMAYw0R3kc= github.com/containers/storage v1.24.1/go.mod h1:0xJL06Dmd+ZYXIUdnBUPN0JnhHGgwMkLvnnAonJfWJU= github.com/containers/storage v1.24.3 h1:8UB4S62l4hrU6Yw3dbsLCJtLg7Ofo39IN2HdckBIX4E= github.com/containers/storage v1.24.3/go.mod h1:0xJL06Dmd+ZYXIUdnBUPN0JnhHGgwMkLvnnAonJfWJU= @@ -441,7 +440,6 @@ github.com/opencontainers/runtime-spec v1.0.3-0.20200817204227-f9c09b4ea1df/go.m github.com/opencontainers/runtime-tools v0.9.0 h1:FYgwVsKRI/H9hU32MJ/4MLOzXWodKK5zsQavY8NPMkU= github.com/opencontainers/runtime-tools v0.9.0/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= github.com/opencontainers/selinux v1.5.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g= -github.com/opencontainers/selinux v1.6.0 h1:+bIAS/Za3q5FTwWym4fTB0vObnfCf3G/NC7K6Jx62mY= github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= github.com/opencontainers/selinux v1.8.0 h1:+77ba4ar4jsCbL1GLbFL8fFM57w6suPfSS9PDLDY7KM= github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= @@ -557,16 +555,13 @@ github.com/vbatts/tar-split v0.11.1/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpc github.com/vbauerster/mpb/v5 v5.3.0 h1:vgrEJjUzHaSZKDRRxul5Oh4C72Yy/5VEMb0em+9M0mQ= github.com/vbauerster/mpb/v5 v5.3.0/go.mod h1:4yTkvAb8Cm4eylAp6t0JRq6pXDkFJ4krUlDqWYkakAs= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= -github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852 h1:cPXZWzzG0NllBLdjWoD1nDfaqu98YMv+OneaKc8sPOA= github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243 h1:R43TdZy32XXSXjJn7M/HhALJ9imq6ztLnChfYJpVDnM= github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE= github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= @@ -706,7 +701,6 @@ golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637 h1:O5hKNaGxIT4A8OTMnuh6UpmBdI3SAPxlZ3g0olDrJVM= golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -833,8 +827,8 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt k8s.io/api v0.0.0-20190620084959-7cf5895f2711 h1:BblVYz/wE5WtBsD/Gvu54KyBUTJMflolzc5I2DTvh50= k8s.io/api v0.0.0-20190620084959-7cf5895f2711/go.mod h1:TBhBqb1AWbBQbW3XRusr7n7E4v2+5ZY8r8sAMnyFC5A= k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719/go.mod h1:I4A+glKBHiTgiEjQiCCQfCAIcIMFGt291SmsvcrFzJA= -k8s.io/apimachinery v0.20.0 h1:jjzbTJRXk0unNS71L7h3lxGDH/2HPxMPaQY+MjECKL8= -k8s.io/apimachinery v0.20.0/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.20.1 h1:LAhz8pKbgR8tUwn7boK+b2HZdt7MiTu2mkYtFMUjTRQ= +k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/client-go v0.0.0-20190620085101-78d2af792bab h1:E8Fecph0qbNsAbijJJQryKu4Oi9QTp5cVpjTE+nqg6g= k8s.io/client-go v0.0.0-20190620085101-78d2af792bab/go.mod h1:E95RaSlHr79aHaX0aGSwcPNfygDiPKOVXdmivCIZT0k= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= diff --git a/libpod/define/info.go b/libpod/define/info.go index f0e05801c..00146da48 100644 --- a/libpod/define/info.go +++ b/libpod/define/info.go @@ -12,6 +12,15 @@ type Info struct { } //HostInfo describes the libpod host +type SecurityInfo struct { + AppArmorEnabled bool `json:"apparmorEnabled"` + DefaultCapabilities string `json:"capabilities"` + Rootless bool `json:"rootless"` + SECCOMPEnabled bool `json:"seccompEnabled"` + SELinuxEnabled bool `json:"selinuxEnabled"` +} + +//HostInfo describes the libpod host type HostInfo struct { Arch string `json:"arch"` BuildahVersion string `json:"buildahVersion"` @@ -29,8 +38,8 @@ type HostInfo struct { OCIRuntime *OCIRuntimeInfo `json:"ociRuntime"` OS string `json:"os"` RemoteSocket *RemoteSocket `json:"remoteSocket,omitempty"` - Rootless bool `json:"rootless"` RuntimeInfo map[string]interface{} `json:"runtimeInfo,omitempty"` + Security SecurityInfo `json:"security"` Slirp4NetNS SlirpInfo `json:"slirp4netns,omitempty"` SwapFree int64 `json:"swapFree"` SwapTotal int64 `json:"swapTotal"` diff --git a/libpod/filters/containers.go b/libpod/filters/containers.go index 2520c4f30..505429de6 100644 --- a/libpod/filters/containers.go +++ b/libpod/filters/containers.go @@ -203,6 +203,37 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo } return false }, nil + case "pod": + var pods []*libpod.Pod + for _, podNameOrID := range filterValues { + p, err := r.LookupPod(podNameOrID) + if err != nil { + if errors.Cause(err) == define.ErrNoSuchPod { + continue + } + return nil, err + } + pods = append(pods, p) + } + return func(c *libpod.Container) bool { + // if no pods match, quick out + if len(pods) < 1 { + return false + } + // if the container has no pod id, quick out + if len(c.PodID()) < 1 { + return false + } + for _, p := range pods { + // we already looked up by name or id, so id match + // here is ok + if p.ID() == c.PodID() { + return true + } + } + return false + }, nil + } return nil, errors.Errorf("%s is an invalid filter", filter) } diff --git a/libpod/filters/helpers.go b/libpod/filters/helpers.go new file mode 100644 index 000000000..859db3a9a --- /dev/null +++ b/libpod/filters/helpers.go @@ -0,0 +1,20 @@ +package lpfilters + +import ( + "net/url" + "strings" + + "github.com/pkg/errors" +) + +func ParseFilterArgumentsIntoFilters(filters []string) (url.Values, error) { + parsedFilters := make(url.Values) + for _, f := range filters { + t := strings.SplitN(f, "=", 2) + if len(t) < 2 { + return parsedFilters, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f) + } + parsedFilters.Add(t[0], t[1]) + } + return parsedFilters, nil +} diff --git a/libpod/info.go b/libpod/info.go index 2f64a107e..1b3550abd 100644 --- a/libpod/info.go +++ b/libpod/info.go @@ -13,6 +13,8 @@ import ( "time" "github.com/containers/buildah" + "github.com/containers/common/pkg/apparmor" + "github.com/containers/common/pkg/seccomp" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/linkmode" "github.com/containers/podman/v2/pkg/cgroups" @@ -20,6 +22,7 @@ import ( "github.com/containers/podman/v2/pkg/rootless" "github.com/containers/storage" "github.com/containers/storage/pkg/system" + "github.com/opencontainers/selinux/go-selinux" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -98,10 +101,16 @@ func (r *Runtime) hostInfo() (*define.HostInfo, error) { MemFree: mi.MemFree, MemTotal: mi.MemTotal, OS: runtime.GOOS, - Rootless: rootless.IsRootless(), - Slirp4NetNS: define.SlirpInfo{}, - SwapFree: mi.SwapFree, - SwapTotal: mi.SwapTotal, + Security: define.SecurityInfo{ + AppArmorEnabled: apparmor.IsEnabled(), + DefaultCapabilities: strings.Join(r.config.Containers.DefaultCapabilities, ","), + Rootless: rootless.IsRootless(), + SECCOMPEnabled: seccomp.IsEnabled(), + SELinuxEnabled: selinux.GetEnabled(), + }, + Slirp4NetNS: define.SlirpInfo{}, + SwapFree: mi.SwapFree, + SwapTotal: mi.SwapTotal, } // CGroups version diff --git a/nix/default.nix b/nix/default.nix index a1a8c5287..13b4585ea 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -38,10 +38,10 @@ let doCheck = false; enableParallelBuilding = true; outputs = [ "out" ]; - nativeBuildInputs = [ bash git go-md2man installShellFiles makeWrapper pkg-config which ]; + nativeBuildInputs = [ bash gitMinimal go-md2man installShellFiles makeWrapper pkg-config which ]; buildInputs = [ glibc glibc.static gpgme libassuan libgpgerror libseccomp libapparmor libselinux ]; prePatch = '' - export CFLAGS='-static' + export CFLAGS='-static -pthread' export LDFLAGS='-s -w -static-libgcc -static' export EXTRA_LDFLAGS='-s -w -linkmode external -extldflags "-static -lm"' export BUILDTAGS='static netgo osusergo exclude_graphdriver_btrfs exclude_graphdriver_devicemapper seccomp apparmor selinux' diff --git a/nix/nixpkgs.json b/nix/nixpkgs.json index 81a2f974c..8e001f6b2 100644 --- a/nix/nixpkgs.json +++ b/nix/nixpkgs.json @@ -1,7 +1,10 @@ { "url": "https://github.com/nixos/nixpkgs", - "rev": "6e089d30148953df7abb3a1167169afc7848499c", - "date": "2020-11-05T09:56:30+01:00", - "sha256": "0ydqjkz7payl16psx445jwh6dc6lgbvj2w11xin1dqvbpcp03jcy", - "fetchSubmodules": false + "rev": "6ea2fd15d881006b41ea5bbed0f76bffcd85f9f9", + "date": "2020-12-20T13:26:58+10:00", + "path": "/nix/store/kyw1ackbp9qh1jzlzwmjvi5i3541ym5z-nixpkgs", + "sha256": "0kgqmw4ki10b37530jh912sn49x3gay5cnmfr8yz2yp8nvkrk236", + "fetchSubmodules": false, + "deepClone": false, + "leaveDotGit": false } diff --git a/pkg/api/handlers/compat/containers_archive.go b/pkg/api/handlers/compat/containers_archive.go index d8197415c..083c72ce8 100644 --- a/pkg/api/handlers/compat/containers_archive.go +++ b/pkg/api/handlers/compat/containers_archive.go @@ -3,11 +3,13 @@ package compat import ( "fmt" "net/http" + "os" "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/api/handlers/utils" "github.com/containers/podman/v2/pkg/copy" + "github.com/containers/podman/v2/pkg/domain/infra/abi" "github.com/gorilla/schema" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -44,58 +46,47 @@ func handleHeadAndGet(w http.ResponseWriter, r *http.Request, decoder *schema.De } containerName := utils.GetName(r) - - ctr, err := runtime.LookupContainer(containerName) - if errors.Cause(err) == define.ErrNoSuchCtr { - utils.Error(w, "Not found.", http.StatusNotFound, errors.Wrap(err, "the container doesn't exists")) + containerEngine := abi.ContainerEngine{Libpod: runtime} + statReport, err := containerEngine.ContainerStat(r.Context(), containerName, query.Path) + + // NOTE + // The statReport may actually be set even in case of an error. That's + // the case when we're looking at a symlink pointing to nirvana. In + // such cases, we really need the FileInfo but we also need the error. + if statReport != nil { + statHeader, err := copy.EncodeFileInfo(&statReport.FileInfo) + if err != nil { + utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) + return + } + w.Header().Add(copy.XDockerContainerPathStatHeader, statHeader) + } + + if errors.Cause(err) == define.ErrNoSuchCtr || errors.Cause(err) == copy.ENOENT { + // 404 is returned for an absent container and path. The + // clients must deal with it accordingly. + utils.Error(w, "Not found.", http.StatusNotFound, err) return } else if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) return } - source, err := copy.CopyItemForContainer(ctr, query.Path, true, true) - defer source.CleanUp() - if err != nil { - utils.Error(w, "Not found.", http.StatusNotFound, errors.Wrapf(err, "error stating container path %q", query.Path)) - return - } - - // NOTE: Docker always sets the header. - info, err := source.Stat() - if err != nil { - utils.Error(w, "Not found.", http.StatusNotFound, errors.Wrapf(err, "error stating container path %q", query.Path)) - return - } - statHeader, err := copy.EncodeFileInfo(info) - if err != nil { - utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) - return - } - w.Header().Add(copy.XDockerContainerPathStatHeader, statHeader) - // Our work is done when the user is interested in the header only. if r.Method == http.MethodHead { w.WriteHeader(http.StatusOK) return } - // Alright, the users wants data from the container. - destination, err := copy.CopyItemForWriter(w) - if err != nil { - utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) - return - } - - copier, err := copy.GetCopier(&source, &destination, false) + copyFunc, err := containerEngine.ContainerCopyToArchive(r.Context(), containerName, query.Path, w) if err != nil { utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) return } + w.Header().Set("Content-Type", "application/x-tar") w.WriteHeader(http.StatusOK) - if err := copier.Copy(); err != nil { - logrus.Errorf("Error during copy: %v", err) - return + if err := copyFunc(); err != nil { + logrus.Error(err.Error()) } } @@ -113,36 +104,22 @@ func handlePut(w http.ResponseWriter, r *http.Request, decoder *schema.Decoder, return } - ctrName := utils.GetName(r) - - ctr, err := runtime.LookupContainer(ctrName) - if err != nil { - utils.Error(w, "Not found", http.StatusNotFound, errors.Wrapf(err, "the %s container doesn't exists", ctrName)) - return - } + containerName := utils.GetName(r) + containerEngine := abi.ContainerEngine{Libpod: runtime} - destination, err := copy.CopyItemForContainer(ctr, query.Path, true, false) - defer destination.CleanUp() - if err != nil { - utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) + copyFunc, err := containerEngine.ContainerCopyFromArchive(r.Context(), containerName, query.Path, r.Body) + if errors.Cause(err) == define.ErrNoSuchCtr || os.IsNotExist(err) { + // 404 is returned for an absent container and path. The + // clients must deal with it accordingly. + utils.Error(w, "Not found.", http.StatusNotFound, errors.Wrap(err, "the container doesn't exists")) return - } - - source, err := copy.CopyItemForReader(r.Body) - defer source.CleanUp() - if err != nil { - utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) + } else if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) return } - copier, err := copy.GetCopier(&source, &destination, false) - if err != nil { - utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) - return - } w.WriteHeader(http.StatusOK) - if err := copier.Copy(); err != nil { - logrus.Errorf("Error during copy: %v", err) - return + if err := copyFunc(); err != nil { + logrus.Error(err.Error()) } } diff --git a/pkg/api/server/register_archive.go b/pkg/api/server/register_archive.go index b2d2543c4..9ff0ad0eb 100644 --- a/pkg/api/server/register_archive.go +++ b/pkg/api/server/register_archive.go @@ -4,7 +4,6 @@ import ( "net/http" "github.com/containers/podman/v2/pkg/api/handlers/compat" - "github.com/containers/podman/v2/pkg/api/handlers/libpod" "github.com/gorilla/mux" ) @@ -92,7 +91,7 @@ func (s *APIServer) registerAchiveHandlers(r *mux.Router) error { Libpod */ - // swagger:operation POST /libpod/containers/{name}/copy libpod libpodPutArchive + // swagger:operation POST /libpod/containers/{name}/archive libpod libpodPutArchive // --- // summary: Copy files into a container // description: Copy a tar archive of files into a container @@ -133,7 +132,7 @@ func (s *APIServer) registerAchiveHandlers(r *mux.Router) error { // 500: // $ref: "#/responses/InternalError" - // swagger:operation GET /libpod/containers/{name}/copy libpod libpodGetArchive + // swagger:operation GET /libpod/containers/{name}/archive libpod libpodGetArchive // --- // summary: Copy files from a container // description: Copy a tar archive of files from a container @@ -164,8 +163,7 @@ func (s *APIServer) registerAchiveHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/libpod/containers/{name}/copy"), s.APIHandler(libpod.Archive)).Methods(http.MethodGet, http.MethodPost) - r.HandleFunc(VersionedPath("/libpod/containers/{name}/archive"), s.APIHandler(libpod.Archive)).Methods(http.MethodGet, http.MethodPost) + r.HandleFunc(VersionedPath("/libpod/containers/{name}/archive"), s.APIHandler(compat.Archive)).Methods(http.MethodGet, http.MethodPut, http.MethodHead) return nil } diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go index 870c6a90c..b80dea545 100644 --- a/pkg/api/server/register_containers.go +++ b/pkg/api/server/register_containers.go @@ -690,6 +690,7 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // - `label`=(`key` or `"key=value"`) of an container label // - `name=<name>` a container's name // - `network`=(`<network id>` or `<network name>`) + // - `pod`=(`<pod id>` or `<pod name>`) // - `publish`=(`<port>[/<proto>]` or `<startport-endport>/[<proto>]`) // - `since`=(`<container id>` or `<container name>`) // - `status`=(`created`, `restarting`, `running`, `removing`, `paused`, `exited` or `dead`) diff --git a/pkg/bindings/containers/archive.go b/pkg/bindings/containers/archive.go new file mode 100644 index 000000000..d1bbc0b95 --- /dev/null +++ b/pkg/bindings/containers/archive.go @@ -0,0 +1,92 @@ +package containers + +import ( + "context" + "io" + "net/http" + "net/url" + + "github.com/containers/podman/v2/pkg/bindings" + "github.com/containers/podman/v2/pkg/copy" + "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/pkg/errors" +) + +// Stat checks if the specified path is on the container. Note that the stat +// report may be set even in case of an error. This happens when the path +// resolves to symlink pointing to a non-existent path. +func Stat(ctx context.Context, nameOrID string, path string) (*entities.ContainerStatReport, error) { + conn, err := bindings.GetClient(ctx) + if err != nil { + return nil, err + } + params := url.Values{} + params.Set("path", path) + + response, err := conn.DoRequest(nil, http.MethodHead, "/containers/%s/archive", params, nil, nameOrID) + if err != nil { + return nil, err + } + + var finalErr error + if response.StatusCode == http.StatusNotFound { + finalErr = copy.ENOENT + } else if response.StatusCode != http.StatusOK { + finalErr = errors.New(response.Status) + } + + var statReport *entities.ContainerStatReport + + fileInfo, err := copy.ExtractFileInfoFromHeader(&response.Header) + if err != nil && finalErr == nil { + return nil, err + } + + if fileInfo != nil { + statReport = &entities.ContainerStatReport{FileInfo: *fileInfo} + } + + return statReport, finalErr +} + +func CopyFromArchive(ctx context.Context, nameOrID string, path string, reader io.Reader) (entities.ContainerCopyFunc, error) { + conn, err := bindings.GetClient(ctx) + if err != nil { + return nil, err + } + params := url.Values{} + params.Set("path", path) + + return func() error { + response, err := conn.DoRequest(reader, http.MethodPut, "/containers/%s/archive", params, nil, nameOrID) + if err != nil { + return err + } + if response.StatusCode != http.StatusOK { + return errors.New(response.Status) + } + return response.Process(nil) + }, nil +} + +func CopyToArchive(ctx context.Context, nameOrID string, path string, writer io.Writer) (entities.ContainerCopyFunc, error) { + conn, err := bindings.GetClient(ctx) + if err != nil { + return nil, err + } + params := url.Values{} + params.Set("path", path) + + response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/archive", params, nil, nameOrID) + if err != nil { + return nil, err + } + if response.StatusCode != http.StatusOK { + return nil, response.Process(nil) + } + + return func() error { + _, err := io.Copy(writer, response.Body) + return err + }, nil +} diff --git a/pkg/bindings/containers/attach.go b/pkg/bindings/containers/attach.go index 91b155fc4..69ae7a32f 100644 --- a/pkg/bindings/containers/attach.go +++ b/pkg/bindings/containers/attach.go @@ -26,7 +26,10 @@ import ( ) // Attach attaches to a running container -func Attach(ctx context.Context, nameOrID string, detachKeys *string, logs, stream *bool, stdin io.Reader, stdout io.Writer, stderr io.Writer, attachReady chan bool) error { +func Attach(ctx context.Context, nameOrID string, stdin io.Reader, stdout io.Writer, stderr io.Writer, attachReady chan bool, options *AttachOptions) error { + if options == nil { + options = new(AttachOptions) + } isSet := struct { stdin bool stdout bool @@ -55,27 +58,24 @@ func Attach(ctx context.Context, nameOrID string, detachKeys *string, logs, stre } // Do we need to wire in stdin? - ctnr, err := Inspect(ctx, nameOrID, bindings.PFalse) + ctnr, err := Inspect(ctx, nameOrID, new(InspectOptions).WithSize(false)) if err != nil { return err } - params := url.Values{} + params, err := options.ToParams() + if err != nil { + return err + } detachKeysInBytes := []byte{} - if detachKeys != nil { - params.Add("detachKeys", *detachKeys) + if options.Changed("DetachKeys") { + params.Add("detachKeys", options.GetDetachKeys()) - detachKeysInBytes, err = term.ToBytes(*detachKeys) + detachKeysInBytes, err = term.ToBytes(options.GetDetachKeys()) if err != nil { return errors.Wrapf(err, "invalid detach keys") } } - if logs != nil { - params.Add("logs", fmt.Sprintf("%t", *logs)) - } - if stream != nil { - params.Add("stream", fmt.Sprintf("%t", *stream)) - } if isSet.stdin { params.Add("stdin", "true") } @@ -278,13 +278,19 @@ func DemuxFrame(r io.Reader, buffer []byte, length int) (frame []byte, err error } // ResizeContainerTTY sets container's TTY height and width in characters -func ResizeContainerTTY(ctx context.Context, nameOrID string, height *int, width *int) error { - return resizeTTY(ctx, bindings.JoinURL("containers", nameOrID, "resize"), height, width) +func ResizeContainerTTY(ctx context.Context, nameOrID string, options *ResizeTTYOptions) error { + if options == nil { + options = new(ResizeTTYOptions) + } + return resizeTTY(ctx, bindings.JoinURL("containers", nameOrID, "resize"), options.Height, options.Width) } // ResizeExecTTY sets session's TTY height and width in characters -func ResizeExecTTY(ctx context.Context, nameOrID string, height *int, width *int) error { - return resizeTTY(ctx, bindings.JoinURL("exec", nameOrID, "resize"), height, width) +func ResizeExecTTY(ctx context.Context, nameOrID string, options *ResizeExecTTYOptions) error { + if options == nil { + options = new(ResizeExecTTYOptions) + } + return resizeTTY(ctx, bindings.JoinURL("exec", nameOrID, "resize"), options.Height, options.Width) } // resizeTTY set size of TTY of container @@ -337,9 +343,9 @@ func attachHandleResize(ctx, winCtx context.Context, winChange chan os.Signal, i var resizeErr error if isExec { - resizeErr = ResizeExecTTY(ctx, id, &h, &w) + resizeErr = ResizeExecTTY(ctx, id, new(ResizeExecTTYOptions).WithHeight(h).WithWidth(w)) } else { - resizeErr = ResizeContainerTTY(ctx, id, &h, &w) + resizeErr = ResizeContainerTTY(ctx, id, new(ResizeTTYOptions).WithHeight(h).WithWidth(w)) } if resizeErr != nil { logrus.Warnf("failed to resize TTY: %v", err) @@ -361,7 +367,10 @@ func setRawTerminal(file *os.File) (*terminal.State, error) { } // ExecStartAndAttach starts and attaches to a given exec session. -func ExecStartAndAttach(ctx context.Context, sessionID string, streams *define.AttachStreams) error { +func ExecStartAndAttach(ctx context.Context, sessionID string, options *ExecStartAndAttachOptions) error { + if options == nil { + options = new(ExecStartAndAttachOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return err @@ -450,10 +459,10 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, streams *define.A go attachHandleResize(ctx, winCtx, winChange, true, sessionID, terminalFile) } - if streams.AttachInput { + if options.GetAttachInput() { go func() { logrus.Debugf("Copying STDIN to socket") - _, err := utils.CopyDetachable(socket, streams.InputStream, []byte{}) + _, err := utils.CopyDetachable(socket, options.InputStream, []byte{}) if err != nil { logrus.Error("failed to write input to service: " + err.Error()) } @@ -463,11 +472,11 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, streams *define.A buffer := make([]byte, 1024) if isTerm { logrus.Debugf("Handling terminal attach to exec") - if !streams.AttachOutput { + if !options.GetAttachOutput() { return fmt.Errorf("exec session %s has a terminal and must have STDOUT enabled", sessionID) } // If not multiplex'ed, read from server and write to stdout - _, err := utils.CopyDetachable(streams.OutputStream, socket, []byte{}) + _, err := utils.CopyDetachable(options.GetOutputStream(), socket, []byte{}) if err != nil { return err } @@ -489,22 +498,22 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, streams *define.A switch { case fd == 0: - if streams.AttachInput { + if options.GetAttachInput() { // Write STDIN to STDOUT (echoing characters // typed by another attach session) - if _, err := streams.OutputStream.Write(frame[0:l]); err != nil { + if _, err := options.GetOutputStream().Write(frame[0:l]); err != nil { return err } } case fd == 1: - if streams.AttachOutput { - if _, err := streams.OutputStream.Write(frame[0:l]); err != nil { + if options.GetAttachOutput() { + if _, err := options.GetOutputStream().Write(frame[0:l]); err != nil { return err } } case fd == 2: - if streams.AttachError { - if _, err := streams.ErrorStream.Write(frame[0:l]); err != nil { + if options.GetAttachError() { + if _, err := options.GetErrorStream().Write(frame[0:l]); err != nil { return err } } diff --git a/pkg/bindings/containers/checkpoint.go b/pkg/bindings/containers/checkpoint.go index f466f8a34..c250558a6 100644 --- a/pkg/bindings/containers/checkpoint.go +++ b/pkg/bindings/containers/checkpoint.go @@ -3,8 +3,6 @@ package containers import ( "context" "net/http" - "net/url" - "strconv" "github.com/containers/podman/v2/pkg/bindings" "github.com/containers/podman/v2/pkg/domain/entities" @@ -12,27 +10,18 @@ import ( // Checkpoint checkpoints the given container (identified by nameOrID). All additional // options are options and allow for more fine grained control of the checkpoint process. -func Checkpoint(ctx context.Context, nameOrID string, keep, leaveRunning, tcpEstablished, ignoreRootFS *bool, export *string) (*entities.CheckpointReport, error) { +func Checkpoint(ctx context.Context, nameOrID string, options *CheckpointOptions) (*entities.CheckpointReport, error) { var report entities.CheckpointReport + if options == nil { + options = new(CheckpointOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if keep != nil { - params.Set("keep", strconv.FormatBool(*keep)) - } - if leaveRunning != nil { - params.Set("leaveRunning", strconv.FormatBool(*leaveRunning)) - } - if tcpEstablished != nil { - params.Set("TCPestablished", strconv.FormatBool(*tcpEstablished)) - } - if ignoreRootFS != nil { - params.Set("ignoreRootFS", strconv.FormatBool(*ignoreRootFS)) - } - if export != nil { - params.Set("export", *export) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/checkpoint", params, nil, nameOrID) if err != nil { @@ -43,33 +32,23 @@ func Checkpoint(ctx context.Context, nameOrID string, keep, leaveRunning, tcpEst // Restore restores a checkpointed container to running. The container is identified by the nameOrID option. All // additional options are optional and allow finer control of the restore process. -func Restore(ctx context.Context, nameOrID string, keep, tcpEstablished, ignoreRootFS, ignoreStaticIP, ignoreStaticMAC *bool, name, importArchive *string) (*entities.RestoreReport, error) { +func Restore(ctx context.Context, nameOrID string, options *RestoreOptions) (*entities.RestoreReport, error) { var report entities.RestoreReport + if options == nil { + options = new(RestoreOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if keep != nil { - params.Set("keep", strconv.FormatBool(*keep)) - } - if tcpEstablished != nil { - params.Set("TCPestablished", strconv.FormatBool(*tcpEstablished)) - } - if ignoreRootFS != nil { - params.Set("ignoreRootFS", strconv.FormatBool(*ignoreRootFS)) - } - if ignoreStaticIP != nil { - params.Set("ignoreStaticIP", strconv.FormatBool(*ignoreStaticIP)) - } - if ignoreStaticMAC != nil { - params.Set("ignoreStaticMAC", strconv.FormatBool(*ignoreStaticMAC)) - } - if name != nil { - params.Set("name", *name) + params, err := options.ToParams() + if err != nil { + return nil, err } - if importArchive != nil { - params.Set("import", *importArchive) + // The import key is a reserved golang term + params.Del("ImportArchive") + if i := options.GetImportAchive(); options.Changed("ImportArchive") { + params.Set("import", i) } response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/restore", params, nil, nameOrID) if err != nil { diff --git a/pkg/bindings/containers/commit.go b/pkg/bindings/containers/commit.go index 9ab4456a3..6205c75bd 100644 --- a/pkg/bindings/containers/commit.go +++ b/pkg/bindings/containers/commit.go @@ -3,8 +3,6 @@ package containers import ( "context" "net/http" - "net/url" - "strconv" "github.com/containers/podman/v2/pkg/api/handlers" "github.com/containers/podman/v2/pkg/bindings" @@ -12,35 +10,20 @@ import ( // Commit creates a container image from a container. The container is defined by nameOrID. Use // the CommitOptions for finer grain control on characteristics of the resulting image. -func Commit(ctx context.Context, nameOrID string, options CommitOptions) (handlers.IDResponse, error) { +func Commit(ctx context.Context, nameOrID string, options *CommitOptions) (handlers.IDResponse, error) { + if options == nil { + options = new(CommitOptions) + } id := handlers.IDResponse{} conn, err := bindings.GetClient(ctx) if err != nil { return id, err } - params := url.Values{} - params.Set("container", nameOrID) - if options.Author != nil { - params.Set("author", *options.Author) - } - for _, change := range options.Changes { - params.Set("changes", change) - } - if options.Comment != nil { - params.Set("comment", *options.Comment) - } - if options.Format != nil { - params.Set("format", *options.Format) - } - if options.Pause != nil { - params.Set("pause", strconv.FormatBool(*options.Pause)) - } - if options.Repo != nil { - params.Set("repo", *options.Repo) - } - if options.Tag != nil { - params.Set("tag", *options.Tag) + params, err := options.ToParams() + if err != nil { + return handlers.IDResponse{}, err } + params.Set("container", nameOrID) response, err := conn.DoRequest(nil, http.MethodPost, "/commit", params, nil) if err != nil { return id, err diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go index 4331ae6c2..650aa9ac5 100644 --- a/pkg/bindings/containers/containers.go +++ b/pkg/bindings/containers/containers.go @@ -25,34 +25,18 @@ var ( // the most recent number of containers. The pod and size booleans indicate that pod information and rootfs // size information should also be included. Finally, the sync bool synchronizes the OCI runtime and // container state. -func List(ctx context.Context, filters map[string][]string, all *bool, last *int, namespace, size, sync *bool) ([]entities.ListContainer, error) { // nolint:typecheck +func List(ctx context.Context, options *ListOptions) ([]entities.ListContainer, error) { // nolint:typecheck + if options == nil { + options = new(ListOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } var containers []entities.ListContainer - params := url.Values{} - if all != nil { - params.Set("all", strconv.FormatBool(*all)) - } - if last != nil { - params.Set("limit", strconv.Itoa(*last)) - } - if size != nil { - params.Set("size", strconv.FormatBool(*size)) - } - if sync != nil { - params.Set("sync", strconv.FormatBool(*sync)) - } - if namespace != nil { - params.Set("namespace", strconv.FormatBool(*namespace)) - } - if filters != nil { - filterString, err := bindings.FiltersToString(filters) - if err != nil { - return nil, err - } - params.Set("filters", filterString) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodGet, "/containers/json", params, nil) if err != nil { @@ -65,19 +49,18 @@ func List(ctx context.Context, filters map[string][]string, all *bool, last *int // used for more granular selection of containers. The main error returned indicates if there were runtime // errors like finding containers. Errors specific to the removal of a container are in the PruneContainerResponse // structure. -func Prune(ctx context.Context, filters map[string][]string) (*entities.ContainerPruneReport, error) { +func Prune(ctx context.Context, options *PruneOptions) (*entities.ContainerPruneReport, error) { + if options == nil { + options = new(PruneOptions) + } var reports *entities.ContainerPruneReport conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if filters != nil { - filterString, err := bindings.FiltersToString(filters) - if err != nil { - return nil, err - } - params.Set("filters", filterString) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodPost, "/containers/prune", params, nil) if err != nil { @@ -89,17 +72,20 @@ func Prune(ctx context.Context, filters map[string][]string) (*entities.Containe // Remove removes a container from local storage. The force bool designates // that the container should be removed forcibly (example, even it is running). The volumes // bool dictates that a container's volumes should also be removed. -func Remove(ctx context.Context, nameOrID string, force, volumes *bool) error { +func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) error { + if options == nil { + options = new(RemoveOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return err } params := url.Values{} - if force != nil { - params.Set("force", strconv.FormatBool(*force)) + if v := options.GetVolumes(); options.Changed("Volumes") { + params.Set("v", strconv.FormatBool(v)) } - if volumes != nil { - params.Set("v", strconv.FormatBool(*volumes)) + if force := options.GetForce(); options.Changed("Force") { + params.Set("force", strconv.FormatBool(force)) } response, err := conn.DoRequest(nil, http.MethodDelete, "/containers/%s", params, nil, nameOrID) if err != nil { @@ -112,14 +98,17 @@ func Remove(ctx context.Context, nameOrID string, force, volumes *bool) error { // or a partial/full ID. The size bool determines whether the size of the container's root filesystem // should be calculated. Calculating the size of a container requires extra work from the filesystem and // is therefore slower. -func Inspect(ctx context.Context, nameOrID string, size *bool) (*define.InspectContainerData, error) { +func Inspect(ctx context.Context, nameOrID string, options *InspectOptions) (*define.InspectContainerData, error) { + if options == nil { + options = new(InspectOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if size != nil { - params.Set("size", strconv.FormatBool(*size)) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/json", params, nil, nameOrID) if err != nil { @@ -132,12 +121,18 @@ func Inspect(ctx context.Context, nameOrID string, size *bool) (*define.InspectC // Kill sends a given signal to a given container. The signal should be the string // representation of a signal like 'SIGKILL'. The nameOrID can be a container name // or a partial/full ID -func Kill(ctx context.Context, nameOrID string, sig string) error { +func Kill(ctx context.Context, nameOrID string, sig string, options *KillOptions) error { + if options == nil { + options = new(KillOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return err } - params := url.Values{} + params, err := options.ToParams() + if err != nil { + return err + } params.Set("signal", sig) response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/kill", params, nil, nameOrID) if err != nil { @@ -149,7 +144,11 @@ func Kill(ctx context.Context, nameOrID string, sig string) error { // Pause pauses a given container. The nameOrID can be a container name // or a partial/full ID. -func Pause(ctx context.Context, nameOrID string) error { +func Pause(ctx context.Context, nameOrID string, options *PauseOptions) error { + if options == nil { + options = new(PauseOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return err @@ -164,14 +163,17 @@ func Pause(ctx context.Context, nameOrID string) error { // Restart restarts a running container. The nameOrID can be a container name // or a partial/full ID. The optional timeout specifies the number of seconds to wait // for the running container to stop before killing it. -func Restart(ctx context.Context, nameOrID string, timeout *int) error { +func Restart(ctx context.Context, nameOrID string, options *RestartOptions) error { + if options == nil { + options = new(RestartOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return err } params := url.Values{} - if timeout != nil { - params.Set("t", strconv.Itoa(*timeout)) + if options.Changed("Timeout") { + params.Set("t", strconv.Itoa(options.GetTimeout())) } response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/restart", params, nil, nameOrID) if err != nil { @@ -183,15 +185,18 @@ func Restart(ctx context.Context, nameOrID string, timeout *int) error { // Start starts a non-running container.The nameOrID can be a container name // or a partial/full ID. The optional parameter for detach keys are to override the default // detach key sequence. -func Start(ctx context.Context, nameOrID string, detachKeys *string) error { +func Start(ctx context.Context, nameOrID string, options *StartOptions) error { + if options == nil { + options = new(StartOptions) + } logrus.Infof("Going to start container %q", nameOrID) conn, err := bindings.GetClient(ctx) if err != nil { return err } - params := url.Values{} - if detachKeys != nil { - params.Set("detachKeys", *detachKeys) + params, err := options.ToParams() + if err != nil { + return err } response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/start", params, nil, nameOrID) if err != nil { @@ -200,14 +205,18 @@ func Start(ctx context.Context, nameOrID string, detachKeys *string) error { return response.Process(nil) } -func Stats(ctx context.Context, containers []string, stream *bool) (chan entities.ContainerStatsReport, error) { +func Stats(ctx context.Context, containers []string, options *StatsOptions) (chan entities.ContainerStatsReport, error) { + if options == nil { + options = new(StatsOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if stream != nil { - params.Set("stream", strconv.FormatBool(*stream)) + params, err := options.ToParams() + if err != nil { + return nil, err } for _, c := range containers { params.Add("containers", c) @@ -225,8 +234,8 @@ func Stats(ctx context.Context, containers []string, stream *bool) (chan entitie dec := json.NewDecoder(response.Body) doStream := true - if stream != nil { - doStream = *stream + if options.Changed("Stream") { + doStream = options.GetStream() } streamLabel: // label to flatten the scope @@ -253,16 +262,18 @@ func Stats(ctx context.Context, containers []string, stream *bool) (chan entitie // Top gathers statistics about the running processes in a container. The nameOrID can be a container name // or a partial/full ID. The descriptors allow for specifying which data to collect from the process. -func Top(ctx context.Context, nameOrID string, descriptors []string) ([]string, error) { +func Top(ctx context.Context, nameOrID string, options *TopOptions) ([]string, error) { + if options == nil { + options = new(TopOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } params := url.Values{} - - if len(descriptors) > 0 { - // flatten the slice into one string - params.Set("ps_args", strings.Join(descriptors, ",")) + if options.Changed("Descriptors") { + ps_args := strings.Join(options.GetDescriptors(), ",") + params.Add("ps_args", ps_args) } response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/top", params, nil, nameOrID) if err != nil { @@ -287,7 +298,11 @@ func Top(ctx context.Context, nameOrID string, descriptors []string) ([]string, // Unpause resumes the given paused container. The nameOrID can be a container name // or a partial/full ID. -func Unpause(ctx context.Context, nameOrID string) error { +func Unpause(ctx context.Context, nameOrID string, options *UnpauseOptions) error { + if options == nil { + options = new(UnpauseOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return err @@ -302,15 +317,18 @@ func Unpause(ctx context.Context, nameOrID string) error { // Wait blocks until the given container reaches a condition. If not provided, the condition will // default to stopped. If the condition is stopped, an exit code for the container will be provided. The // nameOrID can be a container name or a partial/full ID. -func Wait(ctx context.Context, nameOrID string, condition *define.ContainerStatus) (int32, error) { // nolint +func Wait(ctx context.Context, nameOrID string, options *WaitOptions) (int32, error) { // nolint + if options == nil { + options = new(WaitOptions) + } var exitCode int32 conn, err := bindings.GetClient(ctx) if err != nil { return exitCode, err } params := url.Values{} - if condition != nil { - params.Set("condition", condition.String()) + if options.Changed("Condition") { + params.Set("condition", options.GetCondition().String()) } response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/wait", params, nil, nameOrID) if err != nil { @@ -338,14 +356,17 @@ func Exists(ctx context.Context, nameOrID string, external bool) (bool, error) { // Stop stops a running container. The timeout is optional. The nameOrID can be a container name // or a partial/full ID -func Stop(ctx context.Context, nameOrID string, timeout *uint) error { - params := url.Values{} - conn, err := bindings.GetClient(ctx) +func Stop(ctx context.Context, nameOrID string, options *StopOptions) error { + if options == nil { + options = new(StopOptions) + } + params, err := options.ToParams() if err != nil { return err } - if timeout != nil { - params.Set("t", strconv.Itoa(int(*timeout))) + conn, err := bindings.GetClient(ctx) + if err != nil { + return err } response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/stop", params, nil, nameOrID) if err != nil { @@ -356,7 +377,11 @@ func Stop(ctx context.Context, nameOrID string, timeout *uint) error { // Export creates a tarball of the given name or ID of a container. It // requires an io.Writer be provided to write the tarball. -func Export(ctx context.Context, nameOrID string, w io.Writer) error { +func Export(ctx context.Context, nameOrID string, w io.Writer, options *ExportOptions) error { + if options == nil { + options = new(ExportOptions) + } + _ = options params := url.Values{} conn, err := bindings.GetClient(ctx) if err != nil { @@ -376,7 +401,11 @@ func Export(ctx context.Context, nameOrID string, w io.Writer) error { // ContainerInit takes a created container and executes all of the // preparations to run the container except it will not start // or attach to the container -func ContainerInit(ctx context.Context, nameOrID string) error { +func ContainerInit(ctx context.Context, nameOrID string, options *InitOptions) error { + if options == nil { + options = new(InitOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return err @@ -391,7 +420,11 @@ func ContainerInit(ctx context.Context, nameOrID string) error { return response.Process(nil) } -func ShouldRestart(ctx context.Context, nameOrID string) (bool, error) { +func ShouldRestart(ctx context.Context, nameOrID string, options *ShouldRestartOptions) (bool, error) { + if options == nil { + options = new(ShouldRestartOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return false, err diff --git a/pkg/bindings/containers/create.go b/pkg/bindings/containers/create.go index 5c29ec577..177cf2e9c 100644 --- a/pkg/bindings/containers/create.go +++ b/pkg/bindings/containers/create.go @@ -11,8 +11,12 @@ import ( jsoniter "github.com/json-iterator/go" ) -func CreateWithSpec(ctx context.Context, s *specgen.SpecGenerator) (entities.ContainerCreateResponse, error) { +func CreateWithSpec(ctx context.Context, s *specgen.SpecGenerator, options *CreateOptions) (entities.ContainerCreateResponse, error) { var ccr entities.ContainerCreateResponse + if options == nil { + options = new(CreateOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return ccr, err diff --git a/pkg/bindings/containers/diff.go b/pkg/bindings/containers/diff.go index 1478bd940..015172360 100644 --- a/pkg/bindings/containers/diff.go +++ b/pkg/bindings/containers/diff.go @@ -9,7 +9,11 @@ import ( ) // Diff provides the changes between two container layers -func Diff(ctx context.Context, nameOrID string) ([]archive.Change, error) { +func Diff(ctx context.Context, nameOrID string, options *DiffOptions) ([]archive.Change, error) { + if options == nil { + options = new(DiffOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err diff --git a/pkg/bindings/containers/exec.go b/pkg/bindings/containers/exec.go index e080077c8..98ca975a0 100644 --- a/pkg/bindings/containers/exec.go +++ b/pkg/bindings/containers/exec.go @@ -50,7 +50,11 @@ func ExecCreate(ctx context.Context, nameOrID string, config *handlers.ExecCreat // ExecInspect inspects an existing exec session, returning detailed information // about it. -func ExecInspect(ctx context.Context, sessionID string) (*define.InspectExecSession, error) { +func ExecInspect(ctx context.Context, sessionID string, options *ExecInspectOptions) (*define.InspectExecSession, error) { + if options == nil { + options = new(ExecInspectOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err @@ -72,7 +76,11 @@ func ExecInspect(ctx context.Context, sessionID string) (*define.InspectExecSess } // ExecStart starts (but does not attach to) a given exec session. -func ExecStart(ctx context.Context, sessionID string) error { +func ExecStart(ctx context.Context, sessionID string, options *ExecStartOptions) error { + if options == nil { + options = new(ExecStartOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return err diff --git a/pkg/bindings/containers/healthcheck.go b/pkg/bindings/containers/healthcheck.go index 9de6ffbe0..44b27629b 100644 --- a/pkg/bindings/containers/healthcheck.go +++ b/pkg/bindings/containers/healthcheck.go @@ -10,7 +10,11 @@ import ( // RunHealthCheck executes the container's healthcheck and returns the health status of the // container. -func RunHealthCheck(ctx context.Context, nameOrID string) (*define.HealthCheckResults, error) { +func RunHealthCheck(ctx context.Context, nameOrID string, options *HealthCheckOptions) (*define.HealthCheckResults, error) { + if options == nil { + options = new(HealthCheckOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err diff --git a/pkg/bindings/containers/logs.go b/pkg/bindings/containers/logs.go index a73517bac..04307d880 100644 --- a/pkg/bindings/containers/logs.go +++ b/pkg/bindings/containers/logs.go @@ -5,7 +5,6 @@ import ( "fmt" "io" "net/http" - "net/url" "strconv" "github.com/containers/podman/v2/pkg/bindings" @@ -14,35 +13,20 @@ import ( // Logs obtains a container's logs given the options provided. The logs are then sent to the // stdout|stderr channels as strings. -func Logs(ctx context.Context, nameOrID string, opts LogOptions, stdoutChan, stderrChan chan string) error { +func Logs(ctx context.Context, nameOrID string, options *LogOptions, stdoutChan, stderrChan chan string) error { + if options == nil { + options = new(LogOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return err } - params := url.Values{} - if opts.Follow != nil { - params.Set("follow", strconv.FormatBool(*opts.Follow)) - } - if opts.Since != nil { - params.Set("since", *opts.Since) - } - if opts.Stderr != nil { - params.Set("stderr", strconv.FormatBool(*opts.Stderr)) - } - if opts.Stdout != nil { - params.Set("stdout", strconv.FormatBool(*opts.Stdout)) - } - if opts.Tail != nil { - params.Set("tail", *opts.Tail) - } - if opts.Timestamps != nil { - params.Set("timestamps", strconv.FormatBool(*opts.Timestamps)) - } - if opts.Until != nil { - params.Set("until", *opts.Until) + params, err := options.ToParams() + if err != nil { + return err } // The API requires either stdout|stderr be used. If neither are specified, we specify stdout - if opts.Stdout == nil && opts.Stderr == nil { + if options.Stdout == nil && options.Stderr == nil { params.Set("stdout", strconv.FormatBool(true)) } response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/logs", params, nil, nameOrID) diff --git a/pkg/bindings/containers/mount.go b/pkg/bindings/containers/mount.go index 4c2e0c188..4fd9f89bc 100644 --- a/pkg/bindings/containers/mount.go +++ b/pkg/bindings/containers/mount.go @@ -9,7 +9,11 @@ import ( // Mount mounts an existing container to the filesystem. It returns the path // of the mounted container in string format. -func Mount(ctx context.Context, nameOrID string) (string, error) { +func Mount(ctx context.Context, nameOrID string, options *MountOptions) (string, error) { + if options == nil { + options = new(MountOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return "", err @@ -26,7 +30,11 @@ func Mount(ctx context.Context, nameOrID string) (string, error) { // Unmount unmounts a container from the filesystem. The container must not be running // or the unmount will fail. -func Unmount(ctx context.Context, nameOrID string) error { +func Unmount(ctx context.Context, nameOrID string, options *UnmountOptions) error { + if options == nil { + options = new(UnmountOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return err @@ -39,7 +47,11 @@ func Unmount(ctx context.Context, nameOrID string) error { } // GetMountedContainerPaths returns a map of mounted containers and their mount locations. -func GetMountedContainerPaths(ctx context.Context) (map[string]string, error) { +func GetMountedContainerPaths(ctx context.Context, options *MountedContainerPathsOptions) (map[string]string, error) { + if options == nil { + options = new(MountedContainerPathsOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err diff --git a/pkg/bindings/containers/types.go b/pkg/bindings/containers/types.go index f288c2944..24402e982 100644 --- a/pkg/bindings/containers/types.go +++ b/pkg/bindings/containers/types.go @@ -1,5 +1,13 @@ package containers +import ( + "bufio" + "io" + + "github.com/containers/podman/v2/libpod/define" +) + +//go:generate go run ../generator/generator.go LogOptions // LogOptions describe finer control of log content or // how the content is formatted. type LogOptions struct { @@ -12,6 +20,7 @@ type LogOptions struct { Until *string } +//go:generate go run ../generator/generator.go CommitOptions // CommitOptions describe details about the resulting committed // image as defined by repo and tag. None of these options // are required. @@ -24,3 +33,200 @@ type CommitOptions struct { Repo *string Tag *string } + +//go:generate go run ../generator/generator.go AttachOptions +// AttachOptions are optional options for attaching to containers +type AttachOptions struct { + DetachKeys *string + Logs *bool + Stream *bool +} + +//go:generate go run ../generator/generator.go CheckpointOptions +// CheckpointOptions are optional options for checkpointing containers +type CheckpointOptions struct { + Export *string + IgnoreRootfs *bool + Keep *bool + LeaveRunning *bool + TCPEstablished *bool +} + +//go:generate go run ../generator/generator.go RestoreOptions +// RestoreOptions are optional options for restoring containers +type RestoreOptions struct { + IgnoreRootfs *bool + IgnoreStaticIP *bool + IgnoreStaticMAC *bool + ImportAchive *string + Keep *bool + Name *string + TCPEstablished *bool +} + +//go:generate go run ../generator/generator.go CreateOptions +// CreateOptions are optional options for creating containers +type CreateOptions struct{} + +//go:generate go run ../generator/generator.go DiffOptions +// DiffOptions are optional options for creating containers +type DiffOptions struct{} + +//go:generate go run ../generator/generator.go ExecInspectOptions +// ExecInspectOptions are optional options for inspecting +// exec sessions +type ExecInspectOptions struct{} + +//go:generate go run ../generator/generator.go ExecStartOptions +// ExecStartOptions are optional options for starting +// exec sessions +type ExecStartOptions struct{} + +//go:generate go run ../generator/generator.go HealthCheckOptions +// HealthCheckOptions are optional options for checking +// the health of a container +type HealthCheckOptions struct{} + +//go:generate go run ../generator/generator.go MountOptions +// MountOptions are optional options for mounting +// containers +type MountOptions struct{} + +//go:generate go run ../generator/generator.go UnmountOptions +// UnmountOptions are optional options for unmounting +// containers +type UnmountOptions struct{} + +//go:generate go run ../generator/generator.go MountedContainerPathsOptions +// MountedContainerPathsOptions are optional options for getting +// container mount paths +type MountedContainerPathsOptions struct{} + +//go:generate go run ../generator/generator.go ListOptions +// ListOptions are optional options for listing containers +type ListOptions struct { + All *bool + Filters map[string][]string + Last *int + Namespace *bool + Size *bool + Sync *bool +} + +//go:generate go run ../generator/generator.go PruneOptions +// PruneOptions are optional options for pruning containers +type PruneOptions struct { + Filters map[string][]string +} + +//go:generate go run ../generator/generator.go RemoveOptions +// RemoveOptions are optional options for removing containers +type RemoveOptions struct { + Force *bool + Volumes *bool +} + +//go:generate go run ../generator/generator.go InspectOptions +// InspectOptions are optional options for inspecting containers +type InspectOptions struct { + Size *bool +} + +//go:generate go run ../generator/generator.go KillOptions +// KillOptions are optional options for killing containers +type KillOptions struct { +} + +//go:generate go run ../generator/generator.go PauseOptions +// PauseOptions are optional options for pausing containers +type PauseOptions struct{} + +//go:generate go run ../generator/generator.go RestartOptions +// RestartOptions are optional options for restarting containers +type RestartOptions struct { + Timeout *int +} + +//go:generate go run ../generator/generator.go StartOptions +// StartOptions are optional options for starting containers +type StartOptions struct { + DetachKeys *string +} + +//go:generate go run ../generator/generator.go StatsOptions +// StatsOptions are optional options for getting stats on containers +type StatsOptions struct { + Stream *bool +} + +//go:generate go run ../generator/generator.go TopOptions +// TopOptions are optional options for getting running +// processes in containers +type TopOptions struct { + Descriptors *[]string +} + +//go:generate go run ../generator/generator.go UnpauseOptions +// UnpauseOptions are optional options for unpausing containers +type UnpauseOptions struct{} + +//go:generate go run ../generator/generator.go WaitOptions +// WaitOptions are optional options for waiting on containers +type WaitOptions struct { + Condition *define.ContainerStatus +} + +//go:generate go run ../generator/generator.go StopOptions +// StopOptions are optional options for stopping containers +type StopOptions struct { + Timeout *uint +} + +//go:generate go run ../generator/generator.go ExportOptions +// ExportOptions are optional options for exporting containers +type ExportOptions struct{} + +//go:generate go run ../generator/generator.go InitOptions +// InitOptions are optional options for initing containers +type InitOptions struct{} + +//go:generate go run ../generator/generator.go ShouldRestartOptions +// ShouldRestartOptions +type ShouldRestartOptions struct{} + +//go:generate go run ../generator/generator.go ResizeTTYOptions +// ResizeTTYOptions are optional options for resizing +// container TTYs +type ResizeTTYOptions struct { + Height *int + Width *int +} + +//go:generate go run ../generator/generator.go ResizeExecTTYOptions +// ResizeExecTTYOptions are optional options for resizing +// container ExecTTYs +type ResizeExecTTYOptions struct { + Height *int + Width *int +} + +//go:generate go run ../generator/generator.go ExecStartAndAttachOptions +// ExecStartAndAttachOptions are optional options for resizing +// container ExecTTYs +type ExecStartAndAttachOptions struct { + // OutputStream will be attached to container's STDOUT + OutputStream *io.WriteCloser + // ErrorStream will be attached to container's STDERR + ErrorStream *io.WriteCloser + // InputStream will be attached to container's STDIN + InputStream *bufio.Reader + // AttachOutput is whether to attach to STDOUT + // If false, stdout will not be attached + AttachOutput *bool + // AttachError is whether to attach to STDERR + // If false, stdout will not be attached + AttachError *bool + // AttachInput is whether to attach to STDIN + // If false, stdout will not be attached + AttachInput *bool +} diff --git a/pkg/bindings/containers/types_attach_options.go b/pkg/bindings/containers/types_attach_options.go new file mode 100644 index 000000000..4ffb8ab17 --- /dev/null +++ b/pkg/bindings/containers/types_attach_options.go @@ -0,0 +1,136 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:18.566804404 -0600 CST m=+0.000258831 +*/ + +// Changed +func (o *AttachOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *AttachOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithDetachKeys +func (o *AttachOptions) WithDetachKeys(value string) *AttachOptions { + v := &value + o.DetachKeys = v + return o +} + +// GetDetachKeys +func (o *AttachOptions) GetDetachKeys() string { + var detachKeys string + if o.DetachKeys == nil { + return detachKeys + } + return *o.DetachKeys +} + +// WithLogs +func (o *AttachOptions) WithLogs(value bool) *AttachOptions { + v := &value + o.Logs = v + return o +} + +// GetLogs +func (o *AttachOptions) GetLogs() bool { + var logs bool + if o.Logs == nil { + return logs + } + return *o.Logs +} + +// WithStream +func (o *AttachOptions) WithStream(value bool) *AttachOptions { + v := &value + o.Stream = v + return o +} + +// GetStream +func (o *AttachOptions) GetStream() bool { + var stream bool + if o.Stream == nil { + return stream + } + return *o.Stream +} diff --git a/pkg/bindings/containers/types_checkpoint_options.go b/pkg/bindings/containers/types_checkpoint_options.go new file mode 100644 index 000000000..d03dc8231 --- /dev/null +++ b/pkg/bindings/containers/types_checkpoint_options.go @@ -0,0 +1,168 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:18.714853285 -0600 CST m=+0.000319103 +*/ + +// Changed +func (o *CheckpointOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *CheckpointOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithExport +func (o *CheckpointOptions) WithExport(value string) *CheckpointOptions { + v := &value + o.Export = v + return o +} + +// GetExport +func (o *CheckpointOptions) GetExport() string { + var export string + if o.Export == nil { + return export + } + return *o.Export +} + +// WithIgnoreRootfs +func (o *CheckpointOptions) WithIgnoreRootfs(value bool) *CheckpointOptions { + v := &value + o.IgnoreRootfs = v + return o +} + +// GetIgnoreRootfs +func (o *CheckpointOptions) GetIgnoreRootfs() bool { + var ignoreRootfs bool + if o.IgnoreRootfs == nil { + return ignoreRootfs + } + return *o.IgnoreRootfs +} + +// WithKeep +func (o *CheckpointOptions) WithKeep(value bool) *CheckpointOptions { + v := &value + o.Keep = v + return o +} + +// GetKeep +func (o *CheckpointOptions) GetKeep() bool { + var keep bool + if o.Keep == nil { + return keep + } + return *o.Keep +} + +// WithLeaveRunning +func (o *CheckpointOptions) WithLeaveRunning(value bool) *CheckpointOptions { + v := &value + o.LeaveRunning = v + return o +} + +// GetLeaveRunning +func (o *CheckpointOptions) GetLeaveRunning() bool { + var leaveRunning bool + if o.LeaveRunning == nil { + return leaveRunning + } + return *o.LeaveRunning +} + +// WithTCPEstablished +func (o *CheckpointOptions) WithTCPEstablished(value bool) *CheckpointOptions { + v := &value + o.TCPEstablished = v + return o +} + +// GetTCPEstablished +func (o *CheckpointOptions) GetTCPEstablished() bool { + var tCPEstablished bool + if o.TCPEstablished == nil { + return tCPEstablished + } + return *o.TCPEstablished +} diff --git a/pkg/bindings/containers/types_commit_options.go b/pkg/bindings/containers/types_commit_options.go new file mode 100644 index 000000000..a8b215141 --- /dev/null +++ b/pkg/bindings/containers/types_commit_options.go @@ -0,0 +1,200 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:18.420656951 -0600 CST m=+0.000259662 +*/ + +// Changed +func (o *CommitOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *CommitOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithAuthor +func (o *CommitOptions) WithAuthor(value string) *CommitOptions { + v := &value + o.Author = v + return o +} + +// GetAuthor +func (o *CommitOptions) GetAuthor() string { + var author string + if o.Author == nil { + return author + } + return *o.Author +} + +// WithChanges +func (o *CommitOptions) WithChanges(value []string) *CommitOptions { + v := value + o.Changes = v + return o +} + +// GetChanges +func (o *CommitOptions) GetChanges() []string { + var changes []string + if o.Changes == nil { + return changes + } + return o.Changes +} + +// WithComment +func (o *CommitOptions) WithComment(value string) *CommitOptions { + v := &value + o.Comment = v + return o +} + +// GetComment +func (o *CommitOptions) GetComment() string { + var comment string + if o.Comment == nil { + return comment + } + return *o.Comment +} + +// WithFormat +func (o *CommitOptions) WithFormat(value string) *CommitOptions { + v := &value + o.Format = v + return o +} + +// GetFormat +func (o *CommitOptions) GetFormat() string { + var format string + if o.Format == nil { + return format + } + return *o.Format +} + +// WithPause +func (o *CommitOptions) WithPause(value bool) *CommitOptions { + v := &value + o.Pause = v + return o +} + +// GetPause +func (o *CommitOptions) GetPause() bool { + var pause bool + if o.Pause == nil { + return pause + } + return *o.Pause +} + +// WithRepo +func (o *CommitOptions) WithRepo(value string) *CommitOptions { + v := &value + o.Repo = v + return o +} + +// GetRepo +func (o *CommitOptions) GetRepo() string { + var repo string + if o.Repo == nil { + return repo + } + return *o.Repo +} + +// WithTag +func (o *CommitOptions) WithTag(value string) *CommitOptions { + v := &value + o.Tag = v + return o +} + +// GetTag +func (o *CommitOptions) GetTag() string { + var tag string + if o.Tag == nil { + return tag + } + return *o.Tag +} diff --git a/pkg/bindings/containers/types_create_options.go b/pkg/bindings/containers/types_create_options.go new file mode 100644 index 000000000..4dbce0203 --- /dev/null +++ b/pkg/bindings/containers/types_create_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:19.011789618 -0600 CST m=+0.000259413 +*/ + +// Changed +func (o *CreateOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *CreateOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} diff --git a/pkg/bindings/containers/types_diff_options.go b/pkg/bindings/containers/types_diff_options.go new file mode 100644 index 000000000..be3bbf554 --- /dev/null +++ b/pkg/bindings/containers/types_diff_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:19.159128927 -0600 CST m=+0.000255635 +*/ + +// Changed +func (o *DiffOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *DiffOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} diff --git a/pkg/bindings/containers/types_execinspect_options.go b/pkg/bindings/containers/types_execinspect_options.go new file mode 100644 index 000000000..3c4c870be --- /dev/null +++ b/pkg/bindings/containers/types_execinspect_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:19.303239014 -0600 CST m=+0.000256861 +*/ + +// Changed +func (o *ExecInspectOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ExecInspectOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} diff --git a/pkg/bindings/containers/types_execstart_options.go b/pkg/bindings/containers/types_execstart_options.go new file mode 100644 index 000000000..66fdc82cb --- /dev/null +++ b/pkg/bindings/containers/types_execstart_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:19.447714428 -0600 CST m=+0.000257278 +*/ + +// Changed +func (o *ExecStartOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ExecStartOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} diff --git a/pkg/bindings/containers/types_execstartandattach_options.go b/pkg/bindings/containers/types_execstartandattach_options.go new file mode 100644 index 000000000..43900d29d --- /dev/null +++ b/pkg/bindings/containers/types_execstartandattach_options.go @@ -0,0 +1,186 @@ +package containers + +import ( + "bufio" + "io" + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:22.827903078 -0600 CST m=+0.000269906 +*/ + +// Changed +func (o *ExecStartAndAttachOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ExecStartAndAttachOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithOutputStream +func (o *ExecStartAndAttachOptions) WithOutputStream(value io.WriteCloser) *ExecStartAndAttachOptions { + v := &value + o.OutputStream = v + return o +} + +// GetOutputStream +func (o *ExecStartAndAttachOptions) GetOutputStream() io.WriteCloser { + var outputStream io.WriteCloser + if o.OutputStream == nil { + return outputStream + } + return *o.OutputStream +} + +// WithErrorStream +func (o *ExecStartAndAttachOptions) WithErrorStream(value io.WriteCloser) *ExecStartAndAttachOptions { + v := &value + o.ErrorStream = v + return o +} + +// GetErrorStream +func (o *ExecStartAndAttachOptions) GetErrorStream() io.WriteCloser { + var errorStream io.WriteCloser + if o.ErrorStream == nil { + return errorStream + } + return *o.ErrorStream +} + +// WithInputStream +func (o *ExecStartAndAttachOptions) WithInputStream(value bufio.Reader) *ExecStartAndAttachOptions { + v := &value + o.InputStream = v + return o +} + +// GetInputStream +func (o *ExecStartAndAttachOptions) GetInputStream() bufio.Reader { + var inputStream bufio.Reader + if o.InputStream == nil { + return inputStream + } + return *o.InputStream +} + +// WithAttachOutput +func (o *ExecStartAndAttachOptions) WithAttachOutput(value bool) *ExecStartAndAttachOptions { + v := &value + o.AttachOutput = v + return o +} + +// GetAttachOutput +func (o *ExecStartAndAttachOptions) GetAttachOutput() bool { + var attachOutput bool + if o.AttachOutput == nil { + return attachOutput + } + return *o.AttachOutput +} + +// WithAttachError +func (o *ExecStartAndAttachOptions) WithAttachError(value bool) *ExecStartAndAttachOptions { + v := &value + o.AttachError = v + return o +} + +// GetAttachError +func (o *ExecStartAndAttachOptions) GetAttachError() bool { + var attachError bool + if o.AttachError == nil { + return attachError + } + return *o.AttachError +} + +// WithAttachInput +func (o *ExecStartAndAttachOptions) WithAttachInput(value bool) *ExecStartAndAttachOptions { + v := &value + o.AttachInput = v + return o +} + +// GetAttachInput +func (o *ExecStartAndAttachOptions) GetAttachInput() bool { + var attachInput bool + if o.AttachInput == nil { + return attachInput + } + return *o.AttachInput +} diff --git a/pkg/bindings/containers/types_export_options.go b/pkg/bindings/containers/types_export_options.go new file mode 100644 index 000000000..e325bd2cd --- /dev/null +++ b/pkg/bindings/containers/types_export_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:22.101679998 -0600 CST m=+0.000261669 +*/ + +// Changed +func (o *ExportOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ExportOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} diff --git a/pkg/bindings/containers/types_healthcheck_options.go b/pkg/bindings/containers/types_healthcheck_options.go new file mode 100644 index 000000000..8c4300366 --- /dev/null +++ b/pkg/bindings/containers/types_healthcheck_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:19.593883686 -0600 CST m=+0.000289845 +*/ + +// Changed +func (o *HealthCheckOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *HealthCheckOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} diff --git a/pkg/bindings/containers/types_init_options.go b/pkg/bindings/containers/types_init_options.go new file mode 100644 index 000000000..655362f62 --- /dev/null +++ b/pkg/bindings/containers/types_init_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:22.245077233 -0600 CST m=+0.000255461 +*/ + +// Changed +func (o *InitOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *InitOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} diff --git a/pkg/bindings/containers/types_inspect_options.go b/pkg/bindings/containers/types_inspect_options.go new file mode 100644 index 000000000..884f5524d --- /dev/null +++ b/pkg/bindings/containers/types_inspect_options.go @@ -0,0 +1,104 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:20.635987603 -0600 CST m=+0.000260270 +*/ + +// Changed +func (o *InspectOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *InspectOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithSize +func (o *InspectOptions) WithSize(value bool) *InspectOptions { + v := &value + o.Size = v + return o +} + +// GetSize +func (o *InspectOptions) GetSize() bool { + var size bool + if o.Size == nil { + return size + } + return *o.Size +} diff --git a/pkg/bindings/containers/types_kill_options.go b/pkg/bindings/containers/types_kill_options.go new file mode 100644 index 000000000..3d6fa6224 --- /dev/null +++ b/pkg/bindings/containers/types_kill_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:20.781581076 -0600 CST m=+0.000259040 +*/ + +// Changed +func (o *KillOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *KillOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} diff --git a/pkg/bindings/containers/types_list_options.go b/pkg/bindings/containers/types_list_options.go new file mode 100644 index 000000000..dd74d37b7 --- /dev/null +++ b/pkg/bindings/containers/types_list_options.go @@ -0,0 +1,186 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + "strings" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:20.199081744 -0600 CST m=+0.000270626 +*/ + +// Changed +func (o *ListOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ListOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + fieldName = strings.ToLower(fieldName) + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithAll +func (o *ListOptions) WithAll(value bool) *ListOptions { + v := &value + o.All = v + return o +} + +// GetAll +func (o *ListOptions) GetAll() bool { + var all bool + if o.All == nil { + return all + } + return *o.All +} + +// WithFilters +func (o *ListOptions) WithFilters(value map[string][]string) *ListOptions { + v := value + o.Filters = v + return o +} + +// GetFilters +func (o *ListOptions) GetFilters() map[string][]string { + var filters map[string][]string + if o.Filters == nil { + return filters + } + return o.Filters +} + +// WithLast +func (o *ListOptions) WithLast(value int) *ListOptions { + v := &value + o.Last = v + return o +} + +// GetLast +func (o *ListOptions) GetLast() int { + var last int + if o.Last == nil { + return last + } + return *o.Last +} + +// WithNamespace +func (o *ListOptions) WithNamespace(value bool) *ListOptions { + v := &value + o.Namespace = v + return o +} + +// GetNamespace +func (o *ListOptions) GetNamespace() bool { + var namespace bool + if o.Namespace == nil { + return namespace + } + return *o.Namespace +} + +// WithSize +func (o *ListOptions) WithSize(value bool) *ListOptions { + v := &value + o.Size = v + return o +} + +// GetSize +func (o *ListOptions) GetSize() bool { + var size bool + if o.Size == nil { + return size + } + return *o.Size +} + +// WithSync +func (o *ListOptions) WithSync(value bool) *ListOptions { + v := &value + o.Sync = v + return o +} + +// GetSync +func (o *ListOptions) GetSync() bool { + var sync bool + if o.Sync == nil { + return sync + } + return *o.Sync +} diff --git a/pkg/bindings/containers/types_log_options.go b/pkg/bindings/containers/types_log_options.go new file mode 100644 index 000000000..a6958242f --- /dev/null +++ b/pkg/bindings/containers/types_log_options.go @@ -0,0 +1,200 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:18.273264471 -0600 CST m=+0.000274536 +*/ + +// Changed +func (o *LogOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *LogOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithFollow +func (o *LogOptions) WithFollow(value bool) *LogOptions { + v := &value + o.Follow = v + return o +} + +// GetFollow +func (o *LogOptions) GetFollow() bool { + var follow bool + if o.Follow == nil { + return follow + } + return *o.Follow +} + +// WithSince +func (o *LogOptions) WithSince(value string) *LogOptions { + v := &value + o.Since = v + return o +} + +// GetSince +func (o *LogOptions) GetSince() string { + var since string + if o.Since == nil { + return since + } + return *o.Since +} + +// WithStderr +func (o *LogOptions) WithStderr(value bool) *LogOptions { + v := &value + o.Stderr = v + return o +} + +// GetStderr +func (o *LogOptions) GetStderr() bool { + var stderr bool + if o.Stderr == nil { + return stderr + } + return *o.Stderr +} + +// WithStdout +func (o *LogOptions) WithStdout(value bool) *LogOptions { + v := &value + o.Stdout = v + return o +} + +// GetStdout +func (o *LogOptions) GetStdout() bool { + var stdout bool + if o.Stdout == nil { + return stdout + } + return *o.Stdout +} + +// WithTail +func (o *LogOptions) WithTail(value string) *LogOptions { + v := &value + o.Tail = v + return o +} + +// GetTail +func (o *LogOptions) GetTail() string { + var tail string + if o.Tail == nil { + return tail + } + return *o.Tail +} + +// WithTimestamps +func (o *LogOptions) WithTimestamps(value bool) *LogOptions { + v := &value + o.Timestamps = v + return o +} + +// GetTimestamps +func (o *LogOptions) GetTimestamps() bool { + var timestamps bool + if o.Timestamps == nil { + return timestamps + } + return *o.Timestamps +} + +// WithUntil +func (o *LogOptions) WithUntil(value string) *LogOptions { + v := &value + o.Until = v + return o +} + +// GetUntil +func (o *LogOptions) GetUntil() string { + var until string + if o.Until == nil { + return until + } + return *o.Until +} diff --git a/pkg/bindings/containers/types_mount_options.go b/pkg/bindings/containers/types_mount_options.go new file mode 100644 index 000000000..c0e253094 --- /dev/null +++ b/pkg/bindings/containers/types_mount_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:19.740822464 -0600 CST m=+0.000250074 +*/ + +// Changed +func (o *MountOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *MountOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} diff --git a/pkg/bindings/containers/types_mountedcontainerpaths_options.go b/pkg/bindings/containers/types_mountedcontainerpaths_options.go new file mode 100644 index 000000000..e368ff131 --- /dev/null +++ b/pkg/bindings/containers/types_mountedcontainerpaths_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:20.048233253 -0600 CST m=+0.000307223 +*/ + +// Changed +func (o *MountedContainerPathsOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *MountedContainerPathsOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} diff --git a/pkg/bindings/containers/types_pause_options.go b/pkg/bindings/containers/types_pause_options.go new file mode 100644 index 000000000..26ad86793 --- /dev/null +++ b/pkg/bindings/containers/types_pause_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:20.929891294 -0600 CST m=+0.000261081 +*/ + +// Changed +func (o *PauseOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *PauseOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} diff --git a/pkg/bindings/containers/types_prune_options.go b/pkg/bindings/containers/types_prune_options.go new file mode 100644 index 000000000..e3c0f4de7 --- /dev/null +++ b/pkg/bindings/containers/types_prune_options.go @@ -0,0 +1,104 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:20.344278799 -0600 CST m=+0.000263499 +*/ + +// Changed +func (o *PruneOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *PruneOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithFilters +func (o *PruneOptions) WithFilters(value map[string][]string) *PruneOptions { + v := value + o.Filters = v + return o +} + +// GetFilters +func (o *PruneOptions) GetFilters() map[string][]string { + var filters map[string][]string + if o.Filters == nil { + return filters + } + return o.Filters +} diff --git a/pkg/bindings/containers/types_remove_options.go b/pkg/bindings/containers/types_remove_options.go new file mode 100644 index 000000000..6f59f0ed5 --- /dev/null +++ b/pkg/bindings/containers/types_remove_options.go @@ -0,0 +1,120 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:20.489968735 -0600 CST m=+0.000264450 +*/ + +// Changed +func (o *RemoveOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *RemoveOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithForce +func (o *RemoveOptions) WithForce(value bool) *RemoveOptions { + v := &value + o.Force = v + return o +} + +// GetForce +func (o *RemoveOptions) GetForce() bool { + var force bool + if o.Force == nil { + return force + } + return *o.Force +} + +// WithVolumes +func (o *RemoveOptions) WithVolumes(value bool) *RemoveOptions { + v := &value + o.Volumes = v + return o +} + +// GetVolumes +func (o *RemoveOptions) GetVolumes() bool { + var volumes bool + if o.Volumes == nil { + return volumes + } + return *o.Volumes +} diff --git a/pkg/bindings/containers/types_resizeexectty_options.go b/pkg/bindings/containers/types_resizeexectty_options.go new file mode 100644 index 000000000..33bb4e78b --- /dev/null +++ b/pkg/bindings/containers/types_resizeexectty_options.go @@ -0,0 +1,120 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:22.680735758 -0600 CST m=+0.000267081 +*/ + +// Changed +func (o *ResizeExecTTYOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ResizeExecTTYOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithHeight +func (o *ResizeExecTTYOptions) WithHeight(value int) *ResizeExecTTYOptions { + v := &value + o.Height = v + return o +} + +// GetHeight +func (o *ResizeExecTTYOptions) GetHeight() int { + var height int + if o.Height == nil { + return height + } + return *o.Height +} + +// WithWidth +func (o *ResizeExecTTYOptions) WithWidth(value int) *ResizeExecTTYOptions { + v := &value + o.Width = v + return o +} + +// GetWidth +func (o *ResizeExecTTYOptions) GetWidth() int { + var width int + if o.Width == nil { + return width + } + return *o.Width +} diff --git a/pkg/bindings/containers/types_resizetty_options.go b/pkg/bindings/containers/types_resizetty_options.go new file mode 100644 index 000000000..29ec54988 --- /dev/null +++ b/pkg/bindings/containers/types_resizetty_options.go @@ -0,0 +1,120 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:22.535788375 -0600 CST m=+0.000266528 +*/ + +// Changed +func (o *ResizeTTYOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ResizeTTYOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithHeight +func (o *ResizeTTYOptions) WithHeight(value int) *ResizeTTYOptions { + v := &value + o.Height = v + return o +} + +// GetHeight +func (o *ResizeTTYOptions) GetHeight() int { + var height int + if o.Height == nil { + return height + } + return *o.Height +} + +// WithWidth +func (o *ResizeTTYOptions) WithWidth(value int) *ResizeTTYOptions { + v := &value + o.Width = v + return o +} + +// GetWidth +func (o *ResizeTTYOptions) GetWidth() int { + var width int + if o.Width == nil { + return width + } + return *o.Width +} diff --git a/pkg/bindings/containers/types_restart_options.go b/pkg/bindings/containers/types_restart_options.go new file mode 100644 index 000000000..13ac099b1 --- /dev/null +++ b/pkg/bindings/containers/types_restart_options.go @@ -0,0 +1,104 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:21.076709643 -0600 CST m=+0.000303354 +*/ + +// Changed +func (o *RestartOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *RestartOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithTimeout +func (o *RestartOptions) WithTimeout(value int) *RestartOptions { + v := &value + o.Timeout = v + return o +} + +// GetTimeout +func (o *RestartOptions) GetTimeout() int { + var timeout int + if o.Timeout == nil { + return timeout + } + return *o.Timeout +} diff --git a/pkg/bindings/containers/types_restore_options.go b/pkg/bindings/containers/types_restore_options.go new file mode 100644 index 000000000..be6e94736 --- /dev/null +++ b/pkg/bindings/containers/types_restore_options.go @@ -0,0 +1,200 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:18.861536405 -0600 CST m=+0.000300026 +*/ + +// Changed +func (o *RestoreOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *RestoreOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithIgnoreRootfs +func (o *RestoreOptions) WithIgnoreRootfs(value bool) *RestoreOptions { + v := &value + o.IgnoreRootfs = v + return o +} + +// GetIgnoreRootfs +func (o *RestoreOptions) GetIgnoreRootfs() bool { + var ignoreRootfs bool + if o.IgnoreRootfs == nil { + return ignoreRootfs + } + return *o.IgnoreRootfs +} + +// WithIgnoreStaticIP +func (o *RestoreOptions) WithIgnoreStaticIP(value bool) *RestoreOptions { + v := &value + o.IgnoreStaticIP = v + return o +} + +// GetIgnoreStaticIP +func (o *RestoreOptions) GetIgnoreStaticIP() bool { + var ignoreStaticIP bool + if o.IgnoreStaticIP == nil { + return ignoreStaticIP + } + return *o.IgnoreStaticIP +} + +// WithIgnoreStaticMAC +func (o *RestoreOptions) WithIgnoreStaticMAC(value bool) *RestoreOptions { + v := &value + o.IgnoreStaticMAC = v + return o +} + +// GetIgnoreStaticMAC +func (o *RestoreOptions) GetIgnoreStaticMAC() bool { + var ignoreStaticMAC bool + if o.IgnoreStaticMAC == nil { + return ignoreStaticMAC + } + return *o.IgnoreStaticMAC +} + +// WithImportAchive +func (o *RestoreOptions) WithImportAchive(value string) *RestoreOptions { + v := &value + o.ImportAchive = v + return o +} + +// GetImportAchive +func (o *RestoreOptions) GetImportAchive() string { + var importAchive string + if o.ImportAchive == nil { + return importAchive + } + return *o.ImportAchive +} + +// WithKeep +func (o *RestoreOptions) WithKeep(value bool) *RestoreOptions { + v := &value + o.Keep = v + return o +} + +// GetKeep +func (o *RestoreOptions) GetKeep() bool { + var keep bool + if o.Keep == nil { + return keep + } + return *o.Keep +} + +// WithName +func (o *RestoreOptions) WithName(value string) *RestoreOptions { + v := &value + o.Name = v + return o +} + +// GetName +func (o *RestoreOptions) GetName() string { + var name string + if o.Name == nil { + return name + } + return *o.Name +} + +// WithTCPEstablished +func (o *RestoreOptions) WithTCPEstablished(value bool) *RestoreOptions { + v := &value + o.TCPEstablished = v + return o +} + +// GetTCPEstablished +func (o *RestoreOptions) GetTCPEstablished() bool { + var tCPEstablished bool + if o.TCPEstablished == nil { + return tCPEstablished + } + return *o.TCPEstablished +} diff --git a/pkg/bindings/containers/types_shouldrestart_options.go b/pkg/bindings/containers/types_shouldrestart_options.go new file mode 100644 index 000000000..c833d0d8b --- /dev/null +++ b/pkg/bindings/containers/types_shouldrestart_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:22.388596051 -0600 CST m=+0.000253693 +*/ + +// Changed +func (o *ShouldRestartOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ShouldRestartOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} diff --git a/pkg/bindings/containers/types_start_options.go b/pkg/bindings/containers/types_start_options.go new file mode 100644 index 000000000..5918af89b --- /dev/null +++ b/pkg/bindings/containers/types_start_options.go @@ -0,0 +1,104 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:21.221364502 -0600 CST m=+0.000276575 +*/ + +// Changed +func (o *StartOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *StartOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithDetachKeys +func (o *StartOptions) WithDetachKeys(value string) *StartOptions { + v := &value + o.DetachKeys = v + return o +} + +// GetDetachKeys +func (o *StartOptions) GetDetachKeys() string { + var detachKeys string + if o.DetachKeys == nil { + return detachKeys + } + return *o.DetachKeys +} diff --git a/pkg/bindings/containers/types_stats_options.go b/pkg/bindings/containers/types_stats_options.go new file mode 100644 index 000000000..f821ea1cd --- /dev/null +++ b/pkg/bindings/containers/types_stats_options.go @@ -0,0 +1,104 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:21.370213399 -0600 CST m=+0.000264334 +*/ + +// Changed +func (o *StatsOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *StatsOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithStream +func (o *StatsOptions) WithStream(value bool) *StatsOptions { + v := &value + o.Stream = v + return o +} + +// GetStream +func (o *StatsOptions) GetStream() bool { + var stream bool + if o.Stream == nil { + return stream + } + return *o.Stream +} diff --git a/pkg/bindings/containers/types_stop_options.go b/pkg/bindings/containers/types_stop_options.go new file mode 100644 index 000000000..14d7633a0 --- /dev/null +++ b/pkg/bindings/containers/types_stop_options.go @@ -0,0 +1,104 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:21.95469621 -0600 CST m=+0.000261399 +*/ + +// Changed +func (o *StopOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *StopOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithTimeout +func (o *StopOptions) WithTimeout(value uint) *StopOptions { + v := &value + o.Timeout = v + return o +} + +// GetTimeout +func (o *StopOptions) GetTimeout() uint { + var timeout uint + if o.Timeout == nil { + return timeout + } + return *o.Timeout +} diff --git a/pkg/bindings/containers/types_top_options.go b/pkg/bindings/containers/types_top_options.go new file mode 100644 index 000000000..95a1ee686 --- /dev/null +++ b/pkg/bindings/containers/types_top_options.go @@ -0,0 +1,104 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:21.515867629 -0600 CST m=+0.000257106 +*/ + +// Changed +func (o *TopOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *TopOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithDescriptors +func (o *TopOptions) WithDescriptors(value []string) *TopOptions { + v := &value + o.Descriptors = v + return o +} + +// GetDescriptors +func (o *TopOptions) GetDescriptors() []string { + var descriptors []string + if o.Descriptors == nil { + return descriptors + } + return *o.Descriptors +} diff --git a/pkg/bindings/containers/types_unmount_options.go b/pkg/bindings/containers/types_unmount_options.go new file mode 100644 index 000000000..a29bd8216 --- /dev/null +++ b/pkg/bindings/containers/types_unmount_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:19.891657824 -0600 CST m=+0.000326668 +*/ + +// Changed +func (o *UnmountOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *UnmountOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} diff --git a/pkg/bindings/containers/types_unpause_options.go b/pkg/bindings/containers/types_unpause_options.go new file mode 100644 index 000000000..44c077df2 --- /dev/null +++ b/pkg/bindings/containers/types_unpause_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:21.661356277 -0600 CST m=+0.000262608 +*/ + +// Changed +func (o *UnpauseOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *UnpauseOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} diff --git a/pkg/bindings/containers/types_wait_options.go b/pkg/bindings/containers/types_wait_options.go new file mode 100644 index 000000000..18d36c377 --- /dev/null +++ b/pkg/bindings/containers/types_wait_options.go @@ -0,0 +1,105 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + "github.com/containers/podman/v2/libpod/define" + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 13:33:21.809397978 -0600 CST m=+0.000267049 +*/ + +// Changed +func (o *WaitOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *WaitOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithCondition +func (o *WaitOptions) WithCondition(value define.ContainerStatus) *WaitOptions { + v := &value + o.Condition = v + return o +} + +// GetCondition +func (o *WaitOptions) GetCondition() define.ContainerStatus { + var condition define.ContainerStatus + if o.Condition == nil { + return condition + } + return *o.Condition +} diff --git a/pkg/bindings/generate/types_kube_options.go b/pkg/bindings/generate/types_kube_options.go index fbb26f554..68488aaee 100644 --- a/pkg/bindings/generate/types_kube_options.go +++ b/pkg/bindings/generate/types_kube_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-17 09:35:23.528143172 -0600 CST m=+0.000203394 +Created 2020-12-18 15:58:20.522950566 -0600 CST m=+0.000154384 */ // Changed @@ -56,10 +56,10 @@ func (o *KubeOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/generate/types_systemd_options.go b/pkg/bindings/generate/types_systemd_options.go index 20e032f5a..0e8a46aa0 100644 --- a/pkg/bindings/generate/types_systemd_options.go +++ b/pkg/bindings/generate/types_systemd_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-17 09:35:23.663318384 -0600 CST m=+0.000158454 +Created 2020-12-18 15:58:20.661450253 -0600 CST m=+0.000135779 */ // Changed @@ -56,10 +56,10 @@ func (o *SystemdOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/generator/generator.go b/pkg/bindings/generator/generator.go index 1fd428451..8c79aebae 100644 --- a/pkg/bindings/generator/generator.go +++ b/pkg/bindings/generator/generator.go @@ -69,10 +69,10 @@ func (o *{{.StructName}}) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/images/types_diff_options.go b/pkg/bindings/images/types_diff_options.go index e3ec8ea3d..d27c8945e 100644 --- a/pkg/bindings/images/types_diff_options.go +++ b/pkg/bindings/images/types_diff_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:47:07.253097326 -0600 CST m=+0.000283385 +Created 2020-12-18 15:58:26.320022698 -0600 CST m=+0.000277796 */ // Changed @@ -56,10 +56,10 @@ func (o *DiffOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/images/types_export_options.go b/pkg/bindings/images/types_export_options.go index a14f9d267..078b27fc0 100644 --- a/pkg/bindings/images/types_export_options.go +++ b/pkg/bindings/images/types_export_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:47:08.103165436 -0600 CST m=+0.000245546 +Created 2020-12-18 15:58:27.173810543 -0600 CST m=+0.000239871 */ // Changed @@ -56,10 +56,10 @@ func (o *ExportOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/images/types_get_options.go b/pkg/bindings/images/types_get_options.go index a1c0f9b09..1161657f7 100644 --- a/pkg/bindings/images/types_get_options.go +++ b/pkg/bindings/images/types_get_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:47:07.536472335 -0600 CST m=+0.000251379 +Created 2020-12-18 15:58:26.609005517 -0600 CST m=+0.000241828 */ // Changed @@ -56,10 +56,10 @@ func (o *GetOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/images/types_history_options.go b/pkg/bindings/images/types_history_options.go index 7b3d82097..6f9854e03 100644 --- a/pkg/bindings/images/types_history_options.go +++ b/pkg/bindings/images/types_history_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:47:07.818738329 -0600 CST m=+0.000253937 +Created 2020-12-18 15:58:26.890854681 -0600 CST m=+0.000243668 */ // Changed @@ -56,10 +56,10 @@ func (o *HistoryOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/images/types_import_options.go b/pkg/bindings/images/types_import_options.go index 6552d63bd..f5e6c8f7e 100644 --- a/pkg/bindings/images/types_import_options.go +++ b/pkg/bindings/images/types_import_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:47:08.67072755 -0600 CST m=+0.000238081 +Created 2020-12-18 15:58:27.740585278 -0600 CST m=+0.000340441 */ // Changed @@ -56,10 +56,10 @@ func (o *ImportOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/images/types_list_options.go b/pkg/bindings/images/types_list_options.go index 0365d0e71..209d72e34 100644 --- a/pkg/bindings/images/types_list_options.go +++ b/pkg/bindings/images/types_list_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:47:07.39332655 -0600 CST m=+0.000275349 +Created 2020-12-18 15:58:26.462967928 -0600 CST m=+0.000289760 */ // Changed @@ -56,10 +56,10 @@ func (o *ListOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/images/types_load_options.go b/pkg/bindings/images/types_load_options.go index ffba08fcc..6bba573d4 100644 --- a/pkg/bindings/images/types_load_options.go +++ b/pkg/bindings/images/types_load_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:47:07.961817946 -0600 CST m=+0.000243444 +Created 2020-12-18 15:58:27.031848205 -0600 CST m=+0.000279409 */ // Changed @@ -56,10 +56,10 @@ func (o *LoadOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/images/types_prune_options.go b/pkg/bindings/images/types_prune_options.go index 1f7f2cd8b..c29fdae12 100644 --- a/pkg/bindings/images/types_prune_options.go +++ b/pkg/bindings/images/types_prune_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:47:08.24781882 -0600 CST m=+0.000244039 +Created 2020-12-18 15:58:27.316938584 -0600 CST m=+0.000239843 */ // Changed @@ -56,10 +56,10 @@ func (o *PruneOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/images/types_pull_options.go b/pkg/bindings/images/types_pull_options.go index f22517d9e..07f3e079d 100644 --- a/pkg/bindings/images/types_pull_options.go +++ b/pkg/bindings/images/types_pull_options.go @@ -13,7 +13,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:47:09.104988433 -0600 CST m=+0.000274515 +Created 2020-12-18 15:58:28.164648348 -0600 CST m=+0.000243264 */ // Changed @@ -57,10 +57,10 @@ func (o *PullOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/images/types_push_options.go b/pkg/bindings/images/types_push_options.go index b1d6b78fe..f9ce1b835 100644 --- a/pkg/bindings/images/types_push_options.go +++ b/pkg/bindings/images/types_push_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:47:08.817442617 -0600 CST m=+0.000259111 +Created 2020-12-18 15:58:27.881232044 -0600 CST m=+0.000242458 */ // Changed @@ -56,10 +56,10 @@ func (o *PushOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/images/types_remove_options.go b/pkg/bindings/images/types_remove_options.go index 1245aec64..c9692c2b7 100644 --- a/pkg/bindings/images/types_remove_options.go +++ b/pkg/bindings/images/types_remove_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:47:07.115421951 -0600 CST m=+0.000310512 +Created 2020-12-18 15:58:26.180391541 -0600 CST m=+0.000290244 */ // Changed @@ -56,10 +56,10 @@ func (o *RemoveOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/images/types_search_options.go b/pkg/bindings/images/types_search_options.go index 3b89f7acc..e6168ac33 100644 --- a/pkg/bindings/images/types_search_options.go +++ b/pkg/bindings/images/types_search_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:47:08.958897824 -0600 CST m=+0.000238136 +Created 2020-12-18 15:58:28.023569573 -0600 CST m=+0.000245548 */ // Changed @@ -56,10 +56,10 @@ func (o *SearchOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/images/types_tag_options.go b/pkg/bindings/images/types_tag_options.go index 8d1070750..f6396e590 100644 --- a/pkg/bindings/images/types_tag_options.go +++ b/pkg/bindings/images/types_tag_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:47:08.388404224 -0600 CST m=+0.000253809 +Created 2020-12-18 15:58:27.457906682 -0600 CST m=+0.000245071 */ // Changed @@ -56,10 +56,10 @@ func (o *TagOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/images/types_tree_options.go b/pkg/bindings/images/types_tree_options.go index 765f99e83..fb2493f85 100644 --- a/pkg/bindings/images/types_tree_options.go +++ b/pkg/bindings/images/types_tree_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:47:07.676177228 -0600 CST m=+0.000254279 +Created 2020-12-18 15:58:26.749625305 -0600 CST m=+0.000267624 */ // Changed @@ -56,10 +56,10 @@ func (o *TreeOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/images/types_untag_options.go b/pkg/bindings/images/types_untag_options.go index d6ce1f1c7..8faf5c14e 100644 --- a/pkg/bindings/images/types_untag_options.go +++ b/pkg/bindings/images/types_untag_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:47:08.530676487 -0600 CST m=+0.000238259 +Created 2020-12-18 15:58:27.600379913 -0600 CST m=+0.000251449 */ // Changed @@ -56,10 +56,10 @@ func (o *UntagOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/manifests/types_add_options.go b/pkg/bindings/manifests/types_add_options.go index 304779f6e..d45effedc 100644 --- a/pkg/bindings/manifests/types_add_options.go +++ b/pkg/bindings/manifests/types_add_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:58:59.192715649 -0600 CST m=+0.000150326 +Created 2020-12-18 15:57:55.92237379 -0600 CST m=+0.000150701 */ // Changed @@ -56,10 +56,10 @@ func (o *AddOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/manifests/types_create_options.go b/pkg/bindings/manifests/types_create_options.go index 6fccc08b6..da07f1abf 100644 --- a/pkg/bindings/manifests/types_create_options.go +++ b/pkg/bindings/manifests/types_create_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:58:59.053719568 -0600 CST m=+0.000144061 +Created 2020-12-18 15:57:55.784206871 -0600 CST m=+0.000157049 */ // Changed @@ -56,10 +56,10 @@ func (o *CreateOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/manifests/types_inspect_options.go b/pkg/bindings/manifests/types_inspect_options.go index 8cbcbf376..0d32d5bb9 100644 --- a/pkg/bindings/manifests/types_inspect_options.go +++ b/pkg/bindings/manifests/types_inspect_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:58:58.908440858 -0600 CST m=+0.000142730 +Created 2020-12-18 15:57:55.631746481 -0600 CST m=+0.000143104 */ // Changed @@ -56,10 +56,10 @@ func (o *InspectOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/manifests/types_push_options.go b/pkg/bindings/manifests/types_push_options.go index a56b29049..4226733c9 100644 --- a/pkg/bindings/manifests/types_push_options.go +++ b/pkg/bindings/manifests/types_push_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:58:59.466796313 -0600 CST m=+0.000143189 +Created 2020-12-18 15:57:56.197438009 -0600 CST m=+0.000149060 */ // Changed @@ -56,10 +56,10 @@ func (o *PushOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/manifests/types_remove_options.go b/pkg/bindings/manifests/types_remove_options.go index 4f8ca9e95..f99220f6c 100644 --- a/pkg/bindings/manifests/types_remove_options.go +++ b/pkg/bindings/manifests/types_remove_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:58:59.33119706 -0600 CST m=+0.000143915 +Created 2020-12-18 15:57:56.059954408 -0600 CST m=+0.000146015 */ // Changed @@ -56,10 +56,10 @@ func (o *RemoveOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/network/types_connect_options.go b/pkg/bindings/network/types_connect_options.go index 22d1905e4..6fcc4536e 100644 --- a/pkg/bindings/network/types_connect_options.go +++ b/pkg/bindings/network/types_connect_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:59:06.213411549 -0600 CST m=+0.000201795 +Created 2020-12-18 15:58:34.075822786 -0600 CST m=+0.000167237 */ // Changed @@ -56,10 +56,10 @@ func (o *ConnectOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/network/types_create_options.go b/pkg/bindings/network/types_create_options.go index 6af7929c4..e35762912 100644 --- a/pkg/bindings/network/types_create_options.go +++ b/pkg/bindings/network/types_create_options.go @@ -13,7 +13,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:59:05.523424301 -0600 CST m=+0.000180953 +Created 2020-12-18 15:58:33.37307678 -0600 CST m=+0.000176739 */ // Changed @@ -57,10 +57,10 @@ func (o *CreateOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/network/types_disconnect_options.go b/pkg/bindings/network/types_disconnect_options.go index 183032998..821279976 100644 --- a/pkg/bindings/network/types_disconnect_options.go +++ b/pkg/bindings/network/types_disconnect_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:59:06.07634068 -0600 CST m=+0.000179587 +Created 2020-12-18 15:58:33.936088242 -0600 CST m=+0.000168680 */ // Changed @@ -56,10 +56,10 @@ func (o *DisconnectOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/network/types_inspect_options.go b/pkg/bindings/network/types_inspect_options.go index 91adac8f4..7704904ce 100644 --- a/pkg/bindings/network/types_inspect_options.go +++ b/pkg/bindings/network/types_inspect_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:59:05.661597872 -0600 CST m=+0.000168252 +Created 2020-12-18 15:58:33.520438264 -0600 CST m=+0.000172934 */ // Changed @@ -56,10 +56,10 @@ func (o *InspectOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/network/types_list_options.go b/pkg/bindings/network/types_list_options.go index 3f1909ee1..bbd2a71db 100644 --- a/pkg/bindings/network/types_list_options.go +++ b/pkg/bindings/network/types_list_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:59:05.936262707 -0600 CST m=+0.000172058 +Created 2020-12-18 15:58:33.796794129 -0600 CST m=+0.000180368 */ // Changed @@ -56,10 +56,10 @@ func (o *ListOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/network/types_remove_options.go b/pkg/bindings/network/types_remove_options.go index 5f3dc70ec..b24835ae4 100644 --- a/pkg/bindings/network/types_remove_options.go +++ b/pkg/bindings/network/types_remove_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:59:05.798818224 -0600 CST m=+0.000173420 +Created 2020-12-18 15:58:33.658228151 -0600 CST m=+0.000172527 */ // Changed @@ -56,10 +56,10 @@ func (o *RemoveOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/play/types_kube_options.go b/pkg/bindings/play/types_kube_options.go index 29a5fc9b1..91cdd30aa 100644 --- a/pkg/bindings/play/types_kube_options.go +++ b/pkg/bindings/play/types_kube_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:59:11.20490387 -0600 CST m=+0.000181859 +Created 2020-12-18 15:58:02.386833736 -0600 CST m=+0.000171080 */ // Changed @@ -56,10 +56,10 @@ func (o *KubeOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/pods/pods.go b/pkg/bindings/pods/pods.go index c3f98eaab..4fcaf81f1 100644 --- a/pkg/bindings/pods/pods.go +++ b/pkg/bindings/pods/pods.go @@ -2,10 +2,8 @@ package pods import ( "context" - "errors" "net/http" "net/url" - "strconv" "strings" "github.com/containers/podman/v2/pkg/api/handlers" @@ -15,10 +13,14 @@ import ( jsoniter "github.com/json-iterator/go" ) -func CreatePodFromSpec(ctx context.Context, s *specgen.PodSpecGenerator) (*entities.PodCreateReport, error) { +func CreatePodFromSpec(ctx context.Context, s *specgen.PodSpecGenerator, options *CreateOptions) (*entities.PodCreateReport, error) { var ( pcr entities.PodCreateReport ) + if options == nil { + options = new(CreateOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err @@ -49,10 +51,14 @@ func Exists(ctx context.Context, nameOrID string) (bool, error) { } // Inspect returns low-level information about the given pod. -func Inspect(ctx context.Context, nameOrID string) (*entities.PodInspectReport, error) { +func Inspect(ctx context.Context, nameOrID string, options *InspectOptions) (*entities.PodInspectReport, error) { var ( report entities.PodInspectReport ) + if options == nil { + options = new(InspectOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err @@ -66,17 +72,20 @@ func Inspect(ctx context.Context, nameOrID string) (*entities.PodInspectReport, // Kill sends a SIGTERM to all the containers in a pod. The optional signal parameter // can be used to override SIGTERM. -func Kill(ctx context.Context, nameOrID string, signal *string) (*entities.PodKillReport, error) { +func Kill(ctx context.Context, nameOrID string, options *KillOptions) (*entities.PodKillReport, error) { var ( report entities.PodKillReport ) + if options == nil { + options = new(KillOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if signal != nil { - params.Set("signal", *signal) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/kill", params, nil, nameOrID) if err != nil { @@ -86,8 +95,12 @@ func Kill(ctx context.Context, nameOrID string, signal *string) (*entities.PodKi } // Pause pauses all running containers in a given pod. -func Pause(ctx context.Context, nameOrID string) (*entities.PodPauseReport, error) { +func Pause(ctx context.Context, nameOrID string, options *PauseOptions) (*entities.PodPauseReport, error) { var report entities.PodPauseReport + if options == nil { + options = new(PauseOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err @@ -101,8 +114,12 @@ func Pause(ctx context.Context, nameOrID string) (*entities.PodPauseReport, erro // Prune by default removes all non-running pods in local storage. // And with force set true removes all pods. -func Prune(ctx context.Context) ([]*entities.PodPruneReport, error) { +func Prune(ctx context.Context, options *PruneOptions) ([]*entities.PodPruneReport, error) { var reports []*entities.PodPruneReport + if options == nil { + options = new(PruneOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err @@ -116,21 +133,20 @@ func Prune(ctx context.Context) ([]*entities.PodPruneReport, error) { // List returns all pods in local storage. The optional filters parameter can // be used to refine which pods should be listed. -func List(ctx context.Context, filters map[string][]string) ([]*entities.ListPodsReport, error) { +func List(ctx context.Context, options *ListOptions) ([]*entities.ListPodsReport, error) { var ( podsReports []*entities.ListPodsReport ) + if options == nil { + options = new(ListOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if filters != nil { - stringFilter, err := bindings.FiltersToString(filters) - if err != nil { - return nil, err - } - params.Set("filters", stringFilter) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodGet, "/pods/json", params, nil) if err != nil { @@ -140,8 +156,12 @@ func List(ctx context.Context, filters map[string][]string) ([]*entities.ListPod } // Restart restarts all containers in a pod. -func Restart(ctx context.Context, nameOrID string) (*entities.PodRestartReport, error) { +func Restart(ctx context.Context, nameOrID string, options *RestartOptions) (*entities.PodRestartReport, error) { var report entities.PodRestartReport + if options == nil { + options = new(RestartOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err @@ -155,15 +175,18 @@ func Restart(ctx context.Context, nameOrID string) (*entities.PodRestartReport, // Remove deletes a Pod from from local storage. The optional force parameter denotes // that the Pod can be removed even if in a running state. -func Remove(ctx context.Context, nameOrID string, force *bool) (*entities.PodRmReport, error) { +func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) (*entities.PodRmReport, error) { var report entities.PodRmReport + if options == nil { + options = new(RemoveOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if force != nil { - params.Set("force", strconv.FormatBool(*force)) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodDelete, "/pods/%s", params, nil, nameOrID) if err != nil { @@ -173,8 +196,12 @@ func Remove(ctx context.Context, nameOrID string, force *bool) (*entities.PodRmR } // Start starts all containers in a pod. -func Start(ctx context.Context, nameOrID string) (*entities.PodStartReport, error) { +func Start(ctx context.Context, nameOrID string, options *StartOptions) (*entities.PodStartReport, error) { var report entities.PodStartReport + if options == nil { + options = new(StartOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err @@ -192,15 +219,18 @@ func Start(ctx context.Context, nameOrID string) (*entities.PodStartReport, erro // Stop stops all containers in a Pod. The optional timeout parameter can be // used to override the timeout before the container is killed. -func Stop(ctx context.Context, nameOrID string, timeout *int) (*entities.PodStopReport, error) { +func Stop(ctx context.Context, nameOrID string, options *StopOptions) (*entities.PodStopReport, error) { var report entities.PodStopReport + if options == nil { + options = new(StopOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if timeout != nil { - params.Set("t", strconv.Itoa(*timeout)) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/stop", params, nil, nameOrID) if err != nil { @@ -215,15 +245,16 @@ func Stop(ctx context.Context, nameOrID string, timeout *int) (*entities.PodStop // Top gathers statistics about the running processes in a pod. The nameOrID can be a pod name // or a partial/full ID. The descriptors allow for specifying which data to collect from each process. -func Top(ctx context.Context, nameOrID string, descriptors []string) ([]string, error) { +func Top(ctx context.Context, nameOrID string, options *TopOptions) ([]string, error) { + if options == nil { + options = new(TopOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } params := url.Values{} - - if len(descriptors) > 0 { - // flatten the slice into one string + if descriptors := options.GetDescriptors(); len(descriptors) > 0 { params.Set("ps_args", strings.Join(descriptors, ",")) } response, err := conn.DoRequest(nil, http.MethodGet, "/pods/%s/top", params, nil, nameOrID) @@ -248,7 +279,11 @@ func Top(ctx context.Context, nameOrID string, descriptors []string) ([]string, } // Unpause unpauses all paused containers in a Pod. -func Unpause(ctx context.Context, nameOrID string) (*entities.PodUnpauseReport, error) { +func Unpause(ctx context.Context, nameOrID string, options *UnpauseOptions) (*entities.PodUnpauseReport, error) { + if options == nil { + options = new(UnpauseOptions) + } + _ = options var report entities.PodUnpauseReport conn, err := bindings.GetClient(ctx) if err != nil { @@ -262,19 +297,21 @@ func Unpause(ctx context.Context, nameOrID string) (*entities.PodUnpauseReport, } // Stats display resource-usage statistics of one or more pods. -func Stats(ctx context.Context, namesOrIDs []string, options entities.PodStatsOptions) ([]*entities.PodStatsReport, error) { - if options.Latest { - return nil, errors.New("latest is not supported") +func Stats(ctx context.Context, namesOrIDs []string, options *StatsOptions) ([]*entities.PodStatsReport, error) { + if options == nil { + options = new(StatsOptions) } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} + params, err := options.ToParams() + if err != nil { + return nil, err + } for _, i := range namesOrIDs { params.Add("namesOrIDs", i) } - params.Set("all", strconv.FormatBool(options.All)) var reports []*entities.PodStatsReport response, err := conn.DoRequest(nil, http.MethodGet, "/pods/stats", params, nil) diff --git a/pkg/bindings/pods/types.go b/pkg/bindings/pods/types.go new file mode 100644 index 000000000..370c1aaa9 --- /dev/null +++ b/pkg/bindings/pods/types.go @@ -0,0 +1,72 @@ +package pods + +//go:generate go run ../generator/generator.go CreateOptions +// CreateOptions are optional options for creating pods +type CreateOptions struct { +} + +//go:generate go run ../generator/generator.go InspectOptions +// InspectOptions are optional options for inspecting pods +type InspectOptions struct { +} + +//go:generate go run ../generator/generator.go KillOptions +// KillOptions are optional options for killing pods +type KillOptions struct { + Signal *string +} + +//go:generate go run ../generator/generator.go PauseOptions +// PauseOptions are optional options for pausing pods +type PauseOptions struct { +} + +//go:generate go run ../generator/generator.go PruneOptions +// PruneOptions are optional options for pruning pods +type PruneOptions struct { +} + +//go:generate go run ../generator/generator.go ListOptions +// ListOptions are optional options for listing pods +type ListOptions struct { + Filters map[string][]string +} + +//go:generate go run ../generator/generator.go RestartOptions +// RestartOptions are optional options for restarting pods +type RestartOptions struct { +} + +//go:generate go run ../generator/generator.go StartOptions +// StartOptions are optional options for starting pods +type StartOptions struct { +} + +//go:generate go run ../generator/generator.go StopOptions +// StopOptions are optional options for stopping pods +type StopOptions struct { + Timeout *int +} + +//go:generate go run ../generator/generator.go TopOptions +// TopOptions are optional options for getting top on pods +type TopOptions struct { + Descriptors []string +} + +//go:generate go run ../generator/generator.go UnpauseOptions +// UnpauseOptions are optional options for unpausinging pods +type UnpauseOptions struct { +} + +//go:generate go run ../generator/generator.go StatsOptions +// StatsOptions are optional options for getting stats of pods +type StatsOptions struct { + All *bool +} + +//go:generate go run ../generator/generator.go RemoveOptions +// RemoveOptions are optional options for removing pods +type RemoveOptions struct { + Force *bool +} diff --git a/pkg/bindings/pods/types_create_options.go b/pkg/bindings/pods/types_create_options.go new file mode 100644 index 000000000..b6cf0fc53 --- /dev/null +++ b/pkg/bindings/pods/types_create_options.go @@ -0,0 +1,88 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 15:58:40.05490508 -0600 CST m=+0.000156396 +*/ + +// Changed +func (o *CreateOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *CreateOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} diff --git a/pkg/bindings/pods/types_inspect_options.go b/pkg/bindings/pods/types_inspect_options.go new file mode 100644 index 000000000..1c881ce9c --- /dev/null +++ b/pkg/bindings/pods/types_inspect_options.go @@ -0,0 +1,88 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 15:58:40.258801519 -0600 CST m=+0.000175055 +*/ + +// Changed +func (o *InspectOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *InspectOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} diff --git a/pkg/bindings/pods/types_kill_options.go b/pkg/bindings/pods/types_kill_options.go new file mode 100644 index 000000000..cb5bdfd01 --- /dev/null +++ b/pkg/bindings/pods/types_kill_options.go @@ -0,0 +1,104 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 15:58:40.398857339 -0600 CST m=+0.000160135 +*/ + +// Changed +func (o *KillOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *KillOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithSignal +func (o *KillOptions) WithSignal(value string) *KillOptions { + v := &value + o.Signal = v + return o +} + +// GetSignal +func (o *KillOptions) GetSignal() string { + var signal string + if o.Signal == nil { + return signal + } + return *o.Signal +} diff --git a/pkg/bindings/pods/types_list_options.go b/pkg/bindings/pods/types_list_options.go new file mode 100644 index 000000000..d095bf3db --- /dev/null +++ b/pkg/bindings/pods/types_list_options.go @@ -0,0 +1,104 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 15:58:40.818123838 -0600 CST m=+0.000164328 +*/ + +// Changed +func (o *ListOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ListOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithFilters +func (o *ListOptions) WithFilters(value map[string][]string) *ListOptions { + v := value + o.Filters = v + return o +} + +// GetFilters +func (o *ListOptions) GetFilters() map[string][]string { + var filters map[string][]string + if o.Filters == nil { + return filters + } + return o.Filters +} diff --git a/pkg/bindings/pods/types_pause_options.go b/pkg/bindings/pods/types_pause_options.go new file mode 100644 index 000000000..06ee6f81d --- /dev/null +++ b/pkg/bindings/pods/types_pause_options.go @@ -0,0 +1,88 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 15:58:40.538407099 -0600 CST m=+0.000193274 +*/ + +// Changed +func (o *PauseOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *PauseOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} diff --git a/pkg/bindings/pods/types_prune_options.go b/pkg/bindings/pods/types_prune_options.go new file mode 100644 index 000000000..6610aa7cc --- /dev/null +++ b/pkg/bindings/pods/types_prune_options.go @@ -0,0 +1,88 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 15:58:40.678507342 -0600 CST m=+0.000183891 +*/ + +// Changed +func (o *PruneOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *PruneOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} diff --git a/pkg/bindings/pods/types_remove_options.go b/pkg/bindings/pods/types_remove_options.go new file mode 100644 index 000000000..8f18e43c9 --- /dev/null +++ b/pkg/bindings/pods/types_remove_options.go @@ -0,0 +1,104 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 15:58:41.80320679 -0600 CST m=+0.000158149 +*/ + +// Changed +func (o *RemoveOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *RemoveOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithForce +func (o *RemoveOptions) WithForce(value bool) *RemoveOptions { + v := &value + o.Force = v + return o +} + +// GetForce +func (o *RemoveOptions) GetForce() bool { + var force bool + if o.Force == nil { + return force + } + return *o.Force +} diff --git a/pkg/bindings/pods/types_restart_options.go b/pkg/bindings/pods/types_restart_options.go new file mode 100644 index 000000000..9030de1e7 --- /dev/null +++ b/pkg/bindings/pods/types_restart_options.go @@ -0,0 +1,88 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 15:58:40.958315383 -0600 CST m=+0.000168360 +*/ + +// Changed +func (o *RestartOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *RestartOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} diff --git a/pkg/bindings/pods/types_start_options.go b/pkg/bindings/pods/types_start_options.go new file mode 100644 index 000000000..0fce21099 --- /dev/null +++ b/pkg/bindings/pods/types_start_options.go @@ -0,0 +1,88 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 15:58:41.099102916 -0600 CST m=+0.000159629 +*/ + +// Changed +func (o *StartOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *StartOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} diff --git a/pkg/bindings/pods/types_stats_options.go b/pkg/bindings/pods/types_stats_options.go new file mode 100644 index 000000000..d38a9a115 --- /dev/null +++ b/pkg/bindings/pods/types_stats_options.go @@ -0,0 +1,104 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 15:58:41.658228243 -0600 CST m=+0.000160769 +*/ + +// Changed +func (o *StatsOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *StatsOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithAll +func (o *StatsOptions) WithAll(value bool) *StatsOptions { + v := &value + o.All = v + return o +} + +// GetAll +func (o *StatsOptions) GetAll() bool { + var all bool + if o.All == nil { + return all + } + return *o.All +} diff --git a/pkg/bindings/pods/types_stop_options.go b/pkg/bindings/pods/types_stop_options.go new file mode 100644 index 000000000..ac698b8c5 --- /dev/null +++ b/pkg/bindings/pods/types_stop_options.go @@ -0,0 +1,104 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 15:58:41.237892781 -0600 CST m=+0.000155040 +*/ + +// Changed +func (o *StopOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *StopOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithTimeout +func (o *StopOptions) WithTimeout(value int) *StopOptions { + v := &value + o.Timeout = v + return o +} + +// GetTimeout +func (o *StopOptions) GetTimeout() int { + var timeout int + if o.Timeout == nil { + return timeout + } + return *o.Timeout +} diff --git a/pkg/bindings/pods/types_top_options.go b/pkg/bindings/pods/types_top_options.go new file mode 100644 index 000000000..895f62957 --- /dev/null +++ b/pkg/bindings/pods/types_top_options.go @@ -0,0 +1,104 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 15:58:41.375876994 -0600 CST m=+0.000154839 +*/ + +// Changed +func (o *TopOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *TopOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithDescriptors +func (o *TopOptions) WithDescriptors(value []string) *TopOptions { + v := value + o.Descriptors = v + return o +} + +// GetDescriptors +func (o *TopOptions) GetDescriptors() []string { + var descriptors []string + if o.Descriptors == nil { + return descriptors + } + return o.Descriptors +} diff --git a/pkg/bindings/pods/types_unpause_options.go b/pkg/bindings/pods/types_unpause_options.go new file mode 100644 index 000000000..3d647cf25 --- /dev/null +++ b/pkg/bindings/pods/types_unpause_options.go @@ -0,0 +1,88 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-18 15:58:41.515225789 -0600 CST m=+0.000158667 +*/ + +// Changed +func (o *UnpauseOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *UnpauseOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} diff --git a/pkg/bindings/system/types_disk_options.go b/pkg/bindings/system/types_disk_options.go index 2336db19b..f7d2cca06 100644 --- a/pkg/bindings/system/types_disk_options.go +++ b/pkg/bindings/system/types_disk_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-17 09:13:10.734962166 -0600 CST m=+0.000145336 +Created 2020-12-18 15:58:08.087362343 -0600 CST m=+0.000150636 */ // Changed @@ -56,10 +56,10 @@ func (o *DiskOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/system/types_events_options.go b/pkg/bindings/system/types_events_options.go index cb4c5eb0e..6dd64b055 100644 --- a/pkg/bindings/system/types_events_options.go +++ b/pkg/bindings/system/types_events_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-17 09:13:10.308724359 -0600 CST m=+0.000145204 +Created 2020-12-18 15:58:07.675150173 -0600 CST m=+0.000140977 */ // Changed @@ -56,10 +56,10 @@ func (o *EventsOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/system/types_info_options.go b/pkg/bindings/system/types_info_options.go index 7614e084c..3a8960e1b 100644 --- a/pkg/bindings/system/types_info_options.go +++ b/pkg/bindings/system/types_info_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-17 09:13:10.872420889 -0600 CST m=+0.000136023 +Created 2020-12-18 15:58:08.233760126 -0600 CST m=+0.000142369 */ // Changed @@ -56,10 +56,10 @@ func (o *InfoOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/system/types_prune_options.go b/pkg/bindings/system/types_prune_options.go index 6a1e38333..2cd3c8000 100644 --- a/pkg/bindings/system/types_prune_options.go +++ b/pkg/bindings/system/types_prune_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-17 09:13:10.456894557 -0600 CST m=+0.000141897 +Created 2020-12-18 15:58:07.812719858 -0600 CST m=+0.000143214 */ // Changed @@ -56,10 +56,10 @@ func (o *PruneOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/system/types_version_options.go b/pkg/bindings/system/types_version_options.go index fbe29f07c..4974e8d8f 100644 --- a/pkg/bindings/system/types_version_options.go +++ b/pkg/bindings/system/types_version_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-17 09:13:10.596882708 -0600 CST m=+0.000141540 +Created 2020-12-18 15:58:07.950759332 -0600 CST m=+0.000140376 */ // Changed @@ -56,10 +56,10 @@ func (o *VersionOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/test/attach_test.go b/pkg/bindings/test/attach_test.go index 12e51e734..9a46f6309 100644 --- a/pkg/bindings/test/attach_test.go +++ b/pkg/bindings/test/attach_test.go @@ -6,7 +6,6 @@ import ( "time" "github.com/containers/podman/v2/libpod/define" - "github.com/containers/podman/v2/pkg/bindings" "github.com/containers/podman/v2/pkg/bindings/containers" "github.com/containers/podman/v2/pkg/specgen" . "github.com/onsi/ginkgo" @@ -43,7 +42,7 @@ var _ = Describe("Podman containers attach", func() { go func() { <-tickTock.C timeout := uint(5) - err := containers.Stop(bt.conn, id, &timeout) + err := containers.Stop(bt.conn, id, new(containers.StopOptions).WithTimeout(timeout)) if err != nil { GinkgoWriter.Write([]byte(err.Error())) } @@ -53,8 +52,8 @@ var _ = Describe("Podman containers attach", func() { stderr := &bytes.Buffer{} go func() { defer GinkgoRecover() - - err := containers.Attach(bt.conn, id, nil, bindings.PTrue, bindings.PTrue, nil, stdout, stderr, nil) + options := new(containers.AttachOptions).WithLogs(true).WithStream(true) + err := containers.Attach(bt.conn, id, nil, stdout, stderr, nil, options) Expect(err).ShouldNot(HaveOccurred()) }() @@ -69,21 +68,21 @@ var _ = Describe("Podman containers attach", func() { s.Name = "CatAttachTest" s.Terminal = true s.Command = []string{"/bin/cat"} - ctnr, err := containers.CreateWithSpec(bt.conn, s) + ctnr, err := containers.CreateWithSpec(bt.conn, s, nil) Expect(err).ShouldNot(HaveOccurred()) err = containers.Start(bt.conn, ctnr.ID, nil) Expect(err).ShouldNot(HaveOccurred()) wait := define.ContainerStateRunning - _, err = containers.Wait(bt.conn, ctnr.ID, &wait) + _, err = containers.Wait(bt.conn, ctnr.ID, new(containers.WaitOptions).WithCondition(wait)) Expect(err).ShouldNot(HaveOccurred()) tickTock := time.NewTimer(2 * time.Second) go func() { <-tickTock.C timeout := uint(5) - err := containers.Stop(bt.conn, ctnr.ID, &timeout) + err := containers.Stop(bt.conn, ctnr.ID, new(containers.StopOptions).WithTimeout(timeout)) if err != nil { GinkgoWriter.Write([]byte(err.Error())) } @@ -97,8 +96,8 @@ var _ = Describe("Podman containers attach", func() { stderr := &bytes.Buffer{} go func() { defer GinkgoRecover() - - err := containers.Attach(bt.conn, ctnr.ID, nil, bindings.PFalse, bindings.PTrue, stdin, stdout, stderr, nil) + options := new(containers.AttachOptions).WithStream(true) + err := containers.Attach(bt.conn, ctnr.ID, stdin, stdout, stderr, nil, options) Expect(err).ShouldNot(HaveOccurred()) }() diff --git a/pkg/bindings/test/common_test.go b/pkg/bindings/test/common_test.go index 9dff2985f..232d7136f 100644 --- a/pkg/bindings/test/common_test.go +++ b/pkg/bindings/test/common_test.go @@ -198,7 +198,7 @@ func (b *bindingTest) RunTopContainer(containerName *string, insidePod *bool, po if insidePod != nil && podName != nil { s.Pod = *podName } - ctr, err := containers.CreateWithSpec(b.conn, s) + ctr, err := containers.CreateWithSpec(b.conn, s, nil) if err != nil { return "", nil } @@ -207,7 +207,7 @@ func (b *bindingTest) RunTopContainer(containerName *string, insidePod *bool, po return "", err } wait := define.ContainerStateRunning - _, err = containers.Wait(b.conn, ctr.ID, &wait) + _, err = containers.Wait(b.conn, ctr.ID, new(containers.WaitOptions).WithCondition(wait)) return ctr.ID, err } diff --git a/pkg/bindings/test/containers_test.go b/pkg/bindings/test/containers_test.go index 15066ff1a..2ab5e45d0 100644 --- a/pkg/bindings/test/containers_test.go +++ b/pkg/bindings/test/containers_test.go @@ -37,7 +37,7 @@ var _ = Describe("Podman containers ", func() { It("podman pause a bogus container", func() { // Pausing bogus container should return 404 - err = containers.Pause(bt.conn, "foobar") + err = containers.Pause(bt.conn, "foobar", nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) @@ -45,7 +45,7 @@ var _ = Describe("Podman containers ", func() { It("podman unpause a bogus container", func() { // Unpausing bogus container should return 404 - err = containers.Unpause(bt.conn, "foobar") + err = containers.Unpause(bt.conn, "foobar", nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) @@ -56,7 +56,7 @@ var _ = Describe("Podman containers ", func() { var name = "top" _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, name) + err = containers.Pause(bt.conn, name, nil) Expect(err).To(BeNil()) // Ensure container is paused @@ -70,7 +70,7 @@ var _ = Describe("Podman containers ", func() { var name = "top" cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, cid) + err = containers.Pause(bt.conn, cid, nil) Expect(err).To(BeNil()) // Ensure container is paused @@ -84,9 +84,9 @@ var _ = Describe("Podman containers ", func() { var name = "top" _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, name) + err = containers.Pause(bt.conn, name, nil) Expect(err).To(BeNil()) - err = containers.Unpause(bt.conn, name) + err = containers.Unpause(bt.conn, name, nil) Expect(err).To(BeNil()) // Ensure container is unpaused @@ -101,11 +101,11 @@ var _ = Describe("Podman containers ", func() { _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) // Pause by name - err = containers.Pause(bt.conn, name) + err = containers.Pause(bt.conn, name, nil) //paused := "paused" //_, err = containers.Wait(bt.conn, cid, &paused) //Expect(err).To(BeNil()) - err = containers.Unpause(bt.conn, name) + err = containers.Unpause(bt.conn, name, nil) Expect(err).To(BeNil()) // Ensure container is unpaused @@ -119,9 +119,9 @@ var _ = Describe("Podman containers ", func() { var name = "top" _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, name) + err = containers.Pause(bt.conn, name, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, name) + err = containers.Pause(bt.conn, name, nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -132,9 +132,9 @@ var _ = Describe("Podman containers ", func() { var name = "top" cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, cid) + err = containers.Pause(bt.conn, cid, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, cid) + err = containers.Pause(bt.conn, cid, nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -147,7 +147,7 @@ var _ = Describe("Podman containers ", func() { Expect(err).To(BeNil()) err = containers.Stop(bt.conn, name, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, name) + err = containers.Pause(bt.conn, name, nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -160,7 +160,7 @@ var _ = Describe("Podman containers ", func() { Expect(err).To(BeNil()) err = containers.Stop(bt.conn, cid, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, cid) + err = containers.Pause(bt.conn, cid, nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -171,9 +171,9 @@ var _ = Describe("Podman containers ", func() { var name = "top" cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, cid) + err = containers.Pause(bt.conn, cid, nil) Expect(err).To(BeNil()) - err = containers.Remove(bt.conn, cid, bindings.PFalse, bindings.PFalse) + err = containers.Remove(bt.conn, cid, nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -184,9 +184,9 @@ var _ = Describe("Podman containers ", func() { var name = "top" cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, cid) + err = containers.Pause(bt.conn, cid, nil) Expect(err).To(BeNil()) - err = containers.Remove(bt.conn, cid, bindings.PTrue, bindings.PFalse) + err = containers.Remove(bt.conn, cid, new(containers.RemoveOptions).WithForce(true)) Expect(err).To(BeNil()) }) @@ -195,7 +195,7 @@ var _ = Describe("Podman containers ", func() { var name = "top" _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, name) + err = containers.Pause(bt.conn, name, nil) Expect(err).To(BeNil()) err = containers.Stop(bt.conn, name, nil) Expect(err).ToNot(BeNil()) @@ -208,7 +208,7 @@ var _ = Describe("Podman containers ", func() { var name = "top" cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, cid) + err = containers.Pause(bt.conn, cid, nil) Expect(err).To(BeNil()) err = containers.Stop(bt.conn, cid, nil) Expect(err).ToNot(BeNil()) @@ -280,11 +280,11 @@ var _ = Describe("Podman containers ", func() { _, err := bt.RunTopContainer(&name, nil, nil) Expect(err).To(BeNil()) go func() { - exitCode, err = containers.Wait(bt.conn, name, &pause) + exitCode, err = containers.Wait(bt.conn, name, new(containers.WaitOptions).WithCondition(pause)) errChan <- err close(errChan) }() - err = containers.Pause(bt.conn, name) + err = containers.Pause(bt.conn, name, nil) Expect(err).To(BeNil()) wait := <-errChan Expect(wait).To(BeNil()) @@ -294,11 +294,11 @@ var _ = Describe("Podman containers ", func() { go func() { defer GinkgoRecover() - _, waitErr := containers.Wait(bt.conn, name, &running) + _, waitErr := containers.Wait(bt.conn, name, new(containers.WaitOptions).WithCondition(running)) unpauseErrChan <- waitErr close(unpauseErrChan) }() - err = containers.Unpause(bt.conn, name) + err = containers.Unpause(bt.conn, name, nil) Expect(err).To(BeNil()) unPausewait := <-unpauseErrChan Expect(unPausewait).To(BeNil()) @@ -309,7 +309,7 @@ var _ = Describe("Podman containers ", func() { bt.runPodman([]string{"run", "-d", "--name", "hc", "--health-interval", "disable", "--health-retries", "2", "--health-cmd", "ls / || exit 1", alpine.name, "top"}) // bogus name should result in 404 - _, err := containers.RunHealthCheck(bt.conn, "foobar") + _, err := containers.RunHealthCheck(bt.conn, "foobar", nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) @@ -317,7 +317,7 @@ var _ = Describe("Podman containers ", func() { // a container that has no healthcheck should be a 409 var name = "top" bt.RunTopContainer(&name, bindings.PFalse, nil) - _, err = containers.RunHealthCheck(bt.conn, name) + _, err = containers.RunHealthCheck(bt.conn, name, nil) Expect(err).ToNot(BeNil()) code, _ = bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusConflict)) @@ -355,7 +355,7 @@ var _ = Describe("Podman containers ", func() { s := specgen.NewSpecGenerator(alpine.name, false) s.Terminal = true s.Command = []string{"date", "-R"} - r, err := containers.CreateWithSpec(bt.conn, s) + r, err := containers.CreateWithSpec(bt.conn, s, nil) Expect(err).To(BeNil()) err = containers.Start(bt.conn, r.ID, nil) Expect(err).To(BeNil()) @@ -363,7 +363,7 @@ var _ = Describe("Podman containers ", func() { _, err = containers.Wait(bt.conn, r.ID, nil) Expect(err).To(BeNil()) - opts := containers.LogOptions{Stdout: bindings.PTrue, Follow: bindings.PTrue} + opts := new(containers.LogOptions).WithStdout(true).WithFollow(true) go func() { containers.Logs(bt.conn, r.ID, opts, stdoutChan, nil) }() @@ -387,7 +387,7 @@ var _ = Describe("Podman containers ", func() { Expect(err).To(BeNil()) // With descriptors - output, err := containers.Top(bt.conn, cid, []string{"user,pid,hpid"}) + output, err := containers.Top(bt.conn, cid, new(containers.TopOptions).WithDescriptors([]string{"user", "pid", "hpid"})) Expect(err).To(BeNil()) header := strings.Split(output[0], "\t") for _, d := range []string{"USER", "PID", "HPID"} { @@ -399,7 +399,7 @@ var _ = Describe("Podman containers ", func() { Expect(err).ToNot(BeNil()) // With bogus descriptors - _, err = containers.Top(bt.conn, cid, []string{"Me,Neither"}) + _, err = containers.Top(bt.conn, cid, new(containers.TopOptions).WithDescriptors([]string{"Me,Neither"})) Expect(err).To(BeNil()) }) @@ -442,7 +442,7 @@ var _ = Describe("Podman containers ", func() { It("podman kill bogus container", func() { // Killing bogus container should return 404 - err := containers.Kill(bt.conn, "foobar", "SIGTERM") + err := containers.Kill(bt.conn, "foobar", "SIGTERM", nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) @@ -453,7 +453,7 @@ var _ = Describe("Podman containers ", func() { var name = "top" _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Kill(bt.conn, name, "SIGINT") + err = containers.Kill(bt.conn, name, "SIGINT", nil) Expect(err).To(BeNil()) _, err = containers.Exists(bt.conn, name, false) Expect(err).To(BeNil()) @@ -464,7 +464,7 @@ var _ = Describe("Podman containers ", func() { var name = "top" cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Kill(bt.conn, cid, "SIGTERM") + err = containers.Kill(bt.conn, cid, "SIGTERM", nil) Expect(err).To(BeNil()) _, err = containers.Exists(bt.conn, cid, false) Expect(err).To(BeNil()) @@ -475,7 +475,7 @@ var _ = Describe("Podman containers ", func() { var name = "top" cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Kill(bt.conn, cid, "SIGKILL") + err = containers.Kill(bt.conn, cid, "SIGKILL", nil) Expect(err).To(BeNil()) }) @@ -484,7 +484,7 @@ var _ = Describe("Podman containers ", func() { var name = "top" cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Kill(bt.conn, cid, "foobar") + err = containers.Kill(bt.conn, cid, "foobar", nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -494,19 +494,18 @@ var _ = Describe("Podman containers ", func() { // Killing latest container should work var name1 = "first" var name2 = "second" - var latestContainers = 1 _, err := bt.RunTopContainer(&name1, bindings.PFalse, nil) Expect(err).To(BeNil()) _, err = bt.RunTopContainer(&name2, bindings.PFalse, nil) Expect(err).To(BeNil()) - containerLatestList, err := containers.List(bt.conn, nil, nil, &latestContainers, nil, nil, nil) + containerLatestList, err := containers.List(bt.conn, new(containers.ListOptions).WithLast(1)) Expect(err).To(BeNil()) - err = containers.Kill(bt.conn, containerLatestList[0].Names[0], "SIGTERM") + err = containers.Kill(bt.conn, containerLatestList[0].Names[0], "SIGTERM", nil) Expect(err).To(BeNil()) }) It("container init on a bogus container", func() { - err := containers.ContainerInit(bt.conn, "doesnotexist") + err := containers.ContainerInit(bt.conn, "doesnotexist", nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) @@ -514,12 +513,12 @@ var _ = Describe("Podman containers ", func() { It("container init", func() { s := specgen.NewSpecGenerator(alpine.name, false) - ctr, err := containers.CreateWithSpec(bt.conn, s) + ctr, err := containers.CreateWithSpec(bt.conn, s, nil) Expect(err).To(BeNil()) - err = containers.ContainerInit(bt.conn, ctr.ID) + err = containers.ContainerInit(bt.conn, ctr.ID, nil) Expect(err).To(BeNil()) // trying to init again should be an error - err = containers.ContainerInit(bt.conn, ctr.ID) + err = containers.ContainerInit(bt.conn, ctr.ID, nil) Expect(err).ToNot(BeNil()) }) @@ -550,14 +549,14 @@ var _ = Describe("Podman containers ", func() { filtersIncorrect := map[string][]string{ "status": {"dummy"}, } - pruneResponse, err := containers.Prune(bt.conn, filtersIncorrect) + pruneResponse, err := containers.Prune(bt.conn, new(containers.PruneOptions).WithFilters(filtersIncorrect)) Expect(err).ToNot(BeNil()) // Mismatched filter params no container should be pruned. filtersIncorrect = map[string][]string{ "name": {"r"}, } - pruneResponse, err = containers.Prune(bt.conn, filtersIncorrect) + pruneResponse, err = containers.Prune(bt.conn, new(containers.PruneOptions).WithFilters(filtersIncorrect)) Expect(err).To(BeNil()) Expect(len(pruneResponse.Err)).To(Equal(0)) Expect(len(pruneResponse.ID)).To(Equal(0)) @@ -566,7 +565,7 @@ var _ = Describe("Podman containers ", func() { filters := map[string][]string{ "name": {"top"}, } - pruneResponse, err = containers.Prune(bt.conn, filters) + pruneResponse, err = containers.Prune(bt.conn, new(containers.PruneOptions).WithFilters(filters)) Expect(err).To(BeNil()) Expect(len(pruneResponse.Err)).To(Equal(0)) Expect(len(pruneResponse.ID)).To(Equal(1)) @@ -620,7 +619,7 @@ var _ = Describe("Podman containers ", func() { var name = "top" _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - _, err = containers.Inspect(bt.conn, name, bindings.PTrue) + _, err = containers.Inspect(bt.conn, name, new(containers.InspectOptions).WithSize(true)) Expect(err).To(BeNil()) }) @@ -631,12 +630,12 @@ var _ = Describe("Podman containers ", func() { err = containers.Stop(bt.conn, name, nil) Expect(err).To(BeNil()) // Inspecting stopped container with size should succeed - _, err = containers.Inspect(bt.conn, name, bindings.PTrue) + _, err = containers.Inspect(bt.conn, name, new(containers.InspectOptions).WithSize(true)) Expect(err).To(BeNil()) }) It("podman remove bogus container", func() { - err = containers.Remove(bt.conn, "foobar", nil, nil) + err = containers.Remove(bt.conn, "foobar", nil) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) }) @@ -646,7 +645,7 @@ var _ = Describe("Podman containers ", func() { _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, name, nil, nil) + err = containers.Remove(bt.conn, name, nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -657,7 +656,7 @@ var _ = Describe("Podman containers ", func() { cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, cid, nil, nil) + err = containers.Remove(bt.conn, cid, nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -668,7 +667,7 @@ var _ = Describe("Podman containers ", func() { _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, name, bindings.PTrue, nil) + 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)) @@ -679,7 +678,7 @@ var _ = Describe("Podman containers ", func() { cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, cid, bindings.PTrue, nil) + 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)) @@ -690,7 +689,7 @@ var _ = Describe("Podman containers ", func() { _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, name, nil, bindings.PTrue) + 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)) @@ -701,7 +700,7 @@ var _ = Describe("Podman containers ", func() { cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, cid, nil, bindings.PTrue) + 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)) @@ -712,7 +711,7 @@ var _ = Describe("Podman containers ", func() { _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, name, bindings.PTrue, bindings.PTrue) + 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)) @@ -723,7 +722,7 @@ var _ = Describe("Podman containers ", func() { cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, cid, bindings.PTrue, bindings.PTrue) + 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)) @@ -739,12 +738,12 @@ var _ = Describe("Podman containers ", func() { s := specgen.NewSpecGenerator(alpine.name, false) s.Terminal = true s.Command = []string{"date", "-R"} - _, err = containers.CreateWithSpec(bt.conn, s) + _, err = containers.CreateWithSpec(bt.conn, s, nil) Expect(err).To(BeNil()) // Validate list container with id filter filters := make(map[string][]string) filters["id"] = []string{cid} - c, err := containers.List(bt.conn, filters, bindings.PTrue, nil, nil, nil, nil) + c, err := containers.List(bt.conn, new(containers.ListOptions).WithFilters(filters).WithAll(true)) Expect(err).To(BeNil()) Expect(len(c)).To(Equal(1)) }) @@ -758,7 +757,7 @@ var _ = Describe("Podman containers ", func() { lastNum := 1 - c, err := containers.List(bt.conn, nil, bindings.PTrue, &lastNum, nil, nil, nil) + c, err := containers.List(bt.conn, new(containers.ListOptions).WithAll(true).WithLast(lastNum)) Expect(err).To(BeNil()) Expect(len(c)).To(Equal(1)) Expect(c[0].PodName).To(Equal(podName)) diff --git a/pkg/bindings/test/create_test.go b/pkg/bindings/test/create_test.go index fd9ce23ca..2d2d657de 100644 --- a/pkg/bindings/test/create_test.go +++ b/pkg/bindings/test/create_test.go @@ -35,7 +35,7 @@ var _ = Describe("Create containers ", func() { s.Command = []string{"top"} s.Terminal = true s.Name = "top" - ctr, err := containers.CreateWithSpec(bt.conn, s) + ctr, err := containers.CreateWithSpec(bt.conn, s, nil) Expect(err).To(BeNil()) data, err := containers.Inspect(bt.conn, ctr.ID, nil) Expect(err).To(BeNil()) diff --git a/pkg/bindings/test/exec_test.go b/pkg/bindings/test/exec_test.go index e3d90b9ea..8e20a192f 100644 --- a/pkg/bindings/test/exec_test.go +++ b/pkg/bindings/test/exec_test.go @@ -43,7 +43,7 @@ var _ = Describe("Podman containers exec", func() { Expect(err).To(BeNil()) Expect(sessionID).To(Not(Equal(""))) - inspectOut, err := containers.ExecInspect(bt.conn, sessionID) + inspectOut, err := containers.ExecInspect(bt.conn, sessionID, nil) Expect(err).To(BeNil()) Expect(inspectOut.ContainerID).To(Equal(cid)) Expect(inspectOut.ProcessConfig.Entrypoint).To(Equal("echo")) @@ -71,7 +71,7 @@ var _ = Describe("Podman containers exec", func() { }) It("Podman exec inspect on invalid session fails", func() { - _, err := containers.ExecInspect(bt.conn, "0000000000000000000000000000000000000000000000000000000000000000") + _, err := containers.ExecInspect(bt.conn, "0000000000000000000000000000000000000000000000000000000000000000", nil) Expect(err).To(Not(BeNil())) }) }) diff --git a/pkg/bindings/test/images_test.go b/pkg/bindings/test/images_test.go index b6362a631..ae41eced9 100644 --- a/pkg/bindings/test/images_test.go +++ b/pkg/bindings/test/images_test.go @@ -118,7 +118,7 @@ var _ = Describe("Podman images", func() { Expect(len(errs)).To(BeZero()) // To be extra sure, check if the previously created container // is gone as well. - _, err = containers.Inspect(bt.conn, "top", bindings.PFalse) + _, err = containers.Inspect(bt.conn, "top", nil) code, _ = bindings.CheckResponseCode(err) // Now make sure both images are gone. diff --git a/pkg/bindings/test/info_test.go b/pkg/bindings/test/info_test.go index c4473cd19..4d696b59e 100644 --- a/pkg/bindings/test/info_test.go +++ b/pkg/bindings/test/info_test.go @@ -46,12 +46,12 @@ var _ = Describe("Podman info", func() { It("podman info container counts", func() { s := specgen.NewSpecGenerator(alpine.name, false) - _, err := containers.CreateWithSpec(bt.conn, s) + _, err := containers.CreateWithSpec(bt.conn, s, nil) Expect(err).To(BeNil()) idPause, err := bt.RunTopContainer(nil, nil, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, idPause) + err = containers.Pause(bt.conn, idPause, nil) Expect(err).To(BeNil()) idStop, err := bt.RunTopContainer(nil, nil, nil) diff --git a/pkg/bindings/test/pods_test.go b/pkg/bindings/test/pods_test.go index 8498de020..17b142fc2 100644 --- a/pkg/bindings/test/pods_test.go +++ b/pkg/bindings/test/pods_test.go @@ -40,13 +40,13 @@ var _ = Describe("Podman pods", func() { It("inspect pod", func() { //Inspect an invalid pod name - _, err := pods.Inspect(bt.conn, "dummyname") + _, err := pods.Inspect(bt.conn, "dummyname", nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) //Inspect an valid pod name - response, err := pods.Inspect(bt.conn, newpod) + response, err := pods.Inspect(bt.conn, newpod, nil) Expect(err).To(BeNil()) Expect(response.Name).To(Equal(newpod)) }) @@ -59,7 +59,7 @@ var _ = Describe("Podman pods", func() { Expect(len(podSummary)).To(Equal(1)) // Start the pod - _, err = pods.Start(bt.conn, newpod) + _, err = pods.Start(bt.conn, newpod, nil) Expect(err).To(BeNil()) // Adding an alpine container to the existing pod @@ -90,7 +90,7 @@ var _ = Describe("Podman pods", func() { bt.Podcreate(&newpod2) // Start the pod - _, err = pods.Start(bt.conn, newpod) + _, err = pods.Start(bt.conn, newpod, nil) Expect(err).To(BeNil()) _, err = bt.RunTopContainer(nil, bindings.PTrue, &newpod) @@ -99,7 +99,8 @@ var _ = Describe("Podman pods", func() { // Expected err with invalid filter params filters := make(map[string][]string) filters["dummy"] = []string{"dummy"} - filteredPods, err := pods.List(bt.conn, filters) + options := new(pods.ListOptions).WithFilters(filters) + filteredPods, err := pods.List(bt.conn, options) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -107,14 +108,16 @@ var _ = Describe("Podman pods", func() { // Expected empty response with invalid filters filters = make(map[string][]string) filters["name"] = []string{"dummy"} - filteredPods, err = pods.List(bt.conn, filters) + options = new(pods.ListOptions).WithFilters(filters) + filteredPods, err = pods.List(bt.conn, options) Expect(err).To(BeNil()) Expect(len(filteredPods)).To(BeNumerically("==", 0)) // Validate list pod with name filter filters = make(map[string][]string) filters["name"] = []string{newpod2} - filteredPods, err = pods.List(bt.conn, filters) + options = new(pods.ListOptions).WithFilters(filters) + filteredPods, err = pods.List(bt.conn, options) Expect(err).To(BeNil()) Expect(len(filteredPods)).To(BeNumerically("==", 1)) var names []string @@ -125,11 +128,12 @@ var _ = Describe("Podman pods", func() { // Validate list pod with id filter filters = make(map[string][]string) - response, err := pods.Inspect(bt.conn, newpod) + response, err := pods.Inspect(bt.conn, newpod, nil) Expect(err).To(BeNil()) id := response.ID filters["id"] = []string{id} - filteredPods, err = pods.List(bt.conn, filters) + options = new(pods.ListOptions).WithFilters(filters) + filteredPods, err = pods.List(bt.conn, options) Expect(err).To(BeNil()) Expect(len(filteredPods)).To(BeNumerically("==", 1)) names = names[:0] @@ -140,7 +144,8 @@ var _ = Describe("Podman pods", func() { // Using multiple filters filters["name"] = []string{newpod} - filteredPods, err = pods.List(bt.conn, filters) + options = new(pods.ListOptions).WithFilters(filters) + filteredPods, err = pods.List(bt.conn, options) Expect(err).To(BeNil()) Expect(len(filteredPods)).To(BeNumerically("==", 1)) names = names[:0] @@ -168,7 +173,7 @@ var _ = Describe("Podman pods", func() { // TODO fix this Skip("Pod behavior is jacked right now.") // Pause invalid container - _, err := pods.Pause(bt.conn, "dummyName") + _, err := pods.Pause(bt.conn, "dummyName", nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) @@ -180,9 +185,9 @@ var _ = Describe("Podman pods", func() { // Binding needs to be modified to inspect the pod state. // Since we don't have a pod state we inspect the states of the containers within the pod. // Pause a valid container - _, err = pods.Pause(bt.conn, newpod) + _, err = pods.Pause(bt.conn, newpod, nil) Expect(err).To(BeNil()) - response, err := pods.Inspect(bt.conn, newpod) + response, err := pods.Inspect(bt.conn, newpod, nil) Expect(err).To(BeNil()) Expect(response.State).To(Equal(define.PodStatePaused)) for _, i := range response.Containers { @@ -191,9 +196,9 @@ var _ = Describe("Podman pods", func() { } // Unpause a valid container - _, err = pods.Unpause(bt.conn, newpod) + _, err = pods.Unpause(bt.conn, newpod, nil) Expect(err).To(BeNil()) - response, err = pods.Inspect(bt.conn, newpod) + response, err = pods.Inspect(bt.conn, newpod, nil) Expect(err).To(BeNil()) Expect(response.State).To(Equal(define.PodStateRunning)) for _, i := range response.Containers { @@ -204,7 +209,7 @@ var _ = Describe("Podman pods", func() { It("start stop restart pod", func() { // Start an invalid pod - _, err = pods.Start(bt.conn, "dummyName") + _, err = pods.Start(bt.conn, "dummyName", nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) @@ -216,16 +221,16 @@ var _ = Describe("Podman pods", func() { Expect(code).To(BeNumerically("==", http.StatusNotFound)) // Restart an invalid pod - _, err = pods.Restart(bt.conn, "dummyName") + _, err = pods.Restart(bt.conn, "dummyName", nil) Expect(err).ToNot(BeNil()) code, _ = bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) // Start a valid pod and inspect status of each container - _, err = pods.Start(bt.conn, newpod) + _, err = pods.Start(bt.conn, newpod, nil) Expect(err).To(BeNil()) - response, err := pods.Inspect(bt.conn, newpod) + response, err := pods.Inspect(bt.conn, newpod, nil) Expect(err).To(BeNil()) Expect(response.State).To(Equal(define.PodStateRunning)) for _, i := range response.Containers { @@ -234,13 +239,13 @@ var _ = Describe("Podman pods", func() { } // Start an already running pod - _, err = pods.Start(bt.conn, newpod) + _, err = pods.Start(bt.conn, newpod, nil) Expect(err).To(BeNil()) // Stop the running pods _, err = pods.Stop(bt.conn, newpod, nil) Expect(err).To(BeNil()) - response, _ = pods.Inspect(bt.conn, newpod) + response, _ = pods.Inspect(bt.conn, newpod, nil) Expect(response.State).To(Equal(define.PodStateExited)) for _, i := range response.Containers { Expect(define.StringToContainerStatus(i.State)). @@ -251,9 +256,9 @@ var _ = Describe("Podman pods", func() { _, err = pods.Stop(bt.conn, newpod, nil) Expect(err).To(BeNil()) - _, err = pods.Restart(bt.conn, newpod) + _, err = pods.Restart(bt.conn, newpod, nil) Expect(err).To(BeNil()) - response, _ = pods.Inspect(bt.conn, newpod) + response, _ = pods.Inspect(bt.conn, newpod, nil) Expect(response.State).To(Equal(define.PodStateRunning)) for _, i := range response.Containers { Expect(define.StringToContainerStatus(i.State)). @@ -267,7 +272,7 @@ var _ = Describe("Podman pods", func() { var newpod2 string = "newpod2" bt.Podcreate(&newpod2) // No pods pruned since no pod in exited state - pruneResponse, err := pods.Prune(bt.conn) + pruneResponse, err := pods.Prune(bt.conn, nil) Expect(err).To(BeNil()) podSummary, err := pods.List(bt.conn, nil) Expect(err).To(BeNil()) @@ -276,14 +281,14 @@ var _ = Describe("Podman pods", func() { // Prune only one pod which is in exited state. // Start then stop a pod. // pod moves to exited state one pod should be pruned now. - _, err = pods.Start(bt.conn, newpod) + _, err = pods.Start(bt.conn, newpod, nil) Expect(err).To(BeNil()) _, err = pods.Stop(bt.conn, newpod, nil) Expect(err).To(BeNil()) - response, err := pods.Inspect(bt.conn, newpod) + response, err := pods.Inspect(bt.conn, newpod, nil) Expect(err).To(BeNil()) Expect(response.State).To(Equal(define.PodStateExited)) - pruneResponse, err = pods.Prune(bt.conn) + pruneResponse, err = pods.Prune(bt.conn, nil) Expect(err).To(BeNil()) // Validate status and record pod id of pod to be pruned Expect(response.State).To(Equal(define.PodStateExited)) @@ -298,13 +303,13 @@ var _ = Describe("Podman pods", func() { // Test prune multiple pods. bt.Podcreate(&newpod) - _, err = pods.Start(bt.conn, newpod) + _, err = pods.Start(bt.conn, newpod, nil) Expect(err).To(BeNil()) - _, err = pods.Start(bt.conn, newpod2) + _, err = pods.Start(bt.conn, newpod2, nil) Expect(err).To(BeNil()) _, err = pods.Stop(bt.conn, newpod, nil) Expect(err).To(BeNil()) - response, err = pods.Inspect(bt.conn, newpod) + response, err = pods.Inspect(bt.conn, newpod, nil) Expect(err).To(BeNil()) Expect(response.State).To(Equal(define.PodStateExited)) for _, i := range response.Containers { @@ -313,14 +318,14 @@ var _ = Describe("Podman pods", func() { } _, err = pods.Stop(bt.conn, newpod2, nil) Expect(err).To(BeNil()) - response, err = pods.Inspect(bt.conn, newpod2) + response, err = pods.Inspect(bt.conn, newpod2, nil) Expect(err).To(BeNil()) Expect(response.State).To(Equal(define.PodStateExited)) for _, i := range response.Containers { Expect(define.StringToContainerStatus(i.State)). To(Equal(define.ContainerStateExited)) } - _, err = pods.Prune(bt.conn) + _, err = pods.Prune(bt.conn, nil) Expect(err).To(BeNil()) podSummary, err = pods.List(bt.conn, nil) Expect(err).To(BeNil()) @@ -330,7 +335,7 @@ var _ = Describe("Podman pods", func() { It("simple create pod", func() { ps := specgen.PodSpecGenerator{} ps.Name = "foobar" - _, err := pods.CreatePodFromSpec(bt.conn, &ps) + _, err := pods.CreatePodFromSpec(bt.conn, &ps, nil) Expect(err).To(BeNil()) exists, err := pods.Exists(bt.conn, "foobar") @@ -343,7 +348,7 @@ var _ = Describe("Podman pods", func() { var name string = "podA" bt.Podcreate(&name) - _, err := pods.Start(bt.conn, name) + _, err := pods.Start(bt.conn, name, nil) Expect(err).To(BeNil()) // By name @@ -351,7 +356,8 @@ var _ = Describe("Podman pods", func() { Expect(err).To(BeNil()) // With descriptors - output, err := pods.Top(bt.conn, name, []string{"user,pid,hpid"}) + options := new(pods.TopOptions).WithDescriptors([]string{"user,pid,hpid"}) + output, err := pods.Top(bt.conn, name, options) Expect(err).To(BeNil()) header := strings.Split(output[0], "\t") for _, d := range []string{"USER", "PID", "HPID"} { @@ -363,7 +369,8 @@ var _ = Describe("Podman pods", func() { Expect(err).ToNot(BeNil()) // With bogus descriptors - _, err = pods.Top(bt.conn, name, []string{"Me,Neither"}) + options = new(pods.TopOptions).WithDescriptors([]string{"Me,Neither"}) + _, err = pods.Top(bt.conn, name, options) Expect(err).ToNot(BeNil()) }) }) diff --git a/pkg/bindings/test/system_test.go b/pkg/bindings/test/system_test.go index 8a8c94d09..25fda5575 100644 --- a/pkg/bindings/test/system_test.go +++ b/pkg/bindings/test/system_test.go @@ -65,7 +65,7 @@ var _ = Describe("Podman system", func() { It("podman system prune - pod,container stopped", func() { // Start and stop a pod to enter in exited state. - _, err := pods.Start(bt.conn, newpod) + _, err := pods.Start(bt.conn, newpod, nil) Expect(err).To(BeNil()) _, err = pods.Stop(bt.conn, newpod, nil) Expect(err).To(BeNil()) @@ -90,7 +90,7 @@ var _ = Describe("Podman system", func() { It("podman system prune running alpine container", func() { // Start and stop a pod to enter in exited state. - _, err := pods.Start(bt.conn, newpod) + _, err := pods.Start(bt.conn, newpod, nil) Expect(err).To(BeNil()) _, err = pods.Stop(bt.conn, newpod, nil) Expect(err).To(BeNil()) @@ -126,7 +126,7 @@ var _ = Describe("Podman system", func() { It("podman system prune running alpine container volume prune", func() { // Start a pod and leave it running - _, err := pods.Start(bt.conn, newpod) + _, err := pods.Start(bt.conn, newpod, nil) Expect(err).To(BeNil()) // Start and stop a container to enter in exited state. @@ -158,4 +158,61 @@ var _ = Describe("Podman system", func() { // Volume should be pruned now as flag set true Expect(len(systemPruneResponse.VolumePruneReport)).To(Equal(1)) }) + + It("podman system prune running alpine container volume prune --filter", func() { + // Start a pod and leave it running + _, err := pods.Start(bt.conn, newpod, nil) + Expect(err).To(BeNil()) + + // Start and stop a container to enter in exited state. + var name = "top" + _, err = bt.RunTopContainer(&name, bindings.PFalse, nil) + Expect(err).To(BeNil()) + err = containers.Stop(bt.conn, name, nil) + Expect(err).To(BeNil()) + + // Start second container and leave in running + var name2 = "top2" + _, err = bt.RunTopContainer(&name2, bindings.PFalse, nil) + Expect(err).To(BeNil()) + + // Adding an unused volume should work + _, err = volumes.Create(bt.conn, entities.VolumeCreateOptions{}, nil) + Expect(err).To(BeNil()) + + // Adding an unused volume with label should work + _, err = volumes.Create(bt.conn, entities.VolumeCreateOptions{Label: map[string]string{ + "label1": "value1", + }}, nil) + Expect(err).To(BeNil()) + + f := make(map[string][]string) + f["label"] = []string{"label1=idontmatch"} + + options := new(system.PruneOptions).WithAll(true).WithVolumes(true).WithFilters(f) + systemPruneResponse, err := system.Prune(bt.conn, options) + Expect(err).To(BeNil()) + Expect(len(systemPruneResponse.PodPruneReport)).To(Equal(0)) + // TODO fix system filter handling so all components can handle filters + // This check **should** be "Equal(0)" since we are passing label + // filters however the Prune function doesn't seem to pass filters + // to each component. + Expect(len(systemPruneResponse.ContainerPruneReport.ID)).To(Equal(1)) + Expect(len(systemPruneResponse.ImagePruneReport.Report.Id)). + To(BeNumerically(">", 0)) + // Alpine image should not be pruned as used by running container + Expect(systemPruneResponse.ImagePruneReport.Report.Id). + ToNot(ContainElement("docker.io/library/alpine:latest")) + // Volume shouldn't be pruned because the PruneOptions filters doesn't match + Expect(len(systemPruneResponse.VolumePruneReport)).To(Equal(0)) + + // Fix filter and re prune + f["label"] = []string{"label1=value1"} + options = new(system.PruneOptions).WithAll(true).WithVolumes(true).WithFilters(f) + systemPruneResponse, err = system.Prune(bt.conn, options) + Expect(err).To(BeNil()) + + // Volume should be pruned because the PruneOptions filters now match + Expect(len(systemPruneResponse.VolumePruneReport)).To(Equal(1)) + }) }) diff --git a/pkg/bindings/test/volumes_test.go b/pkg/bindings/test/volumes_test.go index 0664a99e4..e0d854b66 100644 --- a/pkg/bindings/test/volumes_test.go +++ b/pkg/bindings/test/volumes_test.go @@ -102,8 +102,7 @@ var _ = Describe("Podman volumes", func() { Expect(code).To(BeNumerically("==", http.StatusConflict)) // Removing with a volume in use with force should work with a stopped container - zero := uint(0) - err = containers.Stop(connText, "vtest", &zero) + err = containers.Stop(connText, "vtest", new(containers.StopOptions).WithTimeout(0)) Expect(err).To(BeNil()) options := new(volumes.RemoveOptions).WithForce(true) err = volumes.Remove(connText, vol.Name, options) diff --git a/pkg/bindings/volumes/types_create_options.go b/pkg/bindings/volumes/types_create_options.go index 442dedc8a..80bdac2d2 100644 --- a/pkg/bindings/volumes/types_create_options.go +++ b/pkg/bindings/volumes/types_create_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:59:25.536700522 -0600 CST m=+0.000174605 +Created 2020-12-18 15:58:14.043860791 -0600 CST m=+0.000188944 */ // Changed @@ -56,10 +56,10 @@ func (o *CreateOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/volumes/types_inspect_options.go b/pkg/bindings/volumes/types_inspect_options.go index f9fc9096b..ba8c70b63 100644 --- a/pkg/bindings/volumes/types_inspect_options.go +++ b/pkg/bindings/volumes/types_inspect_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:59:25.697109241 -0600 CST m=+0.000142064 +Created 2020-12-18 15:58:14.189902005 -0600 CST m=+0.000151439 */ // Changed @@ -56,10 +56,10 @@ func (o *InspectOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/volumes/types_list_options.go b/pkg/bindings/volumes/types_list_options.go index 673643cf1..99dec132c 100644 --- a/pkg/bindings/volumes/types_list_options.go +++ b/pkg/bindings/volumes/types_list_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:59:25.835646604 -0600 CST m=+0.000141801 +Created 2020-12-18 15:58:14.326721724 -0600 CST m=+0.000172471 */ // Changed @@ -56,10 +56,10 @@ func (o *ListOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/volumes/types_prune_options.go b/pkg/bindings/volumes/types_prune_options.go index 94d81e737..cdbc03fc9 100644 --- a/pkg/bindings/volumes/types_prune_options.go +++ b/pkg/bindings/volumes/types_prune_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:59:25.972202545 -0600 CST m=+0.000209541 +Created 2020-12-18 15:58:14.463307398 -0600 CST m=+0.000180868 */ // Changed @@ -56,10 +56,10 @@ func (o *PruneOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/bindings/volumes/types_remove_options.go b/pkg/bindings/volumes/types_remove_options.go index 499abf0f2..923d1353c 100644 --- a/pkg/bindings/volumes/types_remove_options.go +++ b/pkg/bindings/volumes/types_remove_options.go @@ -12,7 +12,7 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-16 11:59:26.109834902 -0600 CST m=+0.000175439 +Created 2020-12-18 15:58:14.60278922 -0600 CST m=+0.000134408 */ // Changed @@ -56,10 +56,10 @@ func (o *RemoveOptions) ToParams() (url.Values, error) { params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) case reflect.Slice: typ := reflect.TypeOf(f.Interface()).Elem() - slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) switch typ.Kind() { case reflect.String: - s, ok := slice.Interface().([]string) + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) if !ok { return nil, errors.New("failed to convert to string slice") } diff --git a/pkg/copy/copy.go b/pkg/copy/copy.go deleted file mode 100644 index 13893deb2..000000000 --- a/pkg/copy/copy.go +++ /dev/null @@ -1,220 +0,0 @@ -package copy - -import ( - "io" - "os" - "path/filepath" - "strings" - - buildahCopiah "github.com/containers/buildah/copier" - "github.com/containers/storage/pkg/archive" - securejoin "github.com/cyphar/filepath-securejoin" - "github.com/pkg/errors" -) - -// ********************************* NOTE ************************************* -// -// Most security bugs are caused by attackers playing around with symlinks -// trying to escape from the container onto the host and/or trick into data -// corruption on the host. Hence, file operations on containers (including -// *stat) should always be handled by `github.com/containers/buildah/copier` -// which makes sure to evaluate files in a chroot'ed environment. -// -// Please make sure to add verbose comments when changing code to make the -// lives of future readers easier. -// -// **************************************************************************** - -// Copier copies data from a source to a destination CopyItem. -type Copier struct { - copyFunc func() error - cleanUpFuncs []deferFunc -} - -// cleanUp releases resources the Copier may hold open. -func (c *Copier) cleanUp() { - for _, f := range c.cleanUpFuncs { - f() - } -} - -// Copy data from a source to a destination CopyItem. -func (c *Copier) Copy() error { - defer c.cleanUp() - return c.copyFunc() -} - -// GetCopiers returns a Copier to copy the source item to destination. Use -// extract to untar the source if it's a tar archive. -func GetCopier(source *CopyItem, destination *CopyItem, extract bool) (*Copier, error) { - copier := &Copier{} - - // First, do the man-page dance. See podman-cp(1) for details. - if err := enforceCopyRules(source, destination); err != nil { - return nil, err - } - - // Destination is a stream (e.g., stdout or an http body). - if destination.info.IsStream { - // Source is a stream (e.g., stdin or an http body). - if source.info.IsStream { - copier.copyFunc = func() error { - _, err := io.Copy(destination.writer, source.reader) - return err - } - return copier, nil - } - root, glob, err := source.buildahGlobs() - if err != nil { - return nil, err - } - copier.copyFunc = func() error { - return buildahCopiah.Get(root, "", source.getOptions(), []string{glob}, destination.writer) - } - return copier, nil - } - - // Destination is either a file or a directory. - if source.info.IsStream { - copier.copyFunc = func() error { - return buildahCopiah.Put(destination.root, destination.resolved, source.putOptions(), source.reader) - } - return copier, nil - } - - tarOptions := &archive.TarOptions{ - Compression: archive.Uncompressed, - CopyPass: true, - } - - root := destination.root - dir := destination.resolved - if !source.info.IsDir { - // When copying a file, make sure to rename the - // destination base path. - nameMap := make(map[string]string) - nameMap[filepath.Base(source.resolved)] = filepath.Base(destination.resolved) - tarOptions.RebaseNames = nameMap - dir = filepath.Dir(dir) - } - - var tarReader io.ReadCloser - if extract && archive.IsArchivePath(source.resolved) { - if !destination.info.IsDir { - return nil, errors.Errorf("cannot extract archive %q to file %q", source.original, destination.original) - } - - reader, err := os.Open(source.resolved) - if err != nil { - return nil, err - } - copier.cleanUpFuncs = append(copier.cleanUpFuncs, func() { reader.Close() }) - - // The stream from stdin may be compressed (e.g., via gzip). - decompressedStream, err := archive.DecompressStream(reader) - if err != nil { - return nil, err - } - - copier.cleanUpFuncs = append(copier.cleanUpFuncs, func() { decompressedStream.Close() }) - tarReader = decompressedStream - } else { - reader, err := archive.TarWithOptions(source.resolved, tarOptions) - if err != nil { - return nil, err - } - copier.cleanUpFuncs = append(copier.cleanUpFuncs, func() { reader.Close() }) - tarReader = reader - } - - copier.copyFunc = func() error { - return buildahCopiah.Put(root, dir, source.putOptions(), tarReader) - } - return copier, nil -} - -// enforceCopyRules enforces the rules for copying from a source to a -// destination as mentioned in the podman-cp(1) man page. Please refer to the -// man page and/or the inline comments for further details. Note that source -// and destination are passed by reference and the their data may be changed. -func enforceCopyRules(source, destination *CopyItem) error { - if source.statError != nil { - return source.statError - } - - // We can copy everything to a stream. - if destination.info.IsStream { - return nil - } - - if source.info.IsStream { - if !(destination.info.IsDir || destination.info.IsStream) { - return errors.New("destination must be a directory or stream when copying from a stream") - } - return nil - } - - // Source is a *directory*. - if source.info.IsDir { - if destination.statError != nil { - // It's okay if the destination does not exist. We - // made sure before that it's parent exists, so it - // would be created while copying. - if os.IsNotExist(destination.statError) { - return nil - } - // Could be a permission error. - return destination.statError - } - - // If the destination exists and is not a directory, we have a - // problem. - if !destination.info.IsDir { - return errors.Errorf("cannot copy directory %q to file %q", source.original, destination.original) - } - - // If the destination exists and is a directory, we need to - // append the source base directory to it. This makes sure - // that copying "/foo/bar" "/tmp" will copy to "/tmp/bar" (and - // not "/tmp"). - newDestination, err := securejoin.SecureJoin(destination.resolved, filepath.Base(source.resolved)) - if err != nil { - return err - } - destination.resolved = newDestination - return nil - } - - // Source is a *file*. - if destination.statError != nil { - // It's okay if the destination does not exist, unless it ends - // with "/". - if !os.IsNotExist(destination.statError) { - return destination.statError - } else if strings.HasSuffix(destination.resolved, "/") { - // Note: this is practically unreachable code as the - // existence of parent directories is enforced early - // on. It's left here as an extra security net. - return errors.Errorf("destination directory %q must exist (trailing %q)", destination.original, "/") - } - // Does not exist and does not end with "/". - return nil - } - - // If the destination is a file, we're good. We will overwrite the - // contents while copying. - if !destination.info.IsDir { - return nil - } - - // If the destination exists and is a directory, we need to append the - // source base directory to it. This makes sure that copying - // "/foo/bar" "/tmp" will copy to "/tmp/bar" (and not "/tmp"). - newDestination, err := securejoin.SecureJoin(destination.resolved, filepath.Base(source.resolved)) - if err != nil { - return err - } - - destination.resolved = newDestination - return nil -} diff --git a/pkg/copy/fileinfo.go b/pkg/copy/fileinfo.go index 08b4eb377..50b0ca9a1 100644 --- a/pkg/copy/fileinfo.go +++ b/pkg/copy/fileinfo.go @@ -5,6 +5,7 @@ import ( "encoding/json" "net/http" "os" + "path/filepath" "strings" "time" @@ -15,6 +16,10 @@ import ( // base64 encoded JSON payload of stating a path in a container. const XDockerContainerPathStatHeader = "X-Docker-Container-Path-Stat" +// ENOENT mimics the stdlib's ENONENT and can be used to implement custom logic +// while preserving the user-visible error message. +var ENOENT = errors.New("No such file or directory") + // FileInfo describes a file or directory and is returned by // (*CopyItem).Stat(). type FileInfo struct { @@ -23,7 +28,6 @@ type FileInfo struct { Mode os.FileMode `json:"mode"` ModTime time.Time `json:"mtime"` IsDir bool `json:"isDir"` - IsStream bool `json:"isStream"` LinkTarget string `json:"linkTarget"` } @@ -54,3 +58,54 @@ func ExtractFileInfoFromHeader(header *http.Header) (*FileInfo, error) { return &info, nil } + +// ResolveHostPath resolves the specified, possibly relative, path on the host. +func ResolveHostPath(path string) (*FileInfo, error) { + resolvedHostPath, err := filepath.Abs(path) + if err != nil { + return nil, err + } + resolvedHostPath = PreserveBasePath(path, resolvedHostPath) + + statInfo, err := os.Stat(resolvedHostPath) + if err != nil { + if os.IsNotExist(err) { + return nil, ENOENT + } + return nil, err + } + + return &FileInfo{ + Name: statInfo.Name(), + Size: statInfo.Size(), + Mode: statInfo.Mode(), + ModTime: statInfo.ModTime(), + IsDir: statInfo.IsDir(), + LinkTarget: resolvedHostPath, + }, nil +} + +// PreserveBasePath makes sure that the original base path (e.g., "/" or "./") +// is preserved. The filepath API among tends to clean up a bit too much but +// we *must* preserve this data by all means. +func PreserveBasePath(original, resolved string) string { + // Handle "/" + if strings.HasSuffix(original, "/") { + if !strings.HasSuffix(resolved, "/") { + resolved += "/" + } + return resolved + } + + // Handle "/." + if strings.HasSuffix(original, "/.") { + if strings.HasSuffix(resolved, "/") { // could be root! + resolved += "." + } else if !strings.HasSuffix(resolved, "/.") { + resolved += "/." + } + return resolved + } + + return resolved +} diff --git a/pkg/copy/item.go b/pkg/copy/item.go deleted file mode 100644 index df8bf30b9..000000000 --- a/pkg/copy/item.go +++ /dev/null @@ -1,588 +0,0 @@ -package copy - -import ( - "io" - "os" - "path/filepath" - "strings" - - buildahCopiah "github.com/containers/buildah/copier" - "github.com/containers/buildah/pkg/chrootuser" - "github.com/containers/buildah/util" - "github.com/containers/podman/v2/libpod" - "github.com/containers/podman/v2/libpod/define" - "github.com/containers/podman/v2/pkg/cgroups" - "github.com/containers/podman/v2/pkg/rootless" - "github.com/containers/storage" - "github.com/containers/storage/pkg/archive" - "github.com/containers/storage/pkg/idtools" - securejoin "github.com/cyphar/filepath-securejoin" - "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -// ********************************* NOTE ************************************* -// -// Most security bugs are caused by attackers playing around with symlinks -// trying to escape from the container onto the host and/or trick into data -// corruption on the host. Hence, file operations on containers (including -// *stat) should always be handled by `github.com/containers/buildah/copier` -// which makes sure to evaluate files in a chroot'ed environment. -// -// Please make sure to add verbose comments when changing code to make the -// lives of future readers easier. -// -// **************************************************************************** - -var ( - _stdin = os.Stdin.Name() - _stdout = os.Stdout.Name() -) - -// CopyItem is the source or destination of a copy operation. Use the -// CopyItemFrom* functions to create one for the specific source/destination -// item. -type CopyItem struct { - // The original path provided by the caller. Useful in error messages. - original string - // The resolved path on the host or container. Maybe altered at - // multiple stages when copying. - resolved string - // The root for copying data in a chroot'ed environment. - root string - - // IDPair of the resolved path. - idPair *idtools.IDPair - // Storage ID mappings. - idMappings *storage.IDMappingOptions - - // Internal FileInfo. We really don't want users to mess with a - // CopyItem but only plug and play with it. - info FileInfo - // Error when creating the upper FileInfo. Some errors are non-fatal, - // for instance, when a destination *base* path does not exist. - statError error - - writer io.Writer - reader io.Reader - - // Needed to clean up resources (e.g., unmount a container). - cleanUpFuncs []deferFunc -} - -// deferFunc allows for returning functions that must be deferred at call sites. -type deferFunc func() - -// Stat returns the FileInfo. -func (item *CopyItem) Stat() (*FileInfo, error) { - return &item.info, item.statError -} - -// CleanUp releases resources such as the container mounts. It *must* be -// called even in case of errors. -func (item *CopyItem) CleanUp() { - for _, f := range item.cleanUpFuncs { - f() - } -} - -// CopyItemForWriter returns a CopyItem for the specified io.WriteCloser. Note -// that the returned item can only act as a copy destination. -func CopyItemForWriter(writer io.Writer) (item CopyItem, _ error) { - item.writer = writer - item.info.IsStream = true - return item, nil -} - -// CopyItemForReader returns a CopyItem for the specified io.ReaderCloser. Note -// that the returned item can only act as a copy source. -// -// Note that the specified reader will be auto-decompressed if needed. -func CopyItemForReader(reader io.Reader) (item CopyItem, _ error) { - item.info.IsStream = true - decompressed, err := archive.DecompressStream(reader) - if err != nil { - return item, err - } - item.reader = decompressed - item.cleanUpFuncs = append(item.cleanUpFuncs, func() { - if err := decompressed.Close(); err != nil { - logrus.Errorf("Error closing decompressed reader of copy item: %v", err) - } - }) - return item, nil -} - -// CopyItemForHost creates a CopyItem for the specified host path. It's a -// destination by default. Use isSource to set it as a destination. -// -// Note that callers *must* call (CopyItem).CleanUp(), even in case of errors. -func CopyItemForHost(hostPath string, isSource bool) (item CopyItem, _ error) { - if hostPath == "-" { - if isSource { - hostPath = _stdin - } else { - hostPath = _stdout - } - } - - if hostPath == _stdin { - return CopyItemForReader(os.Stdin) - } - - if hostPath == _stdout { - return CopyItemForWriter(os.Stdout) - } - - // Now do the dance for the host data. - resolvedHostPath, err := filepath.Abs(hostPath) - if err != nil { - return item, err - } - - resolvedHostPath = preserveBasePath(hostPath, resolvedHostPath) - item.original = hostPath - item.resolved = resolvedHostPath - item.root = "/" - - statInfo, statError := os.Stat(resolvedHostPath) - item.statError = statError - - // It exists, we're done. - if statError == nil { - item.info.Name = statInfo.Name() - item.info.Size = statInfo.Size() - item.info.Mode = statInfo.Mode() - item.info.ModTime = statInfo.ModTime() - item.info.IsDir = statInfo.IsDir() - item.info.LinkTarget = resolvedHostPath - return item, nil - } - - // The source must exist, but let's try to give some human-friendly - // errors. - if isSource { - if os.IsNotExist(item.statError) { - return item, errors.Wrapf(os.ErrNotExist, "%q could not be found on the host", hostPath) - } - return item, item.statError // could be a permission error - } - - // If we're a destination, we need to make sure that the parent - // directory exists. - parent := filepath.Dir(resolvedHostPath) - if _, err := os.Stat(parent); err != nil { - if os.IsNotExist(err) { - return item, errors.Wrapf(os.ErrNotExist, "%q could not be found on the host", parent) - } - return item, err - } - - return item, nil -} - -// CopyItemForContainer creates a CopyItem for the specified path on the -// container. It's a destination by default. Use isSource to set it as a -// destination. Note that the container path may resolve to a path outside of -// the container's mount point if the path hits a volume or mount on the -// container. -// -// Note that callers *must* call (CopyItem).CleanUp(), even in case of errors. -func CopyItemForContainer(container *libpod.Container, containerPath string, pause bool, isSource bool) (item CopyItem, _ error) { - // Mount and pause the container. - containerMountPoint, err := item.mountAndPauseContainer(container, pause) - if err != nil { - return item, err - } - - // Make sure that "/" copies the *contents* of the mount point and not - // the directory. - if containerPath == "/" { - containerPath += "/." - } - - // Now resolve the container's path. It may hit a volume, it may hit a - // bind mount, it may be relative. - resolvedRoot, resolvedContainerPath, err := resolveContainerPaths(container, containerMountPoint, containerPath) - if err != nil { - return item, err - } - resolvedContainerPath = preserveBasePath(containerPath, resolvedContainerPath) - - idMappings, idPair, err := getIDMappingsAndPair(container, containerMountPoint) - if err != nil { - return item, err - } - - item.original = containerPath - item.resolved = resolvedContainerPath - item.root = resolvedRoot - item.idMappings = idMappings - item.idPair = idPair - - statInfo, statError := secureStat(resolvedRoot, resolvedContainerPath) - item.statError = statError - - // It exists, we're done. - if statError == nil { - item.info.IsDir = statInfo.IsDir - item.info.Name = filepath.Base(statInfo.Name) - item.info.Size = statInfo.Size - item.info.Mode = statInfo.Mode - item.info.ModTime = statInfo.ModTime - item.info.IsDir = statInfo.IsDir - item.info.LinkTarget = resolvedContainerPath - return item, nil - } - - // The source must exist, but let's try to give some human-friendly - // errors. - if isSource { - if os.IsNotExist(statError) { - return item, errors.Wrapf(os.ErrNotExist, "%q could not be found on container %s (resolved to %q)", containerPath, container.ID(), resolvedContainerPath) - } - return item, item.statError // could be a permission error - } - - // If we're a destination, we need to make sure that the parent - // directory exists. - parent := filepath.Dir(resolvedContainerPath) - if _, err := secureStat(resolvedRoot, parent); err != nil { - if os.IsNotExist(err) { - return item, errors.Wrapf(os.ErrNotExist, "%q could not be found on container %s (resolved to %q)", containerPath, container.ID(), resolvedContainerPath) - } - return item, err - } - - return item, nil -} - -// putOptions returns PUT options for buildah's copier package. -func (item *CopyItem) putOptions() buildahCopiah.PutOptions { - options := buildahCopiah.PutOptions{} - if item.idMappings != nil { - options.UIDMap = item.idMappings.UIDMap - options.GIDMap = item.idMappings.GIDMap - } - if item.idPair != nil { - options.ChownDirs = item.idPair - options.ChownFiles = item.idPair - } - return options -} - -// getOptions returns GET options for buildah's copier package. -func (item *CopyItem) getOptions() buildahCopiah.GetOptions { - options := buildahCopiah.GetOptions{} - if item.idMappings != nil { - options.UIDMap = item.idMappings.UIDMap - options.GIDMap = item.idMappings.GIDMap - } - if item.idPair != nil { - options.ChownDirs = item.idPair - options.ChownFiles = item.idPair - } - return options - -} - -// mount and pause the container. Also set the item's cleanUpFuncs. Those -// *must* be invoked by callers, even in case of errors. -func (item *CopyItem) mountAndPauseContainer(container *libpod.Container, pause bool) (string, error) { - // Make sure to pause and unpause the container. We cannot pause on - // cgroupsv1 as rootless user, in which case we turn off pausing. - if pause && rootless.IsRootless() { - cgroupv2, _ := cgroups.IsCgroup2UnifiedMode() - if !cgroupv2 { - logrus.Debugf("Cannot pause container for copying as a rootless user on cgroupsv1: default to not pause") - pause = false - } - } - - // Mount and unmount the container. - mountPoint, err := container.Mount() - if err != nil { - return "", err - } - - item.cleanUpFuncs = append(item.cleanUpFuncs, func() { - if err := container.Unmount(false); err != nil { - logrus.Errorf("Error unmounting container after copy operation: %v", err) - } - }) - - // Pause and unpause the container. - if pause { - if err := container.Pause(); err != nil { - // Ignore errors when the container isn't running. No - // need to pause. - if errors.Cause(err) != define.ErrCtrStateInvalid { - return "", err - } - } else { - item.cleanUpFuncs = append(item.cleanUpFuncs, func() { - if err := container.Unpause(); err != nil { - logrus.Errorf("Error unpausing container after copy operation: %v", err) - } - }) - } - } - - return mountPoint, nil -} - -// buildahGlobs returns the root, dir and glob used in buildah's copier -// package. -// -// Note that dir is always empty. -func (item *CopyItem) buildahGlobs() (root string, glob string, err error) { - root = item.root - - // If the root and the resolved path are equal, then dir must be empty - // and the glob must be ".". - if filepath.Clean(root) == filepath.Clean(item.resolved) { - glob = "." - return - } - - glob, err = filepath.Rel(root, item.resolved) - return -} - -// preserveBasePath makes sure that the original base path (e.g., "/" or "./") -// is preserved. The filepath API among tends to clean up a bit too much but -// we *must* preserve this data by all means. -func preserveBasePath(original, resolved string) string { - // Handle "/" - if strings.HasSuffix(original, "/") { - if !strings.HasSuffix(resolved, "/") { - resolved += "/" - } - return resolved - } - - // Handle "/." - if strings.HasSuffix(original, "/.") { - if strings.HasSuffix(resolved, "/") { // could be root! - resolved += "." - } else if !strings.HasSuffix(resolved, "/.") { - resolved += "/." - } - return resolved - } - - return resolved -} - -// secureStat extracts file info for path in a chroot'ed environment in root. -func secureStat(root string, path string) (*buildahCopiah.StatForItem, error) { - var glob string - var err error - - // If root and path are equal, then dir must be empty and the glob must - // be ".". - if filepath.Clean(root) == filepath.Clean(path) { - glob = "." - } else { - glob, err = filepath.Rel(root, path) - if err != nil { - return nil, err - } - } - - globStats, err := buildahCopiah.Stat(root, "", buildahCopiah.StatOptions{}, []string{glob}) - if err != nil { - return nil, err - } - - if len(globStats) != 1 { - return nil, errors.Errorf("internal libpod error: secureStat: expected 1 item but got %d", len(globStats)) - } - - stat, exists := globStats[0].Results[glob] // only one glob passed, so that's okay - if !exists { - return stat, os.ErrNotExist - } - - var statErr error - if stat.Error != "" { - statErr = errors.New(stat.Error) - } - return stat, statErr -} - -// resolveContainerPaths resolves the container's mount point and the container -// path as specified by the user. Both may resolve to paths outside of the -// container's mount point when the container path hits a volume or bind mount. -// -// NOTE: We must take volumes and bind mounts into account as, regrettably, we -// can copy to/from stopped containers. In that case, the volumes and bind -// mounts are not present. For running containers, the runtime (e.g., runc or -// crun) takes care of these mounts. For stopped ones, we need to do quite -// some dance, as done below. -func resolveContainerPaths(container *libpod.Container, mountPoint string, containerPath string) (string, string, error) { - // Let's first make sure we have a path relative to the mount point. - pathRelativeToContainerMountPoint := containerPath - if !filepath.IsAbs(containerPath) { - // If the containerPath is not absolute, it's relative to the - // container's working dir. To be extra careful, let's first - // join the working dir with "/", and the add the containerPath - // to it. - pathRelativeToContainerMountPoint = filepath.Join(filepath.Join("/", container.WorkingDir()), containerPath) - } - // NOTE: the secure join makes sure that we follow symlinks. This way, - // we catch scenarios where the container path symlinks to a volume or - // bind mount. - resolvedPathOnTheContainerMountPoint, err := securejoin.SecureJoin(mountPoint, pathRelativeToContainerMountPoint) - if err != nil { - return "", "", err - } - pathRelativeToContainerMountPoint = strings.TrimPrefix(pathRelativeToContainerMountPoint, mountPoint) - pathRelativeToContainerMountPoint = filepath.Join("/", pathRelativeToContainerMountPoint) - - // Now we have an "absolute container Path" but not yet resolved on the - // host (e.g., "/foo/bar/file.txt"). As mentioned above, we need to - // check if "/foo/bar/file.txt" is on a volume or bind mount. To do - // that, we need to walk *down* the paths to the root. Assuming - // volume-1 is mounted to "/foo" and volume-2 is mounted to "/foo/bar", - // we must select "/foo/bar". Once selected, we need to rebase the - // remainder (i.e, "/file.txt") on the volume's mount point on the - // host. Same applies to bind mounts. - - searchPath := pathRelativeToContainerMountPoint - for { - volume, err := findVolume(container, searchPath) - if err != nil { - return "", "", err - } - if volume != nil { - logrus.Debugf("Container path %q resolved to volume %q on path %q", containerPath, volume.Name(), searchPath) - // We found a matching volume for searchPath. We now - // need to first find the relative path of our input - // path to the searchPath, and then join it with the - // volume's mount point. - pathRelativeToVolume := strings.TrimPrefix(pathRelativeToContainerMountPoint, searchPath) - absolutePathOnTheVolumeMount, err := securejoin.SecureJoin(volume.MountPoint(), pathRelativeToVolume) - if err != nil { - return "", "", err - } - return volume.MountPoint(), absolutePathOnTheVolumeMount, nil - } - - if mount := findBindMount(container, searchPath); mount != nil { - logrus.Debugf("Container path %q resolved to bind mount %q:%q on path %q", containerPath, mount.Source, mount.Destination, searchPath) - // We found a matching bind mount for searchPath. We - // now need to first find the relative path of our - // input path to the searchPath, and then join it with - // the source of the bind mount. - pathRelativeToBindMount := strings.TrimPrefix(pathRelativeToContainerMountPoint, searchPath) - absolutePathOnTheBindMount, err := securejoin.SecureJoin(mount.Source, pathRelativeToBindMount) - if err != nil { - return "", "", err - } - return mount.Source, absolutePathOnTheBindMount, nil - - } - - if searchPath == "/" { - // Cannot go beyond "/", so we're done. - break - } - - // Walk *down* the path (e.g., "/foo/bar/x" -> "/foo/bar"). - searchPath = filepath.Dir(searchPath) - } - - // No volume, no bind mount but just a normal path on the container. - return mountPoint, resolvedPathOnTheContainerMountPoint, nil -} - -// findVolume checks if the specified container path matches a volume inside -// the container. It returns a matching volume or nil. -func findVolume(c *libpod.Container, containerPath string) (*libpod.Volume, error) { - runtime := c.Runtime() - cleanedContainerPath := filepath.Clean(containerPath) - for _, vol := range c.Config().NamedVolumes { - if cleanedContainerPath == filepath.Clean(vol.Dest) { - return runtime.GetVolume(vol.Name) - } - } - return nil, nil -} - -// findBindMount checks if the specified container path matches a bind mount -// inside the container. It returns a matching mount or nil. -func findBindMount(c *libpod.Container, containerPath string) *specs.Mount { - cleanedPath := filepath.Clean(containerPath) - for _, m := range c.Config().Spec.Mounts { - if m.Type != "bind" { - continue - } - if cleanedPath == filepath.Clean(m.Destination) { - mount := m - return &mount - } - } - return nil -} - -// getIDMappingsAndPair returns the ID mappings for the container and the host -// ID pair. -func getIDMappingsAndPair(container *libpod.Container, containerMount string) (*storage.IDMappingOptions, *idtools.IDPair, error) { - user, err := getContainerUser(container, containerMount) - if err != nil { - return nil, nil, err - } - - idMappingOpts, err := container.IDMappings() - if err != nil { - return nil, nil, err - } - - hostUID, hostGID, err := util.GetHostIDs(idtoolsToRuntimeSpec(idMappingOpts.UIDMap), idtoolsToRuntimeSpec(idMappingOpts.GIDMap), user.UID, user.GID) - if err != nil { - return nil, nil, err - } - - idPair := idtools.IDPair{UID: int(hostUID), GID: int(hostGID)} - return &idMappingOpts, &idPair, nil -} - -// getContainerUser returns the specs.User of the container. -func getContainerUser(container *libpod.Container, mountPoint string) (specs.User, error) { - userspec := container.Config().User - - uid, gid, _, err := chrootuser.GetUser(mountPoint, userspec) - u := specs.User{ - UID: uid, - GID: gid, - Username: userspec, - } - - if !strings.Contains(userspec, ":") { - groups, err2 := chrootuser.GetAdditionalGroupsForUser(mountPoint, uint64(u.UID)) - if err2 != nil { - if errors.Cause(err2) != chrootuser.ErrNoSuchUser && err == nil { - err = err2 - } - } else { - u.AdditionalGids = groups - } - } - - return u, err -} - -// idtoolsToRuntimeSpec converts idtools ID mapping to the one of the runtime spec. -func idtoolsToRuntimeSpec(idMaps []idtools.IDMap) (convertedIDMap []specs.LinuxIDMapping) { - for _, idmap := range idMaps { - tempIDMap := specs.LinuxIDMapping{ - ContainerID: uint32(idmap.ContainerID), - HostID: uint32(idmap.HostID), - Size: uint32(idmap.Size), - } - convertedIDMap = append(convertedIDMap, tempIDMap) - } - return convertedIDMap -} diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index 01086a2b3..4442c0030 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -8,6 +8,7 @@ import ( "github.com/containers/image/v5/types" "github.com/containers/podman/v2/libpod/define" + "github.com/containers/podman/v2/pkg/copy" "github.com/containers/podman/v2/pkg/specgen" "github.com/cri-o/ocicni/pkg/ocicni" ) @@ -143,6 +144,10 @@ type ContainerInspectReport struct { *define.InspectContainerData } +type ContainerStatReport struct { + copy.FileInfo +} + type CommitOptions struct { Author string Changes []string diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index ac9073402..80127ea45 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -2,6 +2,7 @@ package entities import ( "context" + "io" "github.com/containers/common/pkg/config" "github.com/containers/podman/v2/libpod/define" @@ -9,6 +10,8 @@ import ( "github.com/spf13/cobra" ) +type ContainerCopyFunc func() error + type ContainerEngine interface { AutoUpdate(ctx context.Context, options AutoUpdateOptions) (*AutoUpdateReport, []error) Config(ctx context.Context) (*config.Config, error) @@ -16,7 +19,8 @@ type ContainerEngine interface { ContainerCheckpoint(ctx context.Context, namesOrIds []string, options CheckpointOptions) ([]*CheckpointReport, error) ContainerCleanup(ctx context.Context, namesOrIds []string, options ContainerCleanupOptions) ([]*ContainerCleanupReport, error) ContainerCommit(ctx context.Context, nameOrID string, options CommitOptions) (*CommitReport, error) - ContainerCp(ctx context.Context, source, dest string, options ContainerCpOptions) error + ContainerCopyFromArchive(ctx context.Context, nameOrID string, path string, reader io.Reader) (ContainerCopyFunc, error) + ContainerCopyToArchive(ctx context.Context, nameOrID string, path string, writer io.Writer) (ContainerCopyFunc, error) ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*ContainerCreateReport, error) ContainerDiff(ctx context.Context, nameOrID string, options DiffOptions) (*DiffReport, error) ContainerExec(ctx context.Context, nameOrID string, options ExecOptions, streams define.AttachStreams) (int, error) @@ -38,6 +42,7 @@ type ContainerEngine interface { 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) + ContainerStat(ctx context.Context, nameOrDir string, path string) (*ContainerStatReport, error) ContainerStats(ctx context.Context, namesOrIds []string, options ContainerStatsOptions) (chan ContainerStatsReport, error) ContainerStop(ctx context.Context, namesOrIds []string, options StopOptions) ([]*StopReport, error) ContainerTop(ctx context.Context, options TopOptions) (*StringSliceReport, error) diff --git a/pkg/domain/entities/system.go b/pkg/domain/entities/system.go index 5266dc4cf..d5118f6a8 100644 --- a/pkg/domain/entities/system.go +++ b/pkg/domain/entities/system.go @@ -17,9 +17,9 @@ type ServiceOptions struct { // SystemPruneOptions provides options to prune system. type SystemPruneOptions struct { - All bool - Volume bool - ContainerPruneOptions + All bool + Volume bool + Filters map[string][]string `json:"filters" schema:"filters"` } // SystemPruneReport provides report after system prune is executed. diff --git a/pkg/domain/infra/abi/archive.go b/pkg/domain/infra/abi/archive.go new file mode 100644 index 000000000..809813756 --- /dev/null +++ b/pkg/domain/infra/abi/archive.go @@ -0,0 +1,172 @@ +package abi + +import ( + "context" + "io" + "strings" + + buildahCopiah "github.com/containers/buildah/copier" + "github.com/containers/buildah/pkg/chrootuser" + "github.com/containers/buildah/util" + "github.com/containers/podman/v2/libpod" + "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/storage" + "github.com/containers/storage/pkg/archive" + "github.com/containers/storage/pkg/idtools" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// NOTE: Only the parent directory of the container path must exist. The path +// itself may be created while copying. +func (ic *ContainerEngine) ContainerCopyFromArchive(ctx context.Context, nameOrID string, containerPath string, reader io.Reader) (entities.ContainerCopyFunc, error) { + container, err := ic.Libpod.LookupContainer(nameOrID) + if err != nil { + return nil, err + } + + unmount := func() { + if err := container.Unmount(false); err != nil { + logrus.Errorf("Error unmounting container: %v", err) + } + } + + _, resolvedRoot, resolvedContainerPath, err := ic.containerStat(container, containerPath) + if err != nil { + unmount() + return nil, err + } + + decompressed, err := archive.DecompressStream(reader) + if err != nil { + unmount() + return nil, err + } + + idMappings, idPair, err := getIDMappingsAndPair(container, resolvedRoot) + if err != nil { + unmount() + return nil, err + } + + logrus.Debugf("Container copy *to* %q (resolved: %q) on container %q (ID: %s)", containerPath, resolvedContainerPath, container.Name(), container.ID()) + + return func() error { + defer unmount() + defer decompressed.Close() + putOptions := buildahCopiah.PutOptions{ + UIDMap: idMappings.UIDMap, + GIDMap: idMappings.GIDMap, + ChownDirs: idPair, + ChownFiles: idPair, + } + return buildahCopiah.Put(resolvedRoot, resolvedContainerPath, putOptions, decompressed) + }, nil +} + +func (ic *ContainerEngine) ContainerCopyToArchive(ctx context.Context, nameOrID string, containerPath string, writer io.Writer) (entities.ContainerCopyFunc, error) { + container, err := ic.Libpod.LookupContainer(nameOrID) + if err != nil { + return nil, err + } + + unmount := func() { + if err := container.Unmount(false); err != nil { + logrus.Errorf("Error unmounting container: %v", err) + } + } + + // Make sure that "/" copies the *contents* of the mount point and not + // the directory. + if containerPath == "/" { + containerPath = "/." + } + + _, resolvedRoot, resolvedContainerPath, err := ic.containerStat(container, containerPath) + if err != nil { + unmount() + return nil, err + } + + idMappings, idPair, err := getIDMappingsAndPair(container, resolvedRoot) + if err != nil { + unmount() + return nil, err + } + + logrus.Debugf("Container copy *from* %q (resolved: %q) on container %q (ID: %s)", containerPath, resolvedContainerPath, container.Name(), container.ID()) + + return func() error { + defer container.Unmount(false) + getOptions := buildahCopiah.GetOptions{ + // Unless the specified path ends with ".", we want to copy the base directory. + KeepDirectoryNames: !strings.HasSuffix(resolvedContainerPath, "."), + UIDMap: idMappings.UIDMap, + GIDMap: idMappings.GIDMap, + ChownDirs: idPair, + ChownFiles: idPair, + } + return buildahCopiah.Get(resolvedRoot, "", getOptions, []string{resolvedContainerPath}, writer) + }, nil +} + +// getIDMappingsAndPair returns the ID mappings for the container and the host +// ID pair. +func getIDMappingsAndPair(container *libpod.Container, containerMount string) (*storage.IDMappingOptions, *idtools.IDPair, error) { + user, err := getContainerUser(container, containerMount) + if err != nil { + return nil, nil, err + } + + idMappingOpts, err := container.IDMappings() + if err != nil { + return nil, nil, err + } + + hostUID, hostGID, err := util.GetHostIDs(idtoolsToRuntimeSpec(idMappingOpts.UIDMap), idtoolsToRuntimeSpec(idMappingOpts.GIDMap), user.UID, user.GID) + if err != nil { + return nil, nil, err + } + + idPair := idtools.IDPair{UID: int(hostUID), GID: int(hostGID)} + return &idMappingOpts, &idPair, nil +} + +// getContainerUser returns the specs.User of the container. +func getContainerUser(container *libpod.Container, mountPoint string) (specs.User, error) { + userspec := container.Config().User + + uid, gid, _, err := chrootuser.GetUser(mountPoint, userspec) + u := specs.User{ + UID: uid, + GID: gid, + Username: userspec, + } + + if !strings.Contains(userspec, ":") { + groups, err2 := chrootuser.GetAdditionalGroupsForUser(mountPoint, uint64(u.UID)) + if err2 != nil { + if errors.Cause(err2) != chrootuser.ErrNoSuchUser && err == nil { + err = err2 + } + } else { + u.AdditionalGids = groups + } + } + + return u, err +} + +// idtoolsToRuntimeSpec converts idtools ID mapping to the one of the runtime spec. +func idtoolsToRuntimeSpec(idMaps []idtools.IDMap) (convertedIDMap []specs.LinuxIDMapping) { + for _, idmap := range idMaps { + tempIDMap := specs.LinuxIDMapping{ + ContainerID: uint32(idmap.ContainerID), + HostID: uint32(idmap.HostID), + Size: uint32(idmap.Size), + } + convertedIDMap = append(convertedIDMap, tempIDMap) + } + return convertedIDMap +} diff --git a/pkg/domain/infra/abi/containers_stat.go b/pkg/domain/infra/abi/containers_stat.go new file mode 100644 index 000000000..c9610d1b9 --- /dev/null +++ b/pkg/domain/infra/abi/containers_stat.go @@ -0,0 +1,251 @@ +package abi + +import ( + "context" + "os" + "path/filepath" + "strings" + + buildahCopiah "github.com/containers/buildah/copier" + "github.com/containers/podman/v2/libpod" + "github.com/containers/podman/v2/pkg/copy" + "github.com/containers/podman/v2/pkg/domain/entities" + securejoin "github.com/cyphar/filepath-securejoin" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +func (ic *ContainerEngine) containerStat(container *libpod.Container, containerPath string) (*entities.ContainerStatReport, string, string, error) { + containerMountPoint, err := container.Mount() + if err != nil { + return nil, "", "", err + } + + // Make sure that "/" copies the *contents* of the mount point and not + // the directory. + if containerPath == "/" { + containerPath += "/." + } + + // Now resolve the container's path. It may hit a volume, it may hit a + // bind mount, it may be relative. + resolvedRoot, resolvedContainerPath, err := resolveContainerPaths(container, containerMountPoint, containerPath) + if err != nil { + return nil, "", "", err + } + + statInfo, statInfoErr := secureStat(resolvedRoot, resolvedContainerPath) + if statInfoErr != nil { + // Not all errors from secureStat map to ErrNotExist, so we + // have to look into the error string. Turning it into an + // ENOENT let's the API handlers return the correct status code + // which is crucuial for the remote client. + if os.IsNotExist(err) || strings.Contains(statInfoErr.Error(), "o such file or directory") { + statInfoErr = copy.ENOENT + } + // If statInfo is nil, there's nothing we can do anymore. A + // non-nil statInfo may indicate a symlink where we must have + // a closer look. + if statInfo == nil { + return nil, "", "", statInfoErr + } + } + + // Now make sure that the info's LinkTarget is relative to the + // container's mount. + var absContainerPath string + + if statInfo.IsSymlink { + // Evaluated symlinks are always relative to the container's mount point. + absContainerPath = statInfo.ImmediateTarget + } else if strings.HasPrefix(resolvedContainerPath, containerMountPoint) { + // If the path is on the container's mount point, strip it off. + absContainerPath = strings.TrimPrefix(resolvedContainerPath, containerMountPoint) + absContainerPath = filepath.Join("/", absContainerPath) + } else { + // No symlink and not on the container's mount point, so let's + // move it back to the original input. It must have evaluated + // to a volume or bind mount but we cannot return host paths. + absContainerPath = containerPath + } + + // Now we need to make sure to preseve the base path as specified by + // the user. The `filepath` packages likes to remove trailing slashes + // and dots that are crucial to the copy logic. + absContainerPath = copy.PreserveBasePath(containerPath, absContainerPath) + resolvedContainerPath = copy.PreserveBasePath(containerPath, resolvedContainerPath) + + info := copy.FileInfo{ + IsDir: statInfo.IsDir, + Name: filepath.Base(absContainerPath), + Size: statInfo.Size, + Mode: statInfo.Mode, + ModTime: statInfo.ModTime, + LinkTarget: absContainerPath, + } + + return &entities.ContainerStatReport{FileInfo: info}, resolvedRoot, resolvedContainerPath, statInfoErr +} + +func (ic *ContainerEngine) ContainerStat(ctx context.Context, nameOrID string, containerPath string) (*entities.ContainerStatReport, error) { + container, err := ic.Libpod.LookupContainer(nameOrID) + if err != nil { + return nil, err + } + + defer func() { + if err := container.Unmount(false); err != nil { + logrus.Errorf("Error unmounting container: %v", err) + } + }() + + statReport, _, _, err := ic.containerStat(container, containerPath) + return statReport, err +} + +// resolveContainerPaths resolves the container's mount point and the container +// path as specified by the user. Both may resolve to paths outside of the +// container's mount point when the container path hits a volume or bind mount. +// +// NOTE: We must take volumes and bind mounts into account as, regrettably, we +// can copy to/from stopped containers. In that case, the volumes and bind +// mounts are not present. For running containers, the runtime (e.g., runc or +// crun) takes care of these mounts. For stopped ones, we need to do quite +// some dance, as done below. +func resolveContainerPaths(container *libpod.Container, mountPoint string, containerPath string) (string, string, error) { + // Let's first make sure we have a path relative to the mount point. + pathRelativeToContainerMountPoint := containerPath + if !filepath.IsAbs(containerPath) { + // If the containerPath is not absolute, it's relative to the + // container's working dir. To be extra careful, let's first + // join the working dir with "/", and the add the containerPath + // to it. + pathRelativeToContainerMountPoint = filepath.Join(filepath.Join("/", container.WorkingDir()), containerPath) + } + resolvedPathOnTheContainerMountPoint := filepath.Join(mountPoint, pathRelativeToContainerMountPoint) + pathRelativeToContainerMountPoint = strings.TrimPrefix(pathRelativeToContainerMountPoint, mountPoint) + pathRelativeToContainerMountPoint = filepath.Join("/", pathRelativeToContainerMountPoint) + + // Now we have an "absolute container Path" but not yet resolved on the + // host (e.g., "/foo/bar/file.txt"). As mentioned above, we need to + // check if "/foo/bar/file.txt" is on a volume or bind mount. To do + // that, we need to walk *down* the paths to the root. Assuming + // volume-1 is mounted to "/foo" and volume-2 is mounted to "/foo/bar", + // we must select "/foo/bar". Once selected, we need to rebase the + // remainder (i.e, "/file.txt") on the volume's mount point on the + // host. Same applies to bind mounts. + + searchPath := pathRelativeToContainerMountPoint + for { + volume, err := findVolume(container, searchPath) + if err != nil { + return "", "", err + } + if volume != nil { + logrus.Debugf("Container path %q resolved to volume %q on path %q", containerPath, volume.Name(), searchPath) + // We found a matching volume for searchPath. We now + // need to first find the relative path of our input + // path to the searchPath, and then join it with the + // volume's mount point. + pathRelativeToVolume := strings.TrimPrefix(pathRelativeToContainerMountPoint, searchPath) + absolutePathOnTheVolumeMount, err := securejoin.SecureJoin(volume.MountPoint(), pathRelativeToVolume) + if err != nil { + return "", "", err + } + return volume.MountPoint(), absolutePathOnTheVolumeMount, nil + } + + if mount := findBindMount(container, searchPath); mount != nil { + logrus.Debugf("Container path %q resolved to bind mount %q:%q on path %q", containerPath, mount.Source, mount.Destination, searchPath) + // We found a matching bind mount for searchPath. We + // now need to first find the relative path of our + // input path to the searchPath, and then join it with + // the source of the bind mount. + pathRelativeToBindMount := strings.TrimPrefix(pathRelativeToContainerMountPoint, searchPath) + absolutePathOnTheBindMount, err := securejoin.SecureJoin(mount.Source, pathRelativeToBindMount) + if err != nil { + return "", "", err + } + return mount.Source, absolutePathOnTheBindMount, nil + + } + + if searchPath == "/" { + // Cannot go beyond "/", so we're done. + break + } + + // Walk *down* the path (e.g., "/foo/bar/x" -> "/foo/bar"). + searchPath = filepath.Dir(searchPath) + } + + // No volume, no bind mount but just a normal path on the container. + return mountPoint, resolvedPathOnTheContainerMountPoint, nil +} + +// findVolume checks if the specified container path matches a volume inside +// the container. It returns a matching volume or nil. +func findVolume(c *libpod.Container, containerPath string) (*libpod.Volume, error) { + runtime := c.Runtime() + cleanedContainerPath := filepath.Clean(containerPath) + for _, vol := range c.Config().NamedVolumes { + if cleanedContainerPath == filepath.Clean(vol.Dest) { + return runtime.GetVolume(vol.Name) + } + } + return nil, nil +} + +// findBindMount checks if the specified container path matches a bind mount +// inside the container. It returns a matching mount or nil. +func findBindMount(c *libpod.Container, containerPath string) *specs.Mount { + cleanedPath := filepath.Clean(containerPath) + for _, m := range c.Config().Spec.Mounts { + if m.Type != "bind" { + continue + } + if cleanedPath == filepath.Clean(m.Destination) { + mount := m + return &mount + } + } + return nil +} + +// secureStat extracts file info for path in a chroot'ed environment in root. +func secureStat(root string, path string) (*buildahCopiah.StatForItem, error) { + var glob string + var err error + + // If root and path are equal, then dir must be empty and the glob must + // be ".". + if filepath.Clean(root) == filepath.Clean(path) { + glob = "." + } else { + glob, err = filepath.Rel(root, path) + if err != nil { + return nil, err + } + } + + globStats, err := buildahCopiah.Stat(root, "", buildahCopiah.StatOptions{}, []string{glob}) + if err != nil { + return nil, err + } + + if len(globStats) != 1 { + return nil, errors.Errorf("internal error: secureStat: expected 1 item but got %d", len(globStats)) + } + + stat, exists := globStats[0].Results[glob] // only one glob passed, so that's okay + if !exists { + return nil, copy.ENOENT + } + + var statErr error + if stat.Error != "" { + statErr = errors.New(stat.Error) + } + return stat, statErr +} diff --git a/pkg/domain/infra/abi/cp.go b/pkg/domain/infra/abi/cp.go deleted file mode 100644 index 362053cce..000000000 --- a/pkg/domain/infra/abi/cp.go +++ /dev/null @@ -1,70 +0,0 @@ -package abi - -import ( - "context" - - "github.com/containers/podman/v2/libpod" - "github.com/containers/podman/v2/pkg/copy" - "github.com/containers/podman/v2/pkg/domain/entities" -) - -func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, options entities.ContainerCpOptions) error { - // Parse user input. - sourceContainerStr, sourcePath, destContainerStr, destPath, err := copy.ParseSourceAndDestination(source, dest) - if err != nil { - return err - } - - // Look up containers. - var sourceContainer, destContainer *libpod.Container - if len(sourceContainerStr) > 0 { - sourceContainer, err = ic.Libpod.LookupContainer(sourceContainerStr) - if err != nil { - return err - } - } - if len(destContainerStr) > 0 { - destContainer, err = ic.Libpod.LookupContainer(destContainerStr) - if err != nil { - return err - } - } - - var sourceItem, destinationItem copy.CopyItem - - // Source ... container OR host. - if sourceContainer != nil { - sourceItem, err = copy.CopyItemForContainer(sourceContainer, sourcePath, options.Pause, true) - defer sourceItem.CleanUp() - if err != nil { - return err - } - } else { - sourceItem, err = copy.CopyItemForHost(sourcePath, true) - if err != nil { - return err - } - } - - // Destination ... container OR host. - if destContainer != nil { - destinationItem, err = copy.CopyItemForContainer(destContainer, destPath, options.Pause, false) - defer destinationItem.CleanUp() - if err != nil { - return err - } - } else { - destinationItem, err = copy.CopyItemForHost(destPath, false) - defer destinationItem.CleanUp() - if err != nil { - return err - } - } - - // Copy from the host to the container. - copier, err := copy.GetCopier(&sourceItem, &destinationItem, options.Extract) - if err != nil { - return err - } - return copier.Copy() -} diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go index 17faa7fff..5f6c95d4f 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io/ioutil" + "net/url" "os" "os/exec" "path/filepath" @@ -180,7 +181,12 @@ func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.Sys found = true } systemPruneReport.PodPruneReport = append(systemPruneReport.PodPruneReport, podPruneReport...) - containerPruneReport, err := ic.ContainerPrune(ctx, options.ContainerPruneOptions) + + // TODO: Figure out cleaner way to handle all of the different PruneOptions + containerPruneOptions := entities.ContainerPruneOptions{} + containerPruneOptions.Filters = (url.Values)(options.Filters) + + containerPruneReport, err := ic.ContainerPrune(ctx, containerPruneOptions) if err != nil { return nil, err } @@ -217,7 +223,9 @@ func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.Sys systemPruneReport.ImagePruneReport.Report.Id = append(systemPruneReport.ImagePruneReport.Report.Id, results...) } if options.Volume { - volumePruneReport, err := ic.pruneVolumesHelper(ctx, nil) + volumePruneOptions := entities.VolumePruneOptions{} + volumePruneOptions.Filters = (url.Values)(options.Filters) + volumePruneReport, err := ic.VolumePrune(ctx, volumePruneOptions) if err != nil { return nil, err } diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index ad7688f62..ad8394675 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -16,7 +16,6 @@ import ( "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/events" "github.com/containers/podman/v2/pkg/api/handlers" - "github.com/containers/podman/v2/pkg/bindings" "github.com/containers/podman/v2/pkg/bindings/containers" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/errorhandling" @@ -35,15 +34,16 @@ func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string, return &entities.BoolReport{Value: exists}, err } -func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []string, options entities.WaitOptions) ([]entities.WaitReport, error) { +func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []string, opts entities.WaitOptions) ([]entities.WaitReport, error) { cons, err := getContainersByContext(ic.ClientCxt, false, false, namesOrIds) if err != nil { return nil, err } responses := make([]entities.WaitReport, 0, len(cons)) + options := new(containers.WaitOptions).WithCondition(opts.Condition) for _, c := range cons { response := entities.WaitReport{Id: c.ID} - exitCode, err := containers.Wait(ic.ClientCxt, c.ID, &options.Condition) + exitCode, err := containers.Wait(ic.ClientCxt, c.ID, options) if err != nil { response.Error = err } else { @@ -61,7 +61,7 @@ func (ic *ContainerEngine) ContainerPause(ctx context.Context, namesOrIds []stri } reports := make([]*entities.PauseUnpauseReport, 0, len(ctrs)) for _, c := range ctrs { - err := containers.Pause(ic.ClientCxt, c.ID) + err := containers.Pause(ic.ClientCxt, c.ID, nil) reports = append(reports, &entities.PauseUnpauseReport{Id: c.ID, Err: err}) } return reports, nil @@ -74,15 +74,15 @@ func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []st } reports := make([]*entities.PauseUnpauseReport, 0, len(ctrs)) for _, c := range ctrs { - err := containers.Unpause(ic.ClientCxt, c.ID) + err := containers.Unpause(ic.ClientCxt, c.ID, nil) reports = append(reports, &entities.PauseUnpauseReport{Id: c.ID, Err: err}) } return reports, nil } -func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []string, options entities.StopOptions) ([]*entities.StopReport, error) { +func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []string, opts entities.StopOptions) ([]*entities.StopReport, error) { reports := []*entities.StopReport{} - for _, cidFile := range options.CIDFiles { + for _, cidFile := range opts.CIDFiles { content, err := ioutil.ReadFile(cidFile) if err != nil { return nil, errors.Wrap(err, "error reading CIDFile") @@ -90,19 +90,23 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin id := strings.Split(string(content), "\n")[0] namesOrIds = append(namesOrIds, id) } - ctrs, err := getContainersByContext(ic.ClientCxt, options.All, options.Ignore, namesOrIds) + ctrs, err := getContainersByContext(ic.ClientCxt, opts.All, opts.Ignore, namesOrIds) if err != nil { return nil, err } + options := new(containers.StopOptions) + if to := opts.Timeout; to != nil { + options.WithTimeout(*to) + } for _, c := range ctrs { report := entities.StopReport{Id: c.ID} - if err = containers.Stop(ic.ClientCxt, c.ID, options.Timeout); err != nil { + if err = containers.Stop(ic.ClientCxt, c.ID, options); err != nil { // These first two are considered non-fatal under the right conditions if errors.Cause(err).Error() == define.ErrCtrStopped.Error() { logrus.Debugf("Container %s is already stopped", c.ID) reports = append(reports, &report) continue - } else if options.All && errors.Cause(err).Error() == define.ErrCtrStateInvalid.Error() { + } else if opts.All && errors.Cause(err).Error() == define.ErrCtrStateInvalid.Error() { logrus.Debugf("Container %s is not running, could not stop", c.ID) reports = append(reports, &report) continue @@ -120,8 +124,8 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin return reports, nil } -func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []string, options entities.KillOptions) ([]*entities.KillReport, error) { - ctrs, err := getContainersByContext(ic.ClientCxt, options.All, false, namesOrIds) +func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []string, opts entities.KillOptions) ([]*entities.KillReport, error) { + ctrs, err := getContainersByContext(ic.ClientCxt, opts.All, false, namesOrIds) if err != nil { return nil, err } @@ -129,40 +133,38 @@ func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []strin for _, c := range ctrs { reports = append(reports, &entities.KillReport{ Id: c.ID, - Err: containers.Kill(ic.ClientCxt, c.ID, options.Signal), + Err: containers.Kill(ic.ClientCxt, c.ID, opts.Signal, nil), }) } return reports, nil } -func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []string, options entities.RestartOptions) ([]*entities.RestartReport, error) { +func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []string, opts entities.RestartOptions) ([]*entities.RestartReport, error) { var ( reports = []*entities.RestartReport{} - timeout *int ) - if options.Timeout != nil { - t := int(*options.Timeout) - timeout = &t + options := new(containers.RestartOptions) + if to := opts.Timeout; to != nil { + options.WithTimeout(int(*to)) } - - ctrs, err := getContainersByContext(ic.ClientCxt, options.All, false, namesOrIds) + ctrs, err := getContainersByContext(ic.ClientCxt, opts.All, false, namesOrIds) if err != nil { return nil, err } for _, c := range ctrs { - if options.Running && c.State != define.ContainerStateRunning.String() { + if opts.Running && c.State != define.ContainerStateRunning.String() { continue } reports = append(reports, &entities.RestartReport{ Id: c.ID, - Err: containers.Restart(ic.ClientCxt, c.ID, timeout), + Err: containers.Restart(ic.ClientCxt, c.ID, options), }) } return reports, nil } -func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, options entities.RmOptions) ([]*entities.RmReport, error) { - for _, cidFile := range options.CIDFiles { +func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, opts entities.RmOptions) ([]*entities.RmReport, error) { + for _, cidFile := range opts.CIDFiles { content, err := ioutil.ReadFile(cidFile) if err != nil { return nil, errors.Wrap(err, "error reading CIDFile") @@ -170,32 +172,35 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, id := strings.Split(string(content), "\n")[0] namesOrIds = append(namesOrIds, id) } - ctrs, err := getContainersByContext(ic.ClientCxt, options.All, options.Ignore, namesOrIds) + ctrs, err := getContainersByContext(ic.ClientCxt, opts.All, opts.Ignore, namesOrIds) if err != nil { return nil, err } // TODO there is no endpoint for container eviction. Need to discuss reports := make([]*entities.RmReport, 0, len(ctrs)) + options := new(containers.RemoveOptions).WithForce(opts.Force).WithVolumes(opts.Volumes) for _, c := range ctrs { reports = append(reports, &entities.RmReport{ Id: c.ID, - Err: containers.Remove(ic.ClientCxt, c.ID, &options.Force, &options.Volumes), + Err: containers.Remove(ic.ClientCxt, c.ID, options), }) } return reports, nil } -func (ic *ContainerEngine) ContainerPrune(ctx context.Context, options entities.ContainerPruneOptions) (*entities.ContainerPruneReport, error) { - return containers.Prune(ic.ClientCxt, options.Filters) +func (ic *ContainerEngine) ContainerPrune(ctx context.Context, opts entities.ContainerPruneOptions) (*entities.ContainerPruneReport, error) { + options := new(containers.PruneOptions).WithFilters(opts.Filters) + return containers.Prune(ic.ClientCxt, options) } -func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]*entities.ContainerInspectReport, []error, error) { +func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, opts entities.InspectOptions) ([]*entities.ContainerInspectReport, []error, error) { var ( reports = make([]*entities.ContainerInspectReport, 0, len(namesOrIds)) errs = []error{} ) + options := new(containers.InspectOptions).WithSize(opts.Size) for _, name := range namesOrIds { - inspect, err := containers.Inspect(ic.ClientCxt, name, &options.Size) + inspect, err := containers.Inspect(ic.ClientCxt, name, options) if err != nil { errModel, ok := err.(entities.ErrorModel) if !ok { @@ -212,30 +217,30 @@ func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []st return reports, errs, nil } -func (ic *ContainerEngine) ContainerTop(ctx context.Context, options entities.TopOptions) (*entities.StringSliceReport, error) { +func (ic *ContainerEngine) ContainerTop(ctx context.Context, opts entities.TopOptions) (*entities.StringSliceReport, error) { switch { - case options.Latest: + case opts.Latest: return nil, errors.New("latest is not supported") - case options.NameOrID == "": + case opts.NameOrID == "": return nil, errors.New("NameOrID must be specified") } - - topOutput, err := containers.Top(ic.ClientCxt, options.NameOrID, options.Descriptors) + options := new(containers.TopOptions).WithDescriptors(opts.Descriptors) + topOutput, err := containers.Top(ic.ClientCxt, opts.NameOrID, options) if err != nil { return nil, err } return &entities.StringSliceReport{Value: topOutput}, nil } -func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrID string, options entities.CommitOptions) (*entities.CommitReport, error) { +func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrID string, opts entities.CommitOptions) (*entities.CommitReport, error) { var ( repo string tag = "latest" ) - if len(options.ImageName) > 0 { - ref, err := reference.Parse(options.ImageName) + if len(opts.ImageName) > 0 { + ref, err := reference.Parse(opts.ImageName) if err != nil { - return nil, errors.Wrapf(err, "error parsing reference %q", options.ImageName) + return nil, errors.Wrapf(err, "error parsing reference %q", opts.ImageName) } if t, ok := ref.(reference.Tagged); ok { tag = t.Tag() @@ -244,19 +249,12 @@ func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrID string, repo = r.Name() } if len(repo) < 1 { - return nil, errors.Errorf("invalid image name %q", options.ImageName) + return nil, errors.Errorf("invalid image name %q", opts.ImageName) } } - commitOpts := containers.CommitOptions{ - Author: &options.Author, - Changes: options.Changes, - Comment: &options.Message, - Format: &options.Format, - Pause: &options.Pause, - Repo: &repo, - Tag: &tag, - } - response, err := containers.Commit(ic.ClientCxt, nameOrID, commitOpts) + options := new(containers.CommitOptions).WithAuthor(opts.Author).WithChanges(opts.Changes).WithComment(opts.Message) + options.WithFormat(opts.Format).WithPause(opts.Pause).WithRepo(repo).WithTag(tag) + response, err := containers.Commit(ic.ClientCxt, nameOrID, options) if err != nil { return nil, err } @@ -274,16 +272,16 @@ func (ic *ContainerEngine) ContainerExport(ctx context.Context, nameOrID string, return err } } - return containers.Export(ic.ClientCxt, nameOrID, w) + return containers.Export(ic.ClientCxt, nameOrID, w, nil) } -func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds []string, options entities.CheckpointOptions) ([]*entities.CheckpointReport, error) { +func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds []string, opts entities.CheckpointOptions) ([]*entities.CheckpointReport, error) { var ( err error ctrs = []entities.ListContainer{} ) - if options.All { + if opts.All { allCtrs, err := getContainersByContext(ic.ClientCxt, true, false, []string{}) if err != nil { return nil, err @@ -302,8 +300,10 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [ } } reports := make([]*entities.CheckpointReport, 0, len(ctrs)) + options := new(containers.CheckpointOptions).WithExport(opts.Export).WithIgnoreRootfs(opts.IgnoreRootFS).WithKeep(opts.Keep) + options.WithLeaveRunning(opts.LeaveRunning).WithTCPEstablished(opts.TCPEstablished) for _, c := range ctrs { - report, err := containers.Checkpoint(ic.ClientCxt, c.ID, &options.Keep, &options.LeaveRunning, &options.TCPEstablished, &options.IgnoreRootFS, &options.Export) + report, err := containers.Checkpoint(ic.ClientCxt, c.ID, options) if err != nil { reports = append(reports, &entities.CheckpointReport{Id: c.ID, Err: err}) } @@ -312,12 +312,12 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [ return reports, nil } -func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []string, options entities.RestoreOptions) ([]*entities.RestoreReport, error) { +func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []string, opts entities.RestoreOptions) ([]*entities.RestoreReport, error) { var ( err error ctrs = []entities.ListContainer{} ) - if options.All { + if opts.All { allCtrs, err := getContainersByContext(ic.ClientCxt, true, false, []string{}) if err != nil { return nil, err @@ -336,8 +336,9 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st } } reports := make([]*entities.RestoreReport, 0, len(ctrs)) + options := new(containers.RestoreOptions) for _, c := range ctrs { - report, err := containers.Restore(ic.ClientCxt, c.ID, &options.Keep, &options.TCPEstablished, &options.IgnoreRootFS, &options.IgnoreStaticIP, &options.IgnoreStaticMAC, &options.Name, &options.Import) + report, err := containers.Restore(ic.ClientCxt, c.ID, options) if err != nil { reports = append(reports, &entities.RestoreReport{Id: c.ID, Err: err}) } @@ -347,7 +348,7 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st } func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*entities.ContainerCreateReport, error) { - response, err := containers.CreateWithSpec(ic.ClientCxt, s) + response, err := containers.CreateWithSpec(ic.ClientCxt, s, nil) if err != nil { return nil, err } @@ -357,27 +358,20 @@ func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecG return &entities.ContainerCreateReport{Id: response.ID}, nil } -func (ic *ContainerEngine) ContainerLogs(_ context.Context, nameOrIDs []string, options entities.ContainerLogsOptions) error { - since := options.Since.Format(time.RFC3339) - tail := strconv.FormatInt(options.Tail, 10) - stdout := options.StdoutWriter != nil - stderr := options.StderrWriter != nil - opts := containers.LogOptions{ - Follow: &options.Follow, - Since: &since, - Stderr: &stderr, - Stdout: &stdout, - Tail: &tail, - Timestamps: &options.Timestamps, - Until: nil, - } +func (ic *ContainerEngine) ContainerLogs(_ context.Context, nameOrIDs []string, opts entities.ContainerLogsOptions) error { + since := opts.Since.Format(time.RFC3339) + tail := strconv.FormatInt(opts.Tail, 10) + stdout := opts.StdoutWriter != nil + stderr := opts.StderrWriter != nil + options := new(containers.LogOptions).WithFollow(opts.Follow).WithSince(since).WithStderr(stderr) + options.WithStdout(stdout).WithTail(tail) var err error stdoutCh := make(chan string) stderrCh := make(chan string) ctx, cancel := context.WithCancel(context.Background()) go func() { - err = containers.Logs(ic.ClientCxt, nameOrIDs[0], opts, stdoutCh, stderrCh) + err = containers.Logs(ic.ClientCxt, nameOrIDs[0], options, stdoutCh, stderrCh) cancel() }() @@ -386,18 +380,18 @@ func (ic *ContainerEngine) ContainerLogs(_ context.Context, nameOrIDs []string, case <-ctx.Done(): return err case line := <-stdoutCh: - if options.StdoutWriter != nil { - _, _ = io.WriteString(options.StdoutWriter, line+"\n") + if opts.StdoutWriter != nil { + _, _ = io.WriteString(opts.StdoutWriter, line+"\n") } case line := <-stderrCh: - if options.StderrWriter != nil { - _, _ = io.WriteString(options.StderrWriter, line+"\n") + if opts.StderrWriter != nil { + _, _ = io.WriteString(opts.StderrWriter, line+"\n") } } } } -func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrID string, options entities.AttachOptions) error { +func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrID string, opts entities.AttachOptions) error { ctrs, err := getContainersByContext(ic.ClientCxt, false, false, []string{nameOrID}) if err != nil { return err @@ -406,8 +400,8 @@ func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrID string, if ctr.State != define.ContainerStateRunning.String() { return errors.Errorf("you can only attach to running containers") } - - return containers.Attach(ic.ClientCxt, nameOrID, &options.DetachKeys, nil, bindings.PTrue, options.Stdin, options.Stdout, options.Stderr, nil) + options := new(containers.AttachOptions).WithStream(true).WithDetachKeys(opts.DetachKeys) + return containers.Attach(ic.ClientCxt, nameOrID, opts.Stdin, opts.Stdout, opts.Stderr, nil, options) } func makeExecConfig(options entities.ExecOptions) *handlers.ExecCreateConfig { @@ -439,12 +433,17 @@ func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrID string, o if err != nil { return 125, err } - - if err := containers.ExecStartAndAttach(ic.ClientCxt, sessionID, &streams); err != nil { + startAndAttachOptions := new(containers.ExecStartAndAttachOptions) + startAndAttachOptions.WithOutputStream(streams.OutputStream).WithErrorStream(streams.ErrorStream) + if streams.InputStream != nil { + startAndAttachOptions.WithInputStream(*streams.InputStream) + } + startAndAttachOptions.WithAttachError(streams.AttachError).WithAttachOutput(streams.AttachOutput).WithAttachInput(streams.AttachInput) + if err := containers.ExecStartAndAttach(ic.ClientCxt, sessionID, startAndAttachOptions); err != nil { return 125, err } - inspectOut, err := containers.ExecInspect(ic.ClientCxt, sessionID) + inspectOut, err := containers.ExecInspect(ic.ClientCxt, sessionID, nil) if err != nil { return 125, err } @@ -460,7 +459,7 @@ func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrID s return "", err } - if err := containers.ExecStart(ic.ClientCxt, sessionID); err != nil { + if err := containers.ExecStart(ic.ClientCxt, sessionID, nil); err != nil { return "", err } @@ -470,15 +469,23 @@ func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrID s func startAndAttach(ic *ContainerEngine, name string, detachKeys *string, input, output, errput *os.File) error { //nolint attachErr := make(chan error) attachReady := make(chan bool) + options := new(containers.AttachOptions).WithStream(true) + if dk := detachKeys; dk != nil { + options.WithDetachKeys(*dk) + } go func() { - err := containers.Attach(ic.ClientCxt, name, detachKeys, bindings.PFalse, bindings.PTrue, input, output, errput, attachReady) + err := containers.Attach(ic.ClientCxt, name, input, output, errput, attachReady, options) attachErr <- err }() // Wait for the attach to actually happen before starting // the container. select { case <-attachReady: - if err := containers.Start(ic.ClientCxt, name, detachKeys); err != nil { + startOptions := new(containers.StartOptions) + if dk := detachKeys; dk != nil { + startOptions.WithDetachKeys(*dk) + } + if err := containers.Start(ic.ClientCxt, name, startOptions); err != nil { return err } case err := <-attachErr: @@ -495,6 +502,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri if err != nil { return nil, err } + removeOptions := new(containers.RemoveOptions).WithVolumes(true).WithForce(false) // There can only be one container if attach was used for i, ctr := range ctrs { name := ctr.ID @@ -527,14 +535,14 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri // Defer the removal, so we can return early if needed and // de-spaghetti the code. defer func() { - shouldRestart, err := containers.ShouldRestart(ic.ClientCxt, ctr.ID) + shouldRestart, err := containers.ShouldRestart(ic.ClientCxt, ctr.ID, nil) if err != nil { logrus.Errorf("Failed to check if %s should restart: %v", ctr.ID, err) return } if !shouldRestart { - if err := containers.Remove(ic.ClientCxt, ctr.ID, bindings.PFalse, bindings.PTrue); err != nil { + if err := containers.Remove(ic.ClientCxt, ctr.ID, removeOptions); err != nil { if errorhandling.Contains(err, define.ErrNoSuchCtr) || errorhandling.Contains(err, define.ErrCtrRemoved) { logrus.Warnf("Container %s does not exist: %v", ctr.ID, err) @@ -564,10 +572,12 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri } // Start the container if it's not running already. if !ctrRunning { - err = containers.Start(ic.ClientCxt, name, &options.DetachKeys) + + err = containers.Start(ic.ClientCxt, name, new(containers.StartOptions).WithDetachKeys(options.DetachKeys)) if err != nil { if ctr.AutoRemove { - if err := containers.Remove(ic.ClientCxt, ctr.ID, bindings.PFalse, bindings.PTrue); err != nil { + rmOptions := new(containers.RemoveOptions).WithForce(false).WithVolumes(true) + if err := containers.Remove(ic.ClientCxt, ctr.ID, rmOptions); err != nil { if errorhandling.Contains(err, define.ErrNoSuchCtr) || errorhandling.Contains(err, define.ErrCtrRemoved) { logrus.Warnf("Container %s does not exist: %v", ctr.ID, err) @@ -588,12 +598,14 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri return reports, nil } -func (ic *ContainerEngine) ContainerList(ctx context.Context, options entities.ContainerListOptions) ([]entities.ListContainer, error) { - return containers.List(ic.ClientCxt, options.Filters, &options.All, &options.Last, &options.Namespace, &options.Size, &options.Sync) +func (ic *ContainerEngine) ContainerList(ctx context.Context, opts entities.ContainerListOptions) ([]entities.ListContainer, error) { + options := new(containers.ListOptions).WithFilters(opts.Filters).WithAll(opts.All).WithLast(opts.Last) + options.WithNamespace(opts.Namespace).WithSize(opts.Size).WithSync(opts.Sync) + return containers.List(ic.ClientCxt, options) } func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) { - con, err := containers.CreateWithSpec(ic.ClientCxt, opts.Spec) + con, err := containers.CreateWithSpec(ic.ClientCxt, opts.Spec, nil) if err != nil { return nil, err } @@ -625,7 +637,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta report.ExitCode = define.ExitCode(err) if opts.Rm { - if rmErr := containers.Remove(ic.ClientCxt, con.ID, bindings.PFalse, bindings.PTrue); rmErr != nil { + if rmErr := containers.Remove(ic.ClientCxt, con.ID, new(containers.RemoveOptions).WithForce(false).WithVolumes(true)); rmErr != nil { logrus.Debugf("unable to remove container %s after failing to start and attach to it", con.ID) } } @@ -636,14 +648,14 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta // Defer the removal, so we can return early if needed and // de-spaghetti the code. defer func() { - shouldRestart, err := containers.ShouldRestart(ic.ClientCxt, con.ID) + shouldRestart, err := containers.ShouldRestart(ic.ClientCxt, con.ID, nil) if err != nil { logrus.Errorf("Failed to check if %s should restart: %v", con.ID, err) return } if !shouldRestart { - if err := containers.Remove(ic.ClientCxt, con.ID, bindings.PFalse, bindings.PTrue); err != nil { + if err := containers.Remove(ic.ClientCxt, con.ID, new(containers.RemoveOptions).WithForce(false).WithVolumes(true)); err != nil { if errorhandling.Contains(err, define.ErrNoSuchCtr) || errorhandling.Contains(err, define.ErrCtrRemoved) { logrus.Warnf("Container %s does not exist: %v", con.ID, err) @@ -705,7 +717,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta } func (ic *ContainerEngine) ContainerDiff(ctx context.Context, nameOrID string, _ entities.DiffOptions) (*entities.DiffReport, error) { - changes, err := containers.Diff(ic.ClientCxt, nameOrID) + changes, err := containers.Diff(ic.ClientCxt, nameOrID, nil) return &entities.DiffReport{Changes: changes}, err } @@ -720,7 +732,7 @@ func (ic *ContainerEngine) ContainerInit(ctx context.Context, namesOrIds []strin } reports := make([]*entities.ContainerInitReport, 0, len(ctrs)) for _, ctr := range ctrs { - err := containers.ContainerInit(ic.ClientCxt, ctr.ID) + err := containers.ContainerInit(ic.ClientCxt, ctr.ID, nil) // When using all, it is NOT considered an error if a container // has already been init'd. if err != nil && options.All && strings.Contains(errors.Cause(err).Error(), define.ErrCtrStateInvalid.Error()) { @@ -772,9 +784,16 @@ func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrID string, o return reports, nil } -func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, options entities.ContainerCpOptions) error { - return nil - // return containers.Copy(ic.ClientCxt, source, dest, options) +func (ic *ContainerEngine) ContainerCopyFromArchive(ctx context.Context, nameOrID string, path string, reader io.Reader) (entities.ContainerCopyFunc, error) { + return containers.CopyFromArchive(ic.ClientCxt, nameOrID, path, reader) +} + +func (ic *ContainerEngine) ContainerCopyToArchive(ctx context.Context, nameOrID string, path string, writer io.Writer) (entities.ContainerCopyFunc, error) { + return containers.CopyToArchive(ic.ClientCxt, nameOrID, path, writer) +} + +func (ic *ContainerEngine) ContainerStat(ctx context.Context, nameOrID string, path string) (*entities.ContainerStatReport, error) { + return containers.Stat(ic.ClientCxt, nameOrID, path) } // Shutdown Libpod engine @@ -785,10 +804,10 @@ func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []stri if options.Latest { return nil, errors.New("latest is not supported for the remote client") } - return containers.Stats(ic.ClientCxt, namesOrIds, &options.Stream) + return containers.Stats(ic.ClientCxt, namesOrIds, new(containers.StatsOptions).WithStream(options.Stream)) } // ShouldRestart reports back whether the containre will restart func (ic *ContainerEngine) ShouldRestart(_ context.Context, id string) (bool, error) { - return containers.ShouldRestart(ic.ClientCxt, id) + return containers.ShouldRestart(ic.ClientCxt, id, nil) } diff --git a/pkg/domain/infra/tunnel/healthcheck.go b/pkg/domain/infra/tunnel/healthcheck.go index ac28712ce..b3ff888d8 100644 --- a/pkg/domain/infra/tunnel/healthcheck.go +++ b/pkg/domain/infra/tunnel/healthcheck.go @@ -9,5 +9,5 @@ import ( ) func (ic *ContainerEngine) HealthCheckRun(ctx context.Context, nameOrID string, options entities.HealthCheckOptions) (*define.HealthCheckResults, error) { - return containers.RunHealthCheck(ic.ClientCxt, nameOrID) + return containers.RunHealthCheck(ic.ClientCxt, nameOrID, nil) } diff --git a/pkg/domain/infra/tunnel/helpers.go b/pkg/domain/infra/tunnel/helpers.go index 63f9546be..0a806d860 100644 --- a/pkg/domain/infra/tunnel/helpers.go +++ b/pkg/domain/infra/tunnel/helpers.go @@ -4,7 +4,6 @@ import ( "context" "github.com/containers/podman/v2/libpod/define" - "github.com/containers/podman/v2/pkg/bindings" "github.com/containers/podman/v2/pkg/bindings/containers" "github.com/containers/podman/v2/pkg/bindings/pods" "github.com/containers/podman/v2/pkg/domain/entities" @@ -18,8 +17,8 @@ func getContainersByContext(contextWithConnection context.Context, all, ignore b if all && len(namesOrIDs) > 0 { return nil, errors.New("cannot lookup containers and all") } - - allContainers, err := containers.List(contextWithConnection, nil, bindings.PTrue, nil, nil, nil, bindings.PTrue) + options := new(containers.ListOptions).WithAll(true).WithSync(true) + allContainers, err := containers.List(contextWithConnection, options) if err != nil { return nil, err } @@ -38,7 +37,7 @@ func getContainersByContext(contextWithConnection context.Context, all, ignore b // First determine if the container exists by doing an inspect. // Inspect takes supports names and IDs and let's us determine // a containers full ID. - inspectData, err := containers.Inspect(contextWithConnection, nameOrID, bindings.PFalse) + inspectData, err := containers.Inspect(contextWithConnection, nameOrID, new(containers.InspectOptions).WithSize(false)) if err != nil { if ignore && errorhandling.Contains(err, define.ErrNoSuchCtr) { continue @@ -90,7 +89,7 @@ func getPodsByContext(contextWithConnection context.Context, all bool, namesOrID // First determine if the pod exists by doing an inspect. // Inspect takes supports names and IDs and let's us determine // a containers full ID. - inspectData, err := pods.Inspect(contextWithConnection, nameOrID) + inspectData, err := pods.Inspect(contextWithConnection, nameOrID, nil) if err != nil { if errorhandling.Contains(err, define.ErrNoSuchPod) { return nil, errors.Wrapf(define.ErrNoSuchPod, "unable to find pod %q", nameOrID) diff --git a/pkg/domain/infra/tunnel/pods.go b/pkg/domain/infra/tunnel/pods.go index ee4978787..1ceff9ca7 100644 --- a/pkg/domain/infra/tunnel/pods.go +++ b/pkg/domain/infra/tunnel/pods.go @@ -16,19 +16,20 @@ func (ic *ContainerEngine) PodExists(ctx context.Context, nameOrID string) (*ent return &entities.BoolReport{Value: exists}, err } -func (ic *ContainerEngine) PodKill(ctx context.Context, namesOrIds []string, options entities.PodKillOptions) ([]*entities.PodKillReport, error) { - _, err := util.ParseSignal(options.Signal) +func (ic *ContainerEngine) PodKill(ctx context.Context, namesOrIds []string, opts entities.PodKillOptions) ([]*entities.PodKillReport, error) { + _, err := util.ParseSignal(opts.Signal) if err != nil { return nil, err } - foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) + foundPods, err := getPodsByContext(ic.ClientCxt, opts.All, namesOrIds) if err != nil { return nil, err } reports := make([]*entities.PodKillReport, 0, len(foundPods)) + options := new(pods.KillOptions).WithSignal(opts.Signal) for _, p := range foundPods { - response, err := pods.Kill(ic.ClientCxt, p.Id, &options.Signal) + response, err := pods.Kill(ic.ClientCxt, p.Id, options) if err != nil { report := entities.PodKillReport{ Errs: []error{err}, @@ -49,7 +50,7 @@ func (ic *ContainerEngine) PodPause(ctx context.Context, namesOrIds []string, op } reports := make([]*entities.PodPauseReport, 0, len(foundPods)) for _, p := range foundPods { - response, err := pods.Pause(ic.ClientCxt, p.Id) + response, err := pods.Pause(ic.ClientCxt, p.Id, nil) if err != nil { report := entities.PodPauseReport{ Errs: []error{err}, @@ -70,7 +71,7 @@ func (ic *ContainerEngine) PodUnpause(ctx context.Context, namesOrIds []string, } reports := make([]*entities.PodUnpauseReport, 0, len(foundPods)) for _, p := range foundPods { - response, err := pods.Unpause(ic.ClientCxt, p.Id) + response, err := pods.Unpause(ic.ClientCxt, p.Id, nil) if err != nil { report := entities.PodUnpauseReport{ Errs: []error{err}, @@ -84,18 +85,19 @@ func (ic *ContainerEngine) PodUnpause(ctx context.Context, namesOrIds []string, return reports, nil } -func (ic *ContainerEngine) PodStop(ctx context.Context, namesOrIds []string, options entities.PodStopOptions) ([]*entities.PodStopReport, error) { +func (ic *ContainerEngine) PodStop(ctx context.Context, namesOrIds []string, opts entities.PodStopOptions) ([]*entities.PodStopReport, error) { timeout := -1 - foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) - if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchPod) { + foundPods, err := getPodsByContext(ic.ClientCxt, opts.All, namesOrIds) + if err != nil && !(opts.Ignore && errors.Cause(err) == define.ErrNoSuchPod) { return nil, err } - if options.Timeout != -1 { - timeout = options.Timeout + if opts.Timeout != -1 { + timeout = opts.Timeout } reports := make([]*entities.PodStopReport, 0, len(foundPods)) + options := new(pods.StopOptions).WithTimeout(timeout) for _, p := range foundPods { - response, err := pods.Stop(ic.ClientCxt, p.Id, &timeout) + response, err := pods.Stop(ic.ClientCxt, p.Id, options) if err != nil { report := entities.PodStopReport{ Errs: []error{err}, @@ -116,7 +118,7 @@ func (ic *ContainerEngine) PodRestart(ctx context.Context, namesOrIds []string, } reports := make([]*entities.PodRestartReport, 0, len(foundPods)) for _, p := range foundPods { - response, err := pods.Restart(ic.ClientCxt, p.Id) + response, err := pods.Restart(ic.ClientCxt, p.Id, nil) if err != nil { report := entities.PodRestartReport{ Errs: []error{err}, @@ -137,7 +139,7 @@ func (ic *ContainerEngine) PodStart(ctx context.Context, namesOrIds []string, op } reports := make([]*entities.PodStartReport, 0, len(foundPods)) for _, p := range foundPods { - response, err := pods.Start(ic.ClientCxt, p.Id) + response, err := pods.Start(ic.ClientCxt, p.Id, nil) if err != nil { report := entities.PodStartReport{ Errs: []error{err}, @@ -151,14 +153,15 @@ func (ic *ContainerEngine) PodStart(ctx context.Context, namesOrIds []string, op return reports, nil } -func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, options entities.PodRmOptions) ([]*entities.PodRmReport, error) { - foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) - if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchPod) { +func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, opts entities.PodRmOptions) ([]*entities.PodRmReport, error) { + foundPods, err := getPodsByContext(ic.ClientCxt, opts.All, namesOrIds) + if err != nil && !(opts.Ignore && errors.Cause(err) == define.ErrNoSuchPod) { return nil, err } reports := make([]*entities.PodRmReport, 0, len(foundPods)) + options := new(pods.RemoveOptions).WithForce(opts.Force) for _, p := range foundPods { - response, err := pods.Remove(ic.ClientCxt, p.Id, &options.Force) + response, err := pods.Remove(ic.ClientCxt, p.Id, options) if err != nil { report := entities.PodRmReport{ Err: err, @@ -173,32 +176,33 @@ func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, optio } func (ic *ContainerEngine) PodPrune(ctx context.Context, opts entities.PodPruneOptions) ([]*entities.PodPruneReport, error) { - return pods.Prune(ic.ClientCxt) + return pods.Prune(ic.ClientCxt, nil) } func (ic *ContainerEngine) PodCreate(ctx context.Context, opts entities.PodCreateOptions) (*entities.PodCreateReport, error) { podSpec := specgen.NewPodSpecGenerator() opts.ToPodSpecGen(podSpec) - return pods.CreatePodFromSpec(ic.ClientCxt, podSpec) + return pods.CreatePodFromSpec(ic.ClientCxt, podSpec, nil) } -func (ic *ContainerEngine) PodTop(ctx context.Context, options entities.PodTopOptions) (*entities.StringSliceReport, error) { +func (ic *ContainerEngine) PodTop(ctx context.Context, opts entities.PodTopOptions) (*entities.StringSliceReport, error) { switch { - case options.Latest: + case opts.Latest: return nil, errors.New("latest is not supported") - case options.NameOrID == "": + case opts.NameOrID == "": return nil, errors.New("NameOrID must be specified") } - - topOutput, err := pods.Top(ic.ClientCxt, options.NameOrID, options.Descriptors) + options := new(pods.TopOptions).WithDescriptors(opts.Descriptors) + topOutput, err := pods.Top(ic.ClientCxt, opts.NameOrID, options) if err != nil { return nil, err } return &entities.StringSliceReport{Value: topOutput}, nil } -func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOptions) ([]*entities.ListPodsReport, error) { - return pods.List(ic.ClientCxt, options.Filters) +func (ic *ContainerEngine) PodPs(ctx context.Context, opts entities.PodPSOptions) ([]*entities.ListPodsReport, error) { + options := new(pods.ListOptions).WithFilters(opts.Filters) + return pods.List(ic.ClientCxt, options) } func (ic *ContainerEngine) PodInspect(ctx context.Context, options entities.PodInspectOptions) (*entities.PodInspectReport, error) { @@ -208,9 +212,10 @@ func (ic *ContainerEngine) PodInspect(ctx context.Context, options entities.PodI case options.NameOrID == "": return nil, errors.New("NameOrID must be specified") } - return pods.Inspect(ic.ClientCxt, options.NameOrID) + return pods.Inspect(ic.ClientCxt, options.NameOrID, nil) } -func (ic *ContainerEngine) PodStats(ctx context.Context, namesOrIds []string, options entities.PodStatsOptions) ([]*entities.PodStatsReport, error) { +func (ic *ContainerEngine) PodStats(ctx context.Context, namesOrIds []string, opts entities.PodStatsOptions) ([]*entities.PodStatsReport, error) { + options := new(pods.StatsOptions).WithAll(opts.All) return pods.Stats(ic.ClientCxt, namesOrIds, options) } diff --git a/pkg/domain/infra/tunnel/system.go b/pkg/domain/infra/tunnel/system.go index 1e4b2e0b4..9013d44c2 100644 --- a/pkg/domain/infra/tunnel/system.go +++ b/pkg/domain/infra/tunnel/system.go @@ -20,7 +20,7 @@ func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command) // SystemPrune prunes unused data from the system. func (ic *ContainerEngine) SystemPrune(ctx context.Context, opts entities.SystemPruneOptions) (*entities.SystemPruneReport, error) { - options := new(system.PruneOptions).WithAll(opts.All).WithVolumes(opts.Volume).WithFilters(opts.ContainerPruneOptions.Filters) + options := new(system.PruneOptions).WithAll(opts.All).WithVolumes(opts.Volume).WithFilters(opts.Filters) return system.Prune(ic.ClientCxt, options) } diff --git a/pkg/errorhandling/errorhandling.go b/pkg/errorhandling/errorhandling.go index ca6b60bc5..21df261fb 100644 --- a/pkg/errorhandling/errorhandling.go +++ b/pkg/errorhandling/errorhandling.go @@ -19,7 +19,12 @@ func JoinErrors(errs []error) error { // blank lines when printing the error. var multiE *multierror.Error multiE = multierror.Append(multiE, errs...) - return errors.New(strings.TrimSpace(multiE.ErrorOrNil().Error())) + + finalErr := multiE.ErrorOrNil() + if finalErr == nil { + return finalErr + } + return errors.New(strings.TrimSpace(finalErr.Error())) } // ErrorsToString converts the slice of errors into a slice of corresponding diff --git a/pkg/signal/signal_linux.go b/pkg/signal/signal_linux.go index 72ab1b97b..305b9d21f 100644 --- a/pkg/signal/signal_linux.go +++ b/pkg/signal/signal_linux.go @@ -1,4 +1,5 @@ // +build linux +// +build !mips,!mipsle,!mips64,!mips64le // Signal handling for Linux only. package signal diff --git a/pkg/signal/signal_linux_mipsx.go b/pkg/signal/signal_linux_mipsx.go new file mode 100644 index 000000000..67638e30a --- /dev/null +++ b/pkg/signal/signal_linux_mipsx.go @@ -0,0 +1,106 @@ +// +build linux +// +build mips mipsle mips64 mips64le + +// Special signal handling for mips architecture +package signal + +// Copyright 2013-2018 Docker, Inc. + +// NOTE: this package has originally been copied from github.com/docker/docker. + +import ( + "os" + "os/signal" + "syscall" + + "golang.org/x/sys/unix" +) + +const ( + sigrtmin = 34 + sigrtmax = 127 +) + +// signalMap is a map of Linux signals. +var signalMap = map[string]syscall.Signal{ + "ABRT": unix.SIGABRT, + "ALRM": unix.SIGALRM, + "BUS": unix.SIGBUS, + "CHLD": unix.SIGCHLD, + "CLD": unix.SIGCLD, + "CONT": unix.SIGCONT, + "FPE": unix.SIGFPE, + "HUP": unix.SIGHUP, + "ILL": unix.SIGILL, + "INT": unix.SIGINT, + "IO": unix.SIGIO, + "IOT": unix.SIGIOT, + "KILL": unix.SIGKILL, + "PIPE": unix.SIGPIPE, + "POLL": unix.SIGPOLL, + "PROF": unix.SIGPROF, + "PWR": unix.SIGPWR, + "QUIT": unix.SIGQUIT, + "SEGV": unix.SIGSEGV, + "EMT": unix.SIGEMT, + "STOP": unix.SIGSTOP, + "SYS": unix.SIGSYS, + "TERM": unix.SIGTERM, + "TRAP": unix.SIGTRAP, + "TSTP": unix.SIGTSTP, + "TTIN": unix.SIGTTIN, + "TTOU": unix.SIGTTOU, + "URG": unix.SIGURG, + "USR1": unix.SIGUSR1, + "USR2": unix.SIGUSR2, + "VTALRM": unix.SIGVTALRM, + "WINCH": unix.SIGWINCH, + "XCPU": unix.SIGXCPU, + "XFSZ": unix.SIGXFSZ, + "RTMIN": sigrtmin, + "RTMIN+1": sigrtmin + 1, + "RTMIN+2": sigrtmin + 2, + "RTMIN+3": sigrtmin + 3, + "RTMIN+4": sigrtmin + 4, + "RTMIN+5": sigrtmin + 5, + "RTMIN+6": sigrtmin + 6, + "RTMIN+7": sigrtmin + 7, + "RTMIN+8": sigrtmin + 8, + "RTMIN+9": sigrtmin + 9, + "RTMIN+10": sigrtmin + 10, + "RTMIN+11": sigrtmin + 11, + "RTMIN+12": sigrtmin + 12, + "RTMIN+13": sigrtmin + 13, + "RTMIN+14": sigrtmin + 14, + "RTMIN+15": sigrtmin + 15, + "RTMAX-14": sigrtmax - 14, + "RTMAX-13": sigrtmax - 13, + "RTMAX-12": sigrtmax - 12, + "RTMAX-11": sigrtmax - 11, + "RTMAX-10": sigrtmax - 10, + "RTMAX-9": sigrtmax - 9, + "RTMAX-8": sigrtmax - 8, + "RTMAX-7": sigrtmax - 7, + "RTMAX-6": sigrtmax - 6, + "RTMAX-5": sigrtmax - 5, + "RTMAX-4": sigrtmax - 4, + "RTMAX-3": sigrtmax - 3, + "RTMAX-2": sigrtmax - 2, + "RTMAX-1": sigrtmax - 1, + "RTMAX": sigrtmax, +} + +// CatchAll catches all signals and relays them to the specified channel. +func CatchAll(sigc chan os.Signal) { + handledSigs := make([]os.Signal, 0, len(signalMap)) + for _, s := range signalMap { + handledSigs = append(handledSigs, s) + } + signal.Notify(sigc, handledSigs...) +} + +// StopCatch stops catching the signals and closes the specified channel. +func StopCatch(sigc chan os.Signal) { + signal.Stop(sigc) + close(sigc) +} diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index 5cc7891ac..c64fe76a6 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -112,11 +112,18 @@ func ToSpecGen(ctx context.Context, containerYAML v1.Container, iid string, newI return nil, err } s.WorkDir = "/" + // We will use "Docker field name" internally here to avoid confusion + // and reference the "Kubernetes field name" when referencing the YAML + // ref: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#notes + entrypoint := []string{} + cmd := []string{} if imageData != nil && imageData.Config != nil { if imageData.Config.WorkingDir != "" { s.WorkDir = imageData.Config.WorkingDir } - s.Command = imageData.Config.Entrypoint + // Pull entrypoint and cmd from image + entrypoint = imageData.Config.Entrypoint + cmd = imageData.Config.Cmd s.Labels = imageData.Config.Labels if len(imageData.Config.StopSignal) > 0 { stopSignal, err := util.ParseSignal(imageData.Config.StopSignal) @@ -126,13 +133,18 @@ func ToSpecGen(ctx context.Context, containerYAML v1.Container, iid string, newI s.StopSignal = &stopSignal } } + // If only the yaml.Command is specified, set it as the entrypoint and drop the image Cmd if len(containerYAML.Command) != 0 { - s.Command = containerYAML.Command + entrypoint = containerYAML.Command + cmd = []string{} } - // doc https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#notes + // Only override the cmd field if yaml.Args is specified + // Keep the image entrypoint, or the yaml.command if specified if len(containerYAML.Args) != 0 { - s.Command = append(s.Command, containerYAML.Args...) + cmd = containerYAML.Args } + + s.Command = append(entrypoint, cmd...) // FIXME, // we are currently ignoring imageData.Config.ExposedPorts if containerYAML.WorkingDir != "" { diff --git a/test/apiv2/45-system.at b/test/apiv2/45-system.at new file mode 100644 index 000000000..7d14fd4b3 --- /dev/null +++ b/test/apiv2/45-system.at @@ -0,0 +1,67 @@ +# -*- sh -*- +# +# system related tests +# + +## ensure system is clean +t POST 'libpod/system/prune?volumes=true&all=true' params='' 200 + +## podman system df +t GET system/df 200 '{"LayersSize":0,"Images":[],"Containers":[],"Volumes":[],"BuildCache":[],"BuilderSize":0}' +t GET libpod/system/df 200 '{"Images":[],"Containers":[],"Volumes":[]}' + +# Create volume. We expect df to report this volume next invocation of system/df +t GET libpod/info 200 +volumepath=$(jq -r ".store.volumePath" <<<"$output") +t POST libpod/volumes/create name=foo1 201 \ + .Name=foo1 \ + .Driver=local \ + .Mountpoint=$volumepath/foo1/_data \ + .CreatedAt~[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.* \ + .Labels={} \ + .Options=null + +t GET system/df 200 '.Volumes[0].Name=foo1' + +t GET libpod/system/df 200 '.Volumes[0].VolumeName=foo1' + +# Create two more volumes to test pruneing +t POST libpod/volumes/create \ + '"Name":"foo2","Label":{"testlabel1":""},"Options":{"type":"tmpfs","o":"nodev,noexec"}}' 201 \ + .Name=foo2 \ + .Driver=local \ + .Mountpoint=$volumepath/foo2/_data \ + .CreatedAt~[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.* \ + .Labels.testlabel1="" \ + .Options.o=nodev,noexec + +t POST libpod/volumes/create \ + '"Name":"foo3","Label":{"testlabel1":"testonly"},"Options":{"type":"tmpfs","o":"nodev,noexec"}}' 201 \ + .Name=foo3 \ + .Driver=local \ + .Mountpoint=$volumepath/foo3/_data \ + .CreatedAt~[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.* \ + .Labels.testlabel1=testonly \ + .Options.o=nodev,noexec + +t GET system/df 200 '.Volumes | length=3' +t GET libpod/system/df 200 '.Volumes | length=3' + +# Prune volumes + +# -G --data-urlencode 'volumes=true&filters={"label":["testlabel1=idontmatch"]}' +t POST 'libpod/system/prune?volumes=true&filters=%7B%22label%22:%5B%22testlabel1=idontmatch%22%5D%7D' params='' 200 + +# nothing should have been pruned +t GET system/df 200 '.Volumes | length=3' +t GET libpod/system/df 200 '.Volumes | length=3' + +# -G --data-urlencode 'volumes=true&filters={"label":["testlabel1=testonly"]}' +# only foo3 should be pruned because of filter +t POST 'libpod/system/prune?volumes=true&filters=%7B%22label%22:%5B%22testlabel1=testonly%22%5D%7D' params='' 200 .VolumePruneReport[0].Id=foo3 +# only foo2 should be pruned because of filter +t POST 'libpod/system/prune?volumes=true&filters=%7B%22label%22:%5B%22testlabel1%22%5D%7D' params='' 200 .VolumePruneReport[0].Id=foo2 +# foo1, the last remaining volume should be pruned without any filters applied +t POST 'libpod/system/prune?volumes=true' params='' 200 .VolumePruneReport[0].Id=foo1 + +# TODO add other system prune tests for pods / images diff --git a/test/e2e/cp_test.go b/test/e2e/cp_test.go index 33908b60e..6fe26c444 100644 --- a/test/e2e/cp_test.go +++ b/test/e2e/cp_test.go @@ -24,7 +24,6 @@ var _ = Describe("Podman cp", func() { ) BeforeEach(func() { - SkipIfRemote("FIXME: Podman-remote cp needs to work") tempdir, err = CreateTempDirInTempDir() if err != nil { os.Exit(1) diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 3a2387559..4fec56105 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -820,8 +820,28 @@ var _ = Describe("Podman play kube", func() { Expect(inspect.OutputToString()).To(ContainSubstring(correctCmd)) }) + // If you do not supply command or args for a Container, the defaults defined in the Docker image are used. + It("podman play kube test correct args and cmd when not specified", func() { + pod := getPod(withCtr(getCtr(withImage(registry), withCmd(nil), withArg(nil)))) + err := generateKubeYaml("pod", pod, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + + // this image's ENTRYPOINT is `/entrypoint.sh` and it's COMMAND is `/etc/docker/registry/config.yml` + Expect(inspect.OutputToString()).To(ContainSubstring(`[/entrypoint.sh /etc/docker/registry/config.yml]`)) + }) + + // If you supply a command but no args for a Container, only the supplied command is used. + // The default EntryPoint and the default Cmd defined in the Docker image are ignored. It("podman play kube test correct command with only set command in yaml file", func() { - pod := getPod(withCtr(getCtr(withCmd([]string{"echo", "hello"}), withArg(nil)))) + pod := getPod(withCtr(getCtr(withImage(registry), withCmd([]string{"echo", "hello"}), withArg(nil)))) err := generateKubeYaml("pod", pod, kubeYaml) Expect(err).To(BeNil()) @@ -837,8 +857,9 @@ var _ = Describe("Podman play kube", func() { Expect(inspect.OutputToString()).To(ContainSubstring(`[echo hello]`)) }) + // If you supply only args for a Container, the default Entrypoint defined in the Docker image is run with the args that you supplied. It("podman play kube test correct command with only set args in yaml file", func() { - pod := getPod(withCtr(getCtr(withImage(redis), withCmd(nil), withArg([]string{"echo", "hello"})))) + pod := getPod(withCtr(getCtr(withImage(registry), withCmd(nil), withArg([]string{"echo", "hello"})))) err := generateKubeYaml("pod", pod, kubeYaml) Expect(err).To(BeNil()) @@ -849,9 +870,27 @@ var _ = Describe("Podman play kube", func() { inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) - // this image's ENTRYPOINT is called `docker-entrypoint.sh` - // so result should be `docker-entrypoint.sh + withArg(...)` - Expect(inspect.OutputToString()).To(ContainSubstring(`[docker-entrypoint.sh echo hello]`)) + // this image's ENTRYPOINT is `/entrypoint.sh` + // so result should be `/entrypoint.sh + withArg(...)` + Expect(inspect.OutputToString()).To(ContainSubstring(`[/entrypoint.sh echo hello]`)) + }) + + // If you supply a command and args, + // the default Entrypoint and the default Cmd defined in the Docker image are ignored. + // Your command is run with your args. + It("podman play kube test correct command with both set args and cmd in yaml file", func() { + pod := getPod(withCtr(getCtr(withImage(registry), withCmd([]string{"echo"}), withArg([]string{"hello"})))) + err := generateKubeYaml("pod", pod, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.OutputToString()).To(ContainSubstring(`[echo hello]`)) }) It("podman play kube test correct output", func() { diff --git a/test/e2e/prune_test.go b/test/e2e/prune_test.go index c02ed5a50..3bc1012df 100644 --- a/test/e2e/prune_test.go +++ b/test/e2e/prune_test.go @@ -349,4 +349,64 @@ var _ = Describe("Podman prune", func() { // all images are unused, so they all should be deleted! Expect(len(images.OutputToStringArray())).To(Equal(len(CACHE_IMAGES))) }) + + It("podman system prune --volumes --filter", func() { + session := podmanTest.Podman([]string{"volume", "create", "--label", "label1=value1", "myvol1"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"volume", "create", "--label", "sharedlabel1=slv1", "myvol2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"volume", "create", "--label", "sharedlabel1=slv2", "myvol3"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"volume", "create", "--label", "sharedlabel1", "myvol4"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"create", "-v", "myvol5:/myvol5", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"create", "-v", "myvol6:/myvol6", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"volume", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(len(session.OutputToStringArray())).To(Equal(7)) + + session = podmanTest.Podman([]string{"system", "prune", "--force", "--volumes", "--filter", "label=label1=value1"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"volume", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(len(session.OutputToStringArray())).To(Equal(6)) + + session = podmanTest.Podman([]string{"system", "prune", "--force", "--volumes", "--filter", "label=sharedlabel1=slv1"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"volume", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(len(session.OutputToStringArray())).To(Equal(5)) + + session = podmanTest.Podman([]string{"system", "prune", "--force", "--volumes", "--filter", "label=sharedlabel1"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"volume", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(len(session.OutputToStringArray())).To(Equal(3)) + + podmanTest.Cleanup() + }) }) diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go index 05571157c..0c5d817ba 100644 --- a/test/e2e/ps_test.go +++ b/test/e2e/ps_test.go @@ -673,4 +673,55 @@ var _ = Describe("Podman ps", func() { Expect(session.LineInOutputContains("test3")).To(BeTrue()) Expect(session.LineInOutputContains("test4")).To(BeTrue()) }) + It("podman ps filter pod", func() { + pod1 := podmanTest.Podman([]string{"pod", "create", "--name", "pod1"}) + pod1.WaitWithDefaultTimeout() + Expect(pod1.ExitCode()).To(BeZero()) + con1 := podmanTest.Podman([]string{"run", "-dt", "--pod", "pod1", ALPINE, "top"}) + con1.WaitWithDefaultTimeout() + Expect(con1.ExitCode()).To(BeZero()) + + pod2 := podmanTest.Podman([]string{"pod", "create", "--name", "pod2"}) + pod2.WaitWithDefaultTimeout() + Expect(pod2.ExitCode()).To(BeZero()) + con2 := podmanTest.Podman([]string{"run", "-dt", "--pod", "pod2", ALPINE, "top"}) + con2.WaitWithDefaultTimeout() + Expect(con2.ExitCode()).To(BeZero()) + + // bogus pod name or id should not result in error + session := podmanTest.Podman([]string{"ps", "--filter", "pod=1234"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + + // filter by pod name + session = podmanTest.Podman([]string{"ps", "-q", "--no-trunc", "--filter", "pod=pod1"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + Expect(len(session.OutputToStringArray())).To(Equal(2)) + Expect(StringInSlice(pod1.OutputToString(), session.OutputToStringArray())) + + // filter by full pod id + session = podmanTest.Podman([]string{"ps", "-q", "--no-trunc", "--filter", "pod=" + pod1.OutputToString()}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + Expect(len(session.OutputToStringArray())).To(Equal(2)) + Expect(StringInSlice(pod1.OutputToString(), session.OutputToStringArray())) + + // filter by partial pod id + session = podmanTest.Podman([]string{"ps", "-q", "--no-trunc", "--filter", "pod=" + pod1.OutputToString()[0:12]}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + Expect(len(session.OutputToStringArray())).To(Equal(2)) + Expect(StringInSlice(pod1.OutputToString(), session.OutputToStringArray())) + + // filter by multiple pods is inclusive + session = podmanTest.Podman([]string{"ps", "-q", "--no-trunc", "--filter", "pod=pod1", "--filter", "pod=pod2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + Expect(len(session.OutputToStringArray())).To(Equal(4)) + Expect(StringInSlice(pod1.OutputToString(), session.OutputToStringArray())) + Expect(StringInSlice(pod2.OutputToString(), session.OutputToStringArray())) + + }) + }) diff --git a/test/system/065-cp.bats b/test/system/065-cp.bats index 43bdf217d..73b07ea45 100644 --- a/test/system/065-cp.bats +++ b/test/system/065-cp.bats @@ -8,8 +8,6 @@ load helpers @test "podman cp file from host to container" { - skip_if_remote "podman-remote does not yet handle cp" - srcdir=$PODMAN_TMPDIR/cp-test-file-host-to-ctr mkdir -p $srcdir local -a randomcontent=( @@ -57,60 +55,16 @@ load helpers is "$output" 'Error: ".*/IdoNotExist" could not be found on the host' \ "copy nonexistent host path" - # Container path does not exist. Notice that the error message shows how - # the specified container is resolved. + # Container (parent) path does not exist. run_podman 125 cp $srcdir/hostfile0 cpcontainer:/IdoNotExist/ - is "$output" 'Error: "/IdoNotExist/" could not be found on container.*(resolved to .*/IdoNotExist.*' \ + is "$output" 'Error: "/IdoNotExist/" could not be found on container cpcontainer: No such file or directory' \ "copy into nonexistent path in container" run_podman rm -f cpcontainer } -@test "podman cp --extract=true tar archive to container" { - skip_if_remote "podman-remote does not yet handle cp" - - # Create tempfile with random name and content - dirname=cp-test-extract - srcdir=$PODMAN_TMPDIR/$dirname - mkdir -p $srcdir - rand_filename=$(random_string 20) - rand_content=$(random_string 50) - echo $rand_content > $srcdir/$rand_filename - chmod 644 $srcdir/$rand_filename - - # Now tar it up! - tar_file=$PODMAN_TMPDIR/archive.tar.gz - tar -C $PODMAN_TMPDIR -zvcf $tar_file $dirname - - run_podman run -d --name cpcontainer $IMAGE sleep infinity - - # First just copy without extracting the archive. - run_podman cp $tar_file cpcontainer:/tmp - # Now remove the archive which will also test if it exists and is a file. - # To save expensive exec'ing, create a file for the next tests. - run_podman exec cpcontainer sh -c "rm /tmp/archive.tar.gz; touch /tmp/file.txt" - - # Now copy with extracting the archive. NOTE that Podman should - # auto-decompress the file if needed. - run_podman cp --extract=true $tar_file cpcontainer:/tmp - run_podman exec cpcontainer cat /tmp/$dirname/$rand_filename - is "$output" "$rand_content" - - # Test extract on non archive. - run_podman cp --extract=true $srcdir/$rand_filename cpcontainer:/foo.txt - - # Cannot extract an archive to a file! - run_podman 125 cp --extract=true $tar_file cpcontainer:/tmp/file.txt - is "$output" 'Error: cannot extract archive .* to file "/tmp/file.txt"' - - run_podman rm -f cpcontainer -} - - @test "podman cp file from container to host" { - skip_if_remote "podman-remote does not yet handle cp" - srcdir=$PODMAN_TMPDIR/cp-test-file-ctr-to-host mkdir -p $srcdir @@ -153,8 +107,6 @@ load helpers @test "podman cp dir from host to container" { - skip_if_remote "podman-remote does not yet handle cp" - dirname=dir-test srcdir=$PODMAN_TMPDIR/$dirname mkdir -p $srcdir @@ -195,8 +147,6 @@ load helpers @test "podman cp dir from container to host" { - skip_if_remote "podman-remote does not yet handle cp" - srcdir=$PODMAN_TMPDIR/dir-test mkdir -p $srcdir @@ -230,8 +180,6 @@ load helpers @test "podman cp file from host to container volume" { - skip_if_remote "podman-remote does not yet handle cp" - srcdir=$PODMAN_TMPDIR/cp-test-volume mkdir -p $srcdir echo "This file should be in volume2" > $srcdir/hostfile @@ -268,8 +216,6 @@ load helpers @test "podman cp file from host to container mount" { - skip_if_remote "podman-remote does not yet handle cp" - srcdir=$PODMAN_TMPDIR/cp-test-mount-src mountdir=$PODMAN_TMPDIR/cp-test-mount mkdir -p $srcdir $mountdir @@ -296,8 +242,6 @@ load helpers # perform wildcard expansion in the container. We should get both # files copied into the host. @test "podman cp * - wildcard copy multiple files from container to host" { - skip_if_remote "podman-remote does not yet handle cp" - srcdir=$PODMAN_TMPDIR/cp-test-in dstdir=$PODMAN_TMPDIR/cp-test-out mkdir -p $srcdir $dstdir @@ -321,8 +265,6 @@ load helpers # Create a file on the host; make a symlink in the container pointing # into host-only space. Try to podman-cp that symlink. It should fail. @test "podman cp - will not recognize symlink pointing into host space" { - skip_if_remote "podman-remote does not yet handle cp" - srcdir=$PODMAN_TMPDIR/cp-test-in dstdir=$PODMAN_TMPDIR/cp-test-out mkdir -p $srcdir $dstdir @@ -350,8 +292,6 @@ load helpers # in the container pointing to 'file*' (file star). Try to podman-cp # this invalid double symlink. It must fail. @test "podman cp - will not expand globs in host space (#3829)" { - skip_if_remote "podman-remote does not yet handle cp" - srcdir=$PODMAN_TMPDIR/cp-test-in dstdir=$PODMAN_TMPDIR/cp-test-out mkdir -p $srcdir $dstdir @@ -372,8 +312,6 @@ load helpers # Another symlink into host space, this one named '*' (star). cp should fail. @test "podman cp - will not expand wildcard" { - skip_if_remote "podman-remote does not yet handle cp" - srcdir=$PODMAN_TMPDIR/cp-test-in dstdir=$PODMAN_TMPDIR/cp-test-out mkdir -p $srcdir $dstdir @@ -394,8 +332,6 @@ load helpers # THIS IS EXTREMELY WEIRD. Podman expands symlinks in weird ways. @test "podman cp into container: weird symlink expansion" { - skip_if_remote "podman-remote does not yet handle cp" - srcdir=$PODMAN_TMPDIR/cp-test-in dstdir=$PODMAN_TMPDIR/cp-test-out mkdir -p $srcdir $dstdir @@ -427,7 +363,7 @@ load helpers is "$output" "" "output from podman cp 1" run_podman 125 cp --pause=false $srcdir/$rand_filename2 cpcontainer:/tmp/d2/x/ - is "$output" 'Error: "/tmp/d2/x/" could not be found on container.*' "cp will not create nonexistent destination directory" + is "$output" 'Error: "/tmp/d2/x/" could not be found on container cpcontainer: No such file or directory' "cp will not create nonexistent destination directory" run_podman cp --pause=false $srcdir/$rand_filename3 cpcontainer:/tmp/d3/x is "$output" "" "output from podman cp 3" @@ -454,8 +390,6 @@ load helpers # rhbz1741718 : file copied into container:/var/lib/foo appears as /foo # (docker only, never seems to have affected podman. Make sure it never does). @test "podman cp into a subdirectory matching GraphRoot" { - skip_if_remote "podman-remote does not yet handle cp" - # Create tempfile with random name and content srcdir=$PODMAN_TMPDIR/cp-test-in mkdir -p $srcdir @@ -491,8 +425,6 @@ load helpers @test "podman cp from stdin to container" { - skip_if_remote "podman-remote does not yet handle cp" - # Create tempfile with random name and content srcdir=$PODMAN_TMPDIR/cp-test-stdin mkdir -p $srcdir @@ -525,24 +457,22 @@ load helpers # Input stream must be a (compressed) tar archive. run_podman 125 cp - cpcontainer:/tmp < $srcdir/$rand_filename - is "$output" "Error:.*: error reading tar stream.*" "input stream must be a (compressed) tar archive" + is "$output" "Error: source must be a (compressed) tar archive when copying from stdin" # Destination must be a directory (on an existing file). run_podman exec cpcontainer touch /tmp/file.txt run_podman 125 cp /dev/stdin cpcontainer:/tmp/file.txt < $tar_file - is "$output" 'Error: destination must be a directory or stream when copying from a stream' + is "$output" 'Error: destination must be a directory when copying from stdin' # Destination must be a directory (on an absent path). run_podman 125 cp /dev/stdin cpcontainer:/tmp/IdoNotExist < $tar_file - is "$output" 'Error: destination must be a directory or stream when copying from a stream' + is "$output" 'Error: destination must be a directory when copying from stdin' run_podman rm -f cpcontainer } @test "podman cp from container to stdout" { - skip_if_remote "podman-remote does not yet handle cp" - srcdir=$PODMAN_TMPDIR/cp-test-stdout mkdir -p $srcdir rand_content=$(random_string 50) @@ -579,9 +509,9 @@ load helpers fi tar xvf $srcdir/stdout.tar -C $srcdir - run cat $srcdir/file.txt + run cat $srcdir/tmp/file.txt is "$output" "$rand_content" - run cat $srcdir/empty.txt + run cat $srcdir/tmp/empty.txt is "$output" "" run_podman rm -f cpcontainer diff --git a/test/system/helpers.bash b/test/system/helpers.bash index a4b89ec99..0572c6866 100644 --- a/test/system/helpers.bash +++ b/test/system/helpers.bash @@ -14,7 +14,7 @@ PODMAN_TEST_IMAGE_FQN="$PODMAN_TEST_IMAGE_REGISTRY/$PODMAN_TEST_IMAGE_USER/$PODM IMAGE=$PODMAN_TEST_IMAGE_FQN # Default timeout for a podman command. -PODMAN_TIMEOUT=${PODMAN_TIMEOUT:-60} +PODMAN_TIMEOUT=${PODMAN_TIMEOUT:-120} # Prompt to display when logging podman commands; distinguish root/rootless _LOG_PROMPT='$' diff --git a/vendor/github.com/containers/buildah/.cirrus.yml b/vendor/github.com/containers/buildah/.cirrus.yml index 65ce26080..589de9c61 100644 --- a/vendor/github.com/containers/buildah/.cirrus.yml +++ b/vendor/github.com/containers/buildah/.cirrus.yml @@ -178,7 +178,7 @@ gce_instance: image_name: "${FEDORA_CACHE_IMAGE_NAME}" image_name: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}" image_name: "${UBUNTU_CACHE_IMAGE_NAME}" - image_name: "${PRIOR_UBUNTU_CACHE_IMAGE_NAME}" +# image_name: "${PRIOR_UBUNTU_CACHE_IMAGE_NAME}" # Separate scripts for separate outputs, makes debugging easier. setup_script: '${SCRIPT_BASE}/setup.sh |& ${_TIMESTAMP}' diff --git a/vendor/github.com/containers/buildah/Makefile b/vendor/github.com/containers/buildah/Makefile index e70dd161d..45f8a8ec8 100644 --- a/vendor/github.com/containers/buildah/Makefile +++ b/vendor/github.com/containers/buildah/Makefile @@ -39,6 +39,14 @@ SOURCES=*.go imagebuildah/*.go bind/*.go chroot/*.go cmd/buildah/*.go copier/*.g LINTFLAGS ?= +ifeq ($(DEBUG), 1) + override GOGCFLAGS += -N -l +endif + +# make all DEBUG=1 +# Note: Uses the -N -l go compiler options to disable compiler optimizations +# and inlining. Using these build options allows you to subsequently +# use source debugging tools like delve. all: bin/buildah bin/imgtype docs # Update nix/nixpkgs.json its latest stable commit @@ -56,7 +64,7 @@ static: .PHONY: bin/buildah bin/buildah: $(SOURCES) - $(GO_BUILD) $(BUILDAH_LDFLAGS) -o $@ $(BUILDFLAGS) ./cmd/buildah + $(GO_BUILD) $(BUILDAH_LDFLAGS) -gcflags "$(GOGCFLAGS)" -o $@ $(BUILDFLAGS) ./cmd/buildah .PHONY: buildah buildah: bin/buildah diff --git a/vendor/github.com/containers/buildah/copier/copier.go b/vendor/github.com/containers/buildah/copier/copier.go index 84b636202..ef0e4778d 100644 --- a/vendor/github.com/containers/buildah/copier/copier.go +++ b/vendor/github.com/containers/buildah/copier/copier.go @@ -10,6 +10,7 @@ import ( "net" "os" "os/user" + "path" "path/filepath" "strconv" "strings" @@ -202,11 +203,11 @@ type StatOptions struct { // If root and directory are both not specified, the current root directory is // used, and relative names in the globs list are treated as being relative to // the current working directory. -// If root is specified and the current OS supports it, the stat() is performed -// in a chrooted context. If the directory is specified as an absolute path, -// it should either be the root directory or a subdirectory of the root -// directory. Otherwise, the directory is treated as a path relative to the -// root directory. +// If root is specified and the current OS supports it, and the calling process +// has the necessary privileges, the stat() is performed in a chrooted context. +// If the directory is specified as an absolute path, it should either be the +// root directory or a subdirectory of the root directory. Otherwise, the +// directory is treated as a path relative to the root directory. // Relative names in the glob list are treated as being relative to the // directory. func Stat(root string, directory string, options StatOptions, globs []string) ([]*StatsForGlob, error) { @@ -229,18 +230,19 @@ func Stat(root string, directory string, options StatOptions, globs []string) ([ // GetOptions controls parts of Get()'s behavior. type GetOptions struct { - UIDMap, GIDMap []idtools.IDMap // map from hostIDs to containerIDs in the output archive - Excludes []string // contents to pretend don't exist, using the OS-specific path separator - ExpandArchives bool // extract the contents of named items that are archives - ChownDirs *idtools.IDPair // set ownership on directories. no effect on archives being extracted - ChmodDirs *os.FileMode // set permissions on directories. no effect on archives being extracted - ChownFiles *idtools.IDPair // set ownership of files. no effect on archives being extracted - ChmodFiles *os.FileMode // set permissions on files. no effect on archives being extracted - StripSetuidBit bool // strip the setuid bit off of items being copied. no effect on archives being extracted - StripSetgidBit bool // strip the setgid bit off of items being copied. no effect on archives being extracted - StripStickyBit bool // strip the sticky bit off of items being copied. no effect on archives being extracted - StripXattrs bool // don't record extended attributes of items being copied. no effect on archives being extracted - KeepDirectoryNames bool // don't strip the top directory's basename from the paths of items in subdirectories + UIDMap, GIDMap []idtools.IDMap // map from hostIDs to containerIDs in the output archive + Excludes []string // contents to pretend don't exist, using the OS-specific path separator + ExpandArchives bool // extract the contents of named items that are archives + ChownDirs *idtools.IDPair // set ownership on directories. no effect on archives being extracted + ChmodDirs *os.FileMode // set permissions on directories. no effect on archives being extracted + ChownFiles *idtools.IDPair // set ownership of files. no effect on archives being extracted + ChmodFiles *os.FileMode // set permissions on files. no effect on archives being extracted + StripSetuidBit bool // strip the setuid bit off of items being copied. no effect on archives being extracted + StripSetgidBit bool // strip the setgid bit off of items being copied. no effect on archives being extracted + StripStickyBit bool // strip the sticky bit off of items being copied. no effect on archives being extracted + StripXattrs bool // don't record extended attributes of items being copied. no effect on archives being extracted + KeepDirectoryNames bool // don't strip the top directory's basename from the paths of items in subdirectories + Rename map[string]string // rename items with the specified names, or under the specified names } // Get produces an archive containing items that match the specified glob @@ -248,11 +250,11 @@ type GetOptions struct { // If root and directory are both not specified, the current root directory is // used, and relative names in the globs list are treated as being relative to // the current working directory. -// If root is specified and the current OS supports it, the contents are read -// in a chrooted context. If the directory is specified as an absolute path, -// it should either be the root directory or a subdirectory of the root -// directory. Otherwise, the directory is treated as a path relative to the -// root directory. +// If root is specified and the current OS supports it, and the calling process +// has the necessary privileges, the contents are read in a chrooted context. +// If the directory is specified as an absolute path, it should either be the +// root directory or a subdirectory of the root directory. Otherwise, the +// directory is treated as a path relative to the root directory. // Relative names in the glob list are treated as being relative to the // directory. func Get(root string, directory string, options GetOptions, globs []string, bulkWriter io.Writer) error { @@ -278,25 +280,28 @@ func Get(root string, directory string, options GetOptions, globs []string, bulk // PutOptions controls parts of Put()'s behavior. type PutOptions struct { - UIDMap, GIDMap []idtools.IDMap // map from containerIDs to hostIDs when writing contents to disk - DefaultDirOwner *idtools.IDPair // set ownership of implicitly-created directories, default is ChownDirs, or 0:0 if ChownDirs not set - DefaultDirMode *os.FileMode // set permissions on implicitly-created directories, default is ChmodDirs, or 0755 if ChmodDirs not set - ChownDirs *idtools.IDPair // set ownership of newly-created directories - ChmodDirs *os.FileMode // set permissions on newly-created directories - ChownFiles *idtools.IDPair // set ownership of newly-created files - ChmodFiles *os.FileMode // set permissions on newly-created files - StripXattrs bool // don't bother trying to set extended attributes of items being copied - IgnoreXattrErrors bool // ignore any errors encountered when attempting to set extended attributes + UIDMap, GIDMap []idtools.IDMap // map from containerIDs to hostIDs when writing contents to disk + DefaultDirOwner *idtools.IDPair // set ownership of implicitly-created directories, default is ChownDirs, or 0:0 if ChownDirs not set + DefaultDirMode *os.FileMode // set permissions on implicitly-created directories, default is ChmodDirs, or 0755 if ChmodDirs not set + ChownDirs *idtools.IDPair // set ownership of newly-created directories + ChmodDirs *os.FileMode // set permissions on newly-created directories + ChownFiles *idtools.IDPair // set ownership of newly-created files + ChmodFiles *os.FileMode // set permissions on newly-created files + StripXattrs bool // don't bother trying to set extended attributes of items being copied + IgnoreXattrErrors bool // ignore any errors encountered when attempting to set extended attributes + NoOverwriteDirNonDir bool // instead of quietly overwriting directories with non-directories, return an error + Rename map[string]string // rename items with the specified names, or under the specified names } // Put extracts an archive from the bulkReader at the specified directory. // If root and directory are both not specified, the current root directory is // used. -// If root is specified and the current OS supports it, the contents are written -// in a chrooted context. If the directory is specified as an absolute path, -// it should either be the root directory or a subdirectory of the root -// directory. Otherwise, the directory is treated as a path relative to the -// root directory. +// If root is specified and the current OS supports it, and the calling process +// has the necessary privileges, the contents are written in a chrooted +// context. If the directory is specified as an absolute path, it should +// either be the root directory or a subdirectory of the root directory. +// Otherwise, the directory is treated as a path relative to the root +// directory. func Put(root string, directory string, options PutOptions, bulkReader io.Reader) error { req := request{ Request: requestPut, @@ -325,11 +330,12 @@ type MkdirOptions struct { // need to be created will be given the specified ownership and permissions. // If root and directory are both not specified, the current root directory is // used. -// If root is specified and the current OS supports it, the directory is -// created in a chrooted context. If the directory is specified as an absolute -// path, it should either be the root directory or a subdirectory of the root -// directory. Otherwise, the directory is treated as a path relative to the -// root directory. +// If root is specified and the current OS supports it, and the calling process +// has the necessary privileges, the directory is created in a chrooted +// context. If the directory is specified as an absolute path, it should +// either be the root directory or a subdirectory of the root directory. +// Otherwise, the directory is treated as a path relative to the root +// directory. func Mkdir(root string, directory string, options MkdirOptions) error { req := request{ Request: requestMkdir, @@ -547,13 +553,13 @@ func copierWithSubprocess(bulkReader io.Reader, bulkWriter io.Writer, req reques return nil, errors.Wrap(err, step) } if err = encoder.Encode(req); err != nil { - return killAndReturn(err, "error encoding request") + return killAndReturn(err, "error encoding request for copier subprocess") } if err = decoder.Decode(&resp); err != nil { - return killAndReturn(err, "error decoding response") + return killAndReturn(err, "error decoding response from copier subprocess") } if err = encoder.Encode(&request{Request: requestQuit}); err != nil { - return killAndReturn(err, "error encoding request") + return killAndReturn(err, "error encoding request for copier subprocess") } stdinWrite.Close() stdinWrite = nil @@ -626,7 +632,7 @@ func copierMain() { // Read a request. req := new(request) if err := decoder.Decode(req); err != nil { - fmt.Fprintf(os.Stderr, "error decoding request: %v", err) + fmt.Fprintf(os.Stderr, "error decoding request from copier parent process: %v", err) os.Exit(1) } if req.Request == requestQuit { @@ -717,12 +723,12 @@ func copierMain() { } resp, cb, err := copierHandler(bulkReader, bulkWriter, *req) if err != nil { - fmt.Fprintf(os.Stderr, "error handling request %#v: %v", *req, err) + fmt.Fprintf(os.Stderr, "error handling request %#v from copier parent process: %v", *req, err) os.Exit(1) } // Encode the response. if err := encoder.Encode(resp); err != nil { - fmt.Fprintf(os.Stderr, "error encoding response %#v: %v", *req, err) + fmt.Fprintf(os.Stderr, "error encoding response %#v for copier parent process: %v", *req, err) os.Exit(1) } // If there's bulk data to transfer, run the callback to either @@ -1118,6 +1124,34 @@ func copierHandlerGet(bulkWriter io.Writer, req request, pm *fileutils.PatternMa return &response{Stat: statResponse.Stat, Get: getResponse{}}, cb, nil } +func handleRename(rename map[string]string, name string) string { + if rename == nil { + return name + } + // header names always use '/', so use path instead of filepath to manipulate it + if directMapping, ok := rename[name]; ok { + return directMapping + } + prefix, remainder := path.Split(name) + for prefix != "" { + if mappedPrefix, ok := rename[prefix]; ok { + return path.Join(mappedPrefix, remainder) + } + if prefix[len(prefix)-1] == '/' { + if mappedPrefix, ok := rename[prefix[:len(prefix)-1]]; ok { + return path.Join(mappedPrefix, remainder) + } + } + newPrefix, middlePart := path.Split(prefix) + if newPrefix == prefix { + return name + } + prefix = newPrefix + remainder = path.Join(middlePart, remainder) + } + return name +} + func copierHandlerGetOne(srcfi os.FileInfo, symlinkTarget, name, contentPath string, options GetOptions, tw *tar.Writer, hardlinkChecker *util.HardlinkChecker, idMappings *idtools.IDMappings) error { // build the header using the name provided hdr, err := tar.FileInfoHeader(srcfi, symlinkTarget) @@ -1127,6 +1161,9 @@ func copierHandlerGetOne(srcfi os.FileInfo, symlinkTarget, name, contentPath str if name != "" { hdr.Name = filepath.ToSlash(name) } + if options.Rename != nil { + hdr.Name = handleRename(options.Rename, hdr.Name) + } if options.StripSetuidBit { hdr.Mode &^= cISUID } @@ -1164,6 +1201,9 @@ func copierHandlerGetOne(srcfi os.FileInfo, symlinkTarget, name, contentPath str tr := tar.NewReader(rc) hdr, err := tr.Next() for err == nil { + if options.Rename != nil { + hdr.Name = handleRename(options.Rename, hdr.Name) + } if err = tw.WriteHeader(hdr); err != nil { return errors.Wrapf(err, "error writing tar header from %q to pipe", contentPath) } @@ -1311,8 +1351,13 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM createFile := func(path string, tr *tar.Reader) (int64, error) { f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC|os.O_EXCL, 0600) if err != nil && os.IsExist(err) { - if err = os.Remove(path); err != nil { - return 0, errors.Wrapf(err, "copier: put: error removing file to be overwritten %q", path) + if req.PutOptions.NoOverwriteDirNonDir { + if st, err2 := os.Lstat(path); err2 == nil && st.IsDir() { + return 0, errors.Wrapf(err, "copier: put: error creating file at %q", path) + } + } + if err = os.RemoveAll(path); err != nil { + return 0, errors.Wrapf(err, "copier: put: error removing item to be overwritten %q", path) } f, err = os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC|os.O_EXCL, 0600) } @@ -1360,6 +1405,14 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM tr := tar.NewReader(bulkReader) hdr, err := tr.Next() for err == nil { + if len(hdr.Name) == 0 { + // no name -> ignore the entry + hdr, err = tr.Next() + continue + } + if req.PutOptions.Rename != nil { + hdr.Name = handleRename(req.PutOptions.Rename, hdr.Name) + } // figure out who should own this new item if idMappings != nil && !idMappings.Empty() { containerPair := idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid} @@ -1412,35 +1465,70 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM } case tar.TypeLink: var linkTarget string + if req.PutOptions.Rename != nil { + hdr.Linkname = handleRename(req.PutOptions.Rename, hdr.Linkname) + } if linkTarget, err = resolvePath(targetDirectory, filepath.Join(req.Root, filepath.FromSlash(hdr.Linkname)), nil); err != nil { return errors.Errorf("error resolving hardlink target path %q under root %q", hdr.Linkname, req.Root) } if err = os.Link(linkTarget, path); err != nil && os.IsExist(err) { + if req.PutOptions.NoOverwriteDirNonDir { + if st, err := os.Lstat(path); err == nil && st.IsDir() { + break + } + } if err = os.Remove(path); err == nil { err = os.Link(linkTarget, path) } } case tar.TypeSymlink: + // if req.PutOptions.Rename != nil { + // todo: the general solution requires resolving to an absolute path, handling + // renaming, and then possibly converting back to a relative symlink + // } if err = os.Symlink(filepath.FromSlash(hdr.Linkname), filepath.FromSlash(path)); err != nil && os.IsExist(err) { + if req.PutOptions.NoOverwriteDirNonDir { + if st, err := os.Lstat(path); err == nil && st.IsDir() { + break + } + } if err = os.Remove(path); err == nil { err = os.Symlink(filepath.FromSlash(hdr.Linkname), filepath.FromSlash(path)) } } case tar.TypeChar: if err = mknod(path, chrMode(0600), int(mkdev(devMajor, devMinor))); err != nil && os.IsExist(err) { + if req.PutOptions.NoOverwriteDirNonDir { + if st, err := os.Lstat(path); err == nil && st.IsDir() { + break + } + } if err = os.Remove(path); err == nil { err = mknod(path, chrMode(0600), int(mkdev(devMajor, devMinor))) } } case tar.TypeBlock: if err = mknod(path, blkMode(0600), int(mkdev(devMajor, devMinor))); err != nil && os.IsExist(err) { + if req.PutOptions.NoOverwriteDirNonDir { + if st, err := os.Lstat(path); err == nil && st.IsDir() { + break + } + } if err = os.Remove(path); err == nil { err = mknod(path, blkMode(0600), int(mkdev(devMajor, devMinor))) } } case tar.TypeDir: if err = os.Mkdir(path, 0700); err != nil && os.IsExist(err) { - err = nil + var st os.FileInfo + if st, err = os.Stat(path); err == nil && !st.IsDir() { + // it's not a directory, so remove it and mkdir + if err = os.Remove(path); err == nil { + err = os.Mkdir(path, 0700) + } + } + // either we removed it and retried, or it was a directory, + // in which case we want to just add the new stuff under it } // make a note of the directory's times. we // might create items under it, which will @@ -1453,6 +1541,11 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM }) case tar.TypeFifo: if err = mkfifo(path, 0600); err != nil && os.IsExist(err) { + if req.PutOptions.NoOverwriteDirNonDir { + if st, err := os.Lstat(path); err == nil && st.IsDir() { + break + } + } if err = os.Remove(path); err == nil { err = mkfifo(path, 0600) } diff --git a/vendor/github.com/containers/buildah/copier/syscall_unix.go b/vendor/github.com/containers/buildah/copier/syscall_unix.go index 55f2f368a..2c2806d0a 100644 --- a/vendor/github.com/containers/buildah/copier/syscall_unix.go +++ b/vendor/github.com/containers/buildah/copier/syscall_unix.go @@ -10,7 +10,7 @@ import ( "golang.org/x/sys/unix" ) -var canChroot = true +var canChroot = os.Getuid() == 0 func chroot(root string) (bool, error) { if canChroot { diff --git a/vendor/github.com/containers/buildah/go.mod b/vendor/github.com/containers/buildah/go.mod index 0d795f6b6..ea9a956be 100644 --- a/vendor/github.com/containers/buildah/go.mod +++ b/vendor/github.com/containers/buildah/go.mod @@ -5,10 +5,10 @@ go 1.12 require ( github.com/containerd/containerd v1.4.1 // indirect github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784 - github.com/containers/common v0.29.0 + github.com/containers/common v0.31.0 github.com/containers/image/v5 v5.8.1 github.com/containers/ocicrypt v1.0.3 - github.com/containers/storage v1.24.1 + github.com/containers/storage v1.24.3 github.com/docker/distribution v2.7.1+incompatible github.com/docker/go-units v0.4.0 github.com/docker/libnetwork v0.8.0-dev.2.0.20190625141545-5a177b73e316 @@ -21,7 +21,7 @@ require ( github.com/moby/sys/mount v0.1.1 // indirect github.com/moby/term v0.0.0-20200915141129-7f0af18e79f2 // indirect github.com/onsi/ginkgo v1.14.2 - github.com/onsi/gomega v1.10.3 + github.com/onsi/gomega v1.10.4 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6 github.com/opencontainers/runc v1.0.0-rc91 diff --git a/vendor/github.com/containers/buildah/go.sum b/vendor/github.com/containers/buildah/go.sum index e3413bc68..c2082c5ef 100644 --- a/vendor/github.com/containers/buildah/go.sum +++ b/vendor/github.com/containers/buildah/go.sum @@ -73,8 +73,8 @@ github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDG github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784 h1:rqUVLD8I859xRgUx/WMC3v7QAFqbLKZbs+0kqYboRJc= github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containers/common v0.29.0 h1:hTMC+urdkk5bKfhL/OgCixIX5xjJgQ2l2jPG745ECFQ= -github.com/containers/common v0.29.0/go.mod h1:yT4GTUHsKRmpaDb+mecXRnIMre7W3ZgwXqaYMywXlaA= +github.com/containers/common v0.31.0 h1:SRnjfoqbjfaojpY9YJq9JBPEslwB5hoXJbaE+5zMFwM= +github.com/containers/common v0.31.0/go.mod h1:yT4GTUHsKRmpaDb+mecXRnIMre7W3ZgwXqaYMywXlaA= github.com/containers/image/v5 v5.8.1 h1:aHW8a/Kd0dTJ7PTL/fc6y12sJqHxWgqilu+XyHfjD8Q= github.com/containers/image/v5 v5.8.1/go.mod h1:blOEFd/iFdeyh891ByhCVUc+xAcaI3gBegXECwz9UbQ= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE= @@ -84,6 +84,8 @@ github.com/containers/ocicrypt v1.0.3/go.mod h1:CUBa+8MRNL/VkpxYIpaMtgn1WgXGyvPQ github.com/containers/storage v1.23.7/go.mod h1:cUT2zHjtx+WlVri30obWmM2gpqpi8jfPsmIzP1TVpEI= github.com/containers/storage v1.24.1 h1:1+f8fy6ly35c8SLet5jzZ8t0WJJs5+xSpfMAYw0R3kc= github.com/containers/storage v1.24.1/go.mod h1:0xJL06Dmd+ZYXIUdnBUPN0JnhHGgwMkLvnnAonJfWJU= +github.com/containers/storage v1.24.3 h1:8UB4S62l4hrU6Yw3dbsLCJtLg7Ofo39IN2HdckBIX4E= +github.com/containers/storage v1.24.3/go.mod h1:0xJL06Dmd+ZYXIUdnBUPN0JnhHGgwMkLvnnAonJfWJU= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -303,6 +305,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= +github.com/onsi/gomega v1.10.4 h1:NiTx7EEvBzu9sFOD1zORteLSt3o8gnlvZZwSE9TnY9U= +github.com/onsi/gomega v1.10.4/go.mod h1:g/HbgYopi++010VEqkFgJHKC09uJiW9UkXvMUuKHUCQ= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -482,6 +486,8 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 h1:wBouT66WTYFXdxfVdz9sVWARVd/2vfGcmI45D2gj45M= golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= diff --git a/vendor/github.com/containers/buildah/install.md b/vendor/github.com/containers/buildah/install.md index 119315d1f..90e844c3e 100644 --- a/vendor/github.com/containers/buildah/install.md +++ b/vendor/github.com/containers/buildah/install.md @@ -69,15 +69,35 @@ sudo apt-get update sudo apt-get -y install buildah ``` -The [Kubic project](https://build.opensuse.org/project/show/devel:kubic:libcontainers:stable) -provides packages for Debian 10. The Kubic packages for Debian Testing/Bullseye and Debian Unstable/Sid -have been discontinued to avoid -[conflicts](https://github.com/containers/buildah/issues/2797) with the official packages. +If you would prefer newer (though not as well-tested) packages, +the [Kubic project](https://build.opensuse.org/package/show/devel:kubic:libcontainers:stable/buildah) +provides packages for Debian 10 and newer. The packages in Kubic project repos are more frequently +updated than the one in Debian's official repositories, due to how Debian works. +The build sources for the Kubic packages can be found [here](https://gitlab.com/rhcontainerbot/buildah/-/tree/debian/debian). + +CAUTION: On Debian 11 and newer, including Testing and Sid/Unstable, we highly recommend you use Buildah, Podman and Skopeo ONLY from EITHER the Kubic repo +OR the official Debian repos. Mixing and matching may lead to unpredictable situations including installation conflicts. + +```bash +# Debian 10 +echo 'deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_10/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list +curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_10/Release.key | sudo apt-key add - +sudo apt-get update +sudo apt-get -y install buildah + +# Debian Testing +echo 'deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_Testing/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list +curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_Testing/Release.key | sudo apt-key add - +sudo apt-get update +sudo apt-get -y install buildah + +# Debian Sid/Unstable +echo 'deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_Unstable/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list +curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_Unstable/Release.key | sudo apt-key add - +sudo apt-get update +sudo apt-get -y install buildah +``` -Caution: If you upgrade from Debian 10 to Testing/Bullseye or -Unstable/Sid you would likely end up downgrading Buildah because the version in -OBS is more frequently updated than the one in Debian's official repositories, -due to how Debian works. ### [Fedora](https://www.fedoraproject.org) @@ -125,7 +145,8 @@ sudo yum -y install buildah #### [Raspberry Pi OS armhf (ex Raspbian)](https://www.raspberrypi.org/downloads/raspberry-pi-os/) -The Kubic project provides packages for Raspbian 10. +The [Kubic project](https://build.opensuse.org/package/show/devel:kubic:libcontainers:stable/buildah) provides +packages for Raspbian 10. ```bash # Raspbian 10 @@ -135,6 +156,8 @@ sudo apt-get update -qq sudo apt-get -qq -y install buildah ``` +The build sources for the Kubic packages can be found [here](https://gitlab.com/rhcontainerbot/buildah/-/tree/debian/debian). + #### [Raspberry Pi OS arm64 (beta)](https://downloads.raspberrypi.org/raspios_arm64/images/) Raspberry Pi OS use the standard Debian's repositories, @@ -160,7 +183,16 @@ sudo apt-get -y update sudo apt-get -y install buildah ``` -The [Kubic project](https://build.opensuse.org/package/show/devel:kubic:libcontainers:stable/buildah) provides packages for some older but supported Ubuntu versions (it should also work with direct derivatives like Pop!\_OS). +If you would prefer newer (though not as well-tested) packages, +the [Kubic project](https://build.opensuse.org/package/show/devel:kubic:libcontainers:stable/buildah) +provides packages for active Ubuntu releases 18.04 and newer (it should also work with direct derivatives like Pop!\_OS). +The packages in Kubic project repos are more frequently updated than the one in Ubuntu's official repositories, due to how Debian/Ubuntu works. +Checkout the Kubic project page for a list of supported Ubuntu version and architecture combinations. +The build sources for the Kubic packages can be found [here](https://gitlab.com/rhcontainerbot/buildah/-/tree/debian/debian). + +CAUTION: On Ubuntu 20.10 and newer, we highly recommend you use Buildah, Podman and Skopeo ONLY from EITHER the Kubic repo +OR the official Ubuntu repos. Mixing and matching may lead to unpredictable situations including installation conflicts. + ```bash . /etc/os-release @@ -473,6 +505,13 @@ cat /etc/containers/policy.json } ``` +## Debug with Delve and the like + +To make a source debug build without optimizations use `DEBUG=1`, like: +``` +make all DEBUG=1 +``` + ## Vendoring Buildah uses Go Modules for vendoring purposes. If you need to update or add a vendored package into Buildah, please follow this procedure: diff --git a/vendor/github.com/containers/buildah/new.go b/vendor/github.com/containers/buildah/new.go index c1abb1cdb..aab17fea2 100644 --- a/vendor/github.com/containers/buildah/new.go +++ b/vendor/github.com/containers/buildah/new.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "math/rand" - "os" "strings" "github.com/containers/buildah/util" @@ -127,27 +126,10 @@ func resolveLocalImage(systemContext *types.SystemContext, store storage.Store, return nil, "", nil, nil } -// getShortNameMode looks up the `CONTAINERS_SHORT_NAME_ALIASING` environment -// variable. If it's "on", return `nil` to use the defaults from -// containers/image and the registries.conf files on the system. If it's -// "off", empty or unset, return types.ShortNameModeDisabled to turn off -// short-name aliasing by default. -// -// TODO: remove this function once we want to default to short-name aliasing. -func getShortNameMode() *types.ShortNameMode { - env := os.Getenv("CONTAINERS_SHORT_NAME_ALIASING") - if strings.ToLower(env) == "on" { - return nil // default to whatever registries.conf and c/image decide - } - mode := types.ShortNameModeDisabled - return &mode -} - func resolveImage(ctx context.Context, systemContext *types.SystemContext, store storage.Store, options BuilderOptions) (types.ImageReference, string, *storage.Image, error) { if systemContext == nil { systemContext = &types.SystemContext{} } - systemContext.ShortNameMode = getShortNameMode() fromImage := options.FromImage // If the image name includes a transport we can use it as it. Special diff --git a/vendor/github.com/containers/buildah/pkg/cli/common.go b/vendor/github.com/containers/buildah/pkg/cli/common.go index 123548d97..1e2db58c4 100644 --- a/vendor/github.com/containers/buildah/pkg/cli/common.go +++ b/vendor/github.com/containers/buildah/pkg/cli/common.go @@ -17,6 +17,7 @@ import ( "github.com/containers/common/pkg/auth" commonComp "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/config" + "github.com/containers/storage/pkg/unshare" "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/spf13/pflag" @@ -366,6 +367,9 @@ func DefaultIsolation() string { if isolation != "" { return isolation } + if unshare.IsRootless() { + return "rootless" + } return buildah.OCI } diff --git a/vendor/github.com/containers/buildah/pkg/parse/parse.go b/vendor/github.com/containers/buildah/pkg/parse/parse.go index fb348b252..f256e6c2a 100644 --- a/vendor/github.com/containers/buildah/pkg/parse/parse.go +++ b/vendor/github.com/containers/buildah/pkg/parse/parse.go @@ -486,7 +486,7 @@ func ValidateVolumeCtrDir(ctrDir string) error { // ValidateVolumeOpts validates a volume's options func ValidateVolumeOpts(options []string) ([]string, error) { - var foundRootPropagation, foundRWRO, foundLabelChange, bindType, foundExec, foundDev, foundSuid int + var foundRootPropagation, foundRWRO, foundLabelChange, bindType, foundExec, foundDev, foundSuid, foundChown int finalOpts := make([]string, 0, len(options)) for _, opt := range options { switch opt { @@ -515,6 +515,11 @@ func ValidateVolumeOpts(options []string) ([]string, error) { if foundLabelChange > 1 { return nil, errors.Errorf("invalid options %q, can only specify 1 'z', 'Z', or 'O' option", strings.Join(options, ", ")) } + case "U": + foundChown++ + if foundChown > 1 { + return nil, errors.Errorf("invalid options %q, can only specify 1 'U' option", strings.Join(options, ", ")) + } case "private", "rprivate", "shared", "rshared", "slave", "rslave", "unbindable", "runbindable": foundRootPropagation++ if foundRootPropagation > 1 { @@ -878,20 +883,12 @@ func NamespaceOptions(c *cobra.Command) (namespaceOptions buildah.NamespaceOptio logrus.Debugf("setting network to disabled") break } - if !filepath.IsAbs(how) { - options.AddOrReplace(buildah.NamespaceOption{ - Name: what, - Path: how, - }) - policy = buildah.NetworkEnabled - logrus.Debugf("setting network configuration to %q", how) - break - } } how = strings.TrimPrefix(how, "ns:") if _, err := os.Stat(how); err != nil { - return nil, buildah.NetworkDefault, errors.Wrapf(err, "error checking for %s namespace at %q", what, how) + return nil, buildah.NetworkDefault, errors.Wrapf(err, "error checking for %s namespace", what) } + policy = buildah.NetworkEnabled logrus.Debugf("setting %q namespace to %q", what, how) options.AddOrReplace(buildah.NamespaceOption{ Name: what, diff --git a/vendor/github.com/containers/buildah/run_linux.go b/vendor/github.com/containers/buildah/run_linux.go index d20d39423..dc2f5c5ad 100644 --- a/vendor/github.com/containers/buildah/run_linux.go +++ b/vendor/github.com/containers/buildah/run_linux.go @@ -506,8 +506,14 @@ func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, bundlePath st return err } + // Get host UID and GID of the container process. + processUID, processGID, err := util.GetHostIDs(spec.Linux.UIDMappings, spec.Linux.GIDMappings, spec.Process.User.UID, spec.Process.User.GID) + if err != nil { + return err + } + // Get the list of explicitly-specified volume mounts. - volumes, err := b.runSetupVolumeMounts(spec.Linux.MountLabel, volumeMounts, optionMounts, int(rootUID), int(rootGID)) + volumes, err := b.runSetupVolumeMounts(spec.Linux.MountLabel, volumeMounts, optionMounts, int(rootUID), int(rootGID), int(processUID), int(processGID)) if err != nil { return err } @@ -1687,7 +1693,7 @@ func (b *Builder) cleanupTempVolumes() { } } -func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string, optionMounts []specs.Mount, rootUID, rootGID int) (mounts []specs.Mount, Err error) { +func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string, optionMounts []specs.Mount, rootUID, rootGID, processUID, processGID int) (mounts []specs.Mount, Err error) { // Make sure the overlay directory is clean before running containerDir, err := b.store.ContainerDirectory(b.ContainerID) @@ -1699,7 +1705,7 @@ func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string, } parseMount := func(mountType, host, container string, options []string) (specs.Mount, error) { - var foundrw, foundro, foundz, foundZ, foundO bool + var foundrw, foundro, foundz, foundZ, foundO, foundU bool var rootProp string for _, opt := range options { switch opt { @@ -1713,6 +1719,8 @@ func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string, foundZ = true case "O": foundO = true + case "U": + foundU = true case "private", "rprivate", "slave", "rslave", "shared", "rshared": rootProp = opt } @@ -1730,6 +1738,11 @@ func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string, return specs.Mount{}, err } } + if foundU { + if err := chownSourceVolume(host, processUID, processGID); err != nil { + return specs.Mount{}, err + } + } if foundO { containerDir, err := b.store.ContainerDirectory(b.ContainerID) if err != nil { @@ -1746,6 +1759,14 @@ func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string, b.TempVolumes[contentDir] = true } + + // If chown true, add correct ownership to the overlay temp directories. + if foundU { + if err := chownSourceVolume(contentDir, processUID, processGID); err != nil { + return specs.Mount{}, err + } + } + return overlayMount, err } if rootProp == "" { @@ -1789,6 +1810,39 @@ func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string, return mounts, nil } +// chownSourceVolume changes the ownership of a volume source directory or file within the host. +func chownSourceVolume(path string, UID, GID int) error { + fi, err := os.Lstat(path) + if err != nil { + // Skip if path does not exist + if os.IsNotExist(err) { + logrus.Debugf("error returning file info of %q: %v", path, err) + return nil + } + return err + } + + currentUID := int(fi.Sys().(*syscall.Stat_t).Uid) + currentGID := int(fi.Sys().(*syscall.Stat_t).Gid) + + if UID != currentUID || GID != currentGID { + err := filepath.Walk(path, func(filePath string, f os.FileInfo, err error) error { + return os.Lchown(filePath, UID, GID) + }) + + if err != nil { + // Skip if path does not exist + if os.IsNotExist(err) { + logrus.Debugf("error changing the uid and gid of %q: %v", path, err) + return nil + } + return err + } + } + + return nil +} + func setupMaskedPaths(g *generate.Generator) { for _, mp := range []string{ "/proc/acpi", diff --git a/vendor/github.com/containers/buildah/troubleshooting.md b/vendor/github.com/containers/buildah/troubleshooting.md index afd9c640a..02631ae13 100644 --- a/vendor/github.com/containers/buildah/troubleshooting.md +++ b/vendor/github.com/containers/buildah/troubleshooting.md @@ -154,5 +154,5 @@ Choose one of the following: * Complete the build operation as a privileged user. * Install and configure fuse-overlayfs. * Install the fuse-overlayfs package for your Linux Distribution. - * Add `mount_program = "/usr/bin/fuse-overlayfs` under `[storage.options]` in your `~/.config/containers/storage.conf` file. + * Add `mount_program = "/usr/bin/fuse-overlayfs"` under `[storage.options]` in your `~/.config/containers/storage.conf` file. --- diff --git a/vendor/github.com/containers/common/pkg/config/containers.conf b/vendor/github.com/containers/common/pkg/config/containers.conf index 483727da0..0587469b2 100644 --- a/vendor/github.com/containers/common/pkg/config/containers.conf +++ b/vendor/github.com/containers/common/pkg/config/containers.conf @@ -380,7 +380,7 @@ default_sysctls = [ # Directory for temporary files. Must be tmpfs (wiped after reboot) # -# tmp_dir = "/var/run/libpod" +# tmp_dir = "/run/libpod" # Directory for libpod named volumes. # By default, this will be configured relative to where containers/storage diff --git a/vendor/github.com/containers/common/pkg/config/default.go b/vendor/github.com/containers/common/pkg/config/default.go index e3a7a8e76..6b7aee987 100644 --- a/vendor/github.com/containers/common/pkg/config/default.go +++ b/vendor/github.com/containers/common/pkg/config/default.go @@ -320,7 +320,7 @@ func defaultConfigFromMemory() (*EngineConfig, error) { func defaultTmpDir() (string, error) { if !unshare.IsRootless() { - return "/var/run/libpod", nil + return "/run/libpod", nil } runtimeDir, err := getRuntimeDir() diff --git a/vendor/github.com/containers/common/pkg/seccomp/seccomp_unsupported.go b/vendor/github.com/containers/common/pkg/seccomp/seccomp_unsupported.go index 84a4c6ed5..8b23ee2c0 100644 --- a/vendor/github.com/containers/common/pkg/seccomp/seccomp_unsupported.go +++ b/vendor/github.com/containers/common/pkg/seccomp/seccomp_unsupported.go @@ -1,10 +1,10 @@ -// +build !seccomp +// +build !linux !seccomp // SPDX-License-Identifier: Apache-2.0 // Copyright 2013-2018 Docker, Inc. -package seccomp +package seccomp import ( "errors" @@ -38,3 +38,9 @@ func LoadProfileFromConfig(config *Seccomp, specgen *specs.Spec) (*specs.LinuxSe func IsEnabled() bool { return false } + +// IsSupported returns true if the system has been configured to support +// seccomp. +func IsSupported() bool { + return false +} diff --git a/vendor/github.com/containers/common/pkg/seccomp/supported.go b/vendor/github.com/containers/common/pkg/seccomp/supported.go index 1177ef630..e04324c8a 100644 --- a/vendor/github.com/containers/common/pkg/seccomp/supported.go +++ b/vendor/github.com/containers/common/pkg/seccomp/supported.go @@ -1,4 +1,4 @@ -// +build !windows +// +build linux,seccomp package seccomp diff --git a/vendor/github.com/containers/common/version/version.go b/vendor/github.com/containers/common/version/version.go index 3d671171f..4366848ea 100644 --- a/vendor/github.com/containers/common/version/version.go +++ b/vendor/github.com/containers/common/version/version.go @@ -1,4 +1,4 @@ package version // Version is the version of the build. -const Version = "0.31.2-dev" +const Version = "0.31.2" diff --git a/vendor/modules.txt b/vendor/modules.txt index 26b782b85..ceb9f6750 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -67,7 +67,7 @@ github.com/containernetworking/plugins/pkg/utils/hwaddr github.com/containernetworking/plugins/pkg/utils/sysctl github.com/containernetworking/plugins/plugins/ipam/host-local/backend github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator -# github.com/containers/buildah v1.18.1-0.20201125084616-dd26b137459c +# github.com/containers/buildah v1.18.1-0.20201217112226-67470615779c github.com/containers/buildah github.com/containers/buildah/bind github.com/containers/buildah/chroot @@ -86,7 +86,7 @@ github.com/containers/buildah/pkg/parse github.com/containers/buildah/pkg/rusage github.com/containers/buildah/pkg/supplemented github.com/containers/buildah/util -# github.com/containers/common v0.31.1 +# github.com/containers/common v0.31.2 github.com/containers/common/pkg/apparmor github.com/containers/common/pkg/apparmor/internal/supported github.com/containers/common/pkg/auth @@ -719,7 +719,7 @@ gopkg.in/yaml.v3 # k8s.io/api v0.0.0-20190620084959-7cf5895f2711 k8s.io/api/apps/v1 k8s.io/api/core/v1 -# k8s.io/apimachinery v0.20.0 +# k8s.io/apimachinery v0.20.1 k8s.io/apimachinery/pkg/api/errors k8s.io/apimachinery/pkg/api/resource k8s.io/apimachinery/pkg/apis/meta/v1 |