diff options
303 files changed, 3192 insertions, 1594 deletions
diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml new file mode 100644 index 000000000..e00b8465c --- /dev/null +++ b/.github/workflows/pr-title.yml @@ -0,0 +1,19 @@ +# Upstream: github.com/tzkhan/pr-update-action + +name: "PR title check" + +on: + pull_request_target: + branches: + - "!master" # causes errors; reason unknown + +jobs: + update_pr: + runs-on: ubuntu-latest + steps: + - uses: tzkhan/pr-update-action@v2 + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" + base-branch-regex: '^(?!master).*$' + title-template: '[%basebranch%]' + title-prefix-space: true diff --git a/cmd/podman/auto-update.go b/cmd/podman/auto-update.go index 677266c83..76bff0c70 100644 --- a/cmd/podman/auto-update.go +++ b/cmd/podman/auto-update.go @@ -18,9 +18,9 @@ var ( Auto-update policies are specified with the "io.containers.autoupdate" label. Containers are expected to run in systemd units created with "podman-generate-systemd --new", or similar units that create new containers in order to run the updated images. - Note that this command is experimental. Please refer to the podman-auto-update(1) man page for details.` + Please refer to the podman-auto-update(1) man page for details.` autoUpdateCommand = &cobra.Command{ - Use: "auto-update [flags]", + Use: "auto-update [options]", Short: "Auto update containers according to their auto-update policy", Long: autoUpdateDescription, RunE: autoUpdate, diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go index 0ec422313..60f4e526c 100644 --- a/cmd/podman/common/create.go +++ b/cmd/podman/common/create.go @@ -115,16 +115,6 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { "cpuset-mems", "", "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.", ) - createFlags.BoolVarP( - &cf.Detach, - "detach", "d", false, - "Run container in background and print container ID", - ) - createFlags.StringVar( - &cf.DetachKeys, - "detach-keys", containerConfig.DetachKeys(), - "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-cf`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`", - ) createFlags.StringSliceVar( &cf.Devices, "device", containerConfig.Devices(), diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index 83a25f4ab..05bb9de13 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -1,6 +1,15 @@ package common -import "github.com/containers/podman/v2/pkg/domain/entities" +import ( + "fmt" + "net" + "strconv" + "strings" + + "github.com/containers/podman/v2/pkg/api/handlers" + "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/specgen" +) type ContainerCLIOpts struct { Annotation []string @@ -23,8 +32,6 @@ type ContainerCLIOpts struct { CPUS float64 CPUSetCPUs string CPUSetMems string - Detach bool - DetachKeys string Devices []string DeviceCGroupRule []string DeviceReadBPs []string @@ -111,3 +118,305 @@ type ContainerCLIOpts struct { CgroupConf []string } + +func stringMaptoArray(m map[string]string) []string { + a := make([]string, 0, len(m)) + for k, v := range m { + a = append(a, fmt.Sprintf("%s=%s", k, v)) + } + return a +} + +// ContainerCreateToContainerCLIOpts converts a compat input struct to cliopts so it can be converted to +// a specgen spec. +func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig) (*ContainerCLIOpts, []string, error) { + var ( + capAdd []string + cappDrop []string + entrypoint string + init bool + specPorts []specgen.PortMapping + ) + + if cc.HostConfig.Init != nil { + init = *cc.HostConfig.Init + } + + // Iterate devices and convert back to string + devices := make([]string, 0, len(cc.HostConfig.Devices)) + for _, dev := range cc.HostConfig.Devices { + devices = append(devices, fmt.Sprintf("%s:%s:%s", dev.PathOnHost, dev.PathInContainer, dev.CgroupPermissions)) + } + + // iterate blkreaddevicebps + readBps := make([]string, 0, len(cc.HostConfig.BlkioDeviceReadBps)) + for _, dev := range cc.HostConfig.BlkioDeviceReadBps { + readBps = append(readBps, dev.String()) + } + + // iterate blkreaddeviceiops + readIops := make([]string, 0, len(cc.HostConfig.BlkioDeviceReadIOps)) + for _, dev := range cc.HostConfig.BlkioDeviceReadIOps { + readIops = append(readIops, dev.String()) + } + + // iterate blkwritedevicebps + writeBps := make([]string, 0, len(cc.HostConfig.BlkioDeviceWriteBps)) + for _, dev := range cc.HostConfig.BlkioDeviceWriteBps { + writeBps = append(writeBps, dev.String()) + } + + // iterate blkwritedeviceiops + writeIops := make([]string, 0, len(cc.HostConfig.BlkioDeviceWriteIOps)) + for _, dev := range cc.HostConfig.BlkioDeviceWriteIOps { + writeIops = append(writeIops, dev.String()) + } + + // entrypoint + // can be a string or slice. if it is a slice, we need to + // marshall it to json; otherwise it should just be the string + // value + if len(cc.Config.Entrypoint) > 0 { + entrypoint = cc.Config.Entrypoint[0] + if len(cc.Config.Entrypoint) > 1 { + b, err := json.Marshal(cc.Config.Entrypoint) + if err != nil { + return nil, nil, err + } + entrypoint = string(b) + } + } + + // expose ports + expose := make([]string, 0, len(cc.Config.ExposedPorts)) + for p := range cc.Config.ExposedPorts { + expose = append(expose, fmt.Sprintf("%s/%s", p.Port(), p.Proto())) + } + + // mounts type=tmpfs/bind,source=,dest=,opt=val + // TODO options + mounts := make([]string, 0, len(cc.HostConfig.Mounts)) + for _, m := range cc.HostConfig.Mounts { + mount := fmt.Sprintf("type=%s", m.Type) + if len(m.Source) > 0 { + mount += fmt.Sprintf("source=%s", m.Source) + } + if len(m.Target) > 0 { + mount += fmt.Sprintf("dest=%s", m.Target) + } + mounts = append(mounts, mount) + } + + //volumes + volumes := make([]string, 0, len(cc.Config.Volumes)) + for v := range cc.Config.Volumes { + volumes = append(volumes, v) + } + + // dns + dns := make([]net.IP, 0, len(cc.HostConfig.DNS)) + for _, d := range cc.HostConfig.DNS { + dns = append(dns, net.ParseIP(d)) + } + + // publish + for port, pbs := range cc.HostConfig.PortBindings { + for _, pb := range pbs { + hostport, err := strconv.Atoi(pb.HostPort) + if err != nil { + return nil, nil, err + } + tmpPort := specgen.PortMapping{ + HostIP: pb.HostIP, + ContainerPort: uint16(port.Int()), + HostPort: uint16(hostport), + Range: 0, + Protocol: port.Proto(), + } + specPorts = append(specPorts, tmpPort) + } + } + + // network names + endpointsConfig := cc.NetworkingConfig.EndpointsConfig + cniNetworks := make([]string, 0, len(endpointsConfig)) + for netName := range endpointsConfig { + cniNetworks = append(cniNetworks, netName) + } + + // netMode + nsmode, _, err := specgen.ParseNetworkNamespace(cc.HostConfig.NetworkMode.NetworkName()) + if err != nil { + return nil, nil, err + } + + netNS := specgen.Namespace{ + NSMode: nsmode.NSMode, + Value: nsmode.Value, + } + + // network + // Note: we cannot emulate compat exactly here. we only allow specifics of networks to be + // defined when there is only one network. + netInfo := entities.NetOptions{ + AddHosts: cc.HostConfig.ExtraHosts, + CNINetworks: cniNetworks, + DNSOptions: cc.HostConfig.DNSOptions, + DNSSearch: cc.HostConfig.DNSSearch, + DNSServers: dns, + Network: netNS, + PublishPorts: specPorts, + } + + // static IP and MAC + if len(endpointsConfig) == 1 { + for _, ep := range endpointsConfig { + // if IP address is provided + if len(ep.IPAddress) > 0 { + staticIP := net.ParseIP(ep.IPAddress) + netInfo.StaticIP = &staticIP + } + // If MAC address is provided + if len(ep.MacAddress) > 0 { + staticMac, err := net.ParseMAC(ep.MacAddress) + if err != nil { + return nil, nil, err + } + netInfo.StaticMAC = &staticMac + } + break + } + } + + // Note: several options here are marked as "don't need". this is based + // on speculation by Matt and I. We think that these come into play later + // like with start. We believe this is just a difference in podman/compat + cliOpts := ContainerCLIOpts{ + //Attach: nil, // dont need? + Authfile: "", + CapAdd: append(capAdd, cc.HostConfig.CapAdd...), + CapDrop: append(cappDrop, cc.HostConfig.CapDrop...), + CGroupParent: cc.HostConfig.CgroupParent, + CIDFile: cc.HostConfig.ContainerIDFile, + CPUPeriod: uint64(cc.HostConfig.CPUPeriod), + CPUQuota: cc.HostConfig.CPUQuota, + CPURTPeriod: uint64(cc.HostConfig.CPURealtimePeriod), + CPURTRuntime: cc.HostConfig.CPURealtimeRuntime, + CPUShares: uint64(cc.HostConfig.CPUShares), + //CPUS: 0, // dont need? + CPUSetCPUs: cc.HostConfig.CpusetCpus, + CPUSetMems: cc.HostConfig.CpusetMems, + //Detach: false, // dont need + //DetachKeys: "", // dont need + Devices: devices, + DeviceCGroupRule: nil, + DeviceReadBPs: readBps, + DeviceReadIOPs: readIops, + DeviceWriteBPs: writeBps, + DeviceWriteIOPs: writeIops, + Entrypoint: &entrypoint, + Env: cc.Config.Env, + Expose: expose, + GroupAdd: cc.HostConfig.GroupAdd, + Hostname: cc.Config.Hostname, + ImageVolume: "bind", + Init: init, + Interactive: cc.Config.OpenStdin, + IPC: string(cc.HostConfig.IpcMode), + Label: stringMaptoArray(cc.Config.Labels), + LogDriver: cc.HostConfig.LogConfig.Type, + LogOptions: stringMaptoArray(cc.HostConfig.LogConfig.Config), + Name: cc.Name, + OOMScoreAdj: cc.HostConfig.OomScoreAdj, + OverrideArch: "", + OverrideOS: "", + OverrideVariant: "", + PID: string(cc.HostConfig.PidMode), + PIDsLimit: cc.HostConfig.PidsLimit, + Privileged: cc.HostConfig.Privileged, + PublishAll: cc.HostConfig.PublishAllPorts, + Quiet: false, + ReadOnly: cc.HostConfig.ReadonlyRootfs, + ReadOnlyTmpFS: true, // podman default + Rm: cc.HostConfig.AutoRemove, + SecurityOpt: cc.HostConfig.SecurityOpt, + StopSignal: cc.Config.StopSignal, + StoreageOpt: stringMaptoArray(cc.HostConfig.StorageOpt), + Sysctl: stringMaptoArray(cc.HostConfig.Sysctls), + Systemd: "true", // podman default + TmpFS: stringMaptoArray(cc.HostConfig.Tmpfs), + TTY: cc.Config.Tty, + //Ulimit: cc.HostConfig.Ulimits, // ask dan, no documented format + Ulimit: []string{"nproc=4194304:4194304"}, + User: cc.Config.User, + UserNS: string(cc.HostConfig.UsernsMode), + UTS: string(cc.HostConfig.UTSMode), + Mount: mounts, + Volume: volumes, + VolumesFrom: cc.HostConfig.VolumesFrom, + Workdir: cc.Config.WorkingDir, + Net: &netInfo, + } + + if len(cc.HostConfig.BlkioWeightDevice) > 0 { + devices := make([]string, 0, len(cc.HostConfig.BlkioWeightDevice)) + for _, d := range cc.HostConfig.BlkioWeightDevice { + devices = append(devices, d.String()) + } + cliOpts.BlkIOWeightDevice = devices + } + if cc.HostConfig.BlkioWeight > 0 { + cliOpts.BlkIOWeight = strconv.Itoa(int(cc.HostConfig.BlkioWeight)) + } + + if cc.HostConfig.Memory > 0 { + cliOpts.Memory = strconv.Itoa(int(cc.HostConfig.Memory)) + } + + if cc.HostConfig.MemoryReservation > 0 { + cliOpts.MemoryReservation = strconv.Itoa(int(cc.HostConfig.MemoryReservation)) + } + + if cc.HostConfig.MemorySwap > 0 { + cliOpts.MemorySwap = strconv.Itoa(int(cc.HostConfig.MemorySwap)) + } + + if cc.Config.StopTimeout != nil { + cliOpts.StopTimeout = uint(*cc.Config.StopTimeout) + } + + if cc.HostConfig.ShmSize > 0 { + cliOpts.ShmSize = strconv.Itoa(int(cc.HostConfig.ShmSize)) + } + + if cc.HostConfig.KernelMemory > 0 { + cliOpts.KernelMemory = strconv.Itoa(int(cc.HostConfig.KernelMemory)) + } + if len(cc.HostConfig.RestartPolicy.Name) > 0 { + policy := cc.HostConfig.RestartPolicy.Name + // only add restart count on failure + if cc.HostConfig.RestartPolicy.IsOnFailure() { + policy += fmt.Sprintf(":%d", cc.HostConfig.RestartPolicy.MaximumRetryCount) + } + cliOpts.Restart = policy + } + + if cc.HostConfig.MemorySwappiness != nil { + cliOpts.MemorySwappiness = *cc.HostConfig.MemorySwappiness + } + if cc.HostConfig.OomKillDisable != nil { + cliOpts.OOMKillDisable = *cc.HostConfig.OomKillDisable + } + if cc.Config.Healthcheck != nil { + cliOpts.HealthCmd = strings.Join(cc.Config.Healthcheck.Test, " ") + cliOpts.HealthInterval = cc.Config.Healthcheck.Interval.String() + cliOpts.HealthRetries = uint(cc.Config.Healthcheck.Retries) + cliOpts.HealthStartPeriod = cc.Config.Healthcheck.StartPeriod.String() + cliOpts.HealthTimeout = cc.Config.Healthcheck.Timeout.String() + } + + // specgen assumes the image name is arg[0] + cmd := []string{cc.Image} + cmd = append(cmd, cc.Config.Cmd...) + return &cliOpts, cmd, nil +} diff --git a/cmd/podman/report/diff.go b/cmd/podman/common/diffChanges.go index edd324bfe..4aa485acc 100644 --- a/cmd/podman/report/diff.go +++ b/cmd/podman/common/diffChanges.go @@ -1,4 +1,4 @@ -package report +package common import ( "fmt" diff --git a/cmd/podman/common/volumes.go b/cmd/podman/common/volumes.go index 2a82451e4..71f897264 100644 --- a/cmd/podman/common/volumes.go +++ b/cmd/podman/common/volumes.go @@ -238,7 +238,7 @@ func getBindMount(args []string) (spec.Mount, error) { var setSource, setDest, setRORW, setSuid, setDev, setExec, setRelabel bool for _, val := range args { - kv := strings.Split(val, "=") + kv := strings.SplitN(val, "=", 2) switch kv[0] { case "bind-nonrecursive": newMount.Options = append(newMount.Options, "bind") @@ -366,7 +366,7 @@ func getTmpfsMount(args []string) (spec.Mount, error) { var setDest, setRORW, setSuid, setDev, setExec, setTmpcopyup bool for _, val := range args { - kv := strings.Split(val, "=") + kv := strings.SplitN(val, "=", 2) switch kv[0] { case "tmpcopyup", "notmpcopyup": if setTmpcopyup { @@ -441,7 +441,7 @@ func getDevptsMount(args []string) (spec.Mount, error) { var setDest bool for _, val := range args { - kv := strings.Split(val, "=") + kv := strings.SplitN(val, "=", 2) switch kv[0] { case "target", "dst", "destination": if len(kv) == 1 { @@ -473,7 +473,7 @@ func getNamedVolume(args []string) (*specgen.NamedVolume, error) { var setSource, setDest, setRORW, setSuid, setDev, setExec bool for _, val := range args { - kv := strings.Split(val, "=") + kv := strings.SplitN(val, "=", 2) switch kv[0] { case "ro", "rw": if setRORW { diff --git a/cmd/podman/containers/attach.go b/cmd/podman/containers/attach.go index cfe7df441..0db7c22d0 100644 --- a/cmd/podman/containers/attach.go +++ b/cmd/podman/containers/attach.go @@ -14,7 +14,7 @@ import ( var ( attachDescription = "The podman attach command allows you to attach to a running container using the container's ID or name, either to view its ongoing output or to control it interactively." attachCommand = &cobra.Command{ - Use: "attach [flags] CONTAINER", + Use: "attach [options] CONTAINER", Short: "Attach to a running container", Long: attachDescription, RunE: attach, diff --git a/cmd/podman/containers/checkpoint.go b/cmd/podman/containers/checkpoint.go index 284393dc0..2606f62c5 100644 --- a/cmd/podman/containers/checkpoint.go +++ b/cmd/podman/containers/checkpoint.go @@ -20,7 +20,7 @@ var ( Checkpoints one or more running containers. The container name or ID can be used. ` checkpointCommand = &cobra.Command{ - Use: "checkpoint [flags] CONTAINER [CONTAINER...]", + Use: "checkpoint [options] CONTAINER [CONTAINER...]", Short: "Checkpoints one or more containers", Long: checkpointDescription, RunE: checkpoint, diff --git a/cmd/podman/containers/cleanup.go b/cmd/podman/containers/cleanup.go index 7913f5a10..54bc64a60 100644 --- a/cmd/podman/containers/cleanup.go +++ b/cmd/podman/containers/cleanup.go @@ -19,7 +19,7 @@ var ( Cleans up mount points and network stacks on one or more containers from the host. The container name or ID can be used. This command is used internally when running containers, but can also be used if container cleanup has failed when a container exits. ` cleanupCommand = &cobra.Command{ - Use: "cleanup [flags] CONTAINER [CONTAINER...]", + Use: "cleanup [options] CONTAINER [CONTAINER...]", Short: "Cleanup network and mountpoints of one or more containers", Long: cleanupDescription, RunE: cleanup, diff --git a/cmd/podman/containers/commit.go b/cmd/podman/containers/commit.go index 31294c66d..1b33d221d 100644 --- a/cmd/podman/containers/commit.go +++ b/cmd/podman/containers/commit.go @@ -18,7 +18,7 @@ var ( commitDescription = `Create an image from a container's changes. Optionally tag the image created, set the author with the --author flag, set the commit message with the --message flag, and make changes to the instructions with the --change flag.` commitCommand = &cobra.Command{ - Use: "commit [flags] CONTAINER [IMAGE]", + Use: "commit [options] CONTAINER [IMAGE]", Short: "Create new image based on the changed container", Long: commitDescription, RunE: commit, diff --git a/cmd/podman/containers/cp.go b/cmd/podman/containers/cp.go index 28a18e83e..5dfe43ded 100644 --- a/cmd/podman/containers/cp.go +++ b/cmd/podman/containers/cp.go @@ -16,7 +16,7 @@ var ( 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. ` cpCommand = &cobra.Command{ - Use: "cp [flags] SRC_PATH DEST_PATH", + Use: "cp [options] SRC_PATH DEST_PATH", Short: "Copy files/folders between a container and the local filesystem", Long: cpDescription, Args: cobra.ExactArgs(2), diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index 809162e76..b7b2a364f 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -15,11 +15,9 @@ import ( "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/domain/entities" - "github.com/containers/podman/v2/pkg/errorhandling" "github.com/containers/podman/v2/pkg/specgen" "github.com/containers/podman/v2/pkg/util" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/pflag" ) @@ -29,7 +27,7 @@ var ( The container ID is then printed to stdout. You can then start it at any time with the podman start <container_id> command. The container will be created with the initial state 'created'.` createCommand = &cobra.Command{ - Use: "create [flags] IMAGE [COMMAND [ARG...]]", + Use: "create [options] IMAGE [COMMAND [ARG...]]", Short: "Create but do not start a container", Long: createDescription, RunE: create, @@ -63,7 +61,6 @@ func createFlags(flags *pflag.FlagSet) { _ = flags.MarkHidden("signature-policy") if registry.IsRemote() { - _ = flags.MarkHidden("authfile") _ = flags.MarkHidden("http-proxy") } } @@ -95,15 +92,6 @@ func create(cmd *cobra.Command, args []string) error { if err != nil { return err } - cidFile, err := openCidFile(cliVals.CIDFile) - if err != nil { - return err - } - - if cidFile != nil { - defer errorhandling.CloseQuiet(cidFile) - defer errorhandling.SyncQuiet(cidFile) - } if err := createInit(cmd); err != nil { return err @@ -140,10 +128,9 @@ func create(cmd *cobra.Command, args []string) error { return err } - if cidFile != nil { - _, err = cidFile.WriteString(report.Id) - if err != nil { - logrus.Error(err) + if cliVals.CIDFile != "" { + if err := util.CreateCidFile(cliVals.CIDFile, report.Id); err != nil { + return err } } @@ -270,20 +257,6 @@ func pullImage(imageName string) (string, error) { return imageName, nil } -func openCidFile(cidfile string) (*os.File, error) { - if cidfile == "" { - return nil, nil - } - cidFile, err := util.OpenExclusiveFile(cidfile) - if err != nil && os.IsExist(err) { - return nil, errors.Errorf("container id file exists. Ensure another container is not using it or delete %s", cidfile) - } - if err != nil { - return nil, errors.Errorf("error opening cidfile %s", cidfile) - } - return cidFile, nil -} - // createPodIfNecessary automatically creates a pod when requested. if the pod name // has the form new:ID, the pod ID is created and the name in the spec generator is replaced // with ID. diff --git a/cmd/podman/containers/diff.go b/cmd/podman/containers/diff.go index a3ca6edf9..caf7e9955 100644 --- a/cmd/podman/containers/diff.go +++ b/cmd/podman/containers/diff.go @@ -1,9 +1,9 @@ package containers import ( - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" @@ -13,7 +13,7 @@ import ( var ( // podman container _diff_ diffCmd = &cobra.Command{ - Use: "diff [flags] CONTAINER", + Use: "diff [options] CONTAINER", Args: validate.IDOrLatestArgs, Short: "Inspect changes to the container's file systems", Long: `Displays changes to the container filesystem's'. The container will be compared to its parent layer.`, @@ -54,10 +54,10 @@ func diff(cmd *cobra.Command, args []string) error { } switch { - case parse.MatchesJSONFormat(diffOpts.Format): - return report.ChangesToJSON(results) + case report.IsJSON(diffOpts.Format): + return common.ChangesToJSON(results) case diffOpts.Format == "": - return report.ChangesToTable(results) + return common.ChangesToTable(results) default: return errors.New("only supported value for '--format' is 'json'") } diff --git a/cmd/podman/containers/exec.go b/cmd/podman/containers/exec.go index e301ca588..88851f619 100644 --- a/cmd/podman/containers/exec.go +++ b/cmd/podman/containers/exec.go @@ -20,7 +20,7 @@ var ( execDescription = `Execute the specified command inside a running container. ` execCommand = &cobra.Command{ - Use: "exec [flags] CONTAINER [COMMAND [ARG...]]", + Use: "exec [options] CONTAINER [COMMAND [ARG...]]", Short: "Run a process in a running container", Long: execDescription, RunE: exec, diff --git a/cmd/podman/containers/exists.go b/cmd/podman/containers/exists.go index 1d79b684d..70b8af159 100644 --- a/cmd/podman/containers/exists.go +++ b/cmd/podman/containers/exists.go @@ -12,7 +12,7 @@ var ( containerExistsDescription = `If the named container exists in local storage, podman container exists exits with 0, otherwise the exit code will be 1.` existsCommand = &cobra.Command{ - Use: "exists [flags] CONTAINER", + Use: "exists [options] CONTAINER", Short: "Check if a container exists in local storage", Long: containerExistsDescription, Example: `podman container exists --external containerID diff --git a/cmd/podman/containers/export.go b/cmd/podman/containers/export.go index 1a59b7fff..f5e02d134 100644 --- a/cmd/podman/containers/export.go +++ b/cmd/podman/containers/export.go @@ -18,7 +18,7 @@ var ( " and saves it on the local machine." exportCommand = &cobra.Command{ - Use: "export [flags] CONTAINER", + Use: "export [options] CONTAINER", Short: "Export container's filesystem contents as a tar archive", Long: exportDescription, RunE: export, diff --git a/cmd/podman/containers/init.go b/cmd/podman/containers/init.go index 5a826bc59..983c0e4e8 100644 --- a/cmd/podman/containers/init.go +++ b/cmd/podman/containers/init.go @@ -15,7 +15,7 @@ var ( initDescription = `Initialize one or more containers, creating the OCI spec and mounts for inspection. Container names or IDs can be used.` initCommand = &cobra.Command{ - Use: "init [flags] CONTAINER [CONTAINER...]", + Use: "init [options] CONTAINER [CONTAINER...]", Short: "Initialize one or more containers", Long: initDescription, RunE: initContainer, diff --git a/cmd/podman/containers/inspect.go b/cmd/podman/containers/inspect.go index 1c208b513..b4e1feccb 100644 --- a/cmd/podman/containers/inspect.go +++ b/cmd/podman/containers/inspect.go @@ -11,7 +11,7 @@ import ( var ( // podman container _inspect_ inspectCmd = &cobra.Command{ - Use: "inspect [flags] CONTAINER [CONTAINER...]", + Use: "inspect [options] CONTAINER [CONTAINER...]", Short: "Display the configuration of a container", Long: `Displays the low-level information on a container identified by name or ID.`, RunE: inspectExec, diff --git a/cmd/podman/containers/kill.go b/cmd/podman/containers/kill.go index a4d536098..1bb071b6d 100644 --- a/cmd/podman/containers/kill.go +++ b/cmd/podman/containers/kill.go @@ -17,7 +17,7 @@ import ( var ( killDescription = "The main process inside each container specified will be sent SIGKILL, or any signal specified with option --signal." killCommand = &cobra.Command{ - Use: "kill [flags] CONTAINER [CONTAINER...]", + Use: "kill [options] CONTAINER [CONTAINER...]", Short: "Kill one or more running containers with a specific signal", Long: killDescription, RunE: kill, diff --git a/cmd/podman/containers/list.go b/cmd/podman/containers/list.go index daf03a51b..78a15559f 100644 --- a/cmd/podman/containers/list.go +++ b/cmd/podman/containers/list.go @@ -10,7 +10,7 @@ import ( var ( // podman container _list_ listCmd = &cobra.Command{ - Use: "list", + Use: "list [options]", Aliases: []string{"ls"}, Args: validate.NoArgs, Short: "List containers", diff --git a/cmd/podman/containers/logs.go b/cmd/podman/containers/logs.go index acc2ab1aa..8ad2d7e16 100644 --- a/cmd/podman/containers/logs.go +++ b/cmd/podman/containers/logs.go @@ -27,7 +27,7 @@ var ( This does not guarantee execution order when combined with podman run (i.e., your run may not have generated any logs at the time you execute podman logs). ` logsCommand = &cobra.Command{ - Use: "logs [flags] CONTAINER [CONTAINER...]", + Use: "logs [options] CONTAINER [CONTAINER...]", Short: "Fetch the logs of one or more containers", Long: logsDescription, Args: func(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/mount.go b/cmd/podman/containers/mount.go index c4dfb513f..335367e18 100644 --- a/cmd/podman/containers/mount.go +++ b/cmd/podman/containers/mount.go @@ -6,7 +6,7 @@ import ( "text/tabwriter" "text/template" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" @@ -25,7 +25,7 @@ var ( ` mountCommand = &cobra.Command{ - Use: "mount [flags] [CONTAINER...]", + Use: "mount [options] [CONTAINER...]", Short: "Mount a working container's root filesystem", Long: mountDescription, RunE: mount, @@ -97,7 +97,7 @@ func mount(_ *cobra.Command, args []string) error { } switch { - case parse.MatchesJSONFormat(mountOpts.Format): + case report.IsJSON(mountOpts.Format): return printJSON(reports) case mountOpts.Format == "": break // print defaults diff --git a/cmd/podman/containers/pause.go b/cmd/podman/containers/pause.go index c5171303d..89a76ab25 100644 --- a/cmd/podman/containers/pause.go +++ b/cmd/podman/containers/pause.go @@ -17,7 +17,7 @@ import ( var ( pauseDescription = `Pauses one or more running containers. The container name or ID can be used.` pauseCommand = &cobra.Command{ - Use: "pause [flags] CONTAINER [CONTAINER...]", + Use: "pause [options] CONTAINER [CONTAINER...]", Short: "Pause all the processes in one or more containers", Long: pauseDescription, RunE: pause, diff --git a/cmd/podman/containers/port.go b/cmd/podman/containers/port.go index 347f06bba..a895f24b1 100644 --- a/cmd/podman/containers/port.go +++ b/cmd/podman/containers/port.go @@ -18,7 +18,7 @@ var ( portDescription = `List port mappings for the CONTAINER, or lookup the public-facing port that is NAT-ed to the PRIVATE_PORT ` portCommand = &cobra.Command{ - Use: "port [flags] CONTAINER [PORT]", + Use: "port [options] CONTAINER [PORT]", Short: "List port mappings or a specific mapping for the container", Long: portDescription, RunE: port, @@ -31,7 +31,7 @@ var ( } containerPortCommand = &cobra.Command{ - Use: "port [flags] CONTAINER [PORT]", + Use: "port [options] CONTAINER [PORT]", Short: portCommand.Short, Long: portDescription, RunE: portCommand.RunE, diff --git a/cmd/podman/containers/prune.go b/cmd/podman/containers/prune.go index cfe6765ac..bfdace086 100644 --- a/cmd/podman/containers/prune.go +++ b/cmd/podman/containers/prune.go @@ -21,7 +21,7 @@ var ( Removes all non running containers`) pruneCommand = &cobra.Command{ - Use: "prune [flags]", + Use: "prune [options]", Short: "Remove all non running containers", Long: pruneDescription, RunE: prune, diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go index 41d309f51..90f4db19c 100644 --- a/cmd/podman/containers/ps.go +++ b/cmd/podman/containers/ps.go @@ -11,9 +11,8 @@ import ( "time" tm "github.com/buger/goterm" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" @@ -27,7 +26,7 @@ import ( var ( psDescription = "Prints out information about the containers" psCommand = &cobra.Command{ - Use: "ps", + Use: "ps [options]", Args: validate.NoArgs, Short: "List containers", Long: psDescription, @@ -92,7 +91,7 @@ func checkFlags(c *cobra.Command) error { if listOpts.Size || listOpts.Namespace { return errors.Errorf("quiet conflicts with size and namespace") } - if c.Flag("format").Changed && !parse.MatchesJSONFormat(listOpts.Format) { + if c.Flag("format").Changed && !report.IsJSON(listOpts.Format) { // Quiet is overridden by Go template output. listOpts.Quiet = false } @@ -179,7 +178,7 @@ func ps(cmd *cobra.Command, args []string) error { } switch { - case parse.MatchesJSONFormat(listOpts.Format): + case report.IsJSON(listOpts.Format): return jsonOut(listContainers) case listOpts.Quiet: return quietOut(listContainers) @@ -371,12 +370,6 @@ func (l psReporter) CreatedHuman() string { // portsToString converts the ports used to a string of the from "port1, port2" // and also groups a continuous list of ports into a readable format. func portsToString(ports []ocicni.PortMapping) string { - type portGroup struct { - first int32 - last int32 - } - portDisplay := []string{} - if len(ports) == 0 { return "" } @@ -385,41 +378,124 @@ func portsToString(ports []ocicni.PortMapping) string { return comparePorts(ports[i], ports[j]) }) - // portGroupMap is used for grouping continuous ports. - portGroupMap := make(map[string]*portGroup) - var groupKeyList []string + portGroups := [][]ocicni.PortMapping{} + currentGroup := []ocicni.PortMapping{} + for i, v := range ports { + var prevPort, nextPort *int32 + if i > 0 { + prevPort = &ports[i-1].ContainerPort + } + if i+1 < len(ports) { + nextPort = &ports[i+1].ContainerPort + } - for _, v := range ports { + port := v.ContainerPort - hostIP := v.HostIP - if hostIP == "" { - hostIP = "0.0.0.0" + // Helper functions + addToCurrentGroup := func(x ocicni.PortMapping) { + currentGroup = append(currentGroup, x) } - // If hostPort and containerPort are not same, consider as individual port. - if v.ContainerPort != v.HostPort { - portDisplay = append(portDisplay, fmt.Sprintf("%s:%d->%d/%s", hostIP, v.HostPort, v.ContainerPort, v.Protocol)) - continue + + addToPortGroup := func(x ocicni.PortMapping) { + portGroups = append(portGroups, []ocicni.PortMapping{x}) + } + + finishCurrentGroup := func() { + portGroups = append(portGroups, currentGroup) + currentGroup = []ocicni.PortMapping{} } - portMapKey := fmt.Sprintf("%s/%s", hostIP, v.Protocol) + // Single entry slice + if prevPort == nil && nextPort == nil { + addToPortGroup(v) + } + + // Start of the slice with len > 0 + if prevPort == nil && nextPort != nil { + isGroup := *nextPort-1 == port + + if isGroup { + // Start with a group + addToCurrentGroup(v) + } else { + // Start with single item + addToPortGroup(v) + } - portgroup, ok := portGroupMap[portMapKey] - if !ok { - portGroupMap[portMapKey] = &portGroup{first: v.ContainerPort, last: v.ContainerPort} - // This list is required to traverse portGroupMap. - groupKeyList = append(groupKeyList, portMapKey) continue } - if portgroup.last == (v.ContainerPort - 1) { - portgroup.last = v.ContainerPort + // Middle of the slice with len > 0 + if prevPort != nil && nextPort != nil { + currentIsGroup := *prevPort+1 == port + nextIsGroup := *nextPort-1 == port + + if currentIsGroup { + // Maybe in the middle of a group + addToCurrentGroup(v) + + if !nextIsGroup { + // End of a group + finishCurrentGroup() + } + } else if nextIsGroup { + // Start of a new group + addToCurrentGroup(v) + } else { + // No group at all + addToPortGroup(v) + } + continue } + + // End of the slice with len > 0 + if prevPort != nil && nextPort == nil { + isGroup := *prevPort+1 == port + + if isGroup { + // End group + addToCurrentGroup(v) + finishCurrentGroup() + } else { + // End single item + addToPortGroup(v) + } + } } - // For each portMapKey, format group list and append to output string. - for _, portKey := range groupKeyList { - group := portGroupMap[portKey] - portDisplay = append(portDisplay, formatGroup(portKey, group.first, group.last)) + + portDisplay := []string{} + for _, group := range portGroups { + if len(group) == 0 { + // Usually should not happen, but better do not crash. + continue + } + + first := group[0] + + hostIP := first.HostIP + if hostIP == "" { + hostIP = "0.0.0.0" + } + + // Single mappings + if len(group) == 1 { + portDisplay = append(portDisplay, + fmt.Sprintf( + "%s:%d->%d/%s", + hostIP, first.HostPort, first.ContainerPort, first.Protocol, + ), + ) + continue + } + + // Group mappings + last := group[len(group)-1] + portDisplay = append(portDisplay, formatGroup( + fmt.Sprintf("%s/%s", hostIP, first.Protocol), + first.HostPort, last.HostPort, + first.ContainerPort, last.ContainerPort, + )) } return strings.Join(portDisplay, ", ") } @@ -440,9 +516,10 @@ func comparePorts(i, j ocicni.PortMapping) bool { return i.Protocol < j.Protocol } -// formatGroup returns the group as <IP:startPort:lastPort->startPort:lastPort/Proto> -// e.g 0.0.0.0:1000-1006->1000-1006/tcp. -func formatGroup(key string, start, last int32) string { +// formatGroup returns the group in the format: +// <IP:firstHost:lastHost->firstCtr:lastCtr/Proto> +// e.g 0.0.0.0:1000-1006->2000-2006/tcp. +func formatGroup(key string, firstHost, lastHost, firstCtr, lastCtr int32) string { parts := strings.Split(key, "/") groupType := parts[0] var ip string @@ -450,12 +527,16 @@ func formatGroup(key string, start, last int32) string { ip = parts[0] groupType = parts[1] } - group := strconv.Itoa(int(start)) - if start != last { - group = fmt.Sprintf("%s-%d", group, last) - } - if ip != "" { - group = fmt.Sprintf("%s:%s->%s", ip, group, group) + + group := func(first, last int32) string { + group := strconv.Itoa(int(first)) + if first != last { + group = fmt.Sprintf("%s-%d", group, last) + } + return group } - return fmt.Sprintf("%s/%s", group, groupType) + hostGroup := group(firstHost, lastHost) + ctrGroup := group(firstCtr, lastCtr) + + return fmt.Sprintf("%s:%s->%s/%s", ip, hostGroup, ctrGroup, groupType) } diff --git a/cmd/podman/containers/restart.go b/cmd/podman/containers/restart.go index 5f6f9c35c..1cc28c20d 100644 --- a/cmd/podman/containers/restart.go +++ b/cmd/podman/containers/restart.go @@ -20,7 +20,7 @@ var ( A timeout before forcibly stopping can be set, but defaults to %d seconds.`, containerConfig.Engine.StopTimeout) restartCommand = &cobra.Command{ - Use: "restart [flags] CONTAINER [CONTAINER...]", + Use: "restart [options] CONTAINER [CONTAINER...]", Short: "Restart one or more containers", Long: restartDescription, RunE: restart, diff --git a/cmd/podman/containers/restore.go b/cmd/podman/containers/restore.go index c996144e3..314bf7564 100644 --- a/cmd/podman/containers/restore.go +++ b/cmd/podman/containers/restore.go @@ -20,7 +20,7 @@ var ( Restores a container from a checkpoint. The container name or ID can be used. ` restoreCommand = &cobra.Command{ - Use: "restore [flags] CONTAINER [CONTAINER...]", + Use: "restore [options] CONTAINER [CONTAINER...]", Short: "Restores one or more containers from a checkpoint", Long: restoreDescription, RunE: restore, diff --git a/cmd/podman/containers/rm.go b/cmd/podman/containers/rm.go index a7739b3ba..ccdd2ef05 100644 --- a/cmd/podman/containers/rm.go +++ b/cmd/podman/containers/rm.go @@ -21,7 +21,7 @@ var ( Command does not remove images. Running or unusable containers will not be removed without the -f option.` rmCommand = &cobra.Command{ - Use: "rm [flags] CONTAINER [CONTAINER...]", + Use: "rm [options] CONTAINER [CONTAINER...]", Short: "Remove one or more containers", Long: rmDescription, RunE: rm, diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go index 19c984bbf..6cadbc7ec 100644 --- a/cmd/podman/containers/run.go +++ b/cmd/podman/containers/run.go @@ -25,7 +25,7 @@ var ( runDescription = "Runs a command in a new container from the given image" runCommand = &cobra.Command{ Args: cobra.MinimumNArgs(1), - Use: "run [flags] IMAGE [COMMAND [ARG...]]", + Use: "run [options] IMAGE [COMMAND [ARG...]]", Short: "Run a command in a new container", Long: runDescription, RunE: run, @@ -63,10 +63,11 @@ func runFlags(flags *pflag.FlagSet) { flags.BoolVar(&runOpts.SigProxy, "sig-proxy", true, "Proxy received signals to the process") flags.BoolVar(&runRmi, "rmi", false, "Remove container image unless used by other containers") flags.UintVar(&runOpts.PreserveFDs, "preserve-fds", 0, "Pass a number of additional file descriptors into the container") + flags.BoolVarP(&runOpts.Detach, "detach", "d", false, "Run container in background and print container ID") + flags.StringVar(&runOpts.DetachKeys, "detach-keys", containerConfig.DetachKeys(), "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-cf`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`") _ = flags.MarkHidden("signature-policy") if registry.IsRemote() { - _ = flags.MarkHidden("authfile") _ = flags.MarkHidden("http-proxy") _ = flags.MarkHidden("preserve-fds") } @@ -110,15 +111,8 @@ func run(cmd *cobra.Command, args []string) error { return errors.Wrapf(err, "error checking authfile path %s", af) } } - cidFile, err := openCidFile(cliVals.CIDFile) - if err != nil { - return err - } - if cidFile != nil { - defer errorhandling.CloseQuiet(cidFile) - defer errorhandling.SyncQuiet(cidFile) - } + runOpts.CIDFile = cliVals.CIDFile runOpts.Rm = cliVals.Rm if err := createInit(cmd); err != nil { return err @@ -172,8 +166,6 @@ func run(cmd *cobra.Command, args []string) error { } } } - runOpts.Detach = cliVals.Detach - runOpts.DetachKeys = cliVals.DetachKeys cliVals.PreserveFDs = runOpts.PreserveFDs s := specgen.NewSpecGenerator(imageName, cliVals.RootFS) if err := common.FillOutSpecGen(s, &cliVals, args); err != nil { @@ -194,14 +186,8 @@ func run(cmd *cobra.Command, args []string) error { if err != nil { return err } - if cidFile != nil { - _, err = cidFile.WriteString(report.Id) - if err != nil { - logrus.Error(err) - } - } - if cliVals.Detach { + if runOpts.Detach { fmt.Println(report.Id) return nil } diff --git a/cmd/podman/containers/runlabel.go b/cmd/podman/containers/runlabel.go index 5ee8c9d6c..b49af36ab 100644 --- a/cmd/podman/containers/runlabel.go +++ b/cmd/podman/containers/runlabel.go @@ -24,7 +24,7 @@ var ( runlabelOptions = runlabelOptionsWrapper{} runlabelDescription = "Executes a command as described by a container image label." runlabelCommand = &cobra.Command{ - Use: "runlabel [flags] LABEL IMAGE [ARG...]", + Use: "runlabel [options] LABEL IMAGE [ARG...]", Short: "Execute the command described by an image label", Long: runlabelDescription, RunE: runlabel, diff --git a/cmd/podman/containers/start.go b/cmd/podman/containers/start.go index 1e58498b6..21f31d360 100644 --- a/cmd/podman/containers/start.go +++ b/cmd/podman/containers/start.go @@ -17,7 +17,7 @@ import ( var ( startDescription = `Starts one or more containers. The container name or ID can be used.` startCommand = &cobra.Command{ - Use: "start [flags] CONTAINER [CONTAINER...]", + Use: "start [options] CONTAINER [CONTAINER...]", Short: "Start one or more containers", Long: startDescription, RunE: start, diff --git a/cmd/podman/containers/stats.go b/cmd/podman/containers/stats.go index c30ea52ec..85e7a1e82 100644 --- a/cmd/podman/containers/stats.go +++ b/cmd/podman/containers/stats.go @@ -7,9 +7,8 @@ import ( "text/template" tm "github.com/buger/goterm" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/cgroups" @@ -26,7 +25,7 @@ import ( var ( statsDescription = "Display percentage of CPU, memory, network I/O, block I/O and PIDs for one or more containers." statsCommand = &cobra.Command{ - Use: "stats [flags] [CONTAINER...]", + Use: "stats [options] [CONTAINER...]", Short: "Display a live stream of container resource usage statistics", Long: statsDescription, RunE: stats, @@ -157,7 +156,7 @@ func outputStats(reports []define.ContainerStats) error { for _, r := range reports { stats = append(stats, containerStats{r}) } - if parse.MatchesJSONFormat(statsOptions.Format) { + if report.IsJSON(statsOptions.Format) { return outputJSON(stats) } format := defaultStatsRow @@ -240,9 +239,9 @@ func combineHumanValues(a, b uint64) string { func outputJSON(stats []containerStats) error { type jstat struct { - Id string `json:"id"` //nolint + Id string `json:"id"` // nolint Name string `json:"name"` - CpuPercent string `json:"cpu_percent"` //nolint + CpuPercent string `json:"cpu_percent"` // nolint MemUsage string `json:"mem_usage"` MemPerc string `json:"mem_percent"` NetIO string `json:"net_io"` diff --git a/cmd/podman/containers/stop.go b/cmd/podman/containers/stop.go index 1c0a5efe4..7c8c1b50e 100644 --- a/cmd/podman/containers/stop.go +++ b/cmd/podman/containers/stop.go @@ -17,7 +17,7 @@ var ( A timeout to forcibly stop the container can also be set but defaults to %d seconds otherwise.`, containerConfig.Engine.StopTimeout) stopCommand = &cobra.Command{ - Use: "stop [flags] CONTAINER [CONTAINER...]", + Use: "stop [options] CONTAINER [CONTAINER...]", Short: "Stop one or more containers", Long: stopDescription, RunE: stop, diff --git a/cmd/podman/containers/top.go b/cmd/podman/containers/top.go index f2632d57a..361d30516 100644 --- a/cmd/podman/containers/top.go +++ b/cmd/podman/containers/top.go @@ -26,7 +26,7 @@ var ( topOptions = entities.TopOptions{} topCommand = &cobra.Command{ - Use: "top [flags] CONTAINER [FORMAT-DESCRIPTORS|ARGS...]", + Use: "top [options] CONTAINER [FORMAT-DESCRIPTORS|ARGS...]", Short: "Display the running processes of a container", Long: topDescription, RunE: top, diff --git a/cmd/podman/containers/unmount.go b/cmd/podman/containers/unmount.go index 424d4cedc..c3159cfed 100644 --- a/cmd/podman/containers/unmount.go +++ b/cmd/podman/containers/unmount.go @@ -19,7 +19,7 @@ var ( An unmount can be forced with the --force flag. ` unmountCommand = &cobra.Command{ - Use: "unmount [flags] CONTAINER [CONTAINER...]", + Use: "unmount [options] CONTAINER [CONTAINER...]", Aliases: []string{"umount"}, Short: "Unmounts working container's root filesystem", Long: description, diff --git a/cmd/podman/containers/unpause.go b/cmd/podman/containers/unpause.go index 43eaad72b..8927fc426 100644 --- a/cmd/podman/containers/unpause.go +++ b/cmd/podman/containers/unpause.go @@ -17,7 +17,7 @@ import ( var ( unpauseDescription = `Unpauses one or more previously paused containers. The container name or ID can be used.` unpauseCommand = &cobra.Command{ - Use: "unpause [flags] CONTAINER [CONTAINER...]", + Use: "unpause [options] CONTAINER [CONTAINER...]", Short: "Unpause the processes in one or more containers", Long: unpauseDescription, RunE: unpause, diff --git a/cmd/podman/containers/wait.go b/cmd/podman/containers/wait.go index 4bc3d20e2..b4986143b 100644 --- a/cmd/podman/containers/wait.go +++ b/cmd/podman/containers/wait.go @@ -19,11 +19,11 @@ var ( waitDescription = `Block until one or more containers stop and then print their exit codes. ` waitCommand = &cobra.Command{ - Use: "wait [flags] CONTAINER [CONTAINER...]", + Use: "wait [options] CONTAINER [CONTAINER...]", Short: "Block on one or more containers", Long: waitDescription, RunE: wait, - Example: `podman wait --interval 5000 ctrID + Example: `podman wait --interval 5s ctrID podman wait ctrID1 ctrID2`, } @@ -32,7 +32,7 @@ var ( Short: waitCommand.Short, Long: waitCommand.Long, RunE: waitCommand.RunE, - Example: `podman container wait --interval 5000 ctrID + Example: `podman container wait --interval 5s ctrID podman container wait ctrID1 ctrID2`, } ) @@ -40,10 +40,11 @@ var ( var ( waitOptions = entities.WaitOptions{} waitCondition string + waitInterval string ) func waitFlags(flags *pflag.FlagSet) { - flags.DurationVarP(&waitOptions.Interval, "interval", "i", time.Duration(250), "Milliseconds to wait before polling for completion") + flags.StringVarP(&waitInterval, "interval", "i", "250ns", "Time Interval to wait before polling for completion") flags.StringVar(&waitCondition, "condition", "stopped", "Condition to wait on") } @@ -70,8 +71,11 @@ func wait(cmd *cobra.Command, args []string) error { err error errs utils.OutputErrors ) - if waitOptions.Interval == 0 { - return errors.New("interval must be greater then 0") + if waitOptions.Interval, err = time.ParseDuration(waitInterval); err != nil { + var err1 error + if waitOptions.Interval, err1 = time.ParseDuration(waitInterval + "ms"); err1 != nil { + return err + } } if !waitOptions.Latest && len(args) == 0 { diff --git a/cmd/podman/diff.go b/cmd/podman/diff.go index 9d2236abe..bad31a4a2 100644 --- a/cmd/podman/diff.go +++ b/cmd/podman/diff.go @@ -17,7 +17,7 @@ var ( // Command: podman _diff_ Object_ID diffDescription = `Displays changes on a container or image's filesystem. The container or image will be compared to its parent layer.` diffCmd = &cobra.Command{ - Use: "diff [flags] {CONTAINER_ID | IMAGE_ID}", + Use: "diff [options] {CONTAINER_ID | IMAGE_ID}", Args: validate.IDOrLatestArgs, Short: "Display the changes to the object's file system", Long: diffDescription, diff --git a/cmd/podman/generate/kube.go b/cmd/podman/generate/kube.go index 2f63ba590..4935fc60c 100644 --- a/cmd/podman/generate/kube.go +++ b/cmd/podman/generate/kube.go @@ -20,7 +20,7 @@ var ( Whether the input is for a container or pod, Podman will always generate the specification as a pod.` kubeCmd = &cobra.Command{ - Use: "kube [flags] CONTAINER | POD", + Use: "kube [options] CONTAINER | POD", Short: "Generate Kubernetes YAML from a container or pod.", Long: kubeDescription, RunE: kube, diff --git a/cmd/podman/generate/systemd.go b/cmd/podman/generate/systemd.go index 02e826549..8e937fa90 100644 --- a/cmd/podman/generate/systemd.go +++ b/cmd/podman/generate/systemd.go @@ -6,7 +6,7 @@ import ( "os" "path/filepath" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/pkg/domain/entities" @@ -24,7 +24,7 @@ var ( The generated units can later be controlled via systemctl(1).` systemdCmd = &cobra.Command{ - Use: "systemd [flags] CTR|POD", + Use: "systemd [options] CTR|POD", Short: "Generate systemd units.", Long: systemdDescription, RunE: systemd, @@ -63,7 +63,7 @@ func systemd(cmd *cobra.Command, args []string) error { logrus.Warnln("The generated units should be placed on your remote system") } - report, err := registry.ContainerEngine().GenerateSystemd(registry.GetContext(), args[0], systemdOptions) + reports, err := registry.ContainerEngine().GenerateSystemd(registry.GetContext(), args[0], systemdOptions) if err != nil { return err } @@ -73,7 +73,7 @@ func systemd(cmd *cobra.Command, args []string) error { if err != nil { return errors.Wrap(err, "error getting current working directory") } - for name, content := range report.Units { + for name, content := range reports.Units { path := filepath.Join(cwd, fmt.Sprintf("%s.service", name)) f, err := os.Create(path) if err != nil { @@ -94,15 +94,15 @@ func systemd(cmd *cobra.Command, args []string) error { } // modify in place so we can print the // paths when --files is set - report.Units[name] = path + reports.Units[name] = path } } switch { - case parse.MatchesJSONFormat(format): - return printJSON(report.Units) + case report.IsJSON(format): + return printJSON(reports.Units) case format == "": - return printDefault(report.Units) + return printDefault(reports.Units) default: return errors.Errorf("unknown --format argument: %s", format) } diff --git a/cmd/podman/images/build.go b/cmd/podman/images/build.go index ac1b2c848..18c31313b 100644 --- a/cmd/podman/images/build.go +++ b/cmd/podman/images/build.go @@ -40,7 +40,7 @@ var ( // Command: podman _diff_ Object_ID buildDescription = "Builds an OCI or Docker image using instructions from one or more Containerfiles and a specified build context directory." buildCmd = &cobra.Command{ - Use: "build [flags] [CONTEXT]", + Use: "build [options] [CONTEXT]", Short: "Build an image using instructions from Containerfiles", Long: buildDescription, Args: cobra.MaximumNArgs(1), @@ -282,8 +282,7 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil flags.Layers = false } - var stdin, stdout, stderr, reporter *os.File - stdin = os.Stdin + var stdout, stderr, reporter *os.File stdout = os.Stdout stderr = os.Stderr reporter = os.Stderr @@ -422,7 +421,6 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil ForceRmIntermediateCtrs: flags.ForceRm, IDMappingOptions: idmappingOptions, IIDFile: flags.Iidfile, - In: stdin, Isolation: isolation, Labels: flags.Label, Layers: flags.Layers, diff --git a/cmd/podman/images/diff.go b/cmd/podman/images/diff.go index 05a05fa04..b7722e5e5 100644 --- a/cmd/podman/images/diff.go +++ b/cmd/podman/images/diff.go @@ -1,9 +1,9 @@ package images import ( - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -13,7 +13,7 @@ import ( var ( // podman container _inspect_ diffCmd = &cobra.Command{ - Use: "diff [flags] IMAGE", + Use: "diff [options] IMAGE", Args: cobra.ExactArgs(1), Short: "Inspect changes to the image's file systems", Long: `Displays changes to the image's filesystem. The image will be compared to its parent layer.`, @@ -51,10 +51,10 @@ func diff(cmd *cobra.Command, args []string) error { } switch { - case parse.MatchesJSONFormat(diffOpts.Format): - return report.ChangesToJSON(results) + case report.IsJSON(diffOpts.Format): + return common.ChangesToJSON(results) case diffOpts.Format == "": - return report.ChangesToTable(results) + return common.ChangesToTable(results) default: return errors.New("only supported value for '--format' is 'json'") } diff --git a/cmd/podman/images/history.go b/cmd/podman/images/history.go index fa4b368c6..3075218d1 100644 --- a/cmd/podman/images/history.go +++ b/cmd/podman/images/history.go @@ -10,9 +10,8 @@ import ( "time" "unicode" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/docker/go-units" "github.com/pkg/errors" @@ -27,7 +26,7 @@ var ( // podman _history_ historyCmd = &cobra.Command{ - Use: "history [flags] IMAGE", + Use: "history [options] IMAGE", Short: "Show history of a specified image", Long: long, Args: cobra.ExactArgs(1), @@ -81,7 +80,7 @@ func history(cmd *cobra.Command, args []string) error { return err } - if parse.MatchesJSONFormat(opts.format) { + if report.IsJSON(opts.format) { var err error if len(results.Layers) == 0 { _, err = fmt.Fprintf(os.Stdout, "[]\n") diff --git a/cmd/podman/images/import.go b/cmd/podman/images/import.go index 1c234e743..e3545da69 100644 --- a/cmd/podman/images/import.go +++ b/cmd/podman/images/import.go @@ -19,7 +19,7 @@ var ( Note remote tar balls can be specified, via web address. Optionally tag the image. You can specify the instructions using the --change option.` importCommand = &cobra.Command{ - Use: "import [flags] PATH [REFERENCE]", + Use: "import [options] PATH [REFERENCE]", Short: "Import a tarball to create a filesystem image", Long: importDescription, RunE: importCon, diff --git a/cmd/podman/images/inspect.go b/cmd/podman/images/inspect.go index 065dfaed2..8f005553d 100644 --- a/cmd/podman/images/inspect.go +++ b/cmd/podman/images/inspect.go @@ -10,7 +10,7 @@ import ( var ( // Command: podman image _inspect_ inspectCmd = &cobra.Command{ - Use: "inspect [flags] IMAGE [IMAGE...]", + Use: "inspect [options] IMAGE [IMAGE...]", Short: "Display the configuration of an image", Long: `Displays the low-level information of an image identified by name or ID.`, RunE: inspectExec, diff --git a/cmd/podman/images/list.go b/cmd/podman/images/list.go index 239da9d28..489b15086 100644 --- a/cmd/podman/images/list.go +++ b/cmd/podman/images/list.go @@ -10,10 +10,9 @@ import ( "time" "unicode" + "github.com/containers/common/pkg/report" "github.com/containers/image/v5/docker/reference" - "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/docker/go-units" "github.com/pkg/errors" @@ -35,7 +34,7 @@ type listFlagType struct { var ( // Command: podman image _list_ listCmd = &cobra.Command{ - Use: "list [flags] [IMAGE]", + Use: "list [options] [IMAGE]", Aliases: []string{"ls"}, Args: cobra.MaximumNArgs(1), Short: "List images in local storage", @@ -108,7 +107,7 @@ func images(cmd *cobra.Command, args []string) error { switch { case listFlag.quiet: return writeID(imgs) - case parse.MatchesJSONFormat(listFlag.format): + case report.IsJSON(listFlag.format): return writeJSON(imgs) default: if cmd.Flag("format").Changed { diff --git a/cmd/podman/images/load.go b/cmd/podman/images/load.go index cc8e71814..02f1b3b39 100644 --- a/cmd/podman/images/load.go +++ b/cmd/podman/images/load.go @@ -22,7 +22,7 @@ import ( var ( loadDescription = "Loads an image from a locally stored archive (tar file) into container storage." loadCommand = &cobra.Command{ - Use: "load [flags] [NAME[:TAG]]", + Use: "load [options] [NAME[:TAG]]", Short: "Load an image from container archive", Long: loadDescription, RunE: load, diff --git a/cmd/podman/images/mount.go b/cmd/podman/images/mount.go index 0a972ea81..28e9264ee 100644 --- a/cmd/podman/images/mount.go +++ b/cmd/podman/images/mount.go @@ -6,7 +6,7 @@ import ( "text/tabwriter" "text/template" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/pkg/domain/entities" @@ -24,7 +24,7 @@ var ( ` mountCommand = &cobra.Command{ - Use: "mount [flags] [IMAGE...]", + Use: "mount [options] [IMAGE...]", Short: "Mount an image's root filesystem", Long: mountDescription, RunE: mount, @@ -80,7 +80,7 @@ func mount(cmd *cobra.Command, args []string) error { } switch { - case parse.MatchesJSONFormat(mountOpts.Format): + case report.IsJSON(mountOpts.Format): return printJSON(reports) case mountOpts.Format == "": break // default format diff --git a/cmd/podman/images/prune.go b/cmd/podman/images/prune.go index 8dc203ead..b6e6b9562 100644 --- a/cmd/podman/images/prune.go +++ b/cmd/podman/images/prune.go @@ -19,7 +19,7 @@ var ( If an image is not being used by a container, it will be removed from the system.` pruneCmd = &cobra.Command{ - Use: "prune", + Use: "prune [options]", Args: validate.NoArgs, Short: "Remove unused images", Long: pruneDescription, diff --git a/cmd/podman/images/pull.go b/cmd/podman/images/pull.go index 448543b4d..35ef80f3c 100644 --- a/cmd/podman/images/pull.go +++ b/cmd/podman/images/pull.go @@ -30,7 +30,7 @@ var ( // Command: podman pull pullCmd = &cobra.Command{ - Use: "pull [flags] IMAGE", + Use: "pull [options] IMAGE", Args: cobra.ExactArgs(1), Short: "Pull an image from a registry", Long: pullDescription, @@ -84,11 +84,11 @@ func pullFlags(flags *pflag.FlagSet) { flags.Bool("disable-content-trust", false, "This is a Docker specific option and is a NOOP") flags.BoolVarP(&pullOptions.Quiet, "quiet", "q", false, "Suppress output information when pulling images") flags.StringVar(&pullOptions.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") + flags.BoolVar(&pullOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") + flags.StringVar(&pullOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") if !registry.IsRemote() { - flags.StringVar(&pullOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") flags.StringVar(&pullOptions.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") - flags.BoolVar(&pullOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") } _ = flags.MarkHidden("signature-policy") } diff --git a/cmd/podman/images/push.go b/cmd/podman/images/push.go index 2943cccc9..718bd4e8c 100644 --- a/cmd/podman/images/push.go +++ b/cmd/podman/images/push.go @@ -29,7 +29,7 @@ var ( // Command: podman push pushCmd = &cobra.Command{ - Use: "push [flags] SOURCE [DESTINATION]", + Use: "push [options] SOURCE [DESTINATION]", Short: "Push an image to a specified destination", Long: pushDescription, RunE: imagePush, @@ -88,11 +88,9 @@ func pushFlags(flags *pflag.FlagSet) { flags.BoolVar(&pushOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") if registry.IsRemote() { - _ = flags.MarkHidden("authfile") _ = flags.MarkHidden("cert-dir") _ = flags.MarkHidden("compress") _ = flags.MarkHidden("quiet") - _ = flags.MarkHidden("tls-verify") } _ = flags.MarkHidden("signature-policy") } diff --git a/cmd/podman/images/rm.go b/cmd/podman/images/rm.go index f6e52a49e..9dddef48f 100644 --- a/cmd/podman/images/rm.go +++ b/cmd/podman/images/rm.go @@ -14,7 +14,7 @@ import ( var ( rmDescription = "Removes one or more previously pulled or locally created images." rmCmd = &cobra.Command{ - Use: "rm [flags] IMAGE [IMAGE...]", + Use: "rm [options] IMAGE [IMAGE...]", Short: "Removes one or more images from local storage", Long: rmDescription, RunE: rm, diff --git a/cmd/podman/images/save.go b/cmd/podman/images/save.go index b164a2534..db1fa7159 100644 --- a/cmd/podman/images/save.go +++ b/cmd/podman/images/save.go @@ -25,7 +25,7 @@ var ( saveDescription = `Save an image to docker-archive or oci-archive on the local machine. Default is docker-archive.` saveCommand = &cobra.Command{ - Use: "save [flags] IMAGE [IMAGE...]", + Use: "save [options] IMAGE [IMAGE...]", Short: "Save image(s) to an archive", Long: saveDescription, RunE: save, diff --git a/cmd/podman/images/search.go b/cmd/podman/images/search.go index 8edd776ce..b1a1442a6 100644 --- a/cmd/podman/images/search.go +++ b/cmd/podman/images/search.go @@ -6,9 +6,9 @@ import ( "text/template" "github.com/containers/common/pkg/auth" + "github.com/containers/common/pkg/report" "github.com/containers/image/v5/types" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -32,7 +32,7 @@ var ( // Command: podman search searchCmd = &cobra.Command{ - Use: "search [flags] TERM", + Use: "search [options] TERM", Short: "Search registry for image", Long: searchDescription, RunE: imageSearch, diff --git a/cmd/podman/images/sign.go b/cmd/podman/images/sign.go index e331a64df..f6c1f9856 100644 --- a/cmd/podman/images/sign.go +++ b/cmd/podman/images/sign.go @@ -12,7 +12,7 @@ import ( var ( signDescription = "Create a signature file that can be used later to verify the image." signCommand = &cobra.Command{ - Use: "sign [flags] IMAGE [IMAGE...]", + Use: "sign [options] IMAGE [IMAGE...]", Short: "Sign an image", Long: signDescription, RunE: sign, diff --git a/cmd/podman/images/tree.go b/cmd/podman/images/tree.go index 0b79c2a4b..237a2ab91 100644 --- a/cmd/podman/images/tree.go +++ b/cmd/podman/images/tree.go @@ -11,7 +11,7 @@ import ( var ( treeDescription = "Prints layer hierarchy of an image in a tree format" treeCmd = &cobra.Command{ - Use: "tree [flags] IMAGE", + Use: "tree [options] IMAGE", Args: cobra.ExactArgs(1), Short: treeDescription, Long: treeDescription, diff --git a/cmd/podman/images/trust_set.go b/cmd/podman/images/trust_set.go index 878ffeea6..2e4b4fe17 100644 --- a/cmd/podman/images/trust_set.go +++ b/cmd/podman/images/trust_set.go @@ -12,7 +12,7 @@ import ( var ( setTrustDescription = "Set default trust policy or add a new trust policy for a registry" setTrustCommand = &cobra.Command{ - Use: "set [flags] REGISTRY", + Use: "set [options] REGISTRY", Short: "Set default trust policy or a new trust policy for a registry", Long: setTrustDescription, Example: "", diff --git a/cmd/podman/images/trust_show.go b/cmd/podman/images/trust_show.go index d1f85d34d..ba3b4e7fb 100644 --- a/cmd/podman/images/trust_show.go +++ b/cmd/podman/images/trust_show.go @@ -14,7 +14,7 @@ import ( var ( showTrustDescription = "Display trust policy for the system" showTrustCommand = &cobra.Command{ - Use: "show [flags] [REGISTRY]", + Use: "show [options] [REGISTRY]", Short: "Display trust policy for the system", Long: showTrustDescription, RunE: showTrust, diff --git a/cmd/podman/images/unmount.go b/cmd/podman/images/unmount.go index f7f6cf8e5..50dc972e8 100644 --- a/cmd/podman/images/unmount.go +++ b/cmd/podman/images/unmount.go @@ -19,7 +19,7 @@ var ( An unmount can be forced with the --force flag. ` unmountCommand = &cobra.Command{ - Use: "unmount [flags] IMAGE [IMAGE...]", + Use: "unmount [options] IMAGE [IMAGE...]", Aliases: []string{"umount"}, Short: "Unmount an image's root filesystem", Long: description, diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go index 85050a497..f1d673a21 100644 --- a/cmd/podman/inspect.go +++ b/cmd/podman/inspect.go @@ -19,7 +19,7 @@ var ( // Command: podman _inspect_ Object_ID inspectCmd = &cobra.Command{ - Use: "inspect [flags] {CONTAINER_ID | IMAGE_ID} [...]", + Use: "inspect [options] {CONTAINER_ID | IMAGE_ID} [...]", Short: "Display the configuration of object denoted by ID", RunE: inspectExec, Long: inspectDescription, diff --git a/cmd/podman/inspect/inspect.go b/cmd/podman/inspect/inspect.go index 3d1ef72aa..9c400d506 100644 --- a/cmd/podman/inspect/inspect.go +++ b/cmd/podman/inspect/inspect.go @@ -9,10 +9,10 @@ import ( "text/tabwriter" "text/template" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/validate" + "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -20,12 +20,18 @@ import ( ) const ( - // ImageType is the image type. - ImageType = "image" - // ContainerType is the container type. - ContainerType = "container" // AllType can be of type ImageType or ContainerType. AllType = "all" + // ContainerType is the container type. + ContainerType = "container" + // ImageType is the image type. + ImageType = "image" + //NetworkType is the network type + NetworkType = "network" + //PodType is the pod type. + PodType = "pod" + //VolumeType is the volume type + VolumeType = "volume" ) // Pull in configured json library @@ -59,15 +65,16 @@ type inspector struct { containerEngine entities.ContainerEngine imageEngine entities.ImageEngine options entities.InspectOptions + podOptions entities.PodInspectOptions } // newInspector creates a new inspector based on the specified options. func newInspector(options entities.InspectOptions) (*inspector, error) { switch options.Type { - case ImageType, ContainerType, AllType: + case ImageType, ContainerType, AllType, PodType, NetworkType, VolumeType: // Valid types. default: - return nil, errors.Errorf("invalid type %q: must be %q, %q or %q", options.Type, ImageType, ContainerType, AllType) + return nil, errors.Errorf("invalid type %q: must be %q, %q, %q, %q, %q, or %q", options.Type, ImageType, ContainerType, PodType, NetworkType, VolumeType, AllType) } if options.Type == ImageType { if options.Latest { @@ -77,10 +84,18 @@ func newInspector(options entities.InspectOptions) (*inspector, error) { return nil, errors.Errorf("size is not supported for type %q", ImageType) } } + if options.Type == PodType && options.Size { + return nil, errors.Errorf("size is not supported for type %q", PodType) + } + podOpts := entities.PodInspectOptions{ + Latest: options.Latest, + Format: options.Format, + } return &inspector{ containerEngine: registry.ContainerEngine(), imageEngine: registry.ImageEngine(), options: options, + podOptions: podOpts, }, nil } @@ -92,17 +107,19 @@ func (i *inspector) inspect(namesOrIDs []string) error { ctx := context.Background() if len(namesOrIDs) == 0 { - if !i.options.Latest { - return errors.New("no containers or images specified") + if !i.options.Latest && !i.options.All { + return errors.New("no names or ids specified") } } tmpType := i.options.Type if i.options.Latest { if len(namesOrIDs) > 0 { - return errors.New("--latest and containers cannot be used together") + return errors.New("--latest and arguments cannot be used together") + } + if i.options.Type == AllType { + tmpType = ContainerType // -l works with --type=all, defaults to containertype } - tmpType = ContainerType // -l works with --type=all } // Inspect - note that AllType requires us to expensively query one-by-one. @@ -132,10 +149,57 @@ func (i *inspector) inspect(namesOrIDs []string) error { for i := range ctrData { data = append(data, ctrData[i]) } + case PodType: + for _, pod := range namesOrIDs { + i.podOptions.NameOrID = pod + podData, err := i.containerEngine.PodInspect(ctx, i.podOptions) + if err != nil { + cause := errors.Cause(err) + if !strings.Contains(cause.Error(), define.ErrNoSuchPod.Error()) { + errs = []error{err} + } else { + return err + } + } else { + errs = nil + data = append(data, podData) + } + } + if i.podOptions.Latest { //latest means there are no names in the namesOrID array + podData, err := i.containerEngine.PodInspect(ctx, i.podOptions) + if err != nil { + cause := errors.Cause(err) + if !strings.Contains(cause.Error(), define.ErrNoSuchPod.Error()) { + errs = []error{err} + } else { + return err + } + } else { + errs = nil + data = append(data, podData) + } + } + case NetworkType: + networkData, allErrs, err := registry.ContainerEngine().NetworkInspect(ctx, namesOrIDs, i.options) + if err != nil { + return err + } + errs = allErrs + for i := range networkData { + data = append(data, networkData[i]) + } + case VolumeType: + volumeData, allErrs, err := i.containerEngine.VolumeInspect(ctx, namesOrIDs, i.options) + if err != nil { + return err + } + errs = allErrs + for i := range volumeData { + data = append(data, volumeData[i]) + } default: - return errors.Errorf("invalid type %q: must be %q, %q or %q", i.options.Type, ImageType, ContainerType, AllType) + return errors.Errorf("invalid type %q: must be %q, %q, %q, %q, %q, or %q", i.options.Type, ImageType, ContainerType, PodType, NetworkType, VolumeType, AllType) } - // Always print an empty array if data == nil { data = []interface{}{} @@ -143,7 +207,7 @@ func (i *inspector) inspect(namesOrIDs []string) error { var err error switch { - case parse.MatchesJSONFormat(i.options.Format) || i.options.Format == "": + case report.IsJSON(i.options.Format) || i.options.Format == "": err = printJSON(data) default: row := inspectNormalize(i.options.Format) @@ -196,11 +260,41 @@ func (i *inspector) inspectAll(ctx context.Context, namesOrIDs []string) ([]inte if err != nil { return nil, nil, err } + if len(errs) == 0 { + data = append(data, imgData[0]) + continue + } + volumeData, errs, err := i.containerEngine.VolumeInspect(ctx, []string{name}, i.options) + if err != nil { + return nil, nil, err + } + if len(errs) == 0 { + data = append(data, volumeData[0]) + continue + } + networkData, errs, err := registry.ContainerEngine().NetworkInspect(ctx, namesOrIDs, i.options) + if err != nil { + return nil, nil, err + } + if len(errs) == 0 { + data = append(data, networkData[0]) + continue + } + i.podOptions.NameOrID = name + podData, err := i.containerEngine.PodInspect(ctx, i.podOptions) + if err != nil { + cause := errors.Cause(err) + if !strings.Contains(cause.Error(), define.ErrNoSuchPod.Error()) { + return nil, nil, err + } + } else { + data = append(data, podData) + continue + } if len(errs) > 0 { allErrs = append(allErrs, errors.Errorf("no such object: %q", name)) continue } - data = append(data, imgData[0]) } return data, allErrs, nil } diff --git a/cmd/podman/login.go b/cmd/podman/login.go index 1556b0dcf..a789cef33 100644 --- a/cmd/podman/login.go +++ b/cmd/podman/login.go @@ -20,7 +20,7 @@ type loginOptionsWrapper struct { var ( loginOptions = loginOptionsWrapper{} loginCommand = &cobra.Command{ - Use: "login [flags] [REGISTRY]", + Use: "login [options] [REGISTRY]", Short: "Login to a container registry", Long: "Login to a container registry on a specified server.", RunE: login, diff --git a/cmd/podman/logout.go b/cmd/podman/logout.go index 1c6fdfb2a..7b5615d30 100644 --- a/cmd/podman/logout.go +++ b/cmd/podman/logout.go @@ -14,7 +14,7 @@ import ( var ( logoutOptions = auth.LogoutOptions{} logoutCommand = &cobra.Command{ - Use: "logout [flags] [REGISTRY]", + Use: "logout [options] [REGISTRY]", Short: "Logout of a container registry", Long: "Remove the cached username and password for the registry.", RunE: logout, diff --git a/cmd/podman/main.go b/cmd/podman/main.go index 007a8716c..9747769c7 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -66,6 +66,7 @@ func main() { // - rootCmd uses cobra default template not ours c.Command.SetHelpTemplate(helpTemplate) c.Command.SetUsageTemplate(usageTemplate) + c.Command.DisableFlagsInUseLine = true } } } diff --git a/cmd/podman/manifest/add.go b/cmd/podman/manifest/add.go index 128bf66a7..4b85f4c2a 100644 --- a/cmd/podman/manifest/add.go +++ b/cmd/podman/manifest/add.go @@ -25,7 +25,7 @@ type manifestAddOptsWrapper struct { var ( manifestAddOpts = manifestAddOptsWrapper{} addCmd = &cobra.Command{ - Use: "add [flags] LIST LIST", + Use: "add [options] LIST LIST", Short: "Add images to a manifest list or image index", Long: "Adds an image to a manifest list or image index.", RunE: add, @@ -56,9 +56,7 @@ func init() { flags.StringVar(&manifestAddOpts.Variant, "variant", "", "override the `Variant` of the specified image") if registry.IsRemote() { - _ = flags.MarkHidden("authfile") _ = flags.MarkHidden("cert-dir") - _ = flags.MarkHidden("tls-verify") } } diff --git a/cmd/podman/manifest/annotate.go b/cmd/podman/manifest/annotate.go index 1b720aa39..861e94034 100644 --- a/cmd/podman/manifest/annotate.go +++ b/cmd/podman/manifest/annotate.go @@ -13,7 +13,7 @@ import ( var ( manifestAnnotateOpts = entities.ManifestAnnotateOptions{} annotateCmd = &cobra.Command{ - Use: "annotate [flags] LIST IMAGE", + Use: "annotate [options] LIST IMAGE", Short: "Add or update information about an entry in a manifest list or image index", Long: "Adds or updates information about an entry in a manifest list or image index.", RunE: annotate, diff --git a/cmd/podman/manifest/create.go b/cmd/podman/manifest/create.go index b5352d24f..956946f9d 100644 --- a/cmd/podman/manifest/create.go +++ b/cmd/podman/manifest/create.go @@ -13,7 +13,7 @@ import ( var ( manifestCreateOpts = entities.ManifestCreateOptions{} createCmd = &cobra.Command{ - Use: "create [flags] LIST [IMAGE]", + Use: "create [options] LIST [IMAGE]", Short: "Create manifest list or image index", Long: "Creates manifest lists or image indexes.", RunE: create, diff --git a/cmd/podman/manifest/push.go b/cmd/podman/manifest/push.go index b43e4531a..91881c1b3 100644 --- a/cmd/podman/manifest/push.go +++ b/cmd/podman/manifest/push.go @@ -22,7 +22,7 @@ type manifestPushOptsWrapper struct { var ( manifestPushOpts = manifestPushOptsWrapper{} pushCmd = &cobra.Command{ - Use: "push [flags] SOURCE DESTINATION", + Use: "push [options] SOURCE DESTINATION", Short: "Push a manifest list or image index to a registry", Long: "Pushes manifest lists and image indexes to registries.", RunE: push, @@ -51,9 +51,7 @@ func init() { flags.BoolVarP(&manifestPushOpts.Quiet, "quiet", "q", false, "don't output progress information when pushing lists") if registry.IsRemote() { - _ = flags.MarkHidden("authfile") _ = flags.MarkHidden("cert-dir") - _ = flags.MarkHidden("tls-verify") } } diff --git a/cmd/podman/networks/create.go b/cmd/podman/networks/create.go index 17f39bd8b..74646090d 100644 --- a/cmd/podman/networks/create.go +++ b/cmd/podman/networks/create.go @@ -14,7 +14,7 @@ import ( var ( networkCreateDescription = `create CNI networks for containers and pods` networkCreateCommand = &cobra.Command{ - Use: "create [flags] [NETWORK]", + Use: "create [options] [NETWORK]", Short: "network create", Long: networkCreateDescription, RunE: networkCreate, @@ -34,9 +34,9 @@ func networkCreateFlags(flags *pflag.FlagSet) { flags.IPNetVar(&networkCreateOptions.Range, "ip-range", net.IPNet{}, "allocate container IP from range") flags.StringVar(&networkCreateOptions.MacVLAN, "macvlan", "", "create a Macvlan connection based on this device") // TODO not supported yet - //flags.StringVar(&networkCreateOptions.IPamDriver, "ipam-driver", "", "IP Address Management Driver") + // flags.StringVar(&networkCreateOptions.IPamDriver, "ipam-driver", "", "IP Address Management Driver") // TODO enable when IPv6 is working - //flags.BoolVar(&networkCreateOptions.IPV6, "IPv6", false, "enable IPv6 networking") + // flags.BoolVar(&networkCreateOptions.IPV6, "IPv6", false, "enable IPv6 networking") flags.IPNetVar(&networkCreateOptions.Subnet, "subnet", net.IPNet{}, "subnet in CIDR format") flags.BoolVar(&networkCreateOptions.DisableDNS, "disable-dns", false, "disable dns plugin") } diff --git a/cmd/podman/networks/inspect.go b/cmd/podman/networks/inspect.go index c36125948..25ee7e574 100644 --- a/cmd/podman/networks/inspect.go +++ b/cmd/podman/networks/inspect.go @@ -1,15 +1,8 @@ package network import ( - "encoding/json" - "fmt" - "os" - "text/tabwriter" - "text/template" - - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/podman/v2/cmd/podman/inspect" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -17,17 +10,14 @@ import ( var ( networkinspectDescription = `Inspect network` networkinspectCommand = &cobra.Command{ - Use: "inspect [flags] NETWORK [NETWORK...]", + Use: "inspect [options] NETWORK [NETWORK...]", Short: "network inspect", Long: networkinspectDescription, RunE: networkInspect, Example: `podman network inspect podman`, Args: cobra.MinimumNArgs(1), } -) - -var ( - networkInspectOptions entities.NetworkInspectOptions + inspectOpts *entities.InspectOptions ) func init() { @@ -36,36 +26,13 @@ func init() { Command: networkinspectCommand, Parent: networkCmd, }) + inspectOpts = new(entities.InspectOptions) flags := networkinspectCommand.Flags() - flags.StringVarP(&networkInspectOptions.Format, "format", "f", "", "Pretty-print network to JSON or using a Go template") + flags.StringVarP(&inspectOpts.Format, "format", "f", "", "Pretty-print network to JSON or using a Go template") } func networkInspect(_ *cobra.Command, args []string) error { - responses, err := registry.ContainerEngine().NetworkInspect(registry.Context(), args, entities.NetworkInspectOptions{}) - if err != nil { - return err - } - - switch { - case parse.MatchesJSONFormat(networkInspectOptions.Format) || networkInspectOptions.Format == "": - b, err := json.MarshalIndent(responses, "", " ") - if err != nil { - return err - } - fmt.Println(string(b)) - default: - row := report.NormalizeFormat(networkInspectOptions.Format) - // There can be more than 1 in the inspect output. - row = "{{range . }}" + row + "{{end}}" - tmpl, err := template.New("inspectNetworks").Parse(row) - if err != nil { - return err - } + inspectOpts.Type = inspect.NetworkType + return inspect.Inspect(args, *inspectOpts) - w := tabwriter.NewWriter(os.Stdout, 8, 2, 0, ' ', 0) - defer w.Flush() - - return tmpl.Execute(w, responses) - } - return nil } diff --git a/cmd/podman/networks/list.go b/cmd/podman/networks/list.go index c53f50c9f..532af631e 100644 --- a/cmd/podman/networks/list.go +++ b/cmd/podman/networks/list.go @@ -19,7 +19,7 @@ import ( var ( networklistDescription = `List networks` networklistCommand = &cobra.Command{ - Use: "ls", + Use: "ls [options]", Args: validate.NoArgs, Short: "network list", Long: networklistDescription, @@ -36,7 +36,7 @@ var ( func networkListFlags(flags *pflag.FlagSet) { // TODO enable filters based on something - //flags.StringSliceVarP(&networklistCommand.Filter, "filter", "f", []string{}, "Pause all running containers") + // flags.StringSliceVarP(&networklistCommand.Filter, "filter", "f", []string{}, "Pause all running containers") flags.StringVarP(&networkListOptions.Format, "format", "f", "", "Pretty-print networks to JSON or using a Go template") flags.BoolVarP(&networkListOptions.Quiet, "quiet", "q", false, "display only names") flags.StringVarP(&networkListOptions.Filter, "filter", "", "", "Provide filter values (e.g. 'name=podman')") diff --git a/cmd/podman/networks/rm.go b/cmd/podman/networks/rm.go index 86aad43cb..3d7db941a 100644 --- a/cmd/podman/networks/rm.go +++ b/cmd/podman/networks/rm.go @@ -16,7 +16,7 @@ import ( var ( networkrmDescription = `Remove networks` networkrmCommand = &cobra.Command{ - Use: "rm [flags] NETWORK [NETWORK...]", + Use: "rm [options] NETWORK [NETWORK...]", Short: "network rm", Long: networkrmDescription, RunE: networkRm, diff --git a/cmd/podman/play/kube.go b/cmd/podman/play/kube.go index 976d720ee..6072ea80c 100644 --- a/cmd/podman/play/kube.go +++ b/cmd/podman/play/kube.go @@ -32,7 +32,7 @@ var ( It creates the pod and containers described in the YAML. The containers within the pod are then started and the ID of the new Pod is output.` kubeCmd = &cobra.Command{ - Use: "kube [flags] KUBEFILE", + Use: "kube [options] KUBEFILE", Short: "Play a pod based on Kubernetes YAML.", Long: kubeDescription, RunE: kube, @@ -54,10 +54,10 @@ func init() { flags.StringVar(&kubeOptions.CredentialsCLI, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") flags.StringVar(&kubeOptions.Network, "network", "", "Connect pod to CNI network(s)") flags.BoolVarP(&kubeOptions.Quiet, "quiet", "q", false, "Suppress output information when pulling images") + flags.BoolVar(&kubeOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") + flags.StringVar(&kubeOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") if !registry.IsRemote() { - flags.StringVar(&kubeOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") flags.StringVar(&kubeOptions.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") - flags.BoolVar(&kubeOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") flags.StringVar(&kubeOptions.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") flags.StringVar(&kubeOptions.SeccompProfileRoot, "seccomp-profile-root", defaultSeccompRoot, "Directory path for seccomp profiles") flags.StringSliceVar(&kubeOptions.ConfigMaps, "configmap", []string{}, "`Pathname` of a YAML file containing a kubernetes configmap") diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go index ac6d83edd..efa84dcb4 100644 --- a/cmd/podman/pods/create.go +++ b/cmd/podman/pods/create.go @@ -27,7 +27,7 @@ var ( You can then start it at any time with the podman pod start <pod_id> command. The pod will be created with the initial state 'created'.` createCommand = &cobra.Command{ - Use: "create", + Use: "create [options]", Args: validate.NoArgs, Short: "Create a new empty pod", Long: podCreateDescription, diff --git a/cmd/podman/pods/inspect.go b/cmd/podman/pods/inspect.go index 142c8d270..7f81ba8fb 100644 --- a/cmd/podman/pods/inspect.go +++ b/cmd/podman/pods/inspect.go @@ -7,9 +7,8 @@ import ( "text/tabwriter" "text/template" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" @@ -26,7 +25,7 @@ var ( By default, this will render all results in a JSON array.`) inspectCmd = &cobra.Command{ - Use: "inspect [flags] POD [POD...]", + Use: "inspect [options] POD [POD...]", Short: "Displays a pod configuration", Long: inspectDescription, RunE: inspect, @@ -62,7 +61,7 @@ func inspect(cmd *cobra.Command, args []string) error { return err } - if parse.MatchesJSONFormat(inspectOptions.Format) { + if report.IsJSON(inspectOptions.Format) { enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") return enc.Encode(responses) diff --git a/cmd/podman/pods/kill.go b/cmd/podman/pods/kill.go index d666d7537..1902a2c80 100644 --- a/cmd/podman/pods/kill.go +++ b/cmd/podman/pods/kill.go @@ -16,7 +16,7 @@ var ( The default signal is SIGKILL, or any signal specified with option --signal.` killCommand = &cobra.Command{ - Use: "kill [flags] POD [POD...]", + Use: "kill [options] POD [POD...]", Short: "Send the specified signal or SIGKILL to containers in pod", Long: podKillDescription, RunE: kill, diff --git a/cmd/podman/pods/pause.go b/cmd/podman/pods/pause.go index 591378983..bba26f90d 100644 --- a/cmd/podman/pods/pause.go +++ b/cmd/podman/pods/pause.go @@ -16,7 +16,7 @@ var ( All running containers within each specified pod will then be paused.` pauseCommand = &cobra.Command{ - Use: "pause [flags] POD [POD...]", + Use: "pause [options] POD [POD...]", Short: "Pause one or more pods", Long: podPauseDescription, RunE: pause, diff --git a/cmd/podman/pods/prune.go b/cmd/podman/pods/prune.go index f13d95ae9..e3eae3f71 100644 --- a/cmd/podman/pods/prune.go +++ b/cmd/podman/pods/prune.go @@ -23,7 +23,7 @@ var ( pruneDescription = fmt.Sprintf(`podman pod prune Removes all exited pods`) pruneCommand = &cobra.Command{ - Use: "prune [flags]", + Use: "prune [options]", Args: validate.NoArgs, Short: "Remove all stopped pods and their containers", Long: pruneDescription, diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go index 0013cca02..688108c1a 100644 --- a/cmd/podman/pods/ps.go +++ b/cmd/podman/pods/ps.go @@ -10,9 +10,8 @@ import ( "text/template" "time" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/docker/go-units" @@ -25,7 +24,7 @@ var ( // Command: podman pod _ps_ psCmd = &cobra.Command{ - Use: "ps", + Use: "ps [options]", Aliases: []string{"ls", "list"}, Short: "List pods", Long: psDescription, @@ -85,7 +84,7 @@ func pods(cmd *cobra.Command, _ []string) error { } switch { - case parse.MatchesJSONFormat(psInput.Format): + case report.IsJSON(psInput.Format): b, err := json.MarshalIndent(responses, "", " ") if err != nil { return err diff --git a/cmd/podman/pods/restart.go b/cmd/podman/pods/restart.go index 11e8b4ebc..119b4ddee 100644 --- a/cmd/podman/pods/restart.go +++ b/cmd/podman/pods/restart.go @@ -16,7 +16,7 @@ var ( All of the containers within each of the specified pods will be restarted. If a container in a pod is not currently running it will be started.` restartCommand = &cobra.Command{ - Use: "restart [flags] POD [POD...]", + Use: "restart [options] POD [POD...]", Short: "Restart one or more pods", Long: podRestartDescription, RunE: restart, diff --git a/cmd/podman/pods/rm.go b/cmd/podman/pods/rm.go index 2975db3e8..714e075e2 100644 --- a/cmd/podman/pods/rm.go +++ b/cmd/podman/pods/rm.go @@ -28,7 +28,7 @@ var ( The pod name or ID can be used. A pod with containers will not be removed without --force. If --force is specified, all containers will be stopped, then removed.`) rmCommand = &cobra.Command{ - Use: "rm [flags] POD [POD...]", + Use: "rm [options] POD [POD...]", Short: "Remove one or more pods", Long: podRmDescription, RunE: rm, diff --git a/cmd/podman/pods/start.go b/cmd/podman/pods/start.go index e2ca3fd41..28ee4769a 100644 --- a/cmd/podman/pods/start.go +++ b/cmd/podman/pods/start.go @@ -24,7 +24,7 @@ var ( All containers defined in the pod will be started.` startCommand = &cobra.Command{ - Use: "start [flags] POD [POD...]", + Use: "start [options] POD [POD...]", Short: "Start one or more pods", Long: podStartDescription, RunE: start, diff --git a/cmd/podman/pods/stats.go b/cmd/podman/pods/stats.go index 2f59e4e47..338f13d3e 100644 --- a/cmd/podman/pods/stats.go +++ b/cmd/podman/pods/stats.go @@ -9,9 +9,8 @@ import ( "time" "github.com/buger/goterm" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/spf13/cobra" @@ -33,7 +32,7 @@ var ( statsDescription = `Display the containers' resource-usage statistics of one or more running pod` // Command: podman pod _pod_ statsCmd = &cobra.Command{ - Use: "stats [flags] [POD...]", + Use: "stats [options] [POD...]", Short: "Display a live stream of resource usage statistics for the containers in one or more pods", Long: statsDescription, RunE: stats, @@ -66,7 +65,7 @@ func stats(cmd *cobra.Command, args []string) error { } row := report.NormalizeFormat(statsOptions.Format) - doJSON := parse.MatchesJSONFormat(row) + doJSON := report.IsJSON(row) headers := report.Headers(entities.PodStatsReport{}, map[string]string{ "CPU": "CPU %", diff --git a/cmd/podman/pods/stop.go b/cmd/podman/pods/stop.go index 20c3f59bf..a2a9b0b57 100644 --- a/cmd/podman/pods/stop.go +++ b/cmd/podman/pods/stop.go @@ -29,7 +29,7 @@ var ( This command will stop all running containers in each of the specified pods.` stopCommand = &cobra.Command{ - Use: "stop [flags] POD [POD...]", + Use: "stop [options] POD [POD...]", Short: "Stop one or more pods", Long: podStopDescription, RunE: stop, diff --git a/cmd/podman/pods/top.go b/cmd/podman/pods/top.go index 9877db404..0ffa724da 100644 --- a/cmd/podman/pods/top.go +++ b/cmd/podman/pods/top.go @@ -23,7 +23,7 @@ var ( topOptions = entities.PodTopOptions{} topCommand = &cobra.Command{ - Use: "top [flags] POD [FORMAT-DESCRIPTORS|ARGS...]", + Use: "top [options] POD [FORMAT-DESCRIPTORS|ARGS...]", Short: "Display the running processes of containers in a pod", Long: topDescription, RunE: top, diff --git a/cmd/podman/pods/unpause.go b/cmd/podman/pods/unpause.go index 8474da57e..15b30db14 100644 --- a/cmd/podman/pods/unpause.go +++ b/cmd/podman/pods/unpause.go @@ -16,7 +16,7 @@ var ( The pod name or ID can be used.` unpauseCommand = &cobra.Command{ - Use: "unpause [flags] POD [POD...]", + Use: "unpause [options] POD [POD...]", Short: "Unpause one or more pods", Long: podUnpauseDescription, RunE: unpause, diff --git a/cmd/podman/report/format.go b/cmd/podman/report/format.go deleted file mode 100644 index 32d92bec5..000000000 --- a/cmd/podman/report/format.go +++ /dev/null @@ -1,68 +0,0 @@ -package report - -import ( - "reflect" - "strings" -) - -// tableReplacer will remove 'table ' prefix and clean up tabs -var tableReplacer = strings.NewReplacer( - "table ", "", - `\t`, "\t", - `\n`, "\n", - " ", "\t", -) - -// escapedReplacer will clean up escaped characters from CLI -var escapedReplacer = strings.NewReplacer( - `\t`, "\t", - `\n`, "\n", -) - -// NormalizeFormat reads given go template format provided by CLI and munges it into what we need -func NormalizeFormat(format string) string { - f := format - // two replacers used so we only remove the prefix keyword `table` - if strings.HasPrefix(f, "table ") { - f = tableReplacer.Replace(f) - } else { - f = escapedReplacer.Replace(format) - } - - if !strings.HasSuffix(f, "\n") { - f += "\n" - } - - return f -} - -// Headers queries the interface for field names -func Headers(object interface{}, overrides map[string]string) []map[string]string { - value := reflect.ValueOf(object) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - // Column header will be field name upper-cased. - headers := make(map[string]string, value.NumField()) - for i := 0; i < value.Type().NumField(); i++ { - field := value.Type().Field(i) - // Recurse to find field names from promoted structs - if field.Type.Kind() == reflect.Struct && field.Anonymous { - h := Headers(reflect.New(field.Type).Interface(), nil) - for k, v := range h[0] { - headers[k] = v - } - continue - } - headers[field.Name] = strings.ToUpper(field.Name) - } - - if len(overrides) > 0 { - // Override column header as provided - for k, v := range overrides { - headers[k] = strings.ToUpper(v) - } - } - return []map[string]string{headers} -} diff --git a/cmd/podman/report/format_test.go b/cmd/podman/report/format_test.go deleted file mode 100644 index 7dd62e899..000000000 --- a/cmd/podman/report/format_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package report - -import ( - "strings" - "testing" -) - -func TestNormalizeFormat(t *testing.T) { - cases := []struct { - format string - expected string - }{ - {"table {{.ID}}", "{{.ID}}\n"}, - {"table {{.ID}} {{.C}}", "{{.ID}}\t{{.C}}\n"}, - {"{{.ID}}", "{{.ID}}\n"}, - {"{{.ID}}\n", "{{.ID}}\n"}, - {"{{.ID}} {{.C}}", "{{.ID}} {{.C}}\n"}, - {"\t{{.ID}}", "\t{{.ID}}\n"}, - {`\t` + "{{.ID}}", "\t{{.ID}}\n"}, - {"table {{.ID}}\t{{.C}}", "{{.ID}}\t{{.C}}\n"}, - {"{{.ID}} table {{.C}}", "{{.ID}} table {{.C}}\n"}, - } - for _, tc := range cases { - tc := tc - - label := strings.ReplaceAll(tc.format, " ", "<sp>") - t.Run("NormalizeFormat/"+label, func(t *testing.T) { - t.Parallel() - actual := NormalizeFormat(tc.format) - if actual != tc.expected { - t.Errorf("Expected %q, actual %q", tc.expected, actual) - } - }) - } -} diff --git a/cmd/podman/report/report.go b/cmd/podman/report/report.go deleted file mode 100644 index 2c4f2e1fd..000000000 --- a/cmd/podman/report/report.go +++ /dev/null @@ -1,6 +0,0 @@ -package report - -import "github.com/containers/podman/v2/cmd/podman/registry" - -// Pull in configured json library -var json = registry.JSONLibrary() diff --git a/cmd/podman/root.go b/cmd/podman/root.go index 6293fa17d..b59b8341a 100644 --- a/cmd/podman/root.go +++ b/cmd/podman/root.go @@ -38,7 +38,7 @@ Description: // command should not use this. const usageTemplate = `Usage:{{if (and .Runnable (not .HasAvailableSubCommands))}} {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}} - {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}} + {{.UseLine}} [command]{{end}}{{if gt (len .Aliases) 0}} Aliases: {{.NameAndAliases}}{{end}}{{if .HasExample}} @@ -49,24 +49,24 @@ Examples: Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}} {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} -Flags: +Options: {{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} {{end}} ` var ( rootCmd = &cobra.Command{ - Use: path.Base(os.Args[0]), - Long: "Manage pods, containers and images", - SilenceUsage: true, - SilenceErrors: true, - TraverseChildren: true, - PersistentPreRunE: persistentPreRunE, - RunE: validate.SubCommandExists, - PersistentPostRunE: persistentPostRunE, - Version: version.Version.String(), + Use: path.Base(os.Args[0]) + " [options]", + Long: "Manage pods, containers and images", + SilenceUsage: true, + SilenceErrors: true, + TraverseChildren: true, + PersistentPreRunE: persistentPreRunE, + RunE: validate.SubCommandExists, + PersistentPostRunE: persistentPostRunE, + Version: version.Version.String(), + DisableFlagsInUseLine: true, } - logLevels = []string{"debug", "info", "warn", "error", "fatal", "panic"} logLevel = "error" useSyslog bool @@ -81,6 +81,7 @@ func init() { ) rootFlags(rootCmd, registry.PodmanConfig()) + rootCmd.SetUsageTemplate(usageTemplate) } func Execute() { diff --git a/cmd/podman/system/connection/add.go b/cmd/podman/system/connection/add.go index df036af1a..0d81a64ca 100644 --- a/cmd/podman/system/connection/add.go +++ b/cmd/podman/system/connection/add.go @@ -27,7 +27,7 @@ const schemaPattern = "^[A-Za-z][A-Za-z0-9+.-]*:" var ( addCmd = &cobra.Command{ - Use: "add [flags] NAME DESTINATION", + Use: "add [options] NAME DESTINATION", Args: cobra.ExactArgs(2), Short: "Record destination for the Podman service", Long: `Add destination to podman configuration. diff --git a/cmd/podman/system/df.go b/cmd/podman/system/df.go index da7bbed02..b11167938 100644 --- a/cmd/podman/system/df.go +++ b/cmd/podman/system/df.go @@ -8,8 +8,8 @@ import ( "text/template" "time" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/docker/go-units" @@ -23,7 +23,7 @@ var ( Show podman disk usage ` dfSystemCommand = &cobra.Command{ - Use: "df", + Use: "df [options]", Args: validate.NoArgs, Short: "Show podman disk usage", Long: dfSystemDescription, diff --git a/cmd/podman/system/events.go b/cmd/podman/system/events.go index aaf572873..368cd41a6 100644 --- a/cmd/podman/system/events.go +++ b/cmd/podman/system/events.go @@ -6,7 +6,7 @@ import ( "os" "text/template" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/libpod/events" @@ -20,7 +20,7 @@ var ( By default, streaming mode is used, printing new events as they occur. Previous events can be listed via --since and --until.` eventsCommand = &cobra.Command{ - Use: "events", + Use: "events [options]", Args: validate.NoArgs, Short: "Show podman events", Long: eventsDescription, @@ -65,7 +65,7 @@ func eventsCmd(cmd *cobra.Command, _ []string) error { ) if cmd.Flags().Changed("format") { - doJSON = parse.MatchesJSONFormat(eventFormat) + doJSON = report.IsJSON(eventFormat) if !doJSON { var err error tmpl, err = template.New("events").Parse(eventFormat) diff --git a/cmd/podman/system/info.go b/cmd/podman/system/info.go index ee720abf8..dece6b37e 100644 --- a/cmd/podman/system/info.go +++ b/cmd/podman/system/info.go @@ -5,7 +5,7 @@ import ( "os" "text/template" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" @@ -20,7 +20,7 @@ var ( Useful for the user and when reporting issues. ` infoCommand = &cobra.Command{ - Use: "info", + Use: "info [options]", Args: validate.NoArgs, Long: infoDescription, Short: "Display podman system information", @@ -70,7 +70,7 @@ func info(cmd *cobra.Command, args []string) error { } switch { - case parse.MatchesJSONFormat(inFormat): + case report.IsJSON(inFormat): b, err := json.MarshalIndent(info, "", " ") if err != nil { return err diff --git a/cmd/podman/system/migrate.go b/cmd/podman/system/migrate.go index 018701fc9..7870df60b 100644 --- a/cmd/podman/system/migrate.go +++ b/cmd/podman/system/migrate.go @@ -21,7 +21,7 @@ var ( ` migrateCommand = &cobra.Command{ - Use: "migrate", + Use: "migrate [options]", Args: validate.NoArgs, Short: "Migrate containers", Long: migrateDescription, diff --git a/cmd/podman/system/prune.go b/cmd/podman/system/prune.go index f34df3698..a229b06b0 100644 --- a/cmd/podman/system/prune.go +++ b/cmd/podman/system/prune.go @@ -25,7 +25,7 @@ var ( `) pruneCommand = &cobra.Command{ - Use: "prune [flags]", + Use: "prune [options]", Short: "Remove unused data", Args: validate.NoArgs, Long: pruneDescription, diff --git a/cmd/podman/system/reset.go b/cmd/podman/system/reset.go index a7a42ea58..0b04c6ee0 100644 --- a/cmd/podman/system/reset.go +++ b/cmd/podman/system/reset.go @@ -22,7 +22,7 @@ var ( All containers will be stopped and removed, and all images, volumes and container content will be removed. ` systemResetCommand = &cobra.Command{ - Use: "reset", + Use: "reset [options]", Args: validate.NoArgs, Short: "Reset podman storage", Long: systemResetDescription, diff --git a/cmd/podman/system/service.go b/cmd/podman/system/service.go index 7c692b07e..2a2b1984f 100644 --- a/cmd/podman/system/service.go +++ b/cmd/podman/system/service.go @@ -26,7 +26,7 @@ Enable a listening service for API access to Podman commands. ` srvCmd = &cobra.Command{ - Use: "service [flags] [URI]", + Use: "service [options] [URI]", Args: cobra.MaximumNArgs(1), Short: "Run API service", Long: srvDescription, diff --git a/cmd/podman/system/varlink.go b/cmd/podman/system/varlink.go index 57e7e4ba6..89669d51a 100644 --- a/cmd/podman/system/varlink.go +++ b/cmd/podman/system/varlink.go @@ -16,7 +16,7 @@ var ( Tools speaking varlink protocol can remotely manage pods, containers and images. ` varlinkCmd = &cobra.Command{ - Use: "varlink [flags] [URI]", + Use: "varlink [options] [URI]", Args: cobra.MinimumNArgs(1), Short: "Run varlink interface", Long: varlinkDescription, diff --git a/cmd/podman/system/version.go b/cmd/podman/system/version.go index 4f47c5fba..b790a7511 100644 --- a/cmd/podman/system/version.go +++ b/cmd/podman/system/version.go @@ -8,9 +8,8 @@ import ( "text/tabwriter" "text/template" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/domain/entities" @@ -19,7 +18,7 @@ import ( var ( versionCommand = &cobra.Command{ - Use: "version", + Use: "version [options]", Args: validate.NoArgs, Short: "Display the Podman Version Information", RunE: version, @@ -42,7 +41,7 @@ func version(cmd *cobra.Command, args []string) error { return err } - if parse.MatchesJSONFormat(versionFormat) { + if report.IsJSON(versionFormat) { s, err := json.MarshalToString(versions) if err != nil { return err diff --git a/cmd/podman/volumes/create.go b/cmd/podman/volumes/create.go index 934a552dc..a54530183 100644 --- a/cmd/podman/volumes/create.go +++ b/cmd/podman/volumes/create.go @@ -15,7 +15,7 @@ var ( createDescription = `If using the default driver, "local", the volume will be created on the host in the volumes directory under container storage.` createCommand = &cobra.Command{ - Use: "create [flags] [NAME]", + Use: "create [options] [NAME]", Short: "Create a new volume", Long: createDescription, RunE: create, diff --git a/cmd/podman/volumes/inspect.go b/cmd/podman/volumes/inspect.go index 8d1350228..c6edcf809 100644 --- a/cmd/podman/volumes/inspect.go +++ b/cmd/podman/volumes/inspect.go @@ -1,17 +1,11 @@ package volumes import ( - "fmt" - "os" - "text/template" - - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/podman/v2/cmd/podman/inspect" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" - "golang.org/x/net/context" ) var ( @@ -19,10 +13,10 @@ var ( Use a Go template to change the format from JSON.` inspectCommand = &cobra.Command{ - Use: "inspect [flags] VOLUME [VOLUME...]", + Use: "inspect [options] VOLUME [VOLUME...]", Short: "Display detailed information on one or more volumes", Long: volumeInspectDescription, - RunE: inspect, + RunE: volumeInspect, Example: `podman volume inspect myvol podman volume inspect --all podman volume inspect --format "{{.Driver}} {{.Scope}}" myvol`, @@ -30,8 +24,7 @@ var ( ) var ( - inspectOpts = entities.VolumeInspectOptions{} - inspectFormat string + inspectOpts *entities.InspectOptions ) func init() { @@ -40,34 +33,16 @@ func init() { Command: inspectCommand, Parent: volumeCmd, }) + inspectOpts = new(entities.InspectOptions) flags := inspectCommand.Flags() flags.BoolVarP(&inspectOpts.All, "all", "a", false, "Inspect all volumes") - flags.StringVarP(&inspectFormat, "format", "f", "json", "Format volume output using Go template") + flags.StringVarP(&inspectOpts.Format, "format", "f", "json", "Format volume output using Go template") } -func inspect(cmd *cobra.Command, args []string) error { +func volumeInspect(cmd *cobra.Command, args []string) error { if (inspectOpts.All && len(args) > 0) || (!inspectOpts.All && len(args) < 1) { return errors.New("provide one or more volume names or use --all") } - responses, err := registry.ContainerEngine().VolumeInspect(context.Background(), args, inspectOpts) - if err != nil { - return err - } - - switch { - case parse.MatchesJSONFormat(inspectFormat), inspectFormat == "": - jsonOut, err := json.MarshalIndent(responses, "", " ") - if err != nil { - return errors.Wrapf(err, "error marshalling inspect JSON") - } - fmt.Println(string(jsonOut)) - default: - row := "{{range . }}" + report.NormalizeFormat(inspectFormat) + "{{end}}" - tmpl, err := template.New("volumeInspect").Parse(row) - if err != nil { - return err - } - return tmpl.Execute(os.Stdout, responses) - } - return nil + inspectOpts.Type = inspect.VolumeType + return inspect.Inspect(args, *inspectOpts) } diff --git a/cmd/podman/volumes/list.go b/cmd/podman/volumes/list.go index 18765a499..b3b2b8ea1 100644 --- a/cmd/podman/volumes/list.go +++ b/cmd/podman/volumes/list.go @@ -8,9 +8,8 @@ import ( "text/tabwriter" "text/template" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" @@ -24,7 +23,7 @@ podman volume ls List all available volumes. The output of the volumes can be filtered and the output format can be changed to JSON or a user specified Go template.` lsCommand = &cobra.Command{ - Use: "ls", + Use: "ls [options]", Aliases: []string{"list"}, Args: validate.NoArgs, Short: "List volumes", @@ -75,7 +74,7 @@ func list(cmd *cobra.Command, args []string) error { } switch { - case parse.MatchesJSONFormat(cliOpts.Format): + case report.IsJSON(cliOpts.Format): return outputJSON(responses) case len(responses) < 1: return nil diff --git a/cmd/podman/volumes/prune.go b/cmd/podman/volumes/prune.go index 78c258bec..79e6f1a54 100644 --- a/cmd/podman/volumes/prune.go +++ b/cmd/podman/volumes/prune.go @@ -21,7 +21,7 @@ var ( The command prompts for confirmation which can be overridden with the --force flag. Note all data will be destroyed.` pruneCommand = &cobra.Command{ - Use: "prune", + Use: "prune [options]", Args: validate.NoArgs, Short: "Remove all unused volumes", Long: volumePruneDescription, diff --git a/cmd/podman/volumes/rm.go b/cmd/podman/volumes/rm.go index 4c960d4d5..4026764ac 100644 --- a/cmd/podman/volumes/rm.go +++ b/cmd/podman/volumes/rm.go @@ -18,7 +18,7 @@ var ( By default only volumes that are not being used by any containers will be removed. To remove the volumes anyways, use the --force flag.` rmCommand = &cobra.Command{ - Use: "rm [flags] VOLUME [VOLUME...]", + Use: "rm [options] VOLUME [VOLUME...]", Aliases: []string{"remove"}, Short: "Remove one or more volumes", Long: volumeRmDescription, diff --git a/completions/zsh/_podman b/completions/zsh/_podman index 067eebbbb..b65c3dbb8 100644 --- a/completions/zsh/_podman +++ b/completions/zsh/_podman @@ -31,48 +31,48 @@ _read_podman_commands() { eval "$_var_ref=(\$_podman_commands)" } -# Run 'podman XX --help', set _podman_flag_list to a formatted list -# of flag options for XX -_read_podman_flags() { +# Run 'podman XX --help', set _podman_option_list to a formatted list +# of option options for XX +_read_podman_options() { local line - local _var_ref=_podman_flags_"${*// /_}" + local _var_ref=_podman_options_"${*// /_}" eval "typeset -ga ${_var_ref}" - typeset -ga _podman_flag_list - _podman_flag_list=(${(P)_var_ref}) - (( $#_podman_flag_list )) && return + typeset -ga _podman_option_list + _podman_option_list=(${(P)_var_ref}) + (( $#_podman_option_list )) && return - # Extract the Flags; strip leading whitespace; pack '-f, --foo' + # Extract the Options; strip leading whitespace; pack '-f, --foo' # as '-f,--foo' (no space); then add '=' to '--foo string'. # The result will be, e.g. '-f,--foo=string Description of Option' _call_program podman podman "$@" --help |\ - sed -n -e '0,/^Flags:/d' -e '/^$/q;p' |\ + sed -n -e '0,/^Options:/d' -e '/^$/q;p' |\ grep '^ \+-' |\ sed -e 's/^ *//' -e 's/^\(-.,\) --/\1--/' |\ sed -e 's/^\(-[^ ]\+\) \([^ ]\+\) /\1=\2 /' |\ - while read flags desc;do - # flags like --foo=string: split into --foo & string + while read options desc;do + # options like --foo=string: split into --foo & string local -a tmpa local optval= - tmpa=(${(s.=.)flags}) + tmpa=(${(s.=.)options}) if [ -n "$tmpa[2]" ]; then - flags=$tmpa[1] + options=$tmpa[1] optval=$tmpa[2] fi # 'podman attach --detach-keys' includes ']' in help msg desc=${desc//\]/\\]} - for flag in ${(s:,:)flags}; do + for option in ${(s:,:)options}; do if [ -n "$optval" ]; then - _podman_flag_list+=("${flag}[$desc]:$(_podman_find_helper ${flags} ${optval} ${desc})") + _podman_option_list+=("${option}[$desc]:$(_podman_find_helper ${options} ${optval} ${desc})") else - _podman_flag_list+=("${flag}[$desc]") + _podman_option_list+=("${option}[$desc]") fi done done - eval "typeset -ga $_var_ref=(\$_podman_flag_list)" + eval "typeset -ga $_var_ref=(\$_podman_option_list)" } # Run 'podman XXX --help', set _podman_usage to the line after "Usage:" @@ -95,9 +95,9 @@ _read_podman_usage() { ############################################################################### # BEGIN custom helpers for individual option arguments -# Find a zsh helper for a given flag or command-line option +# Find a zsh helper for a given option or command-line option _podman_find_helper() { - local flags=$1 + local options=$1 local optval=$2 local desc=$3 local helper= @@ -113,7 +113,7 @@ _podman_find_helper() { helper=_files # For messages like 'restart policy ("always"|"no"|"on-failure") elif optlist=$(expr "$desc" : '.*(\(\"[^\\)]\+|[^\\)]\+\"\))' 2>/dev/null); then - optval=${${flags##--}//-/ } # "--log-level" => "log level" + optval=${${options##--}//-/ } # "--log-level" => "log level" optlist=${optlist//\"/} # "a"|"b"|"c" => a|b|c optlist=${optlist//\|/ } # a|b|c => a b c # FIXME: how to present values _in order_, not sorted alphabetically? @@ -205,8 +205,8 @@ _set_up_podman_args() { _read_podman_usage "$@" typeset -ga _podman_args=() - # E.g. 'podman exec [flags] CONTAINER [...' -> 'CONTAINER [....' - local usage_rhs=$(expr "$_podman_usage" : ".*\[flags\] \+\(.*\)") + # E.g. 'podman exec [options] CONTAINER [...' -> 'CONTAINER [....' + local usage_rhs=$(expr "$_podman_usage" : ".*\[options\] \+\(.*\)") # e.g. podman pod ps which takes no further args if [ -z "$usage_rhs" ]; then @@ -290,14 +290,14 @@ _set_up_podman_args() { # For an endpoint command, i.e. not a subcommand. _podman_terminus() { typeset -A opt_args - typeset -ga _podman_flag_list + typeset -ga _podman_option_list typeset -ga _podman_args integer ret=1 # Find out what args it takes (e.g. image(s), container(s)) and see # if we have helpers for them. _set_up_podman_args "$@" - _arguments -C $_podman_flag_list $_podman_args && ret=0 + _arguments -C $_podman_option_list $_podman_args && ret=0 return ret } @@ -319,7 +319,7 @@ _podman_terminus() { # zsh first calls us with words=(podman container mount) but we don't # want all that full context yet! We want to go a piece at a time, # handling 'container' first, then 'mount'; ending up with our -# final 'podman container mount --help' giving us suitable flags +# final 'podman container mount --help' giving us suitable options # and no subcommands; from which we determine that it's a terminus # and jump to a function that handles non-subcommand arguments. # @@ -333,15 +333,15 @@ _podman_subcommand() { # Run 'podman --help' / 'podman system --help' for our context (initially # empty, then possibly under subcommands); from those, get a list of - # flags appropriate for this context and, if applicable, subcommands. - _read_podman_flags "$@" + # options appropriate for this context and, if applicable, subcommands. + _read_podman_options "$@" _read_podman_commands "$@" # Now, is this a sub-subcommand, or does it have args? if (( $#_podman_commands )); then # Subcommands required (podman, podman system, etc) local cmd=${words// /_} - _arguments -C $_podman_flag_list \ + _arguments -C $_podman_option_list \ "(-): :->command" \ "(-)*:: :->option-or-argument" \ && ret=0 diff --git a/docs/remote-docs.sh b/docs/remote-docs.sh index a9fda4696..67c731e75 100755 --- a/docs/remote-docs.sh +++ b/docs/remote-docs.sh @@ -78,7 +78,7 @@ function html_fn() { # the command name but not its description. function podman_commands() { $PODMAN help "$@" |\ - awk '/^Available Commands:/{ok=1;next}/^Flags:/{ok=0}ok { print $1 }' |\ + awk '/^Available Commands:/{ok=1;next}/^Options:/{ok=0}ok { print $1 }' |\ grep . } diff --git a/docs/source/markdown/podman-auto-update.1.md b/docs/source/markdown/podman-auto-update.1.md index b6941362a..3ab097388 100644 --- a/docs/source/markdown/podman-auto-update.1.md +++ b/docs/source/markdown/podman-auto-update.1.md @@ -31,7 +31,7 @@ Systemd units that start and stop a container cannot run a new image. **--authfile**=*path* Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json, which is set using `podman login`. -If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`. (Not available for remote commands) +If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`. Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE environment variable. `export REGISTRY_AUTH_FILE=path` diff --git a/docs/source/markdown/podman-build.1.md b/docs/source/markdown/podman-build.1.md index 7d0aa5001..f1fddb2fc 100644 --- a/docs/source/markdown/podman-build.1.md +++ b/docs/source/markdown/podman-build.1.md @@ -46,7 +46,7 @@ Set the ARCH of the image to the provided value instead of the architecture of t **--authfile**=*path* Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json, which is set using `podman login`. -If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`. (Not available for remote commands) +If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`. Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE environment variable. `export REGISTRY_AUTH_FILE=path` @@ -111,17 +111,27 @@ network namespaces can be found. **--cpu-period**=*limit* -Limit the CPU CFS (Completely Fair Scheduler) period +Set the CPU period for the Completely Fair Scheduler (CFS), which is a +duration in microseconds. Once the container's CPU quota is used up, it will +not be scheduled to run until the current period ends. Defaults to 100000 +microseconds. -Limit the container's CPU usage. This flag tell the kernel to restrict the container's CPU usage to the period you specify. +On some systems, changing the CPU limits may not be allowed for non-root +users. For more details, see +https://github.com/containers/podman/blob/master/troubleshooting.md#26-running-containers-with-cpu-limits-fails-with-a-permissions-error **--cpu-quota**=*limit* -Limit the CPU CFS (Completely Fair Scheduler) quota +Limit the CPU Completely Fair Scheduler (CFS) quota. Limit the container's CPU usage. By default, containers run with the full -CPU resource. This flag tell the kernel to restrict the container's CPU usage -to the quota you specify. +CPU resource. The limit is a number in microseconds. If you provide a number, +the container will be allowed to use that much CPU time until the CPU period +ends (controllable via **--cpu-period**). + +On some systems, changing the CPU limits may not be allowed for non-root +users. For more details, see +https://github.com/containers/podman/blob/master/troubleshooting.md#26-running-containers-with-cpu-limits-fails-with-a-permissions-error **--cpu-shares**, **-c**=*shares* @@ -470,7 +480,7 @@ When --timestamp is set, the created timestamp is always set to the time specifi **--tls-verify**=*true|false* -Require HTTPS and verify certificates when talking to container registries (defaults to true). (Not available for remote commands) +Require HTTPS and verify certificates when talking to container registries (defaults to true). **--ulimit**=*type*=*soft-limit*[:*hard-limit*] @@ -787,9 +797,9 @@ registries.conf is the configuration file which specifies which container regist ## Troubleshooting -If you are using a useradd command within a Containerfile with a large UID/GID, it will create a large sparse file `/var/log/lastlog`. This can cause the build to hang forever. Go language does not support sparse files correctly, which can lead to some huge files being created in your container image. +### lastlog sparse file -### Solution +If you are using a useradd command within a Containerfile with a large UID/GID, it will create a large sparse file `/var/log/lastlog`. This can cause the build to hang forever. Go language does not support sparse files correctly, which can lead to some huge files being created in your container image. If you are using `useradd` within your build script, you should pass the `--no-log-init or -l` option to the `useradd` command. This option tells useradd to stop creating the lastlog file. diff --git a/docs/source/markdown/podman-container-runlabel.1.md b/docs/source/markdown/podman-container-runlabel.1.md index f56fc7d7b..676ad12d0 100644 --- a/docs/source/markdown/podman-container-runlabel.1.md +++ b/docs/source/markdown/podman-container-runlabel.1.md @@ -44,7 +44,7 @@ Any additional arguments will be appended to the command. **--authfile**=*path* Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json, which is set using `podman login`. -If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`. (Not available for remote commands) +If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`. Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE environment variable. `export REGISTRY_AUTH_FILE=path` @@ -85,7 +85,7 @@ created from this image. Require HTTPS and verify certificates when contacting registries (default: true). If explicitly set to true, then TLS verification will be used. If set to false, then TLS verification will not be used. If not specified, -TLS verification will be used unless the target registry is listed as an insecure registry in registries.conf (Not available for remote commands) +TLS verification will be used unless the target registry is listed as an insecure registry in registries.conf. ## EXAMPLES diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md index 3303ae572..c7e00f594 100644 --- a/docs/source/markdown/podman-create.1.md +++ b/docs/source/markdown/podman-create.1.md @@ -47,7 +47,7 @@ each of stdin, stdout, and stderr. Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE -environment variable. `export REGISTRY_AUTH_FILE=path` (Not available for remote commands) +environment variable. `export REGISTRY_AUTH_FILE=path` **--blkio-weight**=*weight* @@ -103,17 +103,27 @@ Write the pid of the `conmon` process to a file. `conmon` runs in a separate pro **--cpu-period**=*limit* -Limit the CPU CFS (Completely Fair Scheduler) period +Set the CPU period for the Completely Fair Scheduler (CFS), which is a +duration in microseconds. Once the container's CPU quota is used up, it will +not be scheduled to run until the current period ends. Defaults to 100000 +microseconds. -Limit the container's CPU usage. This flag tell the kernel to restrict the container's CPU usage to the period you specify. +On some systems, changing the CPU limits may not be allowed for non-root +users. For more details, see +https://github.com/containers/podman/blob/master/troubleshooting.md#26-running-containers-with-cpu-limits-fails-with-a-permissions-error **--cpu-quota**=*limit* -Limit the CPU CFS (Completely Fair Scheduler) quota +Limit the CPU Completely Fair Scheduler (CFS) quota. Limit the container's CPU usage. By default, containers run with the full -CPU resource. This flag tell the kernel to restrict the container's CPU usage -to the quota you specify. +CPU resource. The limit is a number in microseconds. If you provide a number, +the container will be allowed to use that much CPU time until the CPU period +ends (controllable via **--cpu-period**). + +On some systems, changing the CPU limits may not be allowed for non-root +users. For more details, see +https://github.com/containers/podman/blob/master/troubleshooting.md#26-running-containers-with-cpu-limits-fails-with-a-permissions-error **--cpu-rt-period**=*microseconds* @@ -169,7 +179,13 @@ PID container CPU CPU share **--cpus**=*number* -Number of CPUs. The default is *0.0* which means no limit. +Number of CPUs. The default is *0.0* which means no limit. This is shorthand +for **--cpu-period** and **--cpu-quota**, so you may only set either +**--cpus** or **--cpu-period** and **--cpu-quota**. + +On some systems, changing the CPU limits may not be allowed for non-root +users. For more details, see +https://github.com/containers/podman/blob/master/troubleshooting.md#26-running-containers-with-cpu-limits-fails-with-a-permissions-error **--cpuset-cpus**=*cpus* @@ -183,23 +199,6 @@ If you have four memory nodes on your system (0-3), use `--cpuset-mems=0,1` then processes in your container will only use memory from the first two memory nodes. -**--detach**, **-d**=*true|false* - -Detached mode: run the container in the background and print the new container ID. The default is *false*. - -At any time you can run **podman ps** in -the other shell to view a list of the running containers. You can reattach to a -detached container with **podman attach**. - -When attached in the tty mode, you can detach from the container (and leave it -running) using a configurable key sequence. The default sequence is `ctrl-p,ctrl-q`. -Configure the keys sequence using the **--detach-keys** option, or specifying -it in the **containers.conf** file: see **containers.conf(5)** for more information. - -**--detach-keys**=*sequence* - -Specify the key sequence for detaching a container. Format is a single character `[a-Z]` or one or more `ctrl-<value>` characters where `<value>` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`. Specifying "" will disable this feature. The default is *ctrl-p,ctrl-q*. - **--device**=_host-device_[**:**_container-device_][**:**_permissions_] Add a host device to the container. Optional *permissions* parameter diff --git a/docs/source/markdown/podman-events.1.md b/docs/source/markdown/podman-events.1.md index 0d91cdf17..d0bc3cef8 100644 --- a/docs/source/markdown/podman-events.1.md +++ b/docs/source/markdown/podman-events.1.md @@ -86,6 +86,7 @@ filters are supported: * container=name_or_id * event=event_status (described above) * image=name_or_id + * label=key=value * pod=name_or_id * volume=name_or_id * type=event_type (described above) diff --git a/docs/source/markdown/podman-inspect.1.md b/docs/source/markdown/podman-inspect.1.md index a1dcd1a0e..eb7cf74c6 100644 --- a/docs/source/markdown/podman-inspect.1.md +++ b/docs/source/markdown/podman-inspect.1.md @@ -1,7 +1,7 @@ % podman-inspect(1) ## NAME -podman\-inspect - Display a container or image's configuration +podman\-inspect - Display a container, image, volume, network, or pod's configuration ## SYNOPSIS **podman inspect** [*options*] *name* [...] @@ -9,8 +9,9 @@ podman\-inspect - Display a container or image's configuration ## DESCRIPTION This displays the low-level information on containers and images identified by name or ID. By default, this will render -all results in a JSON array. If the container and image have the same name, this will return container JSON for -unspecified type. If a format is specified, the given template will be executed for each result. +all results in a JSON array. If the inspect type is all, the order of inspection is: containers, images, volumes, network, pods. + So, if a container has the same name as an image, then the container JSON will be returned, and so on. + If a format is specified, the given template will be executed for each result. For more inspection options, see: @@ -25,7 +26,7 @@ For more inspection options, see: **--type**, **-t**=*type* -Return JSON for the specified type. Type can be 'container', 'image' or 'all' (default: all) +Return JSON for the specified type. Type can be 'container', 'image', 'volume', 'network', 'pod', or 'all' (default: all) (Only meaningful when invoked as *podman inspect*) **--format**, **-f**=*format* @@ -38,6 +39,8 @@ The keys of the returned JSON can be used as the values for the --format flag (s Instead of providing the container name or ID, use the last created container. If you use methods other than Podman to run containers such as CRI-O, the last started container could be from either of those methods. +This option can be used to inspect the latest pod created when used with --type pod + The latest option is not supported on the remote client or when invoked as *podman image inspect*. **--size**, **-s** @@ -148,6 +151,20 @@ podman container inspect --latest --format {{.EffectiveCaps}} [CAP_CHOWN CAP_DAC_OVERRIDE CAP_FSETID CAP_FOWNER CAP_MKNOD CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SETFCAP CAP_SETPCAP CAP_NET_BIND_SERVICE CAP_SYS_CHROOT CAP_KILL CAP_AUDIT_WRITE] ``` +``` +# podman inspect myPod --type pod --format "{{.Name}}" +myPod +``` +``` +# podman inspect myVolume --type volume --format "{{.Name}}" +myVolume +``` + +``` +# podman inspect nyNetwork --type network --format "{{.name}}" +myNetwork +``` + ## SEE ALSO podman(1) diff --git a/docs/source/markdown/podman-login.1.md b/docs/source/markdown/podman-login.1.md index 9b4ff74ed..7c09d99fe 100644 --- a/docs/source/markdown/podman-login.1.md +++ b/docs/source/markdown/podman-login.1.md @@ -42,7 +42,7 @@ Username for registry **--authfile**=*path* -Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json (Not available for remote commands) +Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json. Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE environment variable. `export REGISTRY_AUTH_FILE=path` @@ -60,7 +60,7 @@ Default certificates directory is _/etc/containers/certs.d_. (Not available for Require HTTPS and verify certificates when contacting registries (default: true). If explicitly set to true, then TLS verification will be used. If set to false, then TLS verification will not be used. If not specified, -TLS verification will be used unless the target registry is listed as an insecure registry in registries.conf. (Not available for remote commands) +TLS verification will be used unless the target registry is listed as an insecure registry in registries.conf. **--help**, **-h** diff --git a/docs/source/markdown/podman-logout.1.md b/docs/source/markdown/podman-logout.1.md index 0ff954d43..25f6d97b1 100644 --- a/docs/source/markdown/podman-logout.1.md +++ b/docs/source/markdown/podman-logout.1.md @@ -23,7 +23,7 @@ All the cached credentials can be removed by setting the **all** flag. **--authfile**=*path* -Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json (Not available for remote commands) +Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json. Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE environment variable. `export REGISTRY_AUTH_FILE=path` diff --git a/docs/source/markdown/podman-manifest-add.1.md b/docs/source/markdown/podman-manifest-add.1.md index c4d4417c4..4c0bad2ae 100644 --- a/docs/source/markdown/podman-manifest-add.1.md +++ b/docs/source/markdown/podman-manifest-add.1.md @@ -36,7 +36,7 @@ retrieved from the image's configuration information. **--authfile**=*path* Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json, which is set using `podman login`. -If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`. (Not available for remote commands) +If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`. Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE environment variable. `export REGISTRY_AUTH_FILE=path` @@ -71,7 +71,7 @@ image. This option is rarely used. **--tls-verify** -Require HTTPS and verify certificates when talking to container registries (defaults to true). (Not available for remote commands) +Require HTTPS and verify certificates when talking to container registries (defaults to true). **--variant** diff --git a/docs/source/markdown/podman-manifest-push.1.md b/docs/source/markdown/podman-manifest-push.1.md index 33b2a24c5..9cf0b159c 100644 --- a/docs/source/markdown/podman-manifest-push.1.md +++ b/docs/source/markdown/podman-manifest-push.1.md @@ -22,7 +22,7 @@ the list or index itself. **--authfile**=*path* Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json, which is set using `podman login`. -If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`. (Not available for remote commands) +If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`. Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE environment variable. `export REGISTRY_AUTH_FILE=path` @@ -64,7 +64,7 @@ Sign the pushed images using the GPG key that matches the specified fingerprint. **--tls-verify** -Require HTTPS and verify certificates when talking to container registries (defaults to true) (Not available for remote commands) +Require HTTPS and verify certificates when talking to container registries. (defaults to true) ## EXAMPLE diff --git a/docs/source/markdown/podman-play-kube.1.md b/docs/source/markdown/podman-play-kube.1.md index 00ee7c1df..97b0dc09a 100644 --- a/docs/source/markdown/podman-play-kube.1.md +++ b/docs/source/markdown/podman-play-kube.1.md @@ -20,7 +20,7 @@ Note: HostPath volume types created by play kube will be given an SELinux privat **--authfile**=*path* Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json, which is set using `podman login`. -If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`. (Not available for remote commands) +If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`. Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE environment variable. `export REGISTRY_AUTH_FILE=path` @@ -58,7 +58,7 @@ Directory path for seccomp profiles (default: "/var/lib/kubelet/seccomp"). (Not Require HTTPS and verify certificates when contacting registries (default: true). If explicitly set to true, then TLS verification will be used. If set to false, then TLS verification will not be used. If not specified, -TLS verification will be used unless the target registry is listed as an insecure registry in registries.conf. (Not available for remote commands) +TLS verification will be used unless the target registry is listed as an insecure registry in registries.conf. **--help**, **-h** diff --git a/docs/source/markdown/podman-pod-top.1.md b/docs/source/markdown/podman-pod-top.1.md index 6a1fa8c42..1f2ffd662 100644 --- a/docs/source/markdown/podman-pod-top.1.md +++ b/docs/source/markdown/podman-pod-top.1.md @@ -7,7 +7,7 @@ podman\-pod\-top - Display the running processes of containers in a pod **podman pod top** [*options*] *pod* [*format-descriptors*] ## DESCRIPTION -Display the running processes of containers in a pod. The *format-descriptors* are ps (1) compatible AIX format descriptors but extended to print additional information, such as the seccomp mode or the effective capabilities of a given process. The descriptors can either be passed as separated arguments or as a single comma-separated argument. Note that you can also specify options and or flags of ps(1); in this case, Podman will fallback to executing ps with the specified arguments and flags in the container. +Display the running processes of containers in a pod. The *format-descriptors* are ps (1) compatible AIX format descriptors but extended to print additional information, such as the seccomp mode or the effective capabilities of a given process. The descriptors can either be passed as separated arguments or as a single comma-separated argument. Note that you can specify options and/or additionally options of ps(1); in this case, Podman will fallback to executing ps with the specified arguments and options in the container. ## OPTIONS diff --git a/docs/source/markdown/podman-pull.1.md b/docs/source/markdown/podman-pull.1.md index c75cb18b4..46beb4c42 100644 --- a/docs/source/markdown/podman-pull.1.md +++ b/docs/source/markdown/podman-pull.1.md @@ -57,7 +57,7 @@ Note: When using the all-tags flag, Podman will not iterate over the search regi **--authfile**=*path* Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json, which is set using `podman login`. -If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`. (Not available for remote commands) +If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`. Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE environment variable. `export REGISTRY_AUTH_FILE=path` @@ -97,7 +97,7 @@ Suppress output information when pulling images Require HTTPS and verify certificates when contacting registries (default: true). If explicitly set to true, then TLS verification will be used. If set to false, then TLS verification will not be used. If not specified, -TLS verification will be used unless the target registry is listed as an insecure registry in registries.conf. (Not available for remote commands) +TLS verification will be used unless the target registry is listed as an insecure registry in registries.conf. **--help**, **-h** diff --git a/docs/source/markdown/podman-push.1.md b/docs/source/markdown/podman-push.1.md index fffd76801..e9b63dc43 100644 --- a/docs/source/markdown/podman-push.1.md +++ b/docs/source/markdown/podman-push.1.md @@ -46,7 +46,7 @@ Images are pushed from those stored in local image storage. **--authfile**=*path* Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json, which is set using `podman login`. -If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`. (Not available for remote commands) +If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`. Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE environment variable. `export REGISTRY_AUTH_FILE=path` @@ -98,7 +98,7 @@ Add a signature at the destination using the specified key Require HTTPS and verify certificates when contacting registries (default: true). If explicitly set to true, then TLS verification will be used. If set to false, then TLS verification will not be used. If not specified, -TLS verification will be used unless the target registry is listed as an insecure registry in registries.conf. (Not available for remote commands) +TLS verification will be used unless the target registry is listed as an insecure registry in registries.conf. ## EXAMPLE diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md index 9ebf6649b..aad179957 100644 --- a/docs/source/markdown/podman-run.1.md +++ b/docs/source/markdown/podman-run.1.md @@ -57,7 +57,7 @@ each of **stdin**, **stdout**, and **stderr**. **--authfile**[=*path*] -Path to the authentication file. Default is *${XDG_RUNTIME_DIR}/containers/auth.json*. (Not available for remote commands) +Path to the authentication file. Default is *${XDG_RUNTIME_DIR}/containers/auth.json*. Note: You can also override the default path of the authentication file by setting the **REGISTRY_AUTH_FILE** environment variable. @@ -118,15 +118,27 @@ Write the pid of the **conmon** process to a file. As **conmon** runs in a separ **--cpu-period**=*limit* -Limit the container's CPU usage by setting CPU CFS (Completely Fair Scheduler) period. +Set the CPU period for the Completely Fair Scheduler (CFS), which is a +duration in microseconds. Once the container's CPU quota is used up, it will +not be scheduled to run until the current period ends. Defaults to 100000 +microseconds. + +On some systems, changing the CPU limits may not be allowed for non-root +users. For more details, see +https://github.com/containers/podman/blob/master/troubleshooting.md#26-running-containers-with-cpu-limits-fails-with-a-permissions-error **--cpu-quota**=*limit* -Limit the CPU CFS (Completely Fair Scheduler) quota. +Limit the CPU Completely Fair Scheduler (CFS) quota. Limit the container's CPU usage. By default, containers run with the full -CPU resource. This flag tell the kernel to restrict the container's CPU usage -to the quota you specify. +CPU resource. The limit is a number in microseconds. If you provide a number, +the container will be allowed to use that much CPU time until the CPU period +ends (controllable via **--cpu-period**). + +On some systems, changing the CPU limits may not be allowed for non-root +users. For more details, see +https://github.com/containers/podman/blob/master/troubleshooting.md#26-running-containers-with-cpu-limits-fails-with-a-permissions-error **--cpu-rt-period**=*microseconds* @@ -180,7 +192,13 @@ division of CPU shares: **--cpus**=*number* -Number of CPUs. The default is *0.0* which means no limit. +Number of CPUs. The default is *0.0* which means no limit. This is shorthand +for **--cpu-period** and **--cpu-quota**, so you may only set either +**--cpus** or **--cpu-period** and **--cpu-quota**. + +On some systems, changing the CPU limits may not be allowed for non-root +users. For more details, see +https://github.com/containers/podman/blob/master/troubleshooting.md#26-running-containers-with-cpu-limits-fails-with-a-permissions-error **--cpuset-cpus**=*number* @@ -197,20 +215,20 @@ to only use memory from the first two memory nodes. **--detach**, **-d**=**true**|**false** -Detached mode: run the container in the background and print the new container ID. The default is **false**. +Detached mode: run the container in the background and print the new container ID. The default is *false*. At any time you can run **podman ps** in the other shell to view a list of the running containers. You can reattach to a detached container with **podman attach**. When attached in the tty mode, you can detach from the container (and leave it -running) using a configurable key sequence. +running) using a configurable key sequence. The default sequence is `ctrl-p,ctrl-q`. +Configure the keys sequence using the **--detach-keys** option, or specifying +it in the **containers.conf** file: see **containers.conf(5)** for more information. -**--detach-keys**=_sequence_ +**--detach-keys**=*sequence* -Specify the key sequence for detaching a container; _sequence_ is a comma-delimited set -in which each item can be a single character from the [a-Z] range, -or **ctrl**-_value_, where _value_ is one of: **a-z** or **@^[,_**. +Specify the key sequence for detaching a container. Format is a single character `[a-Z]` or one or more `ctrl-<value>` characters where `<value>` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`. Specifying "" will disable this feature. The default is *ctrl-p,ctrl-q*. This option can also be set in **containers.conf**(5) file. diff --git a/docs/source/markdown/podman-wait.1.md b/docs/source/markdown/podman-wait.1.md index 1d85e9af0..f0ccb1f9e 100644 --- a/docs/source/markdown/podman-wait.1.md +++ b/docs/source/markdown/podman-wait.1.md @@ -23,8 +23,8 @@ Condition to wait on (default "stopped") Print usage statement -**--interval**, **-i**=*microseconds* - Microseconds to wait before polling for completion +**--interval**, **-i**=*duration* + Time interval to wait before polling for completion. A duration string is a sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". Time unit defaults to "ms". **--latest**, **-l** @@ -42,6 +42,9 @@ $ podman wait mywebserver $ podman wait --latest 0 +$ podman wait --interval 2s +0 + $ podman wait 860a4b23 1 diff --git a/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md index 87337fa3c..2d5110ad9 100644 --- a/docs/source/markdown/podman.1.md +++ b/docs/source/markdown/podman.1.md @@ -220,7 +220,7 @@ the exit codes follow the `chroot` standard, see below: | [podman-import(1)](podman-import.1.md) | Import a tarball and save it as a filesystem image. | | [podman-info(1)](podman-info.1.md) | Displays Podman related system information. | | [podman-init(1)](podman-init.1.md) | Initialize one or more containers | -| [podman-inspect(1)](podman-inspect.1.md) | Display a container or image's configuration. | +| [podman-inspect(1)](podman-inspect.1.md) | Display a container, image, volume, network, or pod's configuration. | | [podman-kill(1)](podman-kill.1.md) | Kill the main process in one or more containers. | | [podman-load(1)](podman-load.1.md) | Load an image from a container image archive into container storage. | | [podman-login(1)](podman-login.1.md) | Login to a container registry. | @@ -11,12 +11,12 @@ require ( github.com/containerd/containerd v1.4.1 // indirect github.com/containernetworking/cni v0.8.0 github.com/containernetworking/plugins v0.8.7 - github.com/containers/buildah v1.16.4 - github.com/containers/common v0.26.0 + github.com/containers/buildah v1.16.5 + github.com/containers/common v0.26.3 github.com/containers/conmon v2.0.20+incompatible - github.com/containers/image/v5 v5.6.0 + github.com/containers/image/v5 v5.7.0 github.com/containers/psgo v1.5.1 - github.com/containers/storage v1.23.5 + github.com/containers/storage v1.23.7 github.com/coreos/go-systemd/v22 v22.1.0 github.com/cri-o/ocicni v0.2.0 github.com/cyphar/filepath-securejoin v0.2.2 @@ -52,7 +52,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 github.com/rootless-containers/rootlesskit v0.10.1 github.com/sirupsen/logrus v1.7.0 - github.com/spf13/cobra v1.1.0 + github.com/spf13/cobra v1.1.1 github.com/spf13/pflag v1.0.5 github.com/stretchr/objx v0.2.0 // indirect github.com/stretchr/testify v1.6.1 @@ -63,7 +63,6 @@ require ( github.com/vishvananda/netlink v1.1.0 go.etcd.io/bbolt v1.3.5 golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 - golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f @@ -72,6 +71,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.19.2 + k8s.io/apimachinery v0.19.3 k8s.io/client-go v0.0.0-20190620085101-78d2af792bab ) @@ -87,16 +87,16 @@ github.com/containernetworking/cni v0.8.0 h1:BT9lpgGoH4jw3lFC7Odz2prU5ruiYKcgAjM github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/plugins v0.8.7 h1:bU7QieuAp+sACI2vCzESJ3FoT860urYP+lThyZkb/2M= github.com/containernetworking/plugins v0.8.7/go.mod h1:R7lXeZaBzpfqapcAbHRW8/CYwm0dHzbz0XEjofx0uB0= -github.com/containers/buildah v1.16.4 h1:bxthp2FoGcpc2O/RyvbGUAZoefmc5hRBqWQi3BjRu7w= -github.com/containers/buildah v1.16.4/go.mod h1:i1XqXgpCROnfcq4oNtfrFEk7UzNDxLJ/PZ+CnPyoIq8= +github.com/containers/buildah v1.16.5 h1:0tH2TJeZWbspWExHY0A7d7lpdYoxB5VFgRTbzc+qKGs= +github.com/containers/buildah v1.16.5/go.mod h1:tqjupkpg4uqeQWhb7O6puyJwoIbpTkBrWAq1iUK9Wh0= github.com/containers/common v0.21.0/go.mod h1:8w8SVwc+P2p1MOnRMbSKNWXt1Iwd2bKFu2LLZx55DTM= -github.com/containers/common v0.26.0 h1:BCo/S5Dl8aRRG7vze+hoWdCd5xuThIP/tCB5NjTIn6g= -github.com/containers/common v0.26.0/go.mod h1:BCK8f8Ye1gvUVGcokJngJG4YC80c2Bjx/F9GyoIAVMc= +github.com/containers/common v0.26.3 h1:5Kb5fMmJ7/xMiJ+iEbPA+5pQpl/FGxCgJex4nml4Slo= +github.com/containers/common v0.26.3/go.mod h1:hJWZIlrl5MsE2ELNRa+MPp6I1kPbXHauuj0Ym4BsLG4= 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.5.2/go.mod h1:4PyNYR0nwlGq/ybVJD9hWlhmIsNra4Q8uOQX2s6E2uM= -github.com/containers/image/v5 v5.6.0 h1:r4AqIX4NO/X7OJkqX574zITV3fq0ZPn0pSlLsxWF6ww= -github.com/containers/image/v5 v5.6.0/go.mod h1:iUSWo3SOLqJo0CkZkKrHxqR6YWqrT98mkXFpE0MceE8= +github.com/containers/image/v5 v5.7.0 h1:fiTC8/Xbr+zEP6njGTZtPW/3UD7MC93nC9DbUoWdxkA= +github.com/containers/image/v5 v5.7.0/go.mod h1:8aOy+YaItukxghRORkvhq5ibWttHErzDLy6egrKfKos= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= github.com/containers/ocicrypt v1.0.2/go.mod h1:nsOhbP19flrX6rE7ieGFvBlr7modwmNjsqWarIUce4M= @@ -106,8 +106,10 @@ 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.20.2/go.mod h1:oOB9Ie8OVPojvoaKWEGSEtHbXUAs+tSyr7RO7ZGteMc= github.com/containers/storage v1.23.3/go.mod h1:0azTMiuBhArp/VUmH1o4DJAGaaH+qLtEu17pJ/iKJCg= -github.com/containers/storage v1.23.5 h1:He9I6y1vRVXYoQg4v2Q9HFAcX4dI3V5MCCrjeBcjkCY= -github.com/containers/storage v1.23.5/go.mod h1:ha26Q6ngehFNhf3AWoXldvAvwI4jFe3ETQAf/CeZPyM= +github.com/containers/storage v1.23.6 h1:3rcZ1KTNv8q7SkZ75gcrFGYqTeiuI04Zg7m9X1sCg/s= +github.com/containers/storage v1.23.6/go.mod h1:haFs0HRowKwyzvWEx9EgI3WsL8XCSnBDb5f8P5CAxJY= +github.com/containers/storage v1.23.7 h1:43ImvG/npvQSZXRjaudVvKISIuZSfI6qvtSNQQSGO/A= +github.com/containers/storage v1.23.7/go.mod h1:cUT2zHjtx+WlVri30obWmM2gpqpi8jfPsmIzP1TVpEI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -318,8 +320,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.10.8/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.10.11/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.0 h1:wJbzvpYMVGG9iTI9VxpnNZfd4DzMPoCWze3GgSqz8yg= -github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.1 h1:bPb7nMRdOZYDrpPMTA3EInUQrdgoBinqUuSwlGdKDdE= +github.com/klauspost/compress v1.11.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= @@ -360,8 +362,11 @@ github.com/moby/sys/mount v0.1.1 h1:mdhBytJ1SMmMat0gtzWWjFX/87K5j6E/7Q5z7rR0cZY= github.com/moby/sys/mount v0.1.1/go.mod h1:FVQFLDRWwyBjDTBNQXDlWnSFREqOo3OKX9aqhmeoo74= github.com/moby/sys/mountinfo v0.1.0/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o= github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o= -github.com/moby/sys/mountinfo v0.2.0 h1:HgYSHMWCj8D7w7TE/cQJfWrY6W3TUxs3pwGFyC5qCvE= github.com/moby/sys/mountinfo v0.2.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/mountinfo v0.3.1 h1:R+C9GycEzoR3GdwQ7mANRhJORnVDJiRkf0JMY82MeI0= +github.com/moby/sys/mountinfo v0.3.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/mountinfo v0.4.0 h1:1KInV3Huv18akCu58V7lzNlt+jFmqlu1EaErnEHE/VM= +github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/term v0.0.0-20200915141129-7f0af18e79f2 h1:SPoLlS9qUUnXcIY4pvA4CTwYjk0Is5f4UPEkeESr53k= github.com/moby/term v0.0.0-20200915141129-7f0af18e79f2/go.mod h1:TjQg8pa4iejrUrjiz0MCtMV38jdMNW4doKSiBrEvCQQ= github.com/moby/vpnkit v0.4.0/go.mod h1:KyjUrL9cb6ZSNNAUwZfqRjhwwgJ3BJN+kXh0t43WTUQ= @@ -430,8 +435,8 @@ github.com/opencontainers/selinux v1.5.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwy github.com/opencontainers/selinux v1.5.2/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/openshift/imagebuilder v1.1.6 h1:1+YzRxIIefY4QqtCImx6rg+75QrKNfBoPAKxgMo/khM= -github.com/openshift/imagebuilder v1.1.6/go.mod h1:9aJRczxCH0mvT6XQ+5STAQaPWz7OsWcU5/mRkt8IWeo= +github.com/openshift/imagebuilder v1.1.8 h1:gjiIl8pbNj0eC4XWvFJHATdDvYm64p9/pLDLQWoLZPA= +github.com/openshift/imagebuilder v1.1.8/go.mod h1:9aJRczxCH0mvT6XQ+5STAQaPWz7OsWcU5/mRkt8IWeo= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913 h1:TnbXhKzrTOyuvWrjI8W6pcoI9XPbLHFXCdN2dtUw7Rw= @@ -503,9 +508,8 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.7/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.1.0 h1:aq3wCKjTPmzcNWLVGnsFVN4rflK7Uzn10F8/aw8MhdQ= -github.com/spf13/cobra v1.1.0/go.mod h1:yk5b0mALVusDL5fMM6Rd1wgnoO5jUPhwsQ6LQAJTidQ= +github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -815,8 +819,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.19.2 h1:5Gy9vQpAGTKHPVOh5c4plE274X8D/6cuEiTO2zve7tc= -k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= +k8s.io/apimachinery v0.19.3 h1:bpIQXlKjB4cB/oNpnNnV+BybGPR7iP5oYpsOTEJ4hgc= +k8s.io/apimachinery v0.19.3/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= 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/hack/man-page-checker b/hack/man-page-checker index 45f9edbd1..8ee0aaf6d 100755 --- a/hack/man-page-checker +++ b/hack/man-page-checker @@ -92,10 +92,10 @@ function compare_usage() { # man page lists 'foo [*options*]', help msg shows 'foo [flags]'. # Make sure if one has it, the other does too. if expr "$from_man" : "\[\*options\*\]" >/dev/null; then - if expr "$from_help" : "\[flags\]" >/dev/null; then + if expr "$from_help" : "\[options\]" >/dev/null; then : else - echo "WARNING: $cmd: man page shows '[*options*]', help does not show [flags]" + echo "WARNING: $cmd: man page shows '[*options*]', help does not show [options]" rc=1 fi elif expr "$from_help" : "\[flags\]" >/dev/null; then diff --git a/hack/xref-helpmsgs-manpages b/hack/xref-helpmsgs-manpages index 7b617eed7..a7063259f 100755 --- a/hack/xref-helpmsgs-manpages +++ b/hack/xref-helpmsgs-manpages @@ -228,14 +228,14 @@ sub podman_help { # Usage: ... # Available Commands: # .... - # Flags: + # Options: # .... # # Start by identifying the section we're in... if ($line =~ /^Available\s+(Commands):/) { $section = lc $1; } - elsif ($line =~ /^(Flags):/) { + elsif ($line =~ /^(Options):/) { $section = lc $1; } @@ -320,7 +320,7 @@ sub podman_man { } } - # Flags should always be of the form '**-f**' or '**--flag**', + # Options should always be of the form '**-f**' or '**--flag**', # possibly separated by comma-space. elsif ($section eq 'flags') { # e.g. 'podman run --ip6', documented in man page, but nonexistent diff --git a/libpod/container_api.go b/libpod/container_api.go index aef37dd59..a9808a30e 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -249,7 +249,7 @@ func (c *Container) Attach(streams *define.AttachStreams, keys string, resize <- // attaching, and I really do not want to do that right now. // Send a SIGWINCH after attach succeeds so that most programs will // redraw the screen for the new attach session. - attachRdy := make(chan bool) + attachRdy := make(chan bool, 1) if c.config.Spec.Process != nil && c.config.Spec.Process.Terminal { go func() { <-attachRdy diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index eff390e46..a1b4334fb 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -1412,7 +1412,8 @@ func (c *Container) generateResolvConf() (string, error) { // Determine the endpoint for resolv.conf in case it is a symlink resolvPath, err := filepath.EvalSymlinks(resolvConf) - if err != nil { + // resolv.conf doesn't have to exists + if err != nil && !os.IsNotExist(err) { return "", err } @@ -1422,7 +1423,8 @@ func (c *Container) generateResolvConf() (string, error) { } contents, err := ioutil.ReadFile(resolvPath) - if err != nil { + // resolv.conf doesn't have to exists + if err != nil && !os.IsNotExist(err) { return "", errors.Wrapf(err, "unable to read %s", resolvPath) } @@ -1541,11 +1543,38 @@ func (c *Container) getHosts() string { } } - if c.config.NetMode.IsSlirp4netns() { - // When using slirp4netns, the interface gets a static IP - hosts += fmt.Sprintf("# used by slirp4netns\n%s\t%s %s\n", "10.0.2.100", c.Hostname(), c.Config().Name) - } hosts += c.cniHosts() + + // If not making a network namespace, add our own hostname. + if c.Hostname() != "" { + if c.config.NetMode.IsSlirp4netns() { + // When using slirp4netns, the interface gets a static IP + hosts += fmt.Sprintf("# used by slirp4netns\n%s\t%s %s\n", "10.0.2.100", c.Hostname(), c.config.Name) + } else { + hasNetNS := false + netNone := false + for _, ns := range c.config.Spec.Linux.Namespaces { + if ns.Type == spec.NetworkNamespace { + hasNetNS = true + if ns.Path == "" && !c.config.CreateNetNS { + netNone = true + } + break + } + } + if !hasNetNS { + // 127.0.1.1 and host's hostname to match Docker + osHostname, err := os.Hostname() + if err != nil { + osHostname = c.Hostname() + } + hosts += fmt.Sprintf("127.0.1.1 %s\n", osHostname) + } + if netNone { + hosts += fmt.Sprintf("127.0.1.1 %s\n", c.Hostname()) + } + } + } return hosts } diff --git a/libpod/events.go b/libpod/events.go index b519fe324..95317eb01 100644 --- a/libpod/events.go +++ b/libpod/events.go @@ -26,6 +26,12 @@ func (c *Container) newContainerEvent(status events.Status) { e.Name = c.Name() e.Image = c.config.RootfsImageName e.Type = events.Container + + e.Details = events.Details{ + ID: e.ID, + Attributes: c.Labels(), + } + if err := c.runtime.eventer.Write(e); err != nil { logrus.Errorf("unable to write pod event: %q", err) } diff --git a/libpod/events/config.go b/libpod/events/config.go index bb35c03c0..2ec3111fe 100644 --- a/libpod/events/config.go +++ b/libpod/events/config.go @@ -36,6 +36,18 @@ type Event struct { Time time.Time // Type of event that occurred Type Type + + Details +} + +// Details describes specifics about certain events, specifically around +// container events +type Details struct { + // ID is the event ID + ID string + // Attributes can be used to describe specifics about the event + // in the case of a container event, labels for example + Attributes map[string]string } // EventerOptions describe options that need to be passed to create diff --git a/libpod/events/events.go b/libpod/events/events.go index 722c9595e..42939d64c 100644 --- a/libpod/events/events.go +++ b/libpod/events/events.go @@ -69,7 +69,14 @@ func (e *Event) ToHumanReadable() string { var humanFormat string switch e.Type { case Container, Pod: - humanFormat = fmt.Sprintf("%s %s %s %s (image=%s, name=%s)", e.Time, e.Type, e.Status, e.ID, e.Image, e.Name) + humanFormat = fmt.Sprintf("%s %s %s %s (image=%s, name=%s", e.Time, e.Type, e.Status, e.ID, e.Image, e.Name) + // check if the container has labels and add it to the output + if len(e.Attributes) > 0 { + for k, v := range e.Attributes { + humanFormat += fmt.Sprintf(", %s=%s", k, v) + } + } + humanFormat += ")" case Image: humanFormat = fmt.Sprintf("%s %s %s %s %s", e.Time, e.Type, e.Status, e.ID, e.Name) case System: diff --git a/libpod/events/filters.go b/libpod/events/filters.go index c50474007..62891d32c 100644 --- a/libpod/events/filters.go +++ b/libpod/events/filters.go @@ -55,6 +55,24 @@ func generateEventFilter(filter, filterValue string) (func(e *Event) bool, error return func(e *Event) bool { return string(e.Type) == filterValue }, nil + + case "LABEL": + return func(e *Event) bool { + var found bool + // iterate labels and see if we match a key and value + for eventKey, eventValue := range e.Attributes { + filterValueSplit := strings.SplitN(filterValue, "=", 2) + // if the filter isn't right, just return false + if len(filterValueSplit) < 2 { + return false + } + if eventKey == filterValueSplit[0] && eventValue == filterValueSplit[1] { + found = true + break + } + } + return found + }, nil } return nil, errors.Errorf("%s is an invalid filter", filter) } @@ -73,7 +91,7 @@ func generateEventUntilOption(timeUntil time.Time) func(e *Event) bool { } func parseFilter(filter string) (string, string, error) { - filterSplit := strings.Split(filter, "=") + filterSplit := strings.SplitN(filter, "=", 2) if len(filterSplit) != 2 { return "", "", errors.Errorf("%s is an invalid filter", filter) } diff --git a/libpod/events/journal_linux.go b/libpod/events/journal_linux.go index dc55dbc77..5e3be8009 100644 --- a/libpod/events/journal_linux.go +++ b/libpod/events/journal_linux.go @@ -4,6 +4,7 @@ package events import ( "context" + "encoding/json" "strconv" "time" @@ -46,6 +47,15 @@ func (e EventJournalD) Write(ee Event) error { if ee.ContainerExitCode != 0 { m["PODMAN_EXIT_CODE"] = strconv.Itoa(ee.ContainerExitCode) } + // If we have container labels, we need to convert them to a string so they + // can be recorded with the event + if len(ee.Details.Attributes) > 0 { + b, err := json.Marshal(ee.Details.Attributes) + if err != nil { + return err + } + m["PODMAN_LABELS"] = string(b) + } case Volume: m["PODMAN_NAME"] = ee.Name } @@ -59,35 +69,39 @@ func (e EventJournalD) Read(ctx context.Context, options ReadOptions) error { if err != nil { return errors.Wrapf(err, "failed to generate event options") } - j, err := sdjournal.NewJournal() //nolint + j, err := sdjournal.NewJournal() if err != nil { return err } - // TODO AddMatch and Seek seem to conflict - // Issue filed upstream -> https://github.com/coreos/go-systemd/issues/315 - // Leaving commented code in case upstream fixes things - //podmanJournal := sdjournal.Match{Field: "SYSLOG_IDENTIFIER", Value: "podman"} //nolint - //if err := j.AddMatch(podmanJournal.String()); err != nil { - // return errors.Wrap(err, "failed to add filter for event log") - //} + + // match only podman journal entries + podmanJournal := sdjournal.Match{Field: "SYSLOG_IDENTIFIER", Value: "podman"} + if err := j.AddMatch(podmanJournal.String()); err != nil { + return errors.Wrap(err, "failed to add journal filter for event log") + } + if len(options.Since) == 0 && len(options.Until) == 0 && options.Stream { if err := j.SeekTail(); err != nil { return errors.Wrap(err, "failed to seek end of journal") } - } else { - podmanJournal := sdjournal.Match{Field: "SYSLOG_IDENTIFIER", Value: "podman"} //nolint - if err := j.AddMatch(podmanJournal.String()); err != nil { - return errors.Wrap(err, "failed to add filter for event log") + // After SeekTail calling Next moves to a random entry. + // To prevent this we have to call Previous first. + // see: https://bugs.freedesktop.org/show_bug.cgi?id=64614 + if _, err := j.Previous(); err != nil { + return errors.Wrap(err, "failed to move journal cursor to previous entry") } } + // the api requires a next|prev before getting a cursor if _, err := j.Next(); err != nil { - return err + return errors.Wrap(err, "failed to move journal cursor to next entry") } + prevCursor, err := j.GetCursor() if err != nil { - return err + return errors.Wrap(err, "failed to get journal cursor") } + for { select { case <-ctx.Done(): @@ -96,30 +110,26 @@ func (e EventJournalD) Read(ctx context.Context, options ReadOptions) error { default: // fallthrough } + if _, err := j.Next(); err != nil { - return err + return errors.Wrap(err, "failed to move journal cursor to next entry") } newCursor, err := j.GetCursor() if err != nil { - return err + return errors.Wrap(err, "failed to get journal cursor") } if prevCursor == newCursor { if len(options.Until) > 0 || !options.Stream { break } - _ = j.Wait(sdjournal.IndefiniteWait) //nolint + _ = j.Wait(sdjournal.IndefiniteWait) continue } prevCursor = newCursor + entry, err := j.GetEntry() if err != nil { - return err - } - // TODO this keeps us from feeding the podman event parser with - // with regular journal content; it can be removed if the above - // problem with AddMatch is resolved. - if entry.Fields["PODMAN_EVENT"] == "" { - continue + return errors.Wrap(err, "failed to read journal entry") } newEvent, err := newEventFromJournalEntry(entry) if err != nil { @@ -174,6 +184,19 @@ func newEventFromJournalEntry(entry *sdjournal.JournalEntry) (*Event, error) { / newEvent.ContainerExitCode = intCode } } + + // we need to check for the presence of labels recorded to a container event + if stringLabels, ok := entry.Fields["PODMAN_LABELS"]; ok && len(stringLabels) > 0 { + labels := make(map[string]string, 0) + if err := json.Unmarshal([]byte(stringLabels), &labels); err != nil { + return nil, err + } + + // if we have labels, add them to the event + if len(labels) > 0 { + newEvent.Details = Details{Attributes: labels} + } + } case Image: newEvent.ID = entry.Fields["PODMAN_ID"] } diff --git a/libpod/image/filters.go b/libpod/image/filters.go index db647954f..4aff0a7b5 100644 --- a/libpod/image/filters.go +++ b/libpod/image/filters.go @@ -82,7 +82,7 @@ func LabelFilter(ctx context.Context, labelfilter string) ResultFilter { // We need to handle both label=key and label=key=value return func(i *Image) bool { var value string - splitFilter := strings.Split(labelfilter, "=") + splitFilter := strings.SplitN(labelfilter, "=", 2) key := splitFilter[0] if len(splitFilter) > 1 { value = splitFilter[1] @@ -157,7 +157,7 @@ func (ir *Runtime) createFilterFuncs(filters []string, img *Image) ([]ResultFilt var filterFuncs []ResultFilter ctx := context.Background() for _, filter := range filters { - splitFilter := strings.Split(filter, "=") + splitFilter := strings.SplitN(filter, "=", 2) if len(splitFilter) < 2 { return nil, errors.Errorf("invalid filter syntax %s", filter) } diff --git a/libpod/image/image.go b/libpod/image/image.go index 0900944eb..301954703 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -177,7 +177,7 @@ func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile // SaveImages stores one more images in a multi-image archive. // Note that only `docker-archive` supports storing multiple // image. -func (ir *Runtime) SaveImages(ctx context.Context, namesOrIDs []string, format string, outputFile string, quiet bool) (finalErr error) { +func (ir *Runtime) SaveImages(ctx context.Context, namesOrIDs []string, format string, outputFile string, quiet, removeSignatures bool) (finalErr error) { if format != DockerArchive { return errors.Errorf("multi-image archives are only supported in in the %q format", DockerArchive) } @@ -264,7 +264,7 @@ func (ir *Runtime) SaveImages(ctx context.Context, namesOrIDs []string, format s } img := imageMap[id] - copyOptions := getCopyOptions(sys, writer, nil, nil, SigningOptions{}, "", img.tags) + copyOptions := getCopyOptions(sys, writer, nil, nil, SigningOptions{RemoveSignatures: removeSignatures}, "", img.tags) copyOptions.DestinationCtx.SystemRegistriesConfPath = registries.SystemRegistriesConfPath() // For copying, we need a source reference that we can create @@ -469,7 +469,7 @@ func (ir *Runtime) getLocalImage(inputName string) (string, *storage.Image, erro if err != nil { return "", nil, err } - img, err := ir.store.Image(ref.String()) + img, err := ir.store.Image(reference.TagNameOnly(ref).String()) if err == nil { return ref.String(), img, nil } @@ -1584,7 +1584,7 @@ func (i *Image) Comment(ctx context.Context, manifestType string) (string, error } // Save writes a container image to the filesystem -func (i *Image) Save(ctx context.Context, source, format, output string, moreTags []string, quiet, compress bool) error { +func (i *Image) Save(ctx context.Context, source, format, output string, moreTags []string, quiet, compress, removeSignatures bool) error { var ( writer io.Writer destRef types.ImageReference @@ -1636,7 +1636,7 @@ func (i *Image) Save(ctx context.Context, source, format, output string, moreTag return err } } - if err := i.PushImageToReference(ctx, destRef, manifestType, "", "", "", writer, compress, SigningOptions{}, &DockerRegistryOptions{}, additionaltags); err != nil { + if err := i.PushImageToReference(ctx, destRef, manifestType, "", "", "", writer, compress, SigningOptions{RemoveSignatures: removeSignatures}, &DockerRegistryOptions{}, additionaltags); err != nil { return errors.Wrapf(err, "unable to save %q", source) } i.newImageEvent(events.Save) diff --git a/libpod/image/search.go b/libpod/image/search.go index 5f5845989..b9acf4a20 100644 --- a/libpod/image/search.go +++ b/libpod/image/search.go @@ -263,7 +263,7 @@ func searchRepositoryTags(registry, term string, sc *types.SystemContext, option func ParseSearchFilter(filter []string) (*SearchFilter, error) { sFilter := new(SearchFilter) for _, f := range filter { - arr := strings.Split(f, "=") + arr := strings.SplitN(f, "=", 2) switch arr[0] { case "stars": if len(arr) < 2 { diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index f87c311ce..9ff6e40b7 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -254,9 +254,11 @@ func (r *Runtime) setupSlirp4netns(ctr *Container) error { if ctr.config.NetworkOptions != nil { slirpOptions := ctr.config.NetworkOptions["slirp4netns"] for _, o := range slirpOptions { - parts := strings.Split(o, "=") + parts := strings.SplitN(o, "=", 2) + if len(parts) < 2 { + return errors.Errorf("unknown option for slirp4netns: %q", o) + } option, value := parts[0], parts[1] - switch option { case "cidr": ipv4, _, err := net.ParseCIDR(value) @@ -823,6 +825,20 @@ func getContainerNetIO(ctr *Container) (*netlink.LinkStatistics, error) { // Produce an InspectNetworkSettings containing information on the container // network. func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, error) { + if c.config.NetNsCtr != "" { + netNsCtr, err := c.runtime.GetContainer(c.config.NetNsCtr) + if err != nil { + return nil, err + } + // Have to sync to ensure that state is populated + if err := netNsCtr.syncContainer(); err != nil { + return nil, err + } + logrus.Debugf("Container %s shares network namespace, retrieving network info of container %s", c.ID(), c.config.NetNsCtr) + + return netNsCtr.getContainerNetworkInfo() + } + settings := new(define.InspectNetworkSettings) settings.Ports = makeInspectPortBindings(c.config.PortMappings) diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index cae8f88fd..00be8e845 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -410,25 +410,14 @@ func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON, return nil, err } - networkSettingsDefault := types.DefaultNetworkSettings{ - EndpointID: "", - Gateway: "", - GlobalIPv6Address: "", - GlobalIPv6PrefixLen: 0, - IPAddress: "", - IPPrefixLen: 0, - IPv6Gateway: "", - MacAddress: l.Config().StaticMAC.String(), - } - - networkSettingsBase := types.NetworkSettingsBase{ - Ports: ports, + n, err := json.Marshal(inspect.NetworkSettings) + if err != nil { + return nil, err } - networkSettings := types.NetworkSettings{ - NetworkSettingsBase: networkSettingsBase, - DefaultNetworkSettings: networkSettingsDefault, - Networks: nil, + networkSettings := types.NetworkSettings{} + if err := json.Unmarshal(n, &networkSettings); err != nil { + return nil, err } c := types.ContainerJSON{ diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go index 8a0b3c922..87c95a24c 100644 --- a/pkg/api/handlers/compat/containers_create.go +++ b/pkg/api/handlers/compat/containers_create.go @@ -1,27 +1,19 @@ package compat import ( - "context" "encoding/json" - "fmt" "net/http" - "strings" - "github.com/containers/common/pkg/config" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" - image2 "github.com/containers/podman/v2/libpod/image" "github.com/containers/podman/v2/pkg/api/handlers" "github.com/containers/podman/v2/pkg/api/handlers/utils" - "github.com/containers/podman/v2/pkg/namespaces" - "github.com/containers/podman/v2/pkg/rootless" - "github.com/containers/podman/v2/pkg/signal" - createconfig "github.com/containers/podman/v2/pkg/spec" + "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/infra/abi" "github.com/containers/podman/v2/pkg/specgen" - "github.com/containers/storage" "github.com/gorilla/schema" "github.com/pkg/errors" - "golang.org/x/sys/unix" ) func CreateContainer(w http.ResponseWriter, r *http.Request) { @@ -56,220 +48,27 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "NewFromLocal()")) return } - containerConfig, err := runtime.GetConfig() + + // Take input structure and convert to cliopts + cliOpts, args, err := common.ContainerCreateToContainerCLIOpts(input) if err != nil { - utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "GetConfig()")) + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "make cli opts()")) return } - cc, err := makeCreateConfig(r.Context(), containerConfig, input, newImage) - if err != nil { - utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "makeCreatConfig()")) + sg := specgen.NewSpecGenerator(newImage.ID(), cliOpts.RootFS) + if err := common.FillOutSpecGen(sg, cliOpts, args); err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "fill out specgen")) return } - cc.Name = query.Name - utils.CreateContainer(r.Context(), w, runtime, &cc) -} - -func makeCreateConfig(ctx context.Context, containerConfig *config.Config, input handlers.CreateContainerConfig, newImage *image2.Image) (createconfig.CreateConfig, error) { - var ( - err error - init bool - ) - env := make(map[string]string) - stopSignal := unix.SIGTERM - if len(input.StopSignal) > 0 { - stopSignal, err = signal.ParseSignal(input.StopSignal) - if err != nil { - return createconfig.CreateConfig{}, err - } - } - - workDir, err := newImage.WorkingDir(ctx) + ic := abi.ContainerEngine{Libpod: runtime} + report, err := ic.ContainerCreate(r.Context(), sg) if err != nil { - return createconfig.CreateConfig{}, err - } - if workDir == "" { - workDir = "/" - } - if len(input.WorkingDir) > 0 { - workDir = input.WorkingDir - } - - // Only use image's Cmd when the user does not set the entrypoint - if input.Entrypoint == nil && len(input.Cmd) == 0 { - cmdSlice, err := newImage.Cmd(ctx) - if err != nil { - return createconfig.CreateConfig{}, err - } - input.Cmd = cmdSlice - } - - if input.Entrypoint == nil { - entrypointSlice, err := newImage.Entrypoint(ctx) - if err != nil { - return createconfig.CreateConfig{}, err - } - input.Entrypoint = entrypointSlice - } - - stopTimeout := containerConfig.Engine.StopTimeout - if input.StopTimeout != nil { - stopTimeout = uint(*input.StopTimeout) - } - c := createconfig.CgroupConfig{ - Cgroups: "", // podman - Cgroupns: "", // podman - CgroupParent: "", // podman - CgroupMode: "", // podman - } - security := createconfig.SecurityConfig{ - CapAdd: input.HostConfig.CapAdd, - CapDrop: input.HostConfig.CapDrop, - LabelOpts: nil, // podman - NoNewPrivs: false, // podman - ApparmorProfile: "", // podman - SeccompProfilePath: "", - SecurityOpts: input.HostConfig.SecurityOpt, - Privileged: input.HostConfig.Privileged, - ReadOnlyRootfs: input.HostConfig.ReadonlyRootfs, - ReadOnlyTmpfs: false, // podman-only - Sysctl: input.HostConfig.Sysctls, - } - - var netmode namespaces.NetworkMode - if rootless.IsRootless() { - netmode = namespaces.NetworkMode(specgen.Slirp) - } - - network := createconfig.NetworkConfig{ - DNSOpt: input.HostConfig.DNSOptions, - DNSSearch: input.HostConfig.DNSSearch, - DNSServers: input.HostConfig.DNS, - ExposedPorts: input.ExposedPorts, - HTTPProxy: false, // podman - IP6Address: "", - IPAddress: "", - LinkLocalIP: nil, // docker-only - MacAddress: input.MacAddress, - NetMode: netmode, - Network: input.HostConfig.NetworkMode.NetworkName(), - NetworkAlias: nil, // docker-only now - PortBindings: input.HostConfig.PortBindings, - Publish: nil, // podmanseccompPath - PublishAll: input.HostConfig.PublishAllPorts, - } - - uts := createconfig.UtsConfig{ - UtsMode: namespaces.UTSMode(input.HostConfig.UTSMode), - NoHosts: false, //podman - HostAdd: input.HostConfig.ExtraHosts, - Hostname: input.Hostname, - } - - z := createconfig.UserConfig{ - GroupAdd: input.HostConfig.GroupAdd, - IDMappings: &storage.IDMappingOptions{}, // podman //TODO <--- fix this, - UsernsMode: namespaces.UsernsMode(input.HostConfig.UsernsMode), - User: input.User, - } - pidConfig := createconfig.PidConfig{PidMode: namespaces.PidMode(input.HostConfig.PidMode)} - // TODO: We should check that these binds are all listed in the `Volumes` - // key since it doesn't make sense to define a `Binds` element for a - // container path which isn't defined as a volume - volumes := input.HostConfig.Binds - - // Docker is more flexible about its input where podman throws - // away incorrectly formatted variables so we cannot reuse the - // parsing of the env input - // [Foo Other=one Blank=] - imgEnv, err := newImage.Env(ctx) - if err != nil { - return createconfig.CreateConfig{}, err - } - input.Env = append(imgEnv, input.Env...) - for _, e := range input.Env { - splitEnv := strings.Split(e, "=") - switch len(splitEnv) { - case 0: - continue - case 1: - env[splitEnv[0]] = "" - default: - env[splitEnv[0]] = strings.Join(splitEnv[1:], "=") - } - } - - // format the tmpfs mounts into a []string from map - tmpfs := make([]string, 0, len(input.HostConfig.Tmpfs)) - for k, v := range input.HostConfig.Tmpfs { - tmpfs = append(tmpfs, fmt.Sprintf("%s:%s", k, v)) - } - - if input.HostConfig.Init != nil && *input.HostConfig.Init { - init = true - } - - m := createconfig.CreateConfig{ - Annotations: nil, // podman - Args: nil, - Cgroup: c, - CidFile: "", - ConmonPidFile: "", // podman - Command: input.Cmd, - UserCommand: input.Cmd, // podman - Detach: false, // - // Devices: input.HostConfig.Devices, - Entrypoint: input.Entrypoint, - Env: env, - HealthCheck: nil, // - Init: init, - InitPath: "", // tbd - Image: input.Image, - ImageID: newImage.ID(), - BuiltinImgVolumes: nil, // podman - ImageVolumeType: "", // podman - Interactive: input.OpenStdin, - // IpcMode: input.HostConfig.IpcMode, - Labels: input.Labels, - LogDriver: input.HostConfig.LogConfig.Type, // is this correct - // LogDriverOpt: input.HostConfig.LogConfig.Config, - Name: input.Name, - Network: network, - Pod: "", // podman - PodmanPath: "", // podman - Quiet: false, // front-end only - Resources: createconfig.CreateResourceConfig{MemorySwappiness: -1}, - RestartPolicy: input.HostConfig.RestartPolicy.Name, - Rm: input.HostConfig.AutoRemove, - StopSignal: stopSignal, - StopTimeout: stopTimeout, - Systemd: false, // podman - Tmpfs: tmpfs, - User: z, - Uts: uts, - Tty: input.Tty, - Mounts: nil, // we populate - // MountsFlag: input.HostConfig.Mounts, - NamedVolumes: nil, // we populate - Volumes: volumes, - VolumesFrom: input.HostConfig.VolumesFrom, - WorkDir: workDir, - Rootfs: "", // podman - Security: security, - Syslog: false, // podman - - Pid: pidConfig, + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "container create")) + return } - - fullCmd := append(input.Entrypoint, input.Cmd...) - if len(fullCmd) > 0 { - m.PodmanPath = fullCmd[0] - if len(fullCmd) == 1 { - m.Args = fullCmd - } else { - m.Args = fullCmd[1:] - } + createResponse := entities.ContainerCreateResponse{ + ID: report.Id, + Warnings: []string{}, } - - return m, nil + utils.WriteResponse(w, http.StatusCreated, createResponse) } diff --git a/pkg/api/handlers/compat/containers_logs.go b/pkg/api/handlers/compat/containers_logs.go index faab66fe7..38a6329b9 100644 --- a/pkg/api/handlers/compat/containers_logs.go +++ b/pkg/api/handlers/compat/containers_logs.go @@ -148,7 +148,13 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) { frame.WriteString(line.Time.Format(time.RFC3339)) frame.WriteString(" ") } + frame.WriteString(line.Msg) + // Log lines in the compat layer require adding EOL + // https://github.com/containers/podman/issues/8058 + if !utils.IsLibpodRequest(r) { + frame.WriteString("\n") + } if writeHeader { binary.BigEndian.PutUint32(header[4:], uint32(frame.Len())) diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go index 3431823bd..d177b2335 100644 --- a/pkg/api/handlers/compat/images.go +++ b/pkg/api/handlers/compat/images.go @@ -60,7 +60,7 @@ func ExportImage(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to close tempfile")) return } - if err := newImage.Save(r.Context(), name, "docker-archive", tmpfile.Name(), []string{}, false, false); err != nil { + if err := newImage.Save(r.Context(), name, "docker-archive", tmpfile.Name(), []string{}, false, false, true); err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to save image")) return } @@ -429,7 +429,7 @@ func ExportImages(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to close tempfile")) return } - if err := runtime.ImageRuntime().SaveImages(r.Context(), images, "docker-archive", tmpfile.Name(), false); err != nil { + if err := runtime.ImageRuntime().SaveImages(r.Context(), images, "docker-archive", tmpfile.Name(), false, true); err != nil { utils.InternalServerError(w, err) return } diff --git a/pkg/api/handlers/compat/images_search.go b/pkg/api/handlers/compat/images_search.go index 131fab69f..b3ceae3ee 100644 --- a/pkg/api/handlers/compat/images_search.go +++ b/pkg/api/handlers/compat/images_search.go @@ -7,6 +7,7 @@ import ( "github.com/containers/image/v5/types" "github.com/containers/podman/v2/libpod/image" "github.com/containers/podman/v2/pkg/api/handlers/utils" + "github.com/containers/podman/v2/pkg/auth" "github.com/gorilla/schema" "github.com/pkg/errors" ) @@ -14,9 +15,10 @@ import ( func SearchImages(w http.ResponseWriter, r *http.Request) { decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { - Term string `json:"term"` - Limit int `json:"limit"` - Filters map[string][]string `json:"filters"` + Term string `json:"term"` + Limit int `json:"limit"` + Filters map[string][]string `json:"filters"` + TLSVerify bool `json:"tlsVerify"` }{ // This is where you can override the golang default value for one of fields } @@ -58,6 +60,18 @@ func SearchImages(w http.ResponseWriter, r *http.Request) { Limit: query.Limit, } + if _, found := r.URL.Query()["tlsVerify"]; found { + options.InsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) + } + + _, authfile, key, err := auth.GetCredentials(r) + if err != nil { + utils.Error(w, "failed to retrieve repository credentials", http.StatusBadRequest, errors.Wrapf(err, "failed to parse %q header for %s", key, r.URL.String())) + return + } + defer auth.RemoveAuthfile(authfile) + options.Authfile = authfile + results, err := image.SearchImages(query.Term, options) if err != nil { utils.BadRequest(w, "term", query.Term, err) diff --git a/pkg/api/handlers/compat/networks.go b/pkg/api/handlers/compat/networks.go index a46784a6c..8011c0a04 100644 --- a/pkg/api/handlers/compat/networks.go +++ b/pkg/api/handlers/compat/networks.go @@ -20,6 +20,7 @@ import ( dockerNetwork "github.com/docker/docker/api/types/network" "github.com/gorilla/schema" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) func InspectNetwork(w http.ResponseWriter, r *http.Request) { @@ -231,6 +232,9 @@ func CreateNetwork(w http.ResponseWriter, r *http.Request) { if len(networkCreate.Name) > 0 { name = networkCreate.Name } + if len(networkCreate.Driver) < 1 { + networkCreate.Driver = network.DefaultNetworkDriver + } // At present I think we should just support the bridge driver // and allow demand to make us consider more if networkCreate.Driver != network.DefaultNetworkDriver { @@ -312,3 +316,81 @@ func RemoveNetwork(w http.ResponseWriter, r *http.Request) { } utils.WriteResponse(w, http.StatusNoContent, "") } + +// Connect adds a container to a network +// TODO: For now this func is a no-op that checks the container name, network name, and +// responds with a 200. This allows the call to remain intact. We need to decide how +// we make this work with CNI networking and setup/teardown. +func Connect(w http.ResponseWriter, r *http.Request) { + runtime := r.Context().Value("runtime").(*libpod.Runtime) + + var netConnect types.NetworkConnect + if err := json.NewDecoder(r.Body).Decode(&netConnect); err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()")) + return + } + config, err := runtime.GetConfig() + if err != nil { + utils.InternalServerError(w, err) + return + } + name := utils.GetName(r) + exists, err := network.Exists(config, name) + if err != nil { + utils.InternalServerError(w, err) + return + } + if !exists { + utils.Error(w, "network not found", http.StatusNotFound, define.ErrNoSuchNetwork) + return + } + if _, err = runtime.LookupContainer(netConnect.Container); err != nil { + if errors.Cause(err) == define.ErrNoSuchCtr { + utils.ContainerNotFound(w, netConnect.Container, err) + return + } + utils.Error(w, "unable to lookup container", http.StatusInternalServerError, err) + return + } + logrus.Warnf("network connect endpoint is not fully implemented - tried to connect container %s to network %s", netConnect.Container, name) + utils.WriteResponse(w, http.StatusOK, "OK") +} + +// Disconnect removes a container from a network +// TODO: For now this func is a no-op that checks the container name, network name, and +// responds with a 200. This allows the call to remain intact. We need to decide how +// we make this work with CNI networking and setup/teardown. +func Disconnect(w http.ResponseWriter, r *http.Request) { + runtime := r.Context().Value("runtime").(*libpod.Runtime) + + var netDisconnect types.NetworkDisconnect + if err := json.NewDecoder(r.Body).Decode(&netDisconnect); err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()")) + return + } + config, err := runtime.GetConfig() + if err != nil { + utils.InternalServerError(w, err) + return + } + name := utils.GetName(r) + exists, err := network.Exists(config, name) + if err != nil { + utils.InternalServerError(w, err) + return + } + if !exists { + utils.Error(w, "network not found", http.StatusNotFound, define.ErrNoSuchNetwork) + return + } + if _, err = runtime.LookupContainer(netDisconnect.Container); err != nil { + if errors.Cause(err) == define.ErrNoSuchCtr { + utils.ContainerNotFound(w, netDisconnect.Container, err) + return + } + utils.Error(w, "unable to lookup container", http.StatusInternalServerError, err) + return + } + logrus.Warnf("network disconnect endpoint is not fully implemented - tried to connect container %s to network %s", netDisconnect.Container, name) + utils.WriteResponse(w, http.StatusOK, "OK") +} diff --git a/pkg/api/handlers/compat/swagger.go b/pkg/api/handlers/compat/swagger.go index 371d02388..0a514822b 100644 --- a/pkg/api/handlers/compat/swagger.go +++ b/pkg/api/handlers/compat/swagger.go @@ -63,3 +63,17 @@ type swagCompatNetworkCreateResponse struct { // in:body Body struct{ types.NetworkCreate } } + +// Network disconnect +// swagger:model NetworkConnectRequest +type swagCompatNetworkConnectRequest struct { + // in:body + Body struct{ types.NetworkConnect } +} + +// Network disconnect +// swagger:model NetworkDisconnectRequest +type swagCompatNetworkDisconnectRequest struct { + // in:body + Body struct{ types.NetworkDisconnect } +} diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index 1292090fb..598a46abe 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -206,7 +206,7 @@ func ExportImage(w http.ResponseWriter, r *http.Request) { utils.Error(w, "unknown format", http.StatusInternalServerError, errors.Errorf("unknown format %q", query.Format)) return } - if err := newImage.Save(r.Context(), name, query.Format, output, []string{}, false, query.Compress); err != nil { + if err := newImage.Save(r.Context(), name, query.Format, output, []string{}, false, query.Compress, true); err != nil { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err) return } @@ -284,6 +284,7 @@ func ExportImages(w http.ResponseWriter, r *http.Request) { Format: query.Format, MultiImageArchive: true, Output: output, + RemoveSignatures: true, } imageEngine := abi.ImageEngine{Libpod: runtime} @@ -636,6 +637,14 @@ func SearchImages(w http.ResponseWriter, r *http.Request) { options.Filter = *filter } + _, authfile, key, err := auth.GetCredentials(r) + if err != nil { + utils.Error(w, "failed to retrieve repository credentials", http.StatusBadRequest, errors.Wrapf(err, "failed to parse %q header for %s", key, r.URL.String())) + return + } + defer auth.RemoveAuthfile(authfile) + options.Authfile = authfile + searchResults, err := image.SearchImages(query.Term, options) if err != nil { utils.BadRequest(w, "term", query.Term, err) diff --git a/pkg/api/handlers/libpod/networks.go b/pkg/api/handlers/libpod/networks.go index b7e2b3988..9f6103c45 100644 --- a/pkg/api/handlers/libpod/networks.go +++ b/pkg/api/handlers/libpod/networks.go @@ -113,15 +113,15 @@ func InspectNetwork(w http.ResponseWriter, r *http.Request) { return } name := utils.GetName(r) - options := entities.NetworkInspectOptions{} + options := entities.InspectOptions{} ic := abi.ContainerEngine{Libpod: runtime} - reports, err := ic.NetworkInspect(r.Context(), []string{name}, options) + reports, errs, err := ic.NetworkInspect(r.Context(), []string{name}, options) + // If the network cannot be found, we return a 404. + if len(errs) > 0 { + utils.Error(w, "Something went wrong", http.StatusNotFound, define.ErrNoSuchNetwork) + return + } if err != nil { - // If the network cannot be found, we return a 404. - if errors.Cause(err) == define.ErrNoSuchNetwork { - utils.Error(w, "Something went wrong", http.StatusNotFound, err) - return - } utils.InternalServerError(w, err) return } diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go index 70466f01b..6bb5f5101 100644 --- a/pkg/api/handlers/types.go +++ b/pkg/api/handlers/types.go @@ -271,6 +271,15 @@ func ImageDataToImageInspect(ctx context.Context, l *libpodImage.Image) (*ImageI if err != nil { return nil, err } + + rootfs := docker.RootFS{} + if info.RootFS != nil { + rootfs.Type = info.RootFS.Type + rootfs.Layers = make([]string, 0, len(info.RootFS.Layers)) + for _, layer := range info.RootFS.Layers { + rootfs.Layers = append(rootfs.Layers, string(layer)) + } + } dockerImageInspect := docker.ImageInspect{ Architecture: l.Architecture, Author: l.Author, @@ -286,7 +295,7 @@ func ImageDataToImageInspect(ctx context.Context, l *libpodImage.Image) (*ImageI Parent: l.Parent, RepoDigests: info.RepoDigests, RepoTags: info.RepoTags, - RootFS: docker.RootFS{}, + RootFS: rootfs, Size: info.Size, Variant: "", VirtualSize: info.VirtualSize, diff --git a/pkg/api/server/register_networks.go b/pkg/api/server/register_networks.go index 61916eedf..6222006e5 100644 --- a/pkg/api/server/register_networks.go +++ b/pkg/api/server/register_networks.go @@ -98,6 +98,63 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error { // $ref: "#/responses/InternalError" r.HandleFunc(VersionedPath("/networks/create"), s.APIHandler(compat.CreateNetwork)).Methods(http.MethodPost) r.HandleFunc("/networks/create", s.APIHandler(compat.CreateNetwork)).Methods(http.MethodPost) + // swagger:operation POST /networks/{name}/connect compat compatConnectNetwork + // --- + // tags: + // - networks (compat) + // summary: Connect container to network + // description: Connect a container to a network. This endpoint is current a no-op + // produces: + // - application/json + // parameters: + // - in: path + // name: name + // type: string + // required: true + // description: the name of the network + // - in: body + // name: create + // description: attributes for connecting a container to a network + // schema: + // $ref: "#/definitions/NetworkConnectRequest" + // responses: + // 200: + // description: OK + // 400: + // $ref: "#/responses/BadParamError" + // 500: + // $ref: "#/responses/InternalError" + r.HandleFunc(VersionedPath("/networks/{name}/connect"), s.APIHandler(compat.Connect)).Methods(http.MethodPost) + r.HandleFunc("/networks/{name}/connect", s.APIHandler(compat.Connect)).Methods(http.MethodPost) + // swagger:operation POST /networks/{name}/disconnect compat compatDisconnectNetwork + // --- + // tags: + // - networks (compat) + // summary: Disconnect container from network + // description: Disconnect a container from a network. This endpoint is current a no-op + // produces: + // - application/json + // parameters: + // - in: path + // name: name + // type: string + // required: true + // description: the name of the network + // - in: body + // name: create + // description: attributes for disconnecting a container from a network + // schema: + // $ref: "#/definitions/NetworkDisconnectRequest" + // responses: + // 200: + // description: OK + // 400: + // $ref: "#/responses/BadParamError" + // 500: + // $ref: "#/responses/InternalError" + r.HandleFunc(VersionedPath("/networks/{name}/disconnect"), s.APIHandler(compat.Disconnect)).Methods(http.MethodPost) + r.HandleFunc("/networks/{name}/disconnect", s.APIHandler(compat.Disconnect)).Methods(http.MethodPost) + // swagger:operation DELETE /libpod/networks/{name} libpod libpodRemoveNetwork // --- // tags: diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go index 3a7662c41..31435ae91 100644 --- a/pkg/bindings/connection.go +++ b/pkg/bindings/connection.go @@ -214,19 +214,23 @@ func sshClient(_url *url.URL, secure bool, passPhrase string, identity string) ( authMethods = append(authMethods, ssh.Password(string(pass))) } + port := _url.Port() + if port == "" { + port = "22" + } + callback := ssh.InsecureIgnoreHostKey() if secure { - key := terminal.HostKey(_url.Hostname()) + host := _url.Hostname() + if port != "22" { + host = fmt.Sprintf("[%s]:%s", host, port) + } + key := terminal.HostKey(host) if key != nil { callback = ssh.FixedHostKey(key) } } - port := _url.Port() - if port == "" { - port = "22" - } - bastion, err := ssh.Dial("tcp", net.JoinHostPort(_url.Hostname(), port), &ssh.ClientConfig{ diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index 46b169284..3fd7c79f4 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -294,6 +294,7 @@ type ContainerListOptions struct { // ContainerRunOptions describes the options needed // to run a container from the CLI type ContainerRunOptions struct { + CIDFile string Detach bool DetachKeys string ErrorStream *os.File diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index a20d3b404..8ab72dbd8 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -51,7 +51,7 @@ type ContainerEngine interface { HealthCheckRun(ctx context.Context, nameOrID string, options HealthCheckOptions) (*define.HealthCheckResults, error) Info(ctx context.Context) (*define.Info, error) NetworkCreate(ctx context.Context, name string, options NetworkCreateOptions) (*NetworkCreateReport, error) - NetworkInspect(ctx context.Context, namesOrIds []string, options NetworkInspectOptions) ([]NetworkInspectReport, error) + NetworkInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]NetworkInspectReport, []error, error) NetworkList(ctx context.Context, options NetworkListOptions) ([]*NetworkListReport, error) NetworkRm(ctx context.Context, namesOrIds []string, options NetworkRmOptions) ([]*NetworkRmReport, error) PlayKube(ctx context.Context, path string, opts PlayKubeOptions) (*PlayKubeReport, error) @@ -76,7 +76,7 @@ type ContainerEngine interface { VarlinkService(ctx context.Context, opts ServiceOptions) error Version(ctx context.Context) (*SystemVersionReport, error) VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IDOrNameResponse, error) - VolumeInspect(ctx context.Context, namesOrIds []string, opts VolumeInspectOptions) ([]*VolumeInspectReport, error) + VolumeInspect(ctx context.Context, namesOrIds []string, opts InspectOptions) ([]*VolumeInspectReport, []error, error) VolumeList(ctx context.Context, opts VolumeListOptions) ([]*VolumeListReport, error) VolumePrune(ctx context.Context) ([]*VolumePruneReport, error) VolumeRm(ctx context.Context, namesOrIds []string, opts VolumeRmOptions) ([]*VolumeRmReport, error) diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index 982fa0cc0..101542a98 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -300,6 +300,8 @@ type ImageSaveOptions struct { MultiImageArchive bool // Output - write image to the specified path. Output string + // Do not save the signature from the source image + RemoveSignatures bool // Quiet - suppress output when copying images Quiet bool } diff --git a/pkg/domain/entities/network.go b/pkg/domain/entities/network.go index 9beeeb042..0bab672a7 100644 --- a/pkg/domain/entities/network.go +++ b/pkg/domain/entities/network.go @@ -18,11 +18,6 @@ type NetworkListReport struct { *libcni.NetworkConfigList } -// NetworkInspectOptions describes options for inspect networks -type NetworkInspectOptions struct { - Format string -} - // NetworkInspectReport describes the results from inspect networks type NetworkInspectReport map[string]interface{} diff --git a/pkg/domain/entities/types.go b/pkg/domain/entities/types.go index 5fdd8afcb..d8ad2d891 100644 --- a/pkg/domain/entities/types.go +++ b/pkg/domain/entities/types.go @@ -56,6 +56,8 @@ type InspectOptions struct { Size bool `json:",omitempty"` // Type -- return JSON for specified type. Type string `json:",omitempty"` + // All -- inspect all + All bool `json:",omitempty"` } // All API and CLI diff commands and diff sub-commands use the same options diff --git a/pkg/domain/entities/volumes.go b/pkg/domain/entities/volumes.go index fb8466d04..1bc1e4301 100644 --- a/pkg/domain/entities/volumes.go +++ b/pkg/domain/entities/volumes.go @@ -105,10 +105,6 @@ type VolumeRmReport struct { Id string //nolint } -type VolumeInspectOptions struct { - All bool -} - type VolumeInspectReport struct { *VolumeConfigResponse } diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 60dbbce6c..98b886845 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -29,6 +29,7 @@ import ( "github.com/containers/podman/v2/pkg/signal" "github.com/containers/podman/v2/pkg/specgen" "github.com/containers/podman/v2/pkg/specgen/generate" + "github.com/containers/podman/v2/pkg/util" "github.com/containers/storage" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -846,6 +847,12 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta return nil, err } + if opts.CIDFile != "" { + if err := util.CreateCidFile(opts.CIDFile, ctr.ID()); err != nil { + return nil, err + } + } + var joinPod bool if len(ctr.PodID()) > 0 { joinPod = true diff --git a/pkg/domain/infra/abi/cp.go b/pkg/domain/infra/abi/cp.go index a0bfcc90c..ab90c8183 100644 --- a/pkg/domain/infra/abi/cp.go +++ b/pkg/domain/infra/abi/cp.go @@ -26,7 +26,7 @@ import ( ) func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, options entities.ContainerCpOptions) (*entities.ContainerCpReport, error) { - var extract bool + extract := options.Extract srcCtr, srcPath := parsePath(ic.Libpod, source) destCtr, destPath := parsePath(ic.Libpod, dest) diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index f9d733c63..25335cf11 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -482,13 +482,13 @@ func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOpti func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string, options entities.ImageSaveOptions) error { if options.MultiImageArchive { nameOrIDs := append([]string{nameOrID}, tags...) - return ir.Libpod.ImageRuntime().SaveImages(ctx, nameOrIDs, options.Format, options.Output, options.Quiet) + return ir.Libpod.ImageRuntime().SaveImages(ctx, nameOrIDs, options.Format, options.Output, options.Quiet, true) } newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID) if err != nil { return err } - return newImage.Save(ctx, nameOrID, options.Format, options.Output, tags, options.Quiet, options.Compress) + return newImage.Save(ctx, nameOrID, options.Format, options.Output, tags, options.Quiet, options.Compress, true) } func (ir *ImageEngine) Diff(_ context.Context, nameOrID string, _ entities.DiffOptions) (*entities.DiffReport, error) { diff --git a/pkg/domain/infra/abi/images_list.go b/pkg/domain/infra/abi/images_list.go index 3e47dc67a..281b04294 100644 --- a/pkg/domain/infra/abi/images_list.go +++ b/pkg/domain/infra/abi/images_list.go @@ -5,6 +5,7 @@ import ( libpodImage "github.com/containers/podman/v2/libpod/image" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/pkg/errors" ) func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) ([]*entities.ImageSummary, error) { @@ -43,12 +44,21 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) VirtualSize: img.VirtualSize, RepoTags: img.Names(), // may include tags and digests } - e.Labels, _ = img.Labels(context.TODO()) + e.Labels, err = img.Labels(ctx) + if err != nil { + return nil, errors.Wrapf(err, "error retrieving label for image %q: you may need to remove the image to resolve the error", img.ID()) + } - ctnrs, _ := img.Containers() + ctnrs, err := img.Containers() + if err != nil { + return nil, errors.Wrapf(err, "error retrieving containers for image %q: you may need to remove the image to resolve the error", img.ID()) + } e.Containers = len(ctnrs) - sz, _ := img.Size(context.TODO()) + sz, err := img.Size(ctx) + if err != nil { + return nil, errors.Wrapf(err, "error retrieving size of image %q: you may need to remove the image to resolve the error", img.ID()) + } e.Size = int64(*sz) summaries = append(summaries, &e) diff --git a/pkg/domain/infra/abi/network.go b/pkg/domain/infra/abi/network.go index f40df828a..4f572fb88 100644 --- a/pkg/domain/infra/abi/network.go +++ b/pkg/domain/infra/abi/network.go @@ -43,21 +43,26 @@ func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.Net return reports, nil } -func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.NetworkInspectOptions) ([]entities.NetworkInspectReport, error) { +func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]entities.NetworkInspectReport, []error, error) { config, err := ic.Libpod.GetConfig() if err != nil { - return nil, err + return nil, nil, err } - + var errs []error rawCNINetworks := make([]entities.NetworkInspectReport, 0, len(namesOrIds)) for _, name := range namesOrIds { rawList, err := network.InspectNetwork(config, name) if err != nil { - return nil, err + if errors.Cause(err) == define.ErrNoSuchNetwork { + errs = append(errs, errors.Errorf("no such network %s", name)) + continue + } else { + return nil, nil, errors.Wrapf(err, "error inspecting network %s", name) + } } rawCNINetworks = append(rawCNINetworks, rawList) } - return rawCNINetworks, nil + return rawCNINetworks, errs, nil } func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, options entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) { diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go index 946f258af..a7262f61b 100644 --- a/pkg/domain/infra/abi/volumes.go +++ b/pkg/domain/infra/abi/volumes.go @@ -4,6 +4,7 @@ import ( "context" "github.com/containers/podman/v2/libpod" + "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/domain/filters" "github.com/containers/podman/v2/pkg/domain/infra/abi/parse" @@ -71,9 +72,10 @@ func (ic *ContainerEngine) VolumeRm(ctx context.Context, namesOrIds []string, op return reports, nil } -func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []string, opts entities.VolumeInspectOptions) ([]*entities.VolumeInspectReport, error) { +func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []string, opts entities.InspectOptions) ([]*entities.VolumeInspectReport, []error, error) { var ( err error + errs []error vols []*libpod.Volume ) @@ -82,13 +84,18 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin if opts.All { vols, err = ic.Libpod.GetAllVolumes() if err != nil { - return nil, err + return nil, nil, err } } else { for _, v := range namesOrIds { vol, err := ic.Libpod.LookupVolume(v) if err != nil { - return nil, errors.Wrapf(err, "error inspecting volume %s", v) + if errors.Cause(err) == define.ErrNoSuchVolume { + errs = append(errs, errors.Errorf("no such volume %s", v)) + continue + } else { + return nil, nil, errors.Wrapf(err, "error inspecting volume %s", v) + } } vols = append(vols, vol) } @@ -98,11 +105,11 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin var uid, gid int uid, err = v.UID() if err != nil { - return nil, err + return nil, nil, err } gid, err = v.GID() if err != nil { - return nil, err + return nil, nil, err } config := entities.VolumeConfigResponse{ Name: v.Name(), @@ -117,7 +124,7 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin } reports = append(reports, &entities.VolumeInspectReport{VolumeConfigResponse: &config}) } - return reports, nil + return reports, errs, nil } func (ic *ContainerEngine) VolumePrune(ctx context.Context) ([]*entities.VolumePruneReport, error) { diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 7913d79cd..8066e1c00 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -21,6 +21,7 @@ import ( "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/errorhandling" "github.com/containers/podman/v2/pkg/specgen" + "github.com/containers/podman/v2/pkg/util" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -558,6 +559,11 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta for _, w := range con.Warnings { fmt.Fprintf(os.Stderr, "%s\n", w) } + if opts.CIDFile != "" { + if err := util.CreateCidFile(opts.CIDFile, con.ID); err != nil { + return nil, err + } + } report := entities.ContainerRunReport{Id: con.ID} diff --git a/pkg/domain/infra/tunnel/network.go b/pkg/domain/infra/tunnel/network.go index d155fdd9e..15527e02c 100644 --- a/pkg/domain/infra/tunnel/network.go +++ b/pkg/domain/infra/tunnel/network.go @@ -5,22 +5,34 @@ import ( "github.com/containers/podman/v2/pkg/bindings/network" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/pkg/errors" ) func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.NetworkListOptions) ([]*entities.NetworkListReport, error) { return network.List(ic.ClientCxt, options) } -func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.NetworkInspectOptions) ([]entities.NetworkInspectReport, error) { - reports := make([]entities.NetworkInspectReport, 0, len(namesOrIds)) +func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]entities.NetworkInspectReport, []error, error) { + var ( + reports = make([]entities.NetworkInspectReport, 0, len(namesOrIds)) + errs = []error{} + ) for _, name := range namesOrIds { report, err := network.Inspect(ic.ClientCxt, name) if err != nil { - return nil, err + errModel, ok := err.(entities.ErrorModel) + if !ok { + return nil, nil, err + } + if errModel.ResponseCode == 404 { + errs = append(errs, errors.Errorf("no such network %q", name)) + continue + } + return nil, nil, err } reports = append(reports, report...) } - return reports, nil + return reports, errs, nil } func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, options entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) { diff --git a/pkg/domain/infra/tunnel/volumes.go b/pkg/domain/infra/tunnel/volumes.go index e432d3292..c0df2bb7b 100644 --- a/pkg/domain/infra/tunnel/volumes.go +++ b/pkg/domain/infra/tunnel/volumes.go @@ -5,6 +5,7 @@ import ( "github.com/containers/podman/v2/pkg/bindings/volumes" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/pkg/errors" ) func (ic *ContainerEngine) VolumeCreate(ctx context.Context, opts entities.VolumeCreateOptions) (*entities.IDOrNameResponse, error) { @@ -35,25 +36,36 @@ func (ic *ContainerEngine) VolumeRm(ctx context.Context, namesOrIds []string, op return reports, nil } -func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []string, opts entities.VolumeInspectOptions) ([]*entities.VolumeInspectReport, error) { +func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []string, opts entities.InspectOptions) ([]*entities.VolumeInspectReport, []error, error) { + var ( + reports = make([]*entities.VolumeInspectReport, 0, len(namesOrIds)) + errs = []error{} + ) if opts.All { vols, err := volumes.List(ic.ClientCxt, nil) if err != nil { - return nil, err + return nil, nil, err } for _, v := range vols { namesOrIds = append(namesOrIds, v.Name) } } - reports := make([]*entities.VolumeInspectReport, 0, len(namesOrIds)) for _, id := range namesOrIds { data, err := volumes.Inspect(ic.ClientCxt, id) if err != nil { - return nil, err + errModel, ok := err.(entities.ErrorModel) + if !ok { + return nil, nil, err + } + if errModel.ResponseCode == 404 { + errs = append(errs, errors.Errorf("no such volume %q", id)) + continue + } + return nil, nil, err } reports = append(reports, &entities.VolumeInspectReport{VolumeConfigResponse: data}) } - return reports, nil + return reports, errs, nil } func (ic *ContainerEngine) VolumePrune(ctx context.Context) ([]*entities.VolumePruneReport, error) { diff --git a/pkg/spec/security.go b/pkg/spec/security.go index e152e3495..5f7db7edb 100644 --- a/pkg/spec/security.go +++ b/pkg/spec/security.go @@ -178,7 +178,7 @@ func (c *SecurityConfig) ConfigureGenerator(g *generate.Generator, user *UserCon for _, opt := range c.SecurityOpts { // Split on both : and = - splitOpt := strings.Split(opt, "=") + splitOpt := strings.SplitN(opt, "=", 2) if len(splitOpt) == 1 { splitOpt = strings.Split(opt, ":") } diff --git a/pkg/spec/storage.go b/pkg/spec/storage.go index 5e2f04e50..ebf5ec196 100644 --- a/pkg/spec/storage.go +++ b/pkg/spec/storage.go @@ -394,7 +394,7 @@ func getBindMount(args []string) (spec.Mount, error) { var setSource, setDest, setRORW, setSuid, setDev, setExec, setRelabel bool for _, val := range args { - kv := strings.Split(val, "=") + kv := strings.SplitN(val, "=", 2) switch kv[0] { case "bind-nonrecursive": newMount.Options = append(newMount.Options, "bind") @@ -517,7 +517,7 @@ func getTmpfsMount(args []string) (spec.Mount, error) { var setDest, setRORW, setSuid, setDev, setExec, setTmpcopyup bool for _, val := range args { - kv := strings.Split(val, "=") + kv := strings.SplitN(val, "=", 2) switch kv[0] { case "tmpcopyup", "notmpcopyup": if setTmpcopyup { @@ -591,7 +591,7 @@ func getNamedVolume(args []string) (*libpod.ContainerNamedVolume, error) { var setSource, setDest, setRORW, setSuid, setDev, setExec bool for _, val := range args { - kv := strings.Split(val, "=") + kv := strings.SplitN(val, "=", 2) switch kv[0] { case "ro", "rw": if setRORW { diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index 105e36bc6..f051537de 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -4,6 +4,7 @@ import ( "context" "os" "path/filepath" + "strings" "github.com/containers/common/pkg/config" "github.com/containers/podman/v2/libpod" @@ -91,11 +92,19 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener if err != nil { return nil, err } - imgName := s.Image - names := newImage.Names() - if len(names) > 0 { - imgName = names[0] + // If the input name changed, we could properly resolve the + // image. Otherwise, it must have been an ID where we're + // defaulting to the first name or an empty one if no names are + // present. + imgName := newImage.InputName + if s.Image == newImage.InputName && strings.HasPrefix(newImage.ID(), s.Image) { + imgName = "" + names := newImage.Names() + if len(names) > 0 { + imgName = names[0] + } } + options = append(options, libpod.WithRootFSFromImage(newImage.ID(), imgName, s.RawImageName)) } if err := s.Validate(); err != nil { diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go index 7adb8be6a..7e4f09dc4 100644 --- a/pkg/specgen/generate/namespaces.go +++ b/pkg/specgen/generate/namespaces.go @@ -342,7 +342,7 @@ func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt return errors.Wrapf(err, "error looking up container to share uts namespace with") } hostname = utsCtr.Hostname() - case s.NetNS.NSMode == specgen.Host || s.UtsNS.NSMode == specgen.Host: + case (s.NetNS.NSMode == specgen.Host && hostname == "") || s.UtsNS.NSMode == specgen.Host: tmpHostname, err := os.Hostname() if err != nil { return errors.Wrap(err, "unable to retrieve hostname of the host") diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index f02432f5b..8454458a8 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -110,7 +110,7 @@ func makeCommand(ctx context.Context, s *specgen.SpecGenerator, img *image.Image // Only use image command if the user did not manually set an // entrypoint. command := s.Command - if command == nil && img != nil && s.Entrypoint == nil { + if (command == nil || len(command) == 0) && img != nil && (s.Entrypoint == nil || len(s.Entrypoint) == 0) { newCmd, err := img.Cmd(ctx) if err != nil { return nil, err diff --git a/pkg/util/utils.go b/pkg/util/utils.go index c3a70e2fb..a9aad657d 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -474,8 +474,8 @@ func getTomlStorage(storeOptions *storage.StoreOptions) *tomlConfig { config.Storage.RunRoot = storeOptions.RunRoot config.Storage.GraphRoot = storeOptions.GraphRoot for _, i := range storeOptions.GraphDriverOptions { - s := strings.Split(i, "=") - if s[0] == "overlay.mount_program" { + s := strings.SplitN(i, "=", 2) + if s[0] == "overlay.mount_program" && len(s) == 2 { config.Storage.Options.MountProgram = s[1] } } @@ -638,3 +638,18 @@ func ValidateSysctls(strSlice []string) (map[string]string, error) { func DefaultContainerConfig() *config.Config { return containerConfig } + +func CreateCidFile(cidfile string, id string) error { + cidFile, err := OpenExclusiveFile(cidfile) + if err != nil { + if os.IsExist(err) { + return errors.Errorf("container id file exists. Ensure another container is not using it or delete %s", cidfile) + } + return errors.Errorf("error opening cidfile %s", cidfile) + } + if _, err = cidFile.WriteString(id); err != nil { + logrus.Error(err) + } + cidFile.Close() + return nil +} diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go index ef310d590..af6c43fec 100644 --- a/pkg/varlinkapi/images.go +++ b/pkg/varlinkapi/images.go @@ -843,7 +843,7 @@ func (i *VarlinkAPI) ImageSave(call iopodman.VarlinkCall, options iopodman.Image saveOutput := bytes.NewBuffer([]byte{}) c := make(chan error) go func() { - err := newImage.Save(getContext(), options.Name, options.Format, output, options.MoreTags, options.Quiet, options.Compress) + err := newImage.Save(getContext(), options.Name, options.Format, output, options.MoreTags, options.Quiet, options.Compress, true) c <- err close(c) }() diff --git a/test/apiv2/01-basic.at b/test/apiv2/01-basic.at index 9d4b04edb..f550d5fc3 100644 --- a/test/apiv2/01-basic.at +++ b/test/apiv2/01-basic.at @@ -59,7 +59,10 @@ t GET info 200 \ .DefaultRuntime~.*$runtime \ .MemTotal~[0-9]\\+ -# Timing: make sure server stays responsive +# Timing: make sure server stays responsive. +# Because /info may need to check storage, it may be slow the first time. +# Let's invoke it once to prime caches, then run ten queries in a timed loop. +t GET info 200 t0=$SECONDS for i in $(seq 1 10); do # FIXME: someday: refactor t(), separate out the 'curl' logic so we @@ -70,7 +73,8 @@ t1=$SECONDS delta_t=$((t1 - t2)) # Desired number of seconds in which we expect to run. -want=7 +# FIXME: 10 seconds is a lot! PR #8076 opened to investigate why. +want=10 if [ $delta_t -le $want ]; then _show_ok 1 "Time for ten /info requests ($delta_t seconds) <= ${want}s" else diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at index 7fbcd2e9c..c7055dfc4 100644 --- a/test/apiv2/20-containers.at +++ b/test/apiv2/20-containers.at @@ -206,16 +206,6 @@ t POST containers/${cid_top}/stop "" 204 t DELETE containers/$cid 204 t DELETE containers/$cid_top 204 -# test the apiv2 create, shouldn't ignore the ENV and WORKDIR from the image -t POST containers/create '"Image":"'$ENV_WORKDIR_IMG'","Env":["testKey1"]' 201 \ - .Id~[0-9a-f]\\{64\\} -cid=$(jq -r '.Id' <<<"$output") -t GET containers/$cid/json 200 \ - .Config.Env~.*REDIS_VERSION= \ - .Config.Env~.*testKey1= \ - .Config.WorkingDir="/data" # default is /data -t DELETE containers/$cid 204 - # test the WORKDIR and StopSignal t POST containers/create '"Image":"'$ENV_WORKDIR_IMG'","WorkingDir":"/dataDir","StopSignal":"9"' 201 \ .Id~[0-9a-f]\\{64\\} diff --git a/test/apiv2/30-volumes.at b/test/apiv2/30-volumes.at index b599680e3..2c38954b6 100644 --- a/test/apiv2/30-volumes.at +++ b/test/apiv2/30-volumes.at @@ -3,12 +3,64 @@ # volume-related tests # -# -# FIXME: endpoints seem to be unimplemented, return 404 -# -if false; then -t GET libpod/volumes/json 200 null -t POST libpod/volumes/create name=foo 201 -fi +## create volume +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 POST libpod/volumes/create '' 201 +t POST libpod/volumes/create \ + '"Name":"foo2","Label":{"testlabel":"testonly"},"Options":{"type":"tmpfs","o":"nodev,noexec"}}' 201 \ + .Name=foo2 \ + .Labels.testlabel=testonly \ + .Options.type=tmpfs \ + .Options.o=nodev,noexec + +# Negative test +# We have created a volume named "foo1" +t POST libpod/volumes/create name=foo1 500 \ + .cause="volume already exists" \ + .message~.* \ + .response=500 + +## list volume +t GET libpod/volumes/json 200 \ + .[0].Name~.* \ + .[0].Mountpoint~.* \ + .[0].CreatedAt~[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.* +# -G --data-urlencode 'filters={"name":["foo1"]}' +t GET libpod/volumes/json?filters=%7B%22name%22%3A%5B%22foo1%22%5D%7D 200 length=1 .[0].Name=foo1 +# -G --data-urlencode 'filters={"name":["notexist"]}' +t GET libpod/volumes/json?filters=%7B%22name%22%3A%5B%22notexists%22%5D%7D 200 length=0 + +## inspect volume +t GET libpod/volumes/foo1/json 200 \ + .Name=foo1 \ + .Mountpoint=$volumepath/foo1/_data \ + .CreatedAt~[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.* +t GET libpod/volumes/notexist/json 404 \ + .cause="no such volume" \ + .message~.* \ + .response=404 + +## Remove volumes +t DELETE libpod/volumes/foo1 204 +#After remove foo1 volume, this volume should not exist +t GET libpod/volumes/foo1/json 404 +# Negative test +t DELETE libpod/volumes/foo1 404 \ + .cause="no such volume" \ + .message~.* \ + .response=404 + +## Prune volumes +t POST libpod/volumes/prune "" 200 +#After prune volumes, there should be no volume existing +t GET libpod/volumes/json 200 length=0 # vim: filetype=sh diff --git a/test/apiv2/test-apiv2 b/test/apiv2/test-apiv2 index 78325eb24..c8ca9df3f 100755 --- a/test/apiv2/test-apiv2 +++ b/test/apiv2/test-apiv2 @@ -179,7 +179,7 @@ function t() { # POST requests require an extra params arg if [[ $method = "POST" ]]; then curl_args="-d $(jsonify $1)" - testname="$testname [$1]" + testname="$testname [$curl_args]" shift fi @@ -204,21 +204,30 @@ function t() { echo "-------------------------------------------------------------" >>$LOG echo "\$ $testname" >>$LOG rm -f $WORKDIR/curl.* - curl -s -X $method ${curl_args} \ - -H 'Content-type: application/json' \ - --dump-header $WORKDIR/curl.headers.out \ - -o $WORKDIR/curl.result.out "$url" - - if [[ $? -eq 7 ]]; then - echo "FATAL: curl failure on $url - cannot continue" >&2 + # -s = silent, but --write-out 'format' gives us important response data + response=$(curl -s -X $method ${curl_args} \ + -H 'Content-type: application/json' \ + --dump-header $WORKDIR/curl.headers.out \ + --write-out '%{http_code}^%{content_type}^%{time_total}' \ + -o $WORKDIR/curl.result.out "$url") + + # Any error from curl is instant bad news, from which we can't recover + rc=$? + if [[ $rc -ne 0 ]]; then + echo "FATAL: curl failure ($rc) on $url - cannot continue" >&2 exit 1 fi - cat $WORKDIR/curl.headers.out >>$LOG 2>/dev/null || true + # Show returned headers (without trailing ^M or empty lines) in log file. + # Sometimes -- I can't remember why! -- we don't get headers. + if [[ -e $WORKDIR/curl.headers.out ]]; then + tr -d '\015' < $WORKDIR/curl.headers.out | egrep '.' >>$LOG + fi - # Log results, if text. If JSON, filter through jq for readability. - content_type=$(sed -ne 's/^Content-Type:[ ]\+//pi' <$WORKDIR/curl.headers.out) + IFS='^' read actual_code content_type time_total <<<"$response" + printf "X-Response-Time: ${time_total}s\n\n" >>$LOG + # Log results, if text. If JSON, filter through jq for readability. if [[ $content_type =~ /octet ]]; then output="[$(file --brief $WORKDIR/curl.result.out)]" echo "$output" >>$LOG @@ -233,10 +242,8 @@ function t() { fi # Test return code - actual_code=$(head -n1 $WORKDIR/curl.headers.out | awk '/^HTTP/ { print $2}') is "$actual_code" "$expected_code" "$testname : status" - # Special case: 204/304, by definition, MUST NOT return content (rfc2616) if [[ $expected_code = 204 || $expected_code = 304 ]]; then if [ -n "$*" ]; then diff --git a/test/e2e/attach_test.go b/test/e2e/attach_test.go index 8065f6298..0c27e05c7 100644 --- a/test/e2e/attach_test.go +++ b/test/e2e/attach_test.go @@ -40,7 +40,7 @@ var _ = Describe("Podman attach", func() { }) It("podman attach to non-running container", func() { - session := podmanTest.Podman([]string{"create", "--name", "test1", "-d", "-i", ALPINE, "ls"}) + session := podmanTest.Podman([]string{"create", "--name", "test1", "-i", ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -50,7 +50,7 @@ var _ = Describe("Podman attach", func() { }) It("podman container attach to non-running container", func() { - session := podmanTest.Podman([]string{"container", "create", "--name", "test1", "-d", "-i", ALPINE, "ls"}) + session := podmanTest.Podman([]string{"container", "create", "--name", "test1", "-i", ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go index 226b71627..3814d161d 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/common_test.go @@ -515,6 +515,14 @@ func (s *PodmanSessionIntegration) InspectPodToJSON() define.InspectPodData { return i } +// InspectPodToJSON takes the sessions output from an inspect and returns json +func (s *PodmanSessionIntegration) InspectPodArrToJSON() []define.InspectPodData { + var i []define.InspectPodData + err := jsoniter.Unmarshal(s.Out.Contents(), &i) + Expect(err).To(BeNil()) + return i +} + // CreatePod creates a pod with no infra container // it optionally takes a pod name func (p *PodmanTestIntegration) CreatePod(name string) (*PodmanSessionIntegration, int, string) { @@ -621,6 +629,13 @@ func SkipIfRootless(reason string) { } } +func SkipIfNotRootless(reason string) { + checkReason(reason) + if os.Geteuid() == 0 { + ginkgo.Skip("[notRootless]: " + reason) + } +} + func SkipIfNotFedora() { info := GetHostDistributionInfo() if info.Distribution != "fedora" { @@ -666,3 +681,9 @@ func (p *PodmanTestIntegration) PodmanAsUser(args []string, uid, gid uint32, cwd podmanSession := p.PodmanAsUserBase(args, uid, gid, cwd, env, false, false, nil) return &PodmanSessionIntegration{podmanSession} } + +// We don't support running Varlink when local +func (p *PodmanTestIntegration) RestartRemoteService() { + p.StopRemoteService() + p.StartRemoteService() +} diff --git a/test/e2e/cp_test.go b/test/e2e/cp_test.go index 0a9fa990c..6d349ba5b 100644 --- a/test/e2e/cp_test.go +++ b/test/e2e/cp_test.go @@ -204,6 +204,42 @@ var _ = Describe("Podman cp", func() { os.Remove("file.tar") }) + It("podman cp tar --extract", func() { + testctr := "testctr" + setup := podmanTest.RunTopContainer(testctr) + setup.WaitWithDefaultTimeout() + Expect(setup.ExitCode()).To(Equal(0)) + + session := podmanTest.Podman([]string{"exec", testctr, "mkdir", "/foo"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + path, err := os.Getwd() + Expect(err).To(BeNil()) + testDirPath := filepath.Join(path, "TestDir4") + err = os.Mkdir(testDirPath, 0777) + Expect(err).To(BeNil()) + defer os.RemoveAll(testDirPath) + f, err := os.Create(filepath.Join(testDirPath, "a.txt")) + Expect(err).To(BeNil()) + _, err = f.Write([]byte("Hello World!!!\n")) + f.Close() + cmd := exec.Command("tar", "-cvf", "file.tar", "TestDir4") + exec.Command("tar", "-cvf", "/home/mvasek/file.tar", testDirPath) + _, err = cmd.Output() + Expect(err).To(BeNil()) + defer os.Remove("file.tar") + + session = podmanTest.Podman([]string{"cp", "--extract", "file.tar", "testctr:/foo/"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"exec", testctr, "cat", "/foo/TestDir4/a.txt"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("Hello World!!!")) + }) + It("podman cp symlink", func() { session := podmanTest.Podman([]string{"run", "-d", ALPINE, "top"}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go index 96a234446..6b0f7a7af 100644 --- a/test/e2e/create_test.go +++ b/test/e2e/create_test.go @@ -626,4 +626,22 @@ var _ = Describe("Podman create", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(BeZero()) }) + + It("podman create -d should fail, can not detach create containers", func() { + session := podmanTest.Podman([]string{"create", "-d", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(125)) + Expect(session.ErrorToString()).To(ContainSubstring("unknown shorthand flag")) + + session = podmanTest.Podman([]string{"create", "--detach", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(125)) + Expect(session.ErrorToString()).To(ContainSubstring("unknown flag")) + + session = podmanTest.Podman([]string{"create", "--detach-keys", "ctrl-x", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(125)) + Expect(session.ErrorToString()).To(ContainSubstring("unknown flag")) + }) + }) diff --git a/test/e2e/generate_systemd_test.go b/test/e2e/generate_systemd_test.go index da2f67754..765844265 100644 --- a/test/e2e/generate_systemd_test.go +++ b/test/e2e/generate_systemd_test.go @@ -244,7 +244,7 @@ var _ = Describe("Podman generate systemd", func() { }) It("podman generate systemd --new with explicit detaching param in middle", func() { - n := podmanTest.Podman([]string{"create", "--name", "foo", "-d", "alpine", "top"}) + n := podmanTest.Podman([]string{"create", "--name", "foo", "alpine", "top"}) n.WaitWithDefaultTimeout() Expect(n.ExitCode()).To(Equal(0)) @@ -253,7 +253,7 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.ExitCode()).To(Equal(0)) // Grepping the output (in addition to unit tests) - found, _ := session.GrepString("--name foo -d alpine top") + found, _ := session.GrepString("--name foo alpine top") Expect(found).To(BeTrue()) }) diff --git a/test/e2e/info_test.go b/test/e2e/info_test.go index 49f5f0ce6..bc4e6212b 100644 --- a/test/e2e/info_test.go +++ b/test/e2e/info_test.go @@ -5,9 +5,9 @@ import ( "io/ioutil" "os" "os/exec" + "os/user" "path/filepath" - "github.com/containers/podman/v2/pkg/rootless" . "github.com/containers/podman/v2/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -78,39 +78,35 @@ var _ = Describe("Podman Info", func() { }) It("podman info rootless storage path", func() { - if !rootless.IsRootless() { - Skip("test of rootless_storage_path is only meaningful as rootless") - } + SkipIfNotRootless("test of rootless_storage_path is only meaningful as rootless") SkipIfRemote("Only tests storage on local client") - oldHOME, hasHOME := os.LookupEnv("HOME") + configPath := filepath.Join(podmanTest.TempDir, ".config", "containers", "storage.conf") + os.Setenv("CONTAINERS_STORAGE_CONF", configPath) defer func() { - if hasHOME { - os.Setenv("HOME", oldHOME) - } else { - os.Unsetenv("HOME") - } + os.Unsetenv("CONTAINERS_STORAGE_CONF") }() - os.Setenv("HOME", podmanTest.TempDir) - configPath := filepath.Join(os.Getenv("HOME"), ".config", "containers", "storage.conf") err := os.RemoveAll(filepath.Dir(configPath)) Expect(err).To(BeNil()) err = os.MkdirAll(filepath.Dir(configPath), os.ModePerm) Expect(err).To(BeNil()) - rootlessStoragePath := `"/tmp/$HOME/$USER/$UID"` + rootlessStoragePath := `"/tmp/$HOME/$USER/$UID/storage"` driver := `"overlay"` storageOpt := `"/usr/bin/fuse-overlayfs"` storageConf := []byte(fmt.Sprintf("[storage]\ndriver=%s\nrootless_storage_path=%s\n[storage.options]\nmount_program=%s", driver, rootlessStoragePath, storageOpt)) err = ioutil.WriteFile(configPath, storageConf, os.ModePerm) Expect(err).To(BeNil()) - expect := filepath.Join("/tmp", os.Getenv("HOME"), os.Getenv("USER"), os.Getenv("UID")) + u, err := user.Current() + Expect(err).To(BeNil()) + + expect := filepath.Join("/tmp", os.Getenv("HOME"), u.Username, u.Uid, "storage") podmanPath := podmanTest.PodmanTest.PodmanBinary cmd := exec.Command(podmanPath, "info", "--format", "{{.Store.GraphRoot}}") out, err := cmd.CombinedOutput() fmt.Println(string(out)) Expect(err).To(BeNil()) - Expect(string(out)).To(ContainSubstring(expect)) + Expect(string(out)).To(Equal(expect)) }) }) diff --git a/test/e2e/init_test.go b/test/e2e/init_test.go index baa5c5717..3e64cfda2 100644 --- a/test/e2e/init_test.go +++ b/test/e2e/init_test.go @@ -45,7 +45,7 @@ var _ = Describe("Podman init", func() { }) It("podman init single container by ID", func() { - session := podmanTest.Podman([]string{"create", "-d", ALPINE, "ls"}) + session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) cid := session.OutputToString() @@ -61,7 +61,7 @@ var _ = Describe("Podman init", func() { It("podman init single container by name", func() { name := "test1" - session := podmanTest.Podman([]string{"create", "--name", name, "-d", ALPINE, "ls"}) + session := podmanTest.Podman([]string{"create", "--name", name, ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) init := podmanTest.Podman([]string{"init", name}) @@ -76,7 +76,7 @@ var _ = Describe("Podman init", func() { It("podman init latest container", func() { SkipIfRemote("--latest flag n/a") - session := podmanTest.Podman([]string{"create", "-d", ALPINE, "ls"}) + session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) init := podmanTest.Podman([]string{"init", "--latest"}) @@ -90,10 +90,10 @@ var _ = Describe("Podman init", func() { }) It("podman init all three containers, one running", func() { - session := podmanTest.Podman([]string{"create", "--name", "test1", "-d", ALPINE, "ls"}) + session := podmanTest.Podman([]string{"create", "--name", "test1", ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session2 := podmanTest.Podman([]string{"create", "--name", "test2", "-d", ALPINE, "ls"}) + session2 := podmanTest.Podman([]string{"create", "--name", "test2", ALPINE, "ls"}) session2.WaitWithDefaultTimeout() Expect(session2.ExitCode()).To(Equal(0)) session3 := podmanTest.Podman([]string{"run", "--name", "test3", "-d", ALPINE, "top"}) diff --git a/test/e2e/inspect_test.go b/test/e2e/inspect_test.go index d4de7a65c..e8a82f9a1 100644 --- a/test/e2e/inspect_test.go +++ b/test/e2e/inspect_test.go @@ -289,4 +289,145 @@ var _ = Describe("Podman inspect", func() { Expect(baseJSON[0].HostConfig.SecurityOpt).To(Equal([]string{"label=type:spc_t,label=level:s0", "seccomp=unconfined"})) }) + It("podman inspect pod", func() { + podName := "testpod" + create := podmanTest.PodmanNoCache([]string{"pod", "create", "--name", podName}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", podName}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.IsJSONOutputValid()).To(BeTrue()) + podData := inspect.InspectPodArrToJSON() + Expect(podData[0].Name).To(Equal(podName)) + }) + + It("podman inspect pod with type", func() { + podName := "testpod" + create := podmanTest.PodmanNoCache([]string{"pod", "create", "--name", podName}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", "--type", "pod", podName}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.IsJSONOutputValid()).To(BeTrue()) + podData := inspect.InspectPodArrToJSON() + Expect(podData[0].Name).To(Equal(podName)) + }) + + It("podman inspect latest pod", func() { + SkipIfRemote("--latest flag n/a") + podName := "testpod" + create := podmanTest.PodmanNoCache([]string{"pod", "create", "--name", podName}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", "--type", "pod", "--latest"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.IsJSONOutputValid()).To(BeTrue()) + podData := inspect.InspectPodArrToJSON() + Expect(podData[0].Name).To(Equal(podName)) + }) + It("podman inspect latest defaults to latest container", func() { + SkipIfRemote("--latest flag n/a") + podName := "testpod" + pod := podmanTest.PodmanNoCache([]string{"pod", "create", "--name", podName}) + pod.WaitWithDefaultTimeout() + Expect(pod.ExitCode()).To(Equal(0)) + + inspect1 := podmanTest.Podman([]string{"inspect", "--type", "pod", podName}) + inspect1.WaitWithDefaultTimeout() + Expect(inspect1.ExitCode()).To(Equal(0)) + Expect(inspect1.IsJSONOutputValid()).To(BeTrue()) + podData := inspect1.InspectPodArrToJSON() + infra := podData[0].Containers[0].Name + + inspect := podmanTest.Podman([]string{"inspect", "--latest"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.IsJSONOutputValid()).To(BeTrue()) + containerData := inspect.InspectContainerToJSON() + Expect(containerData[0].Name).To(Equal(infra)) + }) + + It("podman inspect network", func() { + name, path := generateNetworkConfig(podmanTest) + defer removeConf(path) + + session := podmanTest.Podman([]string{"inspect", name, "--format", "{{.cniVersion}}"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.LineInOutputContains("0.3.0")).To(BeTrue()) + }) + + It("podman inspect a volume", func() { + session := podmanTest.Podman([]string{"volume", "create", "myvol"}) + session.WaitWithDefaultTimeout() + volName := session.OutputToString() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"inspect", volName}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.IsJSONOutputValid()).To(BeTrue()) + }) + + It("podman inspect a volume with --format", func() { + session := podmanTest.Podman([]string{"volume", "create", "myvol"}) + session.WaitWithDefaultTimeout() + volName := session.OutputToString() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"inspect", "--format", "{{.Name}}", volName}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(Equal(volName)) + }) + It("podman inspect --type container on a pod should fail", func() { + podName := "testpod" + create := podmanTest.PodmanNoCache([]string{"pod", "create", "--name", podName}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", "--type", "container", podName}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).To(ExitWithError()) + }) + + It("podman inspect --type network on a container should fail", func() { + ctrName := "testctr" + create := podmanTest.PodmanNoCache([]string{"create", "--name", ctrName, ALPINE}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", "--type", "network", ctrName}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).To(ExitWithError()) + }) + + It("podman inspect --type pod on a container should fail", func() { + ctrName := "testctr" + create := podmanTest.PodmanNoCache([]string{"create", "--name", ctrName, ALPINE}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", "--type", "pod", ctrName}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).To(ExitWithError()) + }) + + It("podman inspect --type volume on a container should fail", func() { + ctrName := "testctr" + create := podmanTest.PodmanNoCache([]string{"create", "--name", ctrName, ALPINE}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", "--type", "volume", ctrName}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).To(ExitWithError()) + }) + }) diff --git a/test/e2e/logs_test.go b/test/e2e/logs_test.go index 664d4831e..4214bd50e 100644 --- a/test/e2e/logs_test.go +++ b/test/e2e/logs_test.go @@ -148,7 +148,7 @@ var _ = Describe("Podman logs", func() { }) It("podman logs on a created container should result in 0 exit code", func() { - session := podmanTest.Podman([]string{"create", "-dt", "--name", "log", ALPINE}) + session := podmanTest.Podman([]string{"create", "-t", "--name", "log", ALPINE}) session.WaitWithDefaultTimeout() Expect(session).To(Exit(0)) diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index 05e9b4e20..95870788e 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -245,6 +245,24 @@ var _ = Describe("Podman pod create", func() { } }) + It("podman container in pod with IP address shares IP address", func() { + SkipIfRootless("Rootless does not support --ip") + podName := "test" + ctrName := "testCtr" + ip := GetRandomIPAddress() + podCreate := podmanTest.Podman([]string{"pod", "create", "--ip", ip, "--name", podName}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate.ExitCode()).To(Equal(0)) + podCtr := podmanTest.Podman([]string{"run", "--name", ctrName, "--pod", podName, "-d", "-t", ALPINE, "top"}) + podCtr.WaitWithDefaultTimeout() + Expect(podCtr.ExitCode()).To(Equal(0)) + ctrInspect := podmanTest.Podman([]string{"inspect", ctrName}) + ctrInspect.WaitWithDefaultTimeout() + Expect(ctrInspect.ExitCode()).To(Equal(0)) + ctrJSON := ctrInspect.InspectContainerToJSON() + Expect(ctrJSON[0].NetworkSettings.IPAddress).To(Equal(ip)) + }) + It("podman create pod with IP address and no infra should fail", func() { name := "test" ip := GetRandomIPAddress() diff --git a/test/e2e/pod_rm_test.go b/test/e2e/pod_rm_test.go index 24643e6b2..24e945d5a 100644 --- a/test/e2e/pod_rm_test.go +++ b/test/e2e/pod_rm_test.go @@ -167,7 +167,7 @@ var _ = Describe("Podman pod rm", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"create", "-d", "--pod", podid1, ALPINE, "ls"}) + session = podmanTest.Podman([]string{"create", "--pod", podid1, ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go index 48ef566ce..11d0b8c9b 100644 --- a/test/e2e/ps_test.go +++ b/test/e2e/ps_test.go @@ -324,7 +324,7 @@ var _ = Describe("Podman ps", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"create", "-dt", ALPINE, "top"}) + session = podmanTest.Podman([]string{"create", "-t", ALPINE, "top"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -411,18 +411,43 @@ var _ = Describe("Podman ps", func() { Expect(output).To(ContainSubstring(podName)) }) - It("podman ps test with port range", func() { - session := podmanTest.RunTopContainer("") + It("podman ps test with single port range", func() { + session := podmanTest.Podman([]string{"run", "-dt", "-p", "2000-2006:2000-2006", ALPINE, "top"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"run", "-dt", "-p", "2000-2006:2000-2006", ALPINE, "top"}) + session = podmanTest.Podman([]string{"ps", "--format", "{{.Ports}}"}) + session.WaitWithDefaultTimeout() + Expect(session.OutputToString()).To(ContainSubstring("0.0.0.0:2000-2006")) + }) + + It("podman ps test with invalid port range", func() { + session := podmanTest.Podman([]string{ + "run", "-p", "1000-2000:2000-3000", "-p", "1999-2999:3001-4001", ALPINE, + }) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(125)) + Expect(session.ErrorToString()).To(ContainSubstring("conflicting port mappings for host port 1999")) + }) + + It("podman ps test with multiple port range", func() { + session := podmanTest.Podman([]string{ + "run", "-dt", + "-p", "3000-3001:3000-3001", + "-p", "3100-3102:4000-4002", + "-p", "30080:30080", + "-p", "30443:30443", + "-p", "8000:8080", + ALPINE, "top"}, + ) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) session = podmanTest.Podman([]string{"ps", "--format", "{{.Ports}}"}) session.WaitWithDefaultTimeout() - Expect(session.OutputToString()).To(ContainSubstring("0.0.0.0:2000-2006")) + Expect(session.OutputToString()).To(ContainSubstring( + "0.0.0.0:3000-3001->3000-3001/tcp, 0.0.0.0:3100-3102->4000-4002/tcp, 0.0.0.0:8000->8080/tcp, 0.0.0.0:30080->30080/tcp, 0.0.0.0:30443->30443/tcp", + )) }) It("podman ps sync flag", func() { diff --git a/test/e2e/restart_test.go b/test/e2e/restart_test.go index 789b4dee5..114bd481a 100644 --- a/test/e2e/restart_test.go +++ b/test/e2e/restart_test.go @@ -54,7 +54,7 @@ var _ = Describe("Podman restart", func() { }) It("Podman restart stopped container by ID", func() { - session := podmanTest.Podman([]string{"create", "-d", ALPINE, "ls"}) + session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) cid := session.OutputToString() diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index e14482db7..9f6fd8602 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -357,7 +357,7 @@ var _ = Describe("Podman run networking", func() { }) It("podman run network expose ports in image metadata", func() { - session := podmanTest.Podman([]string{"create", "--name", "test", "-dt", "-P", nginx}) + session := podmanTest.Podman([]string{"create", "--name", "test", "-t", "-P", nginx}) session.Wait(90) Expect(session.ExitCode()).To(Equal(0)) results := podmanTest.Podman([]string{"inspect", "test"}) @@ -571,4 +571,27 @@ var _ = Describe("Podman run networking", func() { podrm.WaitWithDefaultTimeout() Expect(podrm.ExitCode()).To(BeZero()) }) + + It("podman run net=host adds entry to /etc/hosts", func() { + run := podmanTest.Podman([]string{"run", "--net=host", ALPINE, "cat", "/etc/hosts"}) + run.WaitWithDefaultTimeout() + Expect(run.ExitCode()).To(BeZero()) + Expect(strings.Contains(run.OutputToString(), "127.0.1.1")).To(BeTrue()) + }) + + It("podman run with --net=host and --hostname sets correct hostname", func() { + hostname := "testctr" + run := podmanTest.Podman([]string{"run", "--net=host", "--hostname", hostname, ALPINE, "hostname"}) + run.WaitWithDefaultTimeout() + Expect(run.ExitCode()).To(BeZero()) + Expect(strings.Contains(run.OutputToString(), hostname)).To(BeTrue()) + }) + + It("podman run with --net=none adds hostname to /etc/hosts", func() { + hostname := "testctr" + run := podmanTest.Podman([]string{"run", "--net=none", "--hostname", hostname, ALPINE, "hostname"}) + run.WaitWithDefaultTimeout() + Expect(run.ExitCode()).To(BeZero()) + Expect(strings.Contains(run.OutputToString(), hostname)).To(BeTrue()) + }) }) diff --git a/test/e2e/run_security_labels.go b/test/e2e/run_security_labels.go index 2a0b0467d..0c5621e3f 100644 --- a/test/e2e/run_security_labels.go +++ b/test/e2e/run_security_labels.go @@ -94,7 +94,7 @@ var _ = Describe("Podman generate kube", func() { test1.WaitWithDefaultTimeout() Expect(test1.ExitCode()).To(BeZero()) - commit := podmanTest.Podman([]string{"commit", "-c", "label=io.containers.capabilities=sys_chroot,net_raw", "test1", "image1"}) + commit := podmanTest.Podman([]string{"commit", "-c", "label=io.containers.capabilities=sys_chroot,setuid", "test1", "image1"}) commit.WaitWithDefaultTimeout() Expect(commit.ExitCode()).To(BeZero()) @@ -108,7 +108,7 @@ var _ = Describe("Podman generate kube", func() { ctr := inspect.InspectContainerToJSON() caps := strings.Join(ctr[0].EffectiveCaps, ",") - Expect(caps).To(Equal("CAP_SYS_CHROOT,CAP_NET_RAW")) + Expect(caps).To(Equal("CAP_SYS_CHROOT,CAP_SETUID")) }) diff --git a/test/e2e/run_volume_test.go b/test/e2e/run_volume_test.go index 92d3418e3..1c8a67123 100644 --- a/test/e2e/run_volume_test.go +++ b/test/e2e/run_volume_test.go @@ -489,7 +489,7 @@ VOLUME /test/` Expect(err).To(Not(BeNil())) // Make sure modifications in container disappear when container is stopped - session = podmanTest.Podman([]string{"create", "-d", "-v", fmt.Sprintf("%s:/run/test:O", mountPath), ALPINE, "top"}) + session = podmanTest.Podman([]string{"create", "-v", fmt.Sprintf("%s:/run/test:O", mountPath), ALPINE, "top"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) session = podmanTest.Podman([]string{"start", "-l"}) diff --git a/test/e2e/save_test.go b/test/e2e/save_test.go index 1f1258be3..79fc4d737 100644 --- a/test/e2e/save_test.go +++ b/test/e2e/save_test.go @@ -1,8 +1,12 @@ package integration import ( + "io/ioutil" "os" + "os/exec" "path/filepath" + "strconv" + "strings" "github.com/containers/podman/v2/pkg/rootless" . "github.com/containers/podman/v2/test/utils" @@ -116,6 +120,71 @@ var _ = Describe("Podman save", func() { Expect(save).To(ExitWithError()) }) + It("podman save remove signature", func() { + SkipIfRootless("FIXME: Need get in rootless push sign") + if podmanTest.Host.Arch == "ppc64le" { + Skip("No registry image for ppc64le") + } + tempGNUPGHOME := filepath.Join(podmanTest.TempDir, "tmpGPG") + err := os.Mkdir(tempGNUPGHOME, os.ModePerm) + Expect(err).To(BeNil()) + origGNUPGHOME := os.Getenv("GNUPGHOME") + err = os.Setenv("GNUPGHOME", tempGNUPGHOME) + Expect(err).To(BeNil()) + defer os.Setenv("GNUPGHOME", origGNUPGHOME) + + port := 5000 + session := podmanTest.Podman([]string{"run", "-d", "--name", "registry", "-p", strings.Join([]string{strconv.Itoa(port), strconv.Itoa(port)}, ":"), "docker.io/registry:2.6"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + if !WaitContainerReady(podmanTest, "registry", "listening on", 20, 1) { + Skip("Cannot start docker registry.") + } + + cmd := exec.Command("gpg", "--import", "sign/secret-key.asc") + err = cmd.Run() + Expect(err).To(BeNil()) + + cmd = exec.Command("cp", "/etc/containers/registries.d/default.yaml", "default.yaml") + if err = cmd.Run(); err != nil { + Skip("no signature store to verify") + } + defer func() { + cmd = exec.Command("cp", "default.yaml", "/etc/containers/registries.d/default.yaml") + cmd.Run() + }() + + cmd = exec.Command("cp", "sign/key.gpg", "/tmp/key.gpg") + Expect(cmd.Run()).To(BeNil()) + sigstore := ` +default-docker: + sigstore: file:///var/lib/containers/sigstore + sigstore-staging: file:///var/lib/containers/sigstore +` + Expect(ioutil.WriteFile("/etc/containers/registries.d/default.yaml", []byte(sigstore), 0755)).To(BeNil()) + + session = podmanTest.Podman([]string{"tag", ALPINE, "localhost:5000/alpine"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"push", "--tls-verify=false", "--sign-by", "foo@bar.com", "localhost:5000/alpine"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"rmi", ALPINE, "localhost:5000/alpine"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"pull", "--tls-verify=false", "--signature-policy=sign/policy.json", "localhost:5000/alpine"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + outfile := filepath.Join(podmanTest.TempDir, "temp.tar") + save := podmanTest.Podman([]string{"save", "remove-signatures=true", "-o", outfile, "localhost:5000/alpine"}) + save.WaitWithDefaultTimeout() + Expect(save).To(ExitWithError()) + }) + It("podman save image with digest reference", func() { // pull a digest reference session := podmanTest.PodmanNoCache([]string{"pull", ALPINELISTDIGEST}) diff --git a/test/e2e/search_test.go b/test/e2e/search_test.go index 0cf005529..4f2751099 100644 --- a/test/e2e/search_test.go +++ b/test/e2e/search_test.go @@ -237,7 +237,6 @@ registries = ['{{.Host}}:{{.Port}}']` }) It("podman search attempts HTTP if registry is in registries.insecure and force secure is false", func() { - SkipIfRemote("--tls-verify is not supported on podman-remote search") if podmanTest.Host.Arch == "ppc64le" { Skip("No registry image for ppc64le") } @@ -264,6 +263,10 @@ registries = ['{{.Host}}:{{.Port}}']` registryFileTmpl.Execute(&buffer, registryEndpoints[4]) podmanTest.setRegistriesConfigEnv(buffer.Bytes()) ioutil.WriteFile(fmt.Sprintf("%s/registry4.conf", tempdir), buffer.Bytes(), 0644) + if IsRemote() { + podmanTest.RestartRemoteService() + defer podmanTest.RestartRemoteService() + } search := podmanTest.PodmanNoCache([]string{"search", image}) search.WaitWithDefaultTimeout() @@ -278,7 +281,7 @@ registries = ['{{.Host}}:{{.Port}}']` }) It("podman search doesn't attempt HTTP if force secure is true", func() { - SkipIfRemote("--tls-verify is not supported on podman-remote search") + SkipIfRemote("FIXME This should work on podman-remote") if podmanTest.Host.Arch == "ppc64le" { Skip("No registry image for ppc64le") } @@ -303,6 +306,10 @@ registries = ['{{.Host}}:{{.Port}}']` registryFileTmpl.Execute(&buffer, registryEndpoints[5]) podmanTest.setRegistriesConfigEnv(buffer.Bytes()) ioutil.WriteFile(fmt.Sprintf("%s/registry5.conf", tempdir), buffer.Bytes(), 0644) + if IsRemote() { + podmanTest.RestartRemoteService() + defer podmanTest.RestartRemoteService() + } search := podmanTest.PodmanNoCache([]string{"search", image, "--tls-verify=true"}) search.WaitWithDefaultTimeout() @@ -317,7 +324,7 @@ registries = ['{{.Host}}:{{.Port}}']` }) It("podman search doesn't attempt HTTP if registry is not listed as insecure", func() { - SkipIfRemote("--tls-verify is not supported on podman-remote search") + SkipIfRemote("FIXME This should work on podman-remote") if podmanTest.Host.Arch == "ppc64le" { Skip("No registry image for ppc64le") } @@ -343,6 +350,11 @@ registries = ['{{.Host}}:{{.Port}}']` podmanTest.setRegistriesConfigEnv(buffer.Bytes()) ioutil.WriteFile(fmt.Sprintf("%s/registry6.conf", tempdir), buffer.Bytes(), 0644) + if IsRemote() { + podmanTest.RestartRemoteService() + defer podmanTest.RestartRemoteService() + } + search := podmanTest.PodmanNoCache([]string{"search", image}) search.WaitWithDefaultTimeout() @@ -393,6 +405,11 @@ registries = ['{{.Host}}:{{.Port}}']` podmanTest.setRegistriesConfigEnv(buffer.Bytes()) ioutil.WriteFile(fmt.Sprintf("%s/registry8.conf", tempdir), buffer.Bytes(), 0644) + if IsRemote() { + podmanTest.RestartRemoteService() + defer podmanTest.RestartRemoteService() + } + search := podmanTest.PodmanNoCache([]string{"search", "my-alpine"}) search.WaitWithDefaultTimeout() diff --git a/test/e2e/sign/key.gpg b/test/e2e/sign/key.gpg new file mode 100644 index 000000000..32968fc04 --- /dev/null +++ b/test/e2e/sign/key.gpg @@ -0,0 +1,30 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBF8kNqwBCAC0x3Kog+WlDNwcR6rWIP8Gj2T6LrQ2/3knSyAWzTgC/OBB6Oh0 +KAokXLjy8J3diG3EaSltE7erGG/bZCz8jYvMiwDJScON4zzidotqjoY80E+NeRDg +CC0gqvqmh0ftJIjYNBHzSxqrGRQwzwZU+u6ezlE8+0dvsHcHY+MRnxXJQrdM07EP +Prp85kKckChDlJ1tyGUB/YHieFQmOW5+TERA7ZqQOAQ12Vviv6V4kNfEJJq3MS2c +csZpO323tcHt3oebqsZCIElhX7uVw6GAeCw1tm4NZXs4g1yIC21Of/hzPeC18F72 +splCgKaAOiE9w/nMGLNEYy2NzgEclZLs2Y7jABEBAAG0FGZvb2JhciA8Zm9vQGJh +ci5jb20+iQFUBBMBCAA+FiEERyT4ac7LLibByeabqaoHAy6P2bIFAl8kNqwCGwMF +CQPCZwAFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQqaoHAy6P2bKtuggAgv54 +/F8wgi+uMrtFr8rqNtZMDyXRxfXaXUy5uGNfqHD83yqxweEqxiA8lmFkRHixPWtg +Z2MniFXMVc9kVmg8GNIIuzewXrPqtXztvuURQo9phK68v8fXEqqT6K25wtq8TiQZ +0J3mQIJPPTMe3pCCOyR6+W3iMtQp2AmitxKbzLP3J3GG2i0rG5S147A2rPnzTeMY +hds819+JE7jNMD7FkV+TcQlOVl4wyOQhNEJcjb6rA6EUe5+s85pIFTBSyPMJpJ03 +Y0dLdcSGpKdncGTK2X9+hS96G1+FP/t8hRIDblqUHtBRXe3Ozz6zSqpqu1DbAQSM +bIrLYxXfnZEN+ro0dLkBDQRfJDasAQgAncvLLZUHZkJWDPka3ocysJ7+/lmrXyAj +T3D4r7UM4oaLBOMKjvaKSDw1uW5qYmTxnnsqFDI0O5+XJxD1/0qEf6l2oUpnILdx +Vruf28FuvymbsyhDgs+MBoHz0jLWWPHUW2oWLIqcvaF0BePQ1GS6UoZlmZejsLww +cSpbaAHJng7An/iLuqOBr5EdUA5XMXqmdMFDrjh0uZezImJ2Eacu/hshBdu3IY49 +J5XP18GWrSdUnP27cv3tOii9j5Lfl8QAvCN89vkALIU3eZtnMlWZqLgl5o6COVFm +zpyx+iHOoCznQBt0aGoSNmE/dAqWIQS/xCSFqMHI6kNd9N0oR0rEHwARAQABiQE8 +BBgBCAAmFiEERyT4ac7LLibByeabqaoHAy6P2bIFAl8kNqwCGwwFCQPCZwAACgkQ +qaoHAy6P2bJfjQgAje6YR+p1QaNlTN9l4t2kGzy9RhkfYMrTgI2fEqbS9bFJUy3Y +3mH+vj/r2gN/kaN8LHH4K1d7fAohBsFqSI0flzHHIx2rfti9zAlbXcAErbnG+f0f +k0AaqU7KelU35vjPfNe6Vn7ky6G9CC6jW04NkLZDNFA2GusdYf1aM0LWew5t4WZa +quLVFhL36q9eHaogO/fcPR/quvQefHokk+b541ytwMN9l/g43rTbCvAjrUDHwipb +Gbw91Wg2XjbecRiCXDKWds2M149BpxUzY5xHFtD5t5WSEE/SkkryGTMmTxS3tuQZ +9PdtCPGrNDO6Ts/amORF04Tf+YMJgfv3IWxMeQ== +=y0uZ +-----END PGP PUBLIC KEY BLOCK----- diff --git a/test/e2e/sign/policy.json b/test/e2e/sign/policy.json new file mode 100644 index 000000000..ab01137bf --- /dev/null +++ b/test/e2e/sign/policy.json @@ -0,0 +1,18 @@ +{ + "default": [ + { + "type": "insecureAcceptAnything" + } + ], + "transports": { + "docker": { + "localhost:5000": [ + { + "type": "signedBy", + "keyType": "GPGKeys", + "keyPath": "/tmp/key.gpg" + } + ] + } + } +} diff --git a/test/e2e/start_test.go b/test/e2e/start_test.go index 35b5cab6e..942e00123 100644 --- a/test/e2e/start_test.go +++ b/test/e2e/start_test.go @@ -40,7 +40,7 @@ var _ = Describe("Podman start", func() { }) It("podman start single container by id", func() { - session := podmanTest.Podman([]string{"create", "-d", ALPINE, "ls"}) + session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) cid := session.OutputToString() @@ -50,7 +50,7 @@ var _ = Describe("Podman start", func() { }) It("podman container start single container by id", func() { - session := podmanTest.Podman([]string{"container", "create", "-d", ALPINE, "ls"}) + session := podmanTest.Podman([]string{"container", "create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) cid := session.OutputToString() @@ -61,7 +61,7 @@ var _ = Describe("Podman start", func() { }) It("podman container start single container by short id", func() { - session := podmanTest.Podman([]string{"container", "create", "-d", ALPINE, "ls"}) + session := podmanTest.Podman([]string{"container", "create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) cid := session.OutputToString() @@ -74,7 +74,7 @@ var _ = Describe("Podman start", func() { It("podman start single container by name", func() { name := "foobar99" - session := podmanTest.Podman([]string{"create", "-d", "--name", name, ALPINE, "ls"}) + session := podmanTest.Podman([]string{"create", "--name", name, ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) session = podmanTest.Podman([]string{"start", name}) @@ -98,10 +98,10 @@ var _ = Describe("Podman start", func() { }) It("podman start multiple containers", func() { - session := podmanTest.Podman([]string{"create", "-d", "--name", "foobar99", ALPINE, "ls"}) + session := podmanTest.Podman([]string{"create", "--name", "foobar99", ALPINE, "ls"}) session.WaitWithDefaultTimeout() cid1 := session.OutputToString() - session2 := podmanTest.Podman([]string{"create", "-d", "--name", "foobar100", ALPINE, "ls"}) + session2 := podmanTest.Podman([]string{"create", "--name", "foobar100", ALPINE, "ls"}) session2.WaitWithDefaultTimeout() cid2 := session2.OutputToString() session = podmanTest.Podman([]string{"start", cid1, cid2}) @@ -110,7 +110,7 @@ var _ = Describe("Podman start", func() { }) It("podman start multiple containers with bogus", func() { - session := podmanTest.Podman([]string{"create", "-d", "--name", "foobar99", ALPINE, "ls"}) + session := podmanTest.Podman([]string{"create", "--name", "foobar99", ALPINE, "ls"}) session.WaitWithDefaultTimeout() cid1 := session.OutputToString() session = podmanTest.Podman([]string{"start", cid1, "doesnotexist"}) diff --git a/test/e2e/stop_test.go b/test/e2e/stop_test.go index 1437fd066..c25709a63 100644 --- a/test/e2e/stop_test.go +++ b/test/e2e/stop_test.go @@ -242,7 +242,7 @@ var _ = Describe("Podman stop", func() { defer os.RemoveAll(tmpDir) - session := podmanTest.Podman([]string{"create", "--cidfile", tmpFile, "-d", ALPINE, "top"}) + session := podmanTest.Podman([]string{"create", "--cidfile", tmpFile, ALPINE, "top"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) cid := session.OutputToStringArray()[0] diff --git a/test/e2e/systemd_test.go b/test/e2e/systemd_test.go index 9e717a0eb..218c250cd 100644 --- a/test/e2e/systemd_test.go +++ b/test/e2e/systemd_test.go @@ -59,7 +59,7 @@ WantedBy=multi-user.target Expect(stop.ExitCode()).To(Equal(0)) }() - create := podmanTest.Podman([]string{"create", "-d", "--name", "redis", "redis"}) + create := podmanTest.Podman([]string{"create", "--name", "redis", "redis"}) create.WaitWithDefaultTimeout() Expect(create.ExitCode()).To(Equal(0)) diff --git a/test/e2e/toolbox_test.go b/test/e2e/toolbox_test.go index 4f4113bd4..fbff8d19e 100644 --- a/test/e2e/toolbox_test.go +++ b/test/e2e/toolbox_test.go @@ -222,7 +222,7 @@ var _ = Describe("Toolbox-specific testing", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - Expect(WaitContainerReady(podmanTest, "test", "READY", 2, 1)).To(BeTrue()) + Expect(WaitContainerReady(podmanTest, "test", "READY", 5, 1)).To(BeTrue()) expectedOutput := fmt.Sprintf("%s:x:%s:%s::%s:%s", username, uid, gid, homeDir, shell) @@ -257,7 +257,7 @@ var _ = Describe("Toolbox-specific testing", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - Expect(WaitContainerReady(podmanTest, "test", "READY", 2, 1)).To(BeTrue()) + Expect(WaitContainerReady(podmanTest, "test", "READY", 5, 1)).To(BeTrue()) session = podmanTest.Podman([]string{"exec", "test", "cat", "/etc/group"}) session.WaitWithDefaultTimeout() @@ -301,7 +301,7 @@ var _ = Describe("Toolbox-specific testing", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - Expect(WaitContainerReady(podmanTest, "test", "READY", 2, 1)).To(BeTrue()) + Expect(WaitContainerReady(podmanTest, "test", "READY", 5, 1)).To(BeTrue()) expectedUser := fmt.Sprintf("%s:x:%s:%s::%s:%s", username, uid, gid, homeDir, shell) @@ -358,7 +358,7 @@ var _ = Describe("Toolbox-specific testing", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - Expect(WaitContainerReady(podmanTest, "test", "READY", 2, 1)).To(BeTrue()) + Expect(WaitContainerReady(podmanTest, "test", "READY", 5, 1)).To(BeTrue()) session = podmanTest.Podman([]string{"logs", "test"}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/wait_test.go b/test/e2e/wait_test.go index 4f0129a47..aa8a1f245 100644 --- a/test/e2e/wait_test.go +++ b/test/e2e/wait_test.go @@ -46,6 +46,7 @@ var _ = Describe("Podman wait", func() { Expect(session.ExitCode()).To(Equal(0)) session = podmanTest.Podman([]string{"wait", cid}) session.Wait() + Expect(session.ExitCode()).To(Equal(0)) }) It("podman wait on a sleeping container", func() { @@ -55,22 +56,60 @@ var _ = Describe("Podman wait", func() { Expect(session.ExitCode()).To(Equal(0)) session = podmanTest.Podman([]string{"wait", cid}) session.Wait(20) + Expect(session.ExitCode()).To(Equal(0)) }) It("podman wait on latest container", func() { session := podmanTest.Podman([]string{"run", "-d", ALPINE, "sleep", "1"}) session.Wait(20) Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"wait", "-l"}) - session.Wait(20) + if IsRemote() { + session = podmanTest.Podman([]string{"wait", session.OutputToString()}) + } else { + session = podmanTest.Podman([]string{"wait", "-l"}) + } + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) }) It("podman container wait on latest container", func() { session := podmanTest.Podman([]string{"container", "run", "-d", ALPINE, "sleep", "1"}) session.Wait(20) Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"container", "wait", "-l"}) + if IsRemote() { + session = podmanTest.Podman([]string{"container", "wait", session.OutputToString()}) + } else { + session = podmanTest.Podman([]string{"container", "wait", "-l"}) + } + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + + It("podman container wait on latest container with --interval flag", func() { + session := podmanTest.Podman([]string{"container", "run", "-d", ALPINE, "sleep", "1"}) session.Wait(20) + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.Podman([]string{"container", "wait", "-i", "5000", session.OutputToString()}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + + It("podman container wait on latest container with --interval flag", func() { + session := podmanTest.Podman([]string{"container", "run", "-d", ALPINE, "sleep", "1"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.Podman([]string{"container", "wait", "--interval", "1s", session.OutputToString()}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + + It("podman container wait on container with bogus --interval", func() { + session := podmanTest.Podman([]string{"container", "run", "-d", ALPINE, "sleep", "1"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.Podman([]string{"container", "wait", "--interval", "100days", session.OutputToString()}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(125)) }) It("podman wait on three containers", func() { diff --git a/test/system/015-help.bats b/test/system/015-help.bats index 651fdcd09..22db8be8a 100644 --- a/test/system/015-help.bats +++ b/test/system/015-help.bats @@ -6,7 +6,7 @@ # provides its own --help output. If the usage message ends in '[command]', # treat it as a subcommand, and recurse into its own list of sub-subcommands. # -# Any usage message that ends in '[flags]' is interpreted as a command +# Any usage message that ends in '[options]' is interpreted as a command # that takes no further arguments; we confirm by running with 'invalid-arg' # and confirming that it exits with error status and message. # @@ -17,7 +17,7 @@ load helpers function podman_commands() { dprint "$@" run_podman help "$@" |\ - awk '/^Available Commands:/{ok=1;next}/^Flags:/{ok=0}ok { print $1 }' |\ + awk '/^Available Commands:/{ok=1;next}/^Options:/{ok=0}ok { print $1 }' |\ grep . "$output" } @@ -42,7 +42,7 @@ function check_help() { # e.g. 'podman ps' should not show 'podman container ps' in usage # Trailing space in usage handles 'podman system renumber' which - # has no ' [flags]' + # has no ' [options]' is "$usage " " $command_string .*" "Usage string matches command" # If usage ends in '[command]', recurse into subcommands @@ -52,25 +52,25 @@ function check_help() { continue fi - # We had someone write upper-case '[FLAGS]' once. Prevent it. - if expr "$usage" : '.*\[FLAG' >/dev/null; then - die "'flags' string must be lower-case in usage: $usage" + # We had someone write upper-case '[OPTIONS]' once. Prevent it. + if expr "$usage" : '.*\[OPTION' >/dev/null; then + die "'options' string must be lower-case in usage: $usage" fi - # We had someone do 'podman foo ARG [flags]' one time. Yeah, no. - if expr "$usage" : '.*[A-Z].*\[flag' >/dev/null; then - die "'flags' must precede arguments in usage: $usage" + # We had someone do 'podman foo ARG [options]' one time. Yeah, no. + if expr "$usage" : '.*[A-Z].*\[option' >/dev/null; then + die "'options' must precede arguments in usage: $usage" fi - # Cross-check: if usage includes '[flags]', there must be a - # longer 'Flags:' section in the full --help output; vice-versa, - # if 'Flags:' is in full output, usage line must have '[flags]'. - if expr "$usage" : '.*\[flag' >/dev/null; then - if ! expr "$full_help" : ".*Flags:" >/dev/null; then - die "$command_string: Usage includes '[flags]' but has no 'Flags:' subsection" + # Cross-check: if usage includes '[options]', there must be a + # longer 'Options:' section in the full --help output; vice-versa, + # if 'Options:' is in full output, usage line must have '[options]'. + if expr "$usage" : '.*\[option' >/dev/null; then + if ! expr "$full_help" : ".*Options:" >/dev/null; then + die "$command_string: Usage includes '[options]' but has no 'Options:' subsection" fi - elif expr "$full_help" : ".*Flags:" >/dev/null; then - die "$command_string: --help has 'Flags:' section but no '[flags]' in synopsis" + elif expr "$full_help" : ".*Options:" >/dev/null; then + die "$command_string: --help has 'Options:' section but no '[options]' in synopsis" fi # If usage lists no arguments (strings in ALL CAPS), confirm @@ -102,7 +102,7 @@ function check_help() { # If usage has required arguments, try running without them. # The expression here is 'first capital letter is not in [BRACKETS]'. - # It is intended to handle 'podman foo [flags] ARG' but not ' [ARG]'. + # It is intended to handle 'podman foo [options] ARG' but not ' [ARG]'. if expr "$usage" : '[^A-Z]\+ [A-Z]' >/dev/null; then # Exceptions: these commands don't work rootless if is_rootless; then diff --git a/test/system/030-run.bats b/test/system/030-run.bats index 9f4037730..6b6964c63 100644 --- a/test/system/030-run.bats +++ b/test/system/030-run.bats @@ -413,6 +413,17 @@ json-file | f else is "$output" "" "LogPath (driver=$driver)" fi + + if [[ $driver != 'none' ]]; then + run_podman logs myctr + is "$output" "$msg" "check that podman logs works as expected" + else + run_podman 125 logs myctr + if ! is_remote; then + is "$output" ".*this container is using the 'none' log driver, cannot read logs.*" \ + "podman logs does not work with none log driver" + fi + fi run_podman rm myctr done < <(parse_table "$tests") @@ -462,15 +473,53 @@ json-file | f # run with --runtime should preserve the named runtime @test "podman run : full path to --runtime is preserved" { - skip_if_cgroupsv1 - skip_if_remote - run_podman run -d --runtime '/usr/bin/crun' $IMAGE sleep 60 + skip_if_remote "podman-remote does not support --runtime option" + + # Get configured runtime + run_podman info --format '{{.Host.OCIRuntime.Path}}' + runtime="$output" + + # Assumes that /var/tmp is not mounted noexec; this is usually safe + new_runtime="/var/tmp/myruntime$(random_string 12)" + cp --preserve $runtime $new_runtime + + run_podman run -d --runtime "$new_runtime" $IMAGE sleep 60 cid="$output" run_podman inspect --format '{{.OCIRuntime}}' $cid - is "$output" "/usr/bin/crun" - + is "$output" "$new_runtime" "podman inspect shows configured runtime" run_podman kill $cid + run_podman rm $cid + rm -f $new_runtime +} + +# Regression test for issue #8082 +@test "podman run : look up correct image name" { + # Create a 2nd tag for the local image. Force to lower case, and apply it. + local newtag="localhost/$(random_string 10)/$(random_string 8)" + newtag=${newtag,,} + run_podman tag $IMAGE $newtag + + # Create a container with the 2nd tag and make sure that it's being + # used. #8082 always inaccurately used the 1st tag. + run_podman create $newtag + cid="$output" + + run_podman inspect --format "{{.ImageName}}" $cid + is "$output" "$newtag" "container .ImageName is the container-create name" + + # Same thing, but now with a :tag, and making sure it works with --name + newtag2="${newtag}:$(random_string 6|tr A-Z a-z)" + run_podman tag $IMAGE $newtag2 + + cname="$(random_string 14|tr A-Z a-z)" + run_podman create --name $cname $newtag2 + run_podman inspect --format "{{.ImageName}}" $cname + is "$output" "$newtag2" "container .ImageName is the container-create name" + + # Clean up. + run_podman rm $cid $cname + run_podman untag $IMAGE $newtag $newtag2 } # vim: filetype=sh diff --git a/test/system/090-events.bats b/test/system/090-events.bats new file mode 100644 index 000000000..8a9db41fa --- /dev/null +++ b/test/system/090-events.bats @@ -0,0 +1,27 @@ +#!/usr/bin/env bats -*- bats -*- +# +# tests for podman events functionality +# + +load helpers + +@test "events with a filter by label" { + skip_if_remote "FIXME: -remote does not include labels in event output" + cname=test-$(random_string 30 | tr A-Z a-z) + labelname=$(random_string 10) + labelvalue=$(random_string 15) + + run_podman run --label $labelname=$labelvalue --name $cname --rm $IMAGE ls + + expect=".* container start [0-9a-f]\+ (image=$IMAGE, name=$cname,.* ${labelname}=${labelvalue}" + run_podman events --filter type=container --filter container=$cname --filter label=${labelname}=${labelvalue} --filter event=start --stream=false + is "$output" "$expect" "filtering by container name and label" + + # Same thing, but without the container-name filter + run_podman events --filter type=container --filter label=${labelname}=${labelvalue} --filter event=start --stream=false + is "$output" "$expect" "filtering just by label" + + # Now filter just by container name, no label + run_podman events --filter type=container --filter container=$cname --filter event=start --stream=false + is "$output" "$expect" "filtering just by label" +} diff --git a/test/system/250-systemd.bats b/test/system/250-systemd.bats index 9bd3e15a1..ac3ae2f98 100644 --- a/test/system/250-systemd.bats +++ b/test/system/250-systemd.bats @@ -42,7 +42,7 @@ function teardown() { cname=$(random_string) # See #7407 for --pull=always. - run_podman create --pull=always --name $cname --label "io.containers.autoupdate=image" --detach $IMAGE top + run_podman create --pull=always --name $cname --label "io.containers.autoupdate=image" $IMAGE top run_podman generate systemd --new $cname echo "$output" > "$UNIT_FILE" diff --git a/test/system/260-sdnotify.bats b/test/system/260-sdnotify.bats index 2ddeda96a..c99ba4fa6 100644 --- a/test/system/260-sdnotify.bats +++ b/test/system/260-sdnotify.bats @@ -107,6 +107,7 @@ function _assert_mainpid_is_conmon() { # Done. Stop container, clean up. run_podman exec $cid touch /stop + run_podman wait $cid run_podman rm $cid _stop_socat } @@ -142,6 +143,7 @@ function _assert_mainpid_is_conmon() { # Done. Stop container, clean up. run_podman exec $cid touch /stop + run_podman wait $cid run_podman rm $cid run_podman rmi $_FEDORA _stop_socat diff --git a/troubleshooting.md b/troubleshooting.md index 4b0f2e1e4..c42afb642 100644 --- a/troubleshooting.md +++ b/troubleshooting.md @@ -644,3 +644,39 @@ $ podman run --read-only --rootfs /path/to/rootfs .... Another option would be to create an overlay file system on the directory as a lower and then then allow podman to create the files on the upper. + +### 26) Running containers with CPU limits fails with a permissions error + +On some systemd-based systems, non-root users do not have CPU limit delegation +permissions. This causes setting CPU limits to fail. + +#### Symptom + +Running a container with a CPU limit options such as `--cpus`, `--cpu-period`, +or `--cpu-quota` will fail with an error similar to the following: + + Error: opening file `cpu.max` for writing: Permission denied: OCI runtime permission denied error + +This means that CPU limit delegation is not enabled for the current user. + +#### Solution + +You can verify whether CPU limit delegation is enabled by running the following command: + + cat "/sys/fs/cgroup/user.slice/user-$(id -u).slice/user@$(id -u).service/cgroup.controllers" + +Example output might be: + + memory pids + +In the above example, `cpu` is not listed, which means the curent user does +not have permission to set CPU limits. + +If you want to enable CPU limit delegation for all users, you can create the +file `/etc/systemd/system/user@.service.d/delegate.conf` with the contents: + + [Service] + Delegate=memory pids cpu io + +After logging out and loggin back in, you should have permission to set CPU +limits. diff --git a/vendor/github.com/containers/buildah/.cirrus.yml b/vendor/github.com/containers/buildah/.cirrus.yml index b105f589e..4921c7b8a 100644 --- a/vendor/github.com/containers/buildah/.cirrus.yml +++ b/vendor/github.com/containers/buildah/.cirrus.yml @@ -90,11 +90,13 @@ gce_instance: 'cirrus-ci/only_prs/gate_task': + gce_instance: + memory: "12Gb" # see bors.toml skip: $CIRRUS_BRANCH =~ ".*\.tmp" - timeout_in: 30m + timeout_in: 10m setup_script: '${SCRIPT_BASE}/setup.sh |& ${_TIMESTAMP}' build_script: '${SCRIPT_BASE}/build.sh |& ${_TIMESTAMP}' diff --git a/vendor/github.com/containers/buildah/CHANGELOG.md b/vendor/github.com/containers/buildah/CHANGELOG.md index ca6a98889..ecdcb14fe 100644 --- a/vendor/github.com/containers/buildah/CHANGELOG.md +++ b/vendor/github.com/containers/buildah/CHANGELOG.md @@ -2,6 +2,16 @@ # Changelog +## v1.16.5 (2020-10-21) + copier.copierHandlerPut: don't check length when there are errors + CI: run gating tasks with a lot more memory + Run(): ignore containers.conf's environment configuration + bump(github.com/openshift/imagebuilder) to v1.1.8 + ADD and COPY: descend into excluded directories, sometimes + copier: add more context to a couple of error messages + copier: check an error earlier + Set directory ownership when copied with ID mapping + ## v1.16.4 (2020-10-01) ADD: only expand archives at the right time diff --git a/vendor/github.com/containers/buildah/add.go b/vendor/github.com/containers/buildah/add.go index a3f3c7a37..45b5c6a94 100644 --- a/vendor/github.com/containers/buildah/add.go +++ b/vendor/github.com/containers/buildah/add.go @@ -137,6 +137,29 @@ func getURL(src, mountpoint, renameTarget string, writer io.Writer) error { return errors.Wrapf(err, "error writing content from %q to tar stream", src) } +// includeDirectoryAnyway returns true if "path" is a prefix for an exception +// known to "pm". If "path" is a directory that "pm" claims matches its list +// of patterns, but "pm"'s list of exclusions contains a pattern for which +// "path" is a prefix, then IncludeDirectoryAnyway() will return true. +// This is not always correct, because it relies on the directory part of any +// exception paths to be specified without wildcards. +func includeDirectoryAnyway(path string, pm *fileutils.PatternMatcher) bool { + if !pm.Exclusions() { + return false + } + prefix := strings.TrimPrefix(path, string(os.PathSeparator)) + string(os.PathSeparator) + for _, pattern := range pm.Patterns() { + if !pattern.Exclusion() { + continue + } + spec := strings.TrimPrefix(pattern.String(), string(os.PathSeparator)) + if strings.HasPrefix(spec, prefix) { + return true + } + } + return false +} + // Add copies the contents of the specified sources into the container's root // filesystem, optionally extracting contents of local files that look like // non-empty archives. @@ -363,20 +386,32 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption for _, glob := range localSourceStat.Globbed { rel, err := filepath.Rel(contextDir, glob) if err != nil { - return errors.Wrapf(err, "error computing path of %q", glob) + return errors.Wrapf(err, "error computing path of %q relative to %q", glob, contextDir) } if strings.HasPrefix(rel, ".."+string(os.PathSeparator)) { return errors.Errorf("possible escaping context directory error: %q is outside of %q", glob, contextDir) } // Check for dockerignore-style exclusion of this item. if rel != "." { - matches, err := pm.Matches(filepath.ToSlash(rel)) // nolint:staticcheck + excluded, err := pm.Matches(filepath.ToSlash(rel)) // nolint:staticcheck if err != nil { return errors.Wrapf(err, "error checking if %q(%q) is excluded", glob, rel) } - if matches { - continue + if excluded { + // non-directories that are excluded are excluded, no question, but + // directories can only be skipped if we don't have to allow for the + // possibility of finding things to include under them + globInfo := localSourceStat.Results[glob] + if !globInfo.IsDir || !includeDirectoryAnyway(rel, pm) { + continue + } } + } else { + // Make sure we don't trigger a "copied nothing" error for an empty context + // directory if we were told to copy the context directory itself. We won't + // actually copy it, but we need to make sure that we don't produce an error + // due to potentially not having anything in the tarstream that we passed. + itemsCopied++ } st := localSourceStat.Results[glob] pipeReader, pipeWriter := io.Pipe() @@ -391,6 +426,10 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption return false, false, nil }) } + writer = newTarFilterer(writer, func(hdr *tar.Header) (bool, bool, io.Reader) { + itemsCopied++ + return false, false, nil + }) getOptions := copier.GetOptions{ UIDMap: srcUIDMap, GIDMap: srcGIDMap, @@ -462,10 +501,9 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption } return multiErr.Errors[0] } - itemsCopied++ } if itemsCopied == 0 { - return errors.Wrapf(syscall.ENOENT, "no items matching glob %q copied (%d filtered)", localSourceStat.Glob, len(localSourceStat.Globbed)) + return errors.Wrapf(syscall.ENOENT, "no items matching glob %q copied (%d filtered out)", localSourceStat.Glob, len(localSourceStat.Globbed)) } } return nil diff --git a/vendor/github.com/containers/buildah/buildah.go b/vendor/github.com/containers/buildah/buildah.go index 2ac0210bd..d27689d4a 100644 --- a/vendor/github.com/containers/buildah/buildah.go +++ b/vendor/github.com/containers/buildah/buildah.go @@ -28,7 +28,7 @@ const ( Package = "buildah" // Version for the Package. Bump version in contrib/rpm/buildah.spec // too. - Version = "1.16.4" + Version = "1.16.5" // The value we use to identify what type of information, currently a // serialized Builder structure, we are using as per-container state. // This should only be changed when we make incompatible changes to diff --git a/vendor/github.com/containers/buildah/changelog.txt b/vendor/github.com/containers/buildah/changelog.txt index 048dc61c1..50648cb7f 100644 --- a/vendor/github.com/containers/buildah/changelog.txt +++ b/vendor/github.com/containers/buildah/changelog.txt @@ -1,3 +1,13 @@ +- Changelog for v1.16.5 (2020-10-21) + * copier.copierHandlerPut: don't check length when there are errors + * CI: run gating tasks with a lot more memory + * Run(): ignore containers.conf's environment configuration + * bump(github.com/openshift/imagebuilder) to v1.1.8 + * ADD and COPY: descend into excluded directories, sometimes + * copier: add more context to a couple of error messages + * copier: check an error earlier + * Set directory ownership when copied with ID mapping + - Changelog for v1.16.4 (2020-10-01) * ADD: only expand archives at the right time diff --git a/vendor/github.com/containers/buildah/copier/copier.go b/vendor/github.com/containers/buildah/copier/copier.go index 1021aeb6f..42ddd452f 100644 --- a/vendor/github.com/containers/buildah/copier/copier.go +++ b/vendor/github.com/containers/buildah/copier/copier.go @@ -976,20 +976,7 @@ func copierHandlerGet(bulkWriter io.Writer, req request, pm *fileutils.PatternMa return errorResponse("copier: get: glob %q: %v", glob, err) } globMatchedCount += len(globMatched) - filtered := make([]string, 0, len(globMatched)) - for _, globbed := range globMatched { - rel, excluded, err := pathIsExcluded(req.Root, globbed, pm) - if err != nil { - return errorResponse("copier: get: checking if %q is excluded: %v", globbed, err) - } - if rel == "." || !excluded { - filtered = append(filtered, globbed) - } - } - if len(filtered) == 0 { - return errorResponse("copier: get: glob %q matched nothing (%d filtered out of %v): %v", glob, len(globMatched), globMatched, syscall.ENOENT) - } - queue = append(queue, filtered...) + queue = append(queue, globMatched...) } // no matches -> error if len(queue) == 0 { @@ -1042,6 +1029,9 @@ func copierHandlerGet(bulkWriter io.Writer, req request, pm *fileutils.PatternMa options := req.GetOptions options.ExpandArchives = false walkfn := func(path string, info os.FileInfo, err error) error { + if err != nil { + return errors.Wrapf(err, "copier: get: error reading %q", path) + } // compute the path of this item // relative to the top-level directory, // for the tar header @@ -1049,9 +1039,6 @@ func copierHandlerGet(bulkWriter io.Writer, req request, pm *fileutils.PatternMa if relErr != nil { return errors.Wrapf(relErr, "copier: get: error computing path of %q relative to top directory %q", path, item) } - if err != nil { - return errors.Wrapf(err, "copier: get: error reading %q", path) - } // prefix the original item's name if we're keeping it if relNamePrefix != "" { rel = filepath.Join(relNamePrefix, rel) @@ -1108,7 +1095,7 @@ func copierHandlerGet(bulkWriter io.Writer, req request, pm *fileutils.PatternMa } } if itemsCopied == 0 { - return errors.New("copier: get: copied no items") + return errors.Wrapf(syscall.ENOENT, "copier: get: copied no items") } return nil } @@ -1271,6 +1258,7 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM return errorResponse("copier: put: error mapping container filesystem owner %d:%d to host filesystem owners: %v", dirUID, dirGID, err) } dirUID, dirGID = hostDirPair.UID, hostDirPair.GID + defaultDirUID, defaultDirGID = hostDirPair.UID, hostDirPair.GID if req.PutOptions.ChownFiles != nil { containerFilePair := idtools.IDPair{UID: *fileUID, GID: *fileGID} hostFilePair, err := idMappings.ToHost(containerFilePair) @@ -1399,7 +1387,9 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM case tar.TypeReg, tar.TypeRegA: var written int64 written, err = createFile(path, tr) - if written != hdr.Size { + // only check the length if there wasn't an error, which we'll + // check along with errors for other types of entries + if err == nil && written != hdr.Size { return errors.Errorf("copier: put: error creating %q: incorrect length (%d != %d)", path, written, hdr.Size) } case tar.TypeLink: diff --git a/vendor/github.com/containers/buildah/go.mod b/vendor/github.com/containers/buildah/go.mod index fac079e45..61663cea2 100644 --- a/vendor/github.com/containers/buildah/go.mod +++ b/vendor/github.com/containers/buildah/go.mod @@ -24,7 +24,7 @@ require ( github.com/opencontainers/runtime-spec v1.0.3-0.20200710190001-3e4195d92445 github.com/opencontainers/runtime-tools v0.9.0 github.com/opencontainers/selinux v1.6.0 - github.com/openshift/imagebuilder v1.1.6 + github.com/openshift/imagebuilder v1.1.8 github.com/pkg/errors v0.9.1 github.com/seccomp/libseccomp-golang v0.9.2-0.20200616122406-847368b35ebf github.com/sirupsen/logrus v1.6.0 diff --git a/vendor/github.com/containers/buildah/go.sum b/vendor/github.com/containers/buildah/go.sum index 463f2bdcc..723cf9c40 100644 --- a/vendor/github.com/containers/buildah/go.sum +++ b/vendor/github.com/containers/buildah/go.sum @@ -264,8 +264,8 @@ github.com/opencontainers/selinux v1.5.2 h1:F6DgIsjgBIcDksLW4D5RG9bXok6oqZ3nvMwj github.com/opencontainers/selinux v1.5.2/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/openshift/imagebuilder v1.1.6 h1:1+YzRxIIefY4QqtCImx6rg+75QrKNfBoPAKxgMo/khM= -github.com/openshift/imagebuilder v1.1.6/go.mod h1:9aJRczxCH0mvT6XQ+5STAQaPWz7OsWcU5/mRkt8IWeo= +github.com/openshift/imagebuilder v1.1.8 h1:gjiIl8pbNj0eC4XWvFJHATdDvYm64p9/pLDLQWoLZPA= +github.com/openshift/imagebuilder v1.1.8/go.mod h1:9aJRczxCH0mvT6XQ+5STAQaPWz7OsWcU5/mRkt8IWeo= github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913 h1:TnbXhKzrTOyuvWrjI8W6pcoI9XPbLHFXCdN2dtUw7Rw= github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913/go.mod h1:J6OG6YJVEWopen4avK3VNQSnALmmjvniMmni/YFYAwc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= diff --git a/vendor/github.com/containers/buildah/run_linux.go b/vendor/github.com/containers/buildah/run_linux.go index d83b3a5cc..66a3ba997 100644 --- a/vendor/github.com/containers/buildah/run_linux.go +++ b/vendor/github.com/containers/buildah/run_linux.go @@ -91,11 +91,8 @@ func (b *Builder) Run(command []string, options RunOptions) error { return err } - defaultContainerConfig, err := config.Default() - if err != nil { - return errors.Wrapf(err, "failed to get container config") - } - b.configureEnvironment(g, options, defaultContainerConfig.Containers.Env) + // hardwire the environment to match docker build to avoid subtle and hard-to-debug differences due to containers.conf + b.configureEnvironment(g, options, []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"}) if b.CommonBuildOpts == nil { return errors.Errorf("Invalid format on container you must recreate the container") diff --git a/vendor/github.com/containers/common/pkg/config/config_local.go b/vendor/github.com/containers/common/pkg/config/config_local.go index 8a27c9626..21dab043f 100644 --- a/vendor/github.com/containers/common/pkg/config/config_local.go +++ b/vendor/github.com/containers/common/pkg/config/config_local.go @@ -79,7 +79,7 @@ func (c *ContainersConfig) validateUlimits() error { } func (c *ContainersConfig) validateTZ() error { - if c.TZ == "local" { + if c.TZ == "local" || c.TZ == "" { return nil } diff --git a/vendor/github.com/containers/common/pkg/config/default.go b/vendor/github.com/containers/common/pkg/config/default.go index 4f2443e2f..5f8f4999f 100644 --- a/vendor/github.com/containers/common/pkg/config/default.go +++ b/vendor/github.com/containers/common/pkg/config/default.go @@ -224,14 +224,12 @@ func defaultConfigFromMemory() (*EngineConfig, error) { c.EventsLogFilePath = filepath.Join(c.TmpDir, "events", "events.log") - var storeOpts storage.StoreOptions if path, ok := os.LookupEnv("CONTAINERS_STORAGE_CONF"); ok { - storage.ReloadConfigurationFile(path, &storeOpts) - } else { - storeOpts, err = storage.DefaultStoreOptions(unshare.IsRootless(), unshare.GetRootlessUID()) - if err != nil { - return nil, err - } + storage.SetDefaultConfigFilePath(path) + } + storeOpts, err := storage.DefaultStoreOptions(unshare.IsRootless(), unshare.GetRootlessUID()) + if err != nil { + return nil, err } if storeOpts.GraphRoot == "" { diff --git a/vendor/github.com/containers/common/pkg/report/camelcase/LICENSE.md b/vendor/github.com/containers/common/pkg/report/camelcase/LICENSE.md new file mode 100644 index 000000000..aa4a536ca --- /dev/null +++ b/vendor/github.com/containers/common/pkg/report/camelcase/LICENSE.md @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2015 Fatih Arslan + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/containers/common/pkg/report/camelcase/README.md b/vendor/github.com/containers/common/pkg/report/camelcase/README.md new file mode 100644 index 000000000..105a6ae33 --- /dev/null +++ b/vendor/github.com/containers/common/pkg/report/camelcase/README.md @@ -0,0 +1,58 @@ +# CamelCase [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/fatih/camelcase) [![Build Status](http://img.shields.io/travis/fatih/camelcase.svg?style=flat-square)](https://travis-ci.org/fatih/camelcase) + +CamelCase is a Golang (Go) package to split the words of a camelcase type +string into a slice of words. It can be used to convert a camelcase word (lower +or upper case) into any type of word. + +## Splitting rules: + +1. If string is not valid UTF-8, return it without splitting as + single item array. +2. Assign all unicode characters into one of 4 sets: lower case + letters, upper case letters, numbers, and all other characters. +3. Iterate through characters of string, introducing splits + between adjacent characters that belong to different sets. +4. Iterate through array of split strings, and if a given string + is upper case: + * if subsequent string is lower case: + * move last character of upper case string to beginning of + lower case string + +## Install + +```bash +go get github.com/fatih/camelcase +``` + +## Usage and examples + +```go +splitted := camelcase.Split("GolangPackage") + +fmt.Println(splitted[0], splitted[1]) // prints: "Golang", "Package" +``` + +Both lower camel case and upper camel case are supported. For more info please +check: [http://en.wikipedia.org/wiki/CamelCase](http://en.wikipedia.org/wiki/CamelCase) + +Below are some example cases: + +``` +"" => [] +"lowercase" => ["lowercase"] +"Class" => ["Class"] +"MyClass" => ["My", "Class"] +"MyC" => ["My", "C"] +"HTML" => ["HTML"] +"PDFLoader" => ["PDF", "Loader"] +"AString" => ["A", "String"] +"SimpleXMLParser" => ["Simple", "XML", "Parser"] +"vimRPCPlugin" => ["vim", "RPC", "Plugin"] +"GL11Version" => ["GL", "11", "Version"] +"99Bottles" => ["99", "Bottles"] +"May5" => ["May", "5"] +"BFG9000" => ["BFG", "9000"] +"BöseÜberraschung" => ["Böse", "Überraschung"] +"Two spaces" => ["Two", " ", "spaces"] +"BadUTF8\xe2\xe2\xa1" => ["BadUTF8\xe2\xe2\xa1"] +``` diff --git a/vendor/github.com/containers/common/pkg/report/camelcase/camelcase.go b/vendor/github.com/containers/common/pkg/report/camelcase/camelcase.go new file mode 100644 index 000000000..0a82d1005 --- /dev/null +++ b/vendor/github.com/containers/common/pkg/report/camelcase/camelcase.go @@ -0,0 +1,91 @@ +// Package camelcase is a micro package to split the words of a camelcase type +// string into a slice of words. +package camelcase + +import ( + "unicode" + "unicode/utf8" +) + +// Split splits the camelcase word and returns a list of words. It also +// supports digits. Both lower camel case and upper camel case are supported. +// For more info please check: http://en.wikipedia.org/wiki/CamelCase +// +// Examples +// +// "" => [""] +// "lowercase" => ["lowercase"] +// "Class" => ["Class"] +// "MyClass" => ["My", "Class"] +// "MyC" => ["My", "C"] +// "HTML" => ["HTML"] +// "PDFLoader" => ["PDF", "Loader"] +// "AString" => ["A", "String"] +// "SimpleXMLParser" => ["Simple", "XML", "Parser"] +// "vimRPCPlugin" => ["vim", "RPC", "Plugin"] +// "GL11Version" => ["GL", "11", "Version"] +// "99Bottles" => ["99", "Bottles"] +// "May5" => ["May", "5"] +// "BFG9000" => ["BFG", "9000"] +// "BöseÜberraschung" => ["Böse", "Überraschung"] +// "Two spaces" => ["Two", " ", "spaces"] +// "BadUTF8\xe2\xe2\xa1" => ["BadUTF8\xe2\xe2\xa1"] +// +// Splitting rules +// +// 1) If string is not valid UTF-8, return it without splitting as +// single item array. +// 2) Assign all unicode characters into one of 4 sets: lower case +// letters, upper case letters, numbers, and all other characters. +// 3) Iterate through characters of string, introducing splits +// between adjacent characters that belong to different sets. +// 4) Iterate through array of split strings, and if a given string +// is upper case: +// if subsequent string is lower case: +// move last character of upper case string to beginning of +// lower case string +func Split(src string) (entries []string) { + // don't split invalid utf8 + if !utf8.ValidString(src) { + return []string{src} + } + entries = []string{} + var runes [][]rune + lastClass := 0 + class := 0 + // split into fields based on class of unicode character + for _, r := range src { + switch { + case unicode.IsLower(r): + class = 1 + case unicode.IsUpper(r): + class = 2 + case unicode.IsDigit(r): + class = 3 + default: + class = 4 + } + if class == lastClass { + runes[len(runes)-1] = append(runes[len(runes)-1], r) + } else { + runes = append(runes, []rune{r}) + } + lastClass = class + } + // handle upper case -> lower case sequences, e.g. + // "PDFL", "oader" -> "PDF", "Loader" + for i := 0; i < len(runes)-1; i++ { + if unicode.IsUpper(runes[i][0]) && unicode.IsLower(runes[i+1][0]) { + runes[i+1] = append([]rune{runes[i][len(runes[i])-1]}, runes[i+1]...) + runes[i] = runes[i][:len(runes[i])-1] + } + } + // construct []string from results + for _, s := range runes { + if len(s) > 0 { + entries = append(entries, string(s)) + } + } + + return entries +} diff --git a/vendor/github.com/containers/common/pkg/report/doc.go b/vendor/github.com/containers/common/pkg/report/doc.go new file mode 100644 index 000000000..60d954d7e --- /dev/null +++ b/vendor/github.com/containers/common/pkg/report/doc.go @@ -0,0 +1,46 @@ +/* +Package report provides helper structs/methods/funcs for formatting output + +To format output for an array of structs: + + w := report.NewWriterDefault(os.Stdout) + defer w.Flush() + + headers := report.Headers(struct { + ID string + }{}, nil) + t, _ := report.NewTemplate("command name").Parse("{{range .}}{{.ID}}{{end}}") + t.Execute(t, headers) + t.Execute(t, map[string]string{ + "ID":"fa85da03b40141899f3af3de6d27852b", + }) + // t.IsTable() == false + +or + + w := report.NewWriterDefault(os.Stdout) + defer w.Flush() + + headers := report.Headers(struct { + CID string + }{}, map[string]string{ + "CID":"ID"}) + t, _ := report.NewTemplate("command name").Parse("table {{.CID}}") + t.Execute(t, headers) + t.Execute(t,map[string]string{ + "CID":"fa85da03b40141899f3af3de6d27852b", + }) + // t.IsTable() == true + +Helpers: + + if report.IsJSON(cmd.Flag("format").Value.String()) { + ... process JSON and output + } + +and + + +Note: Your code should not ignore errors +*/ +package report diff --git a/vendor/github.com/containers/common/pkg/report/template.go b/vendor/github.com/containers/common/pkg/report/template.go new file mode 100644 index 000000000..551fbb3cf --- /dev/null +++ b/vendor/github.com/containers/common/pkg/report/template.go @@ -0,0 +1,115 @@ +package report + +import ( + "reflect" + "strings" + "text/template" + + "github.com/containers/common/pkg/report/camelcase" +) + +// Template embeds template.Template to add functionality to methods +type Template struct { + *template.Template + isTable bool +} + +// FuncMap is aliased from template.FuncMap +type FuncMap template.FuncMap + +// tableReplacer will remove 'table ' prefix and clean up tabs +var tableReplacer = strings.NewReplacer( + "table ", "", + `\t`, "\t", + `\n`, "\n", + " ", "\t", +) + +// escapedReplacer will clean up escaped characters from CLI +var escapedReplacer = strings.NewReplacer( + `\t`, "\t", + `\n`, "\n", +) + +// NormalizeFormat reads given go template format provided by CLI and munges it into what we need +func NormalizeFormat(format string) string { + var f string + // two replacers used so we only remove the prefix keyword `table` + if strings.HasPrefix(format, "table ") { + f = tableReplacer.Replace(format) + } else { + f = escapedReplacer.Replace(format) + } + + if !strings.HasSuffix(f, "\n") { + f += "\n" + } + return f +} + +// Headers queries the interface for field names. +// Array of map is returned to support range templates +// Note: unexported fields can be supported by adding field to overrides +// Note: It is left to the developer to write out said headers +// Podman commands use the general rules of: +// 1) unchanged --format includes headers +// 2) --format '{{.ID}" # no headers +// 3) --format 'table {{.ID}}' # includes headers +func Headers(object interface{}, overrides map[string]string) []map[string]string { + value := reflect.ValueOf(object) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + // Column header will be field name upper-cased. + headers := make(map[string]string, value.NumField()) + for i := 0; i < value.Type().NumField(); i++ { + field := value.Type().Field(i) + // Recurse to find field names from promoted structs + if field.Type.Kind() == reflect.Struct && field.Anonymous { + h := Headers(reflect.New(field.Type).Interface(), nil) + for k, v := range h[0] { + headers[k] = v + } + continue + } + name := strings.Join(camelcase.Split(field.Name), " ") + headers[field.Name] = strings.ToUpper(name) + } + + if len(overrides) > 0 { + // Override column header as provided + for k, v := range overrides { + headers[k] = strings.ToUpper(v) + } + } + return []map[string]string{headers} +} + +// NewTemplate creates a new template object +func NewTemplate(name string) *Template { + return &Template{template.New(name), false} +} + +// Parse parses text as a template body for t +func (t *Template) Parse(text string) (*Template, error) { + if strings.HasPrefix(text, "table ") { + t.isTable = true + text = "{{range .}}" + NormalizeFormat(text) + "{{end}}" + } else { + text = NormalizeFormat(text) + } + + tt, err := t.Template.Parse(text) + return &Template{tt, t.isTable}, err +} + +// Funcs adds the elements of the argument map to the template's function map +func (t *Template) Funcs(funcMap FuncMap) *Template { + return &Template{t.Template.Funcs(template.FuncMap(funcMap)), t.isTable} +} + +// IsTable returns true if format string defines a "table" +func (t *Template) IsTable() bool { + return t.isTable +} diff --git a/vendor/github.com/containers/common/pkg/report/validate.go b/vendor/github.com/containers/common/pkg/report/validate.go new file mode 100644 index 000000000..a5eac5328 --- /dev/null +++ b/vendor/github.com/containers/common/pkg/report/validate.go @@ -0,0 +1,13 @@ +package report + +import "regexp" + +var jsonRegex = regexp.MustCompile(`^\s*(json|{{\s*json\s*(\.)?\s*}})\s*$`) + +// JSONFormat test CLI --format string to be a JSON request +// if report.IsJSON(cmd.Flag("format").Value.String()) { +// ... process JSON and output +// } +func IsJSON(s string) bool { + return jsonRegex.MatchString(s) +} diff --git a/vendor/github.com/containers/common/pkg/report/writer.go b/vendor/github.com/containers/common/pkg/report/writer.go new file mode 100644 index 000000000..360ef8265 --- /dev/null +++ b/vendor/github.com/containers/common/pkg/report/writer.go @@ -0,0 +1,27 @@ +package report + +import ( + "io" + "text/tabwriter" +) + +// Writer aliases tabwriter.Writer to provide Podman defaults +type Writer struct { + *tabwriter.Writer +} + +// NewWriter initializes a new report.Writer with given values +func NewWriter(output io.Writer, minwidth, tabwidth, padding int, padchar byte, flags uint) (*Writer, error) { + t := tabwriter.NewWriter(output, minwidth, tabwidth, padding, padchar, flags) + return &Writer{t}, nil +} + +// NewWriterDefault initializes a new report.Writer with Podman defaults +func NewWriterDefault(output io.Writer) (*Writer, error) { + return NewWriter(output, 12, 2, 2, ' ', 0) +} + +// Flush any output left in buffers +func (w *Writer) Flush() error { + return w.Writer.Flush() +} diff --git a/vendor/github.com/containers/common/pkg/seccomp/default_linux.go b/vendor/github.com/containers/common/pkg/seccomp/default_linux.go index def8019a0..ddc25ac67 100644 --- a/vendor/github.com/containers/common/pkg/seccomp/default_linux.go +++ b/vendor/github.com/containers/common/pkg/seccomp/default_linux.go @@ -66,9 +66,13 @@ func DefaultProfile() *Seccomp { "chown", "chown32", "clock_adjtime", + "clock_adjtime64", "clock_getres", + "clock_getres_time64", "clock_gettime", + "clock_gettime64", "clock_nanosleep", + "clock_nanosleep_time64", "clone", "close", "connect", @@ -226,12 +230,14 @@ func DefaultProfile() *Seccomp { "pivot_root", "poll", "ppoll", + "ppoll_time64", "prctl", "pread64", "preadv", "preadv2", "prlimit64", "pselect6", + "pselect6_time64", "pwrite64", "pwritev", "pwritev2", @@ -343,10 +349,13 @@ func DefaultProfile() *Seccomp { "timer_delete", "timer_getoverrun", "timer_gettime", + "timer_gettime64", "timer_settime", "timerfd_create", "timerfd_gettime", + "timerfd_gettime64", "timerfd_settime", + "timerfd_settime64", "times", "tkill", "truncate", @@ -361,6 +370,7 @@ func DefaultProfile() *Seccomp { "unshare", "utime", "utimensat", + "utimensat_time64", "utimes", "vfork", "vmsplice", @@ -642,6 +652,7 @@ func DefaultProfile() *Seccomp { "settimeofday", "stime", "clock_settime", + "clock_settime64", }, Action: ActAllow, Args: []*Arg{}, diff --git a/vendor/github.com/containers/common/pkg/seccomp/seccomp.json b/vendor/github.com/containers/common/pkg/seccomp/seccomp.json index f58b2ae64..bb5956418 100644 --- a/vendor/github.com/containers/common/pkg/seccomp/seccomp.json +++ b/vendor/github.com/containers/common/pkg/seccomp/seccomp.json @@ -68,9 +68,13 @@ "chown", "chown32", "clock_adjtime", + "clock_adjtime64", "clock_getres", + "clock_getres_time64", "clock_gettime", + "clock_gettime64", "clock_nanosleep", + "clock_nanosleep_time64", "clone", "close", "connect", @@ -228,12 +232,14 @@ "pivot_root", "poll", "ppoll", + "ppoll_time64", "prctl", "pread64", "preadv", "preadv2", "prlimit64", "pselect6", + "pselect6_time64", "pwrite64", "pwritev", "pwritev2", @@ -345,10 +351,13 @@ "timer_delete", "timer_getoverrun", "timer_gettime", + "timer_gettime64", "timer_settime", "timerfd_create", "timerfd_gettime", + "timerfd_gettime64", "timerfd_settime", + "timerfd_settime64", "times", "tkill", "truncate", @@ -363,6 +372,7 @@ "unshare", "utime", "utimensat", + "utimensat_time64", "utimes", "vfork", "vmsplice", @@ -749,7 +759,8 @@ "names": [ "settimeofday", "stime", - "clock_settime" + "clock_settime", + "clock_settime64" ], "action": "SCMP_ACT_ALLOW", "args": [], diff --git a/vendor/github.com/containers/common/version/version.go b/vendor/github.com/containers/common/version/version.go index d5d659434..8e497e7fa 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.26.0" +const Version = "0.26.3" diff --git a/vendor/github.com/containers/image/v5/copy/copy.go b/vendor/github.com/containers/image/v5/copy/copy.go index 873bdc67f..d8e3fa106 100644 --- a/vendor/github.com/containers/image/v5/copy/copy.go +++ b/vendor/github.com/containers/image/v5/copy/copy.go @@ -121,8 +121,6 @@ type imageCopier struct { diffIDsAreNeeded bool canModifyManifest bool canSubstituteBlobs bool - ociDecryptConfig *encconfig.DecryptConfig - ociEncryptConfig *encconfig.EncryptConfig ociEncryptLayers *[]int } @@ -259,7 +257,9 @@ func Image(ctx context.Context, policyContext *signature.PolicyContext, destRef, // FIXME? The cache is used for sources and destinations equally, but we only have a SourceCtx and DestinationCtx. // For now, use DestinationCtx (because blob reuse changes the behavior of the destination side more); eventually // we might want to add a separate CommonCtx — or would that be too confusing? - blobInfoCache: blobinfocache.DefaultCache(options.DestinationCtx), + blobInfoCache: blobinfocache.DefaultCache(options.DestinationCtx), + ociDecryptConfig: options.OciDecryptConfig, + ociEncryptConfig: options.OciEncryptConfig, } // Default to using gzip compression unless specified otherwise. if options.DestinationCtx == nil || options.DestinationCtx.CompressionFormat == nil { @@ -605,8 +605,6 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli src: src, // diffIDsAreNeeded is computed later canModifyManifest: len(sigs) == 0 && !destIsDigestedReference, - ociDecryptConfig: options.OciDecryptConfig, - ociEncryptConfig: options.OciEncryptConfig, ociEncryptLayers: options.OciEncryptLayers, } // Ensure _this_ copy sees exactly the intended data when either processing a signed image or signing it. @@ -621,7 +619,7 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli return nil, "", "", err } - destRequiresOciEncryption := (isEncrypted(src) && ic.ociDecryptConfig != nil) || options.OciEncryptLayers != nil + destRequiresOciEncryption := (isEncrypted(src) && ic.c.ociDecryptConfig != nil) || options.OciEncryptLayers != nil // We compute preferredManifestMIMEType only to show it in error messages. // Without having to add this context in an error message, we would be happy enough to know only that no conversion is needed. @@ -633,7 +631,7 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli // If src.UpdatedImageNeedsLayerDiffIDs(ic.manifestUpdates) will be true, it needs to be true by the time we get here. ic.diffIDsAreNeeded = src.UpdatedImageNeedsLayerDiffIDs(*ic.manifestUpdates) // If encrypted and decryption keys provided, we should try to decrypt - ic.diffIDsAreNeeded = ic.diffIDsAreNeeded || (isEncrypted(src) && ic.ociDecryptConfig != nil) || ic.ociEncryptConfig != nil + ic.diffIDsAreNeeded = ic.diffIDsAreNeeded || (isEncrypted(src) && ic.c.ociDecryptConfig != nil) || ic.c.ociEncryptConfig != nil if err := ic.copyLayers(ctx); err != nil { return nil, "", "", err @@ -1048,7 +1046,7 @@ type diffIDResult struct { func (ic *imageCopier) copyLayer(ctx context.Context, srcInfo types.BlobInfo, toEncrypt bool, pool *mpb.Progress) (types.BlobInfo, digest.Digest, error) { cachedDiffID := ic.c.blobInfoCache.UncompressedDigest(srcInfo.Digest) // May be "" // Diffs are needed if we are encrypting an image or trying to decrypt an image - diffIDIsNeeded := ic.diffIDsAreNeeded && cachedDiffID == "" || toEncrypt || (isOciEncrypted(srcInfo.MediaType) && ic.ociDecryptConfig != nil) + diffIDIsNeeded := ic.diffIDsAreNeeded && cachedDiffID == "" || toEncrypt || (isOciEncrypted(srcInfo.MediaType) && ic.c.ociDecryptConfig != nil) // If we already have the blob, and we don't need to compute the diffID, then we don't need to read it from the source. if !diffIDIsNeeded { @@ -1136,8 +1134,6 @@ func (ic *imageCopier) copyLayerFromStream(ctx context.Context, srcStream io.Rea return pipeWriter } } - ic.c.ociDecryptConfig = ic.ociDecryptConfig - ic.c.ociEncryptConfig = ic.ociEncryptConfig blobInfo, err := ic.c.copyBlobFromStream(ctx, srcStream, srcInfo, getDiffIDRecorder, ic.canModifyManifest, false, toEncrypt, bar) // Sets err to nil on success return blobInfo, diffIDChan, err diff --git a/vendor/github.com/containers/image/v5/directory/directory_dest.go b/vendor/github.com/containers/image/v5/directory/directory_dest.go index d70b6c07f..2b81c8360 100644 --- a/vendor/github.com/containers/image/v5/directory/directory_dest.go +++ b/vendor/github.com/containers/image/v5/directory/directory_dest.go @@ -194,7 +194,7 @@ func (d *dirImageDestination) PutBlob(ctx context.Context, stream io.Reader, inp // (e.g. if the blob is a filesystem layer, this signifies that the changes it describes need to be applied again when composing a filesystem tree). // info.Digest must not be empty. // If canSubstitute, TryReusingBlob can use an equivalent equivalent of the desired blob; in that case the returned info may not match the input. -// If the blob has been succesfully reused, returns (true, info, nil); info must contain at least a digest and size. +// If the blob has been successfully reused, returns (true, info, nil); info must contain at least a digest and size. // If the transport can not reuse the requested blob, TryReusingBlob returns (false, {}, nil); it returns a non-nil error only on an unexpected failure. // May use and/or update cache. func (d *dirImageDestination) TryReusingBlob(ctx context.Context, info types.BlobInfo, cache types.BlobInfoCache, canSubstitute bool) (bool, types.BlobInfo, error) { diff --git a/vendor/github.com/containers/image/v5/docker/archive/transport.go b/vendor/github.com/containers/image/v5/docker/archive/transport.go index ff9e27482..9a48cb46c 100644 --- a/vendor/github.com/containers/image/v5/docker/archive/transport.go +++ b/vendor/github.com/containers/image/v5/docker/archive/transport.go @@ -50,7 +50,7 @@ type archiveReference struct { // Must not be set if ref is set. sourceIndex int // If not nil, must have been created from path (but archiveReader.path may point at a temporary - // file, not necesarily path precisely). + // file, not necessarily path precisely). archiveReader *tarfile.Reader // If not nil, must have been created for path archiveWriter *tarfile.Writer diff --git a/vendor/github.com/containers/image/v5/docker/docker_client.go b/vendor/github.com/containers/image/v5/docker/docker_client.go index e4308def1..797be45a2 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_client.go +++ b/vendor/github.com/containers/image/v5/docker/docker_client.go @@ -209,6 +209,7 @@ func dockerCertDir(sys *types.SystemContext, hostPort string) (string, error) { // newDockerClientFromRef returns a new dockerClient instance for refHostname (a host a specified in the Docker image reference, not canonicalized to dockerRegistry) // “write” specifies whether the client will be used for "write" access (in particular passed to lookaside.go:toplevelFromSection) +// signatureBase is always set in the return value func newDockerClientFromRef(sys *types.SystemContext, ref dockerReference, write bool, actions string) (*dockerClient, error) { registry := reference.Domain(ref.ref) auth, err := config.GetCredentials(sys, registry) @@ -216,7 +217,7 @@ func newDockerClientFromRef(sys *types.SystemContext, ref dockerReference, write return nil, errors.Wrapf(err, "error getting username and password") } - sigBase, err := configuredSignatureStorageBase(sys, ref, write) + sigBase, err := SignatureStorageBaseURL(sys, ref, write) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/image/v5/docker/docker_image_dest.go b/vendor/github.com/containers/image/v5/docker/docker_image_dest.go index 576dec495..ac63ac121 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_image_dest.go +++ b/vendor/github.com/containers/image/v5/docker/docker_image_dest.go @@ -78,12 +78,12 @@ func (d *dockerImageDestination) SupportsSignatures(ctx context.Context) error { return err } switch { - case d.c.signatureBase != nil: - return nil case d.c.supportsSignatures: return nil + case d.c.signatureBase != nil: + return nil default: - return errors.Errorf("X-Registry-Supports-Signatures extension not supported, and lookaside is not configured") + return errors.Errorf("Internal error: X-Registry-Supports-Signatures extension not supported, and lookaside should not be empty configuration") } } @@ -284,7 +284,7 @@ func (d *dockerImageDestination) mountBlob(ctx context.Context, srcRepo referenc // (e.g. if the blob is a filesystem layer, this signifies that the changes it describes need to be applied again when composing a filesystem tree). // info.Digest must not be empty. // If canSubstitute, TryReusingBlob can use an equivalent equivalent of the desired blob; in that case the returned info may not match the input. -// If the blob has been succesfully reused, returns (true, info, nil); info must contain at least a digest and size. +// If the blob has been successfully reused, returns (true, info, nil); info must contain at least a digest and size. // If the transport can not reuse the requested blob, TryReusingBlob returns (false, {}, nil); it returns a non-nil error only on an unexpected failure. // May use and/or update cache. func (d *dockerImageDestination) TryReusingBlob(ctx context.Context, info types.BlobInfo, cache types.BlobInfoCache, canSubstitute bool) (bool, types.BlobInfo, error) { @@ -335,7 +335,7 @@ func (d *dockerImageDestination) TryReusingBlob(ctx context.Context, info types. // On success we avoid the actual costly upload; so, in a sense, the success case is "free", but failures are always costly. // Even worse, docker/distribution does not actually reasonably implement canceling uploads // (it would require a "delete" action in the token, and Quay does not give that to anyone, so we can't ask); - // so, be a nice client and don't create unnecesary upload sessions on the server. + // so, be a nice client and don't create unnecessary upload sessions on the server. exists, size, err := d.blobExists(ctx, candidateRepo, candidate.Digest, extraScope) if err != nil { logrus.Debugf("... Failed: %v", err) @@ -479,12 +479,12 @@ func (d *dockerImageDestination) PutSignatures(ctx context.Context, signatures [ return err } switch { - case d.c.signatureBase != nil: - return d.putSignaturesToLookaside(signatures, *instanceDigest) case d.c.supportsSignatures: return d.putSignaturesToAPIExtension(ctx, signatures, *instanceDigest) + case d.c.signatureBase != nil: + return d.putSignaturesToLookaside(signatures, *instanceDigest) default: - return errors.Errorf("X-Registry-Supports-Signatures extension not supported, and lookaside is not configured") + return errors.Errorf("Internal error: X-Registry-Supports-Signatures extension not supported, and lookaside should not be empty configuration") } } @@ -502,9 +502,6 @@ func (d *dockerImageDestination) putSignaturesToLookaside(signatures [][]byte, m // NOTE: Keep this in sync with docs/signature-protocols.md! for i, signature := range signatures { url := signatureStorageURL(d.c.signatureBase, manifestDigest, i) - if url == nil { - return errors.Errorf("Internal error: signatureStorageURL with non-nil base returned nil") - } err := d.putOneSignature(url, signature) if err != nil { return err @@ -517,9 +514,6 @@ func (d *dockerImageDestination) putSignaturesToLookaside(signatures [][]byte, m // is sufficient. for i := len(signatures); ; i++ { url := signatureStorageURL(d.c.signatureBase, manifestDigest, i) - if url == nil { - return errors.Errorf("Internal error: signatureStorageURL with non-nil base returned nil") - } missing, err := d.c.deleteOneSignature(url) if err != nil { return err diff --git a/vendor/github.com/containers/image/v5/docker/docker_image_src.go b/vendor/github.com/containers/image/v5/docker/docker_image_src.go index 4d2a9ed6c..70ca7661e 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_image_src.go +++ b/vendor/github.com/containers/image/v5/docker/docker_image_src.go @@ -53,7 +53,7 @@ func newImageSource(ctx context.Context, sys *types.SystemContext, ref dockerRef // contain the image, it will be used for all future pull actions. Always try the // non-mirror original location last; this both transparently handles the case // of no mirrors configured, and ensures we return the error encountered when - // acessing the upstream location if all endpoints fail. + // accessing the upstream location if all endpoints fail. pullSources, err := registry.PullSourcesFromReference(ref.ref) if err != nil { return nil, err @@ -238,6 +238,9 @@ func (s *dockerImageSource) getExternalBlob(ctx context.Context, urls []string) return nil, 0, errors.New("internal error: getExternalBlob called with no URLs") } for _, url := range urls { + // NOTE: we must not authenticate on additional URLs as those + // can be abused to leak credentials or tokens. Please + // refer to CVE-2020-15157 for more information. resp, err = s.c.makeRequestToResolvedURL(ctx, "GET", url, nil, nil, -1, noAuth, nil) if err == nil { if resp.StatusCode != http.StatusOK { @@ -297,12 +300,12 @@ func (s *dockerImageSource) GetSignatures(ctx context.Context, instanceDigest *d return nil, err } switch { - case s.c.signatureBase != nil: - return s.getSignaturesFromLookaside(ctx, instanceDigest) case s.c.supportsSignatures: return s.getSignaturesFromAPIExtension(ctx, instanceDigest) + case s.c.signatureBase != nil: + return s.getSignaturesFromLookaside(ctx, instanceDigest) default: - return [][]byte{}, nil + return nil, errors.Errorf("Internal error: X-Registry-Supports-Signatures extension not supported, and lookaside should not be empty configuration") } } @@ -336,9 +339,6 @@ func (s *dockerImageSource) getSignaturesFromLookaside(ctx context.Context, inst signatures := [][]byte{} for i := 0; ; i++ { url := signatureStorageURL(s.c.signatureBase, manifestDigest, i) - if url == nil { - return nil, errors.Errorf("Internal error: signatureStorageURL with non-nil base returned nil") - } signature, missing, err := s.getOneSignature(ctx, url) if err != nil { return nil, err @@ -474,24 +474,19 @@ func deleteImage(ctx context.Context, sys *types.SystemContext, ref dockerRefere return errors.Errorf("Failed to delete %v: %s (%v)", deletePath, string(body), delete.Status) } - if c.signatureBase != nil { - manifestDigest, err := manifest.Digest(manifestBody) + manifestDigest, err := manifest.Digest(manifestBody) + if err != nil { + return err + } + + for i := 0; ; i++ { + url := signatureStorageURL(c.signatureBase, manifestDigest, i) + missing, err := c.deleteOneSignature(url) if err != nil { return err } - - for i := 0; ; i++ { - url := signatureStorageURL(c.signatureBase, manifestDigest, i) - if url == nil { - return errors.Errorf("Internal error: signatureStorageURL with non-nil base returned nil") - } - missing, err := c.deleteOneSignature(url) - if err != nil { - return err - } - if missing { - break - } + if missing { + break } } diff --git a/vendor/github.com/containers/image/v5/docker/internal/tarfile/dest.go b/vendor/github.com/containers/image/v5/docker/internal/tarfile/dest.go index 8c38094cf..41d2c5e81 100644 --- a/vendor/github.com/containers/image/v5/docker/internal/tarfile/dest.go +++ b/vendor/github.com/containers/image/v5/docker/internal/tarfile/dest.go @@ -94,7 +94,7 @@ func (d *Destination) HasThreadSafePutBlob() bool { // If stream.Read() at any time, ESPECIALLY at end of input, returns an error, PutBlob MUST 1) fail, and 2) delete any data stored so far. func (d *Destination) PutBlob(ctx context.Context, stream io.Reader, inputInfo types.BlobInfo, cache types.BlobInfoCache, isConfig bool) (types.BlobInfo, error) { // Ouch, we need to stream the blob into a temporary file just to determine the size. - // When the layer is decompressed, we also have to generate the digest on uncompressed datas. + // When the layer is decompressed, we also have to generate the digest on uncompressed data. if inputInfo.Size == -1 || inputInfo.Digest.String() == "" { logrus.Debugf("docker tarfile: input with unknown size, streaming to disk first ...") streamCopy, err := ioutil.TempFile(tmpdir.TemporaryDirectoryForBigFiles(d.sysCtx), "docker-tarfile-blob") @@ -159,7 +159,7 @@ func (d *Destination) PutBlob(ctx context.Context, stream io.Reader, inputInfo t // (e.g. if the blob is a filesystem layer, this signifies that the changes it describes need to be applied again when composing a filesystem tree). // info.Digest must not be empty. // If canSubstitute, TryReusingBlob can use an equivalent equivalent of the desired blob; in that case the returned info may not match the input. -// If the blob has been succesfully reused, returns (true, info, nil); info must contain at least a digest and size. +// If the blob has been successfully reused, returns (true, info, nil); info must contain at least a digest and size. // If the transport can not reuse the requested blob, TryReusingBlob returns (false, {}, nil); it returns a non-nil error only on an unexpected failure. // May use and/or update cache. func (d *Destination) TryReusingBlob(ctx context.Context, info types.BlobInfo, cache types.BlobInfoCache, canSubstitute bool) (bool, types.BlobInfo, error) { diff --git a/vendor/github.com/containers/image/v5/docker/internal/tarfile/src.go b/vendor/github.com/containers/image/v5/docker/internal/tarfile/src.go index 0db9a72b5..bd65ef844 100644 --- a/vendor/github.com/containers/image/v5/docker/internal/tarfile/src.go +++ b/vendor/github.com/containers/image/v5/docker/internal/tarfile/src.go @@ -287,7 +287,7 @@ func (s *Source) GetBlob(ctx context.Context, info types.BlobInfo, cache types.B // In particular, because the v2s2 manifest being generated uses // DiffIDs, any caller of GetBlob is going to be asking for DiffIDs of // layers not their _actual_ digest. The result is that copy/... will - // be verifing a "digest" which is not the actual layer's digest (but + // be verifying a "digest" which is not the actual layer's digest (but // is instead the DiffID). uncompressedStream, _, err := compression.AutoDecompress(underlyingStream) diff --git a/vendor/github.com/containers/image/v5/docker/internal/tarfile/writer.go b/vendor/github.com/containers/image/v5/docker/internal/tarfile/writer.go index fd2c461d0..e0683b3cd 100644 --- a/vendor/github.com/containers/image/v5/docker/internal/tarfile/writer.go +++ b/vendor/github.com/containers/image/v5/docker/internal/tarfile/writer.go @@ -48,7 +48,7 @@ func NewWriter(dest io.Writer) *Writer { } // lock does some sanity checks and locks the Writer. -// If this function suceeds, the caller must call w.unlock. +// If this function succeeds, the caller must call w.unlock. // Do not use Writer.mutex directly. func (w *Writer) lock() error { w.mutex.Lock() @@ -67,7 +67,7 @@ func (w *Writer) unlock() { // tryReusingBlobLocked checks whether the transport already contains, a blob, and if so, returns its metadata. // info.Digest must not be empty. -// If the blob has been succesfully reused, returns (true, info, nil); info must contain at least a digest and size. +// If the blob has been successfully reused, returns (true, info, nil); info must contain at least a digest and size. // If the transport can not reuse the requested blob, tryReusingBlob returns (false, {}, nil); it returns a non-nil error only on an unexpected failure. // The caller must have locked the Writer. func (w *Writer) tryReusingBlobLocked(info types.BlobInfo) (bool, types.BlobInfo, error) { diff --git a/vendor/github.com/containers/image/v5/docker/lookaside.go b/vendor/github.com/containers/image/v5/docker/lookaside.go index 6931fd07b..06d616d01 100644 --- a/vendor/github.com/containers/image/v5/docker/lookaside.go +++ b/vendor/github.com/containers/image/v5/docker/lookaside.go @@ -10,6 +10,7 @@ import ( "strings" "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/internal/rootless" "github.com/containers/image/v5/types" "github.com/containers/storage/pkg/homedir" "github.com/ghodss/yaml" @@ -30,6 +31,12 @@ const builtinRegistriesDirPath = "/etc/containers/registries.d" // userRegistriesDirPath is the path to the per user registries.d. var userRegistriesDir = filepath.FromSlash(".config/containers/registries.d") +// defaultUserDockerDir is the default sigstore directory for unprivileged user +var defaultUserDockerDir = filepath.FromSlash(".local/share/containers/sigstore") + +// defaultDockerDir is the default sigstore directory for root +var defaultDockerDir = "/var/lib/containers/sigstore" + // registryConfiguration is one of the files in registriesDirPath configuring lookaside locations, or the result of merging them all. // NOTE: Keep this in sync with docs/registries.d.md! type registryConfiguration struct { @@ -45,11 +52,18 @@ type registryNamespace struct { } // signatureStorageBase is an "opaque" type representing a lookaside Docker signature storage. -// Users outside of this file should use configuredSignatureStorageBase and signatureStorageURL below. -type signatureStorageBase *url.URL // The only documented value is nil, meaning storage is not supported. - -// configuredSignatureStorageBase reads configuration to find an appropriate signature storage URL for ref, for write access if “write”. -func configuredSignatureStorageBase(sys *types.SystemContext, ref dockerReference, write bool) (signatureStorageBase, error) { +// Users outside of this file should use SignatureStorageBaseURL and signatureStorageURL below. +type signatureStorageBase *url.URL + +// SignatureStorageBaseURL reads configuration to find an appropriate signature storage URL for ref, for write access if “write”. +// the usage of the BaseURL is defined under docker/distribution registries—separate storage of docs/signature-protocols.md +// Warning: This function only exposes configuration in registries.d; +// just because this function returns an URL does not mean that the URL will be used by c/image/docker (e.g. if the registry natively supports X-R-S-S). +func SignatureStorageBaseURL(sys *types.SystemContext, ref types.ImageReference, write bool) (*url.URL, error) { + dr, ok := ref.(dockerReference) + if !ok { + return nil, errors.Errorf("ref must be a dockerReference") + } // FIXME? Loading and parsing the config could be cached across calls. dirPath := registriesDirPath(sys) logrus.Debugf(`Using registries.d directory %s for sigstore configuration`, dirPath) @@ -58,20 +72,23 @@ func configuredSignatureStorageBase(sys *types.SystemContext, ref dockerReferenc return nil, err } - topLevel := config.signatureTopLevel(ref, write) - if topLevel == "" { - return nil, nil - } - - url, err := url.Parse(topLevel) - if err != nil { - return nil, errors.Wrapf(err, "Invalid signature storage URL %s", topLevel) + topLevel := config.signatureTopLevel(dr, write) + var url *url.URL + if topLevel != "" { + url, err = url.Parse(topLevel) + if err != nil { + return nil, errors.Wrapf(err, "Invalid signature storage URL %s", topLevel) + } + } else { + // returns default directory if no sigstore specified in configuration file + url = builtinDefaultSignatureStorageDir(rootless.GetRootlessEUID()) + logrus.Debugf(" No signature storage configuration found for %s, using built-in default %s", dr.PolicyConfigurationIdentity(), url.String()) } // NOTE: Keep this in sync with docs/signature-protocols.md! // FIXME? Restrict to explicitly supported schemes? - repo := reference.Path(ref.ref) // Note that this is without a tag or digest. - if path.Clean(repo) != repo { // Coverage: This should not be reachable because /./ and /../ components are not valid in docker references - return nil, errors.Errorf("Unexpected path elements in Docker reference %s for signature storage", ref.ref.String()) + repo := reference.Path(dr.ref) // Note that this is without a tag or digest. + if path.Clean(repo) != repo { // Coverage: This should not be reachable because /./ and /../ components are not valid in docker references + return nil, errors.Errorf("Unexpected path elements in Docker reference %s for signature storage", dr.ref.String()) } url.Path = url.Path + "/" + repo return url, nil @@ -93,6 +110,14 @@ func registriesDirPath(sys *types.SystemContext) string { return systemRegistriesDirPath } +// builtinDefaultSignatureStorageDir returns default signature storage URL as per euid +func builtinDefaultSignatureStorageDir(euid int) *url.URL { + if euid != 0 { + return &url.URL{Scheme: "file", Path: filepath.Join(homedir.Get(), defaultUserDockerDir)} + } + return &url.URL{Scheme: "file", Path: defaultDockerDir} +} + // loadAndMergeConfig loads configuration files in dirPath func loadAndMergeConfig(dirPath string) (*registryConfiguration, error) { mergedConfig := registryConfiguration{Docker: map[string]registryNamespace{}} @@ -149,7 +174,7 @@ func loadAndMergeConfig(dirPath string) (*registryConfiguration, error) { } // config.signatureTopLevel returns an URL string configured in config for ref, for write access if “write”. -// (the top level of the storage, namespaced by repo.FullName etc.), or "" if no signature storage should be used. +// (the top level of the storage, namespaced by repo.FullName etc.), or "" if nothing has been configured. func (config *registryConfiguration) signatureTopLevel(ref dockerReference, write bool) string { if config.Docker != nil { // Look for a full match. @@ -178,7 +203,6 @@ func (config *registryConfiguration) signatureTopLevel(ref dockerReference, writ return url } } - logrus.Debugf(" No signature storage configuration found for %s", ref.PolicyConfigurationIdentity()) return "" } @@ -196,13 +220,10 @@ func (ns registryNamespace) signatureTopLevel(write bool) string { return "" } -// signatureStorageURL returns an URL usable for acessing signature index in base with known manifestDigest, or nil if not applicable. -// Returns nil iff base == nil. +// signatureStorageURL returns an URL usable for accessing signature index in base with known manifestDigest. +// base is not nil from the caller // NOTE: Keep this in sync with docs/signature-protocols.md! func signatureStorageURL(base signatureStorageBase, manifestDigest digest.Digest, index int) *url.URL { - if base == nil { - return nil - } url := *base url.Path = fmt.Sprintf("%s@%s=%s/signature-%d", url.Path, manifestDigest.Algorithm(), manifestDigest.Hex(), index+1) return &url diff --git a/vendor/github.com/containers/image/v5/docker/tarfile/dest.go b/vendor/github.com/containers/image/v5/docker/tarfile/dest.go index af1690683..e16829d96 100644 --- a/vendor/github.com/containers/image/v5/docker/tarfile/dest.go +++ b/vendor/github.com/containers/image/v5/docker/tarfile/dest.go @@ -86,7 +86,7 @@ func (d *Destination) PutBlob(ctx context.Context, stream io.Reader, inputInfo t // (e.g. if the blob is a filesystem layer, this signifies that the changes it describes need to be applied again when composing a filesystem tree). // info.Digest must not be empty. // If canSubstitute, TryReusingBlob can use an equivalent equivalent of the desired blob; in that case the returned info may not match the input. -// If the blob has been succesfully reused, returns (true, info, nil); info must contain at least a digest and size. +// If the blob has been successfully reused, returns (true, info, nil); info must contain at least a digest and size. // If the transport can not reuse the requested blob, TryReusingBlob returns (false, {}, nil); it returns a non-nil error only on an unexpected failure. // May use and/or update cache. func (d *Destination) TryReusingBlob(ctx context.Context, info types.BlobInfo, cache types.BlobInfoCache, canSubstitute bool) (bool, types.BlobInfo, error) { diff --git a/vendor/github.com/containers/image/v5/internal/pkg/platform/platform_matcher.go b/vendor/github.com/containers/image/v5/internal/pkg/platform/platform_matcher.go index c4d42f3eb..5ea542bcf 100644 --- a/vendor/github.com/containers/image/v5/internal/pkg/platform/platform_matcher.go +++ b/vendor/github.com/containers/image/v5/internal/pkg/platform/platform_matcher.go @@ -131,7 +131,7 @@ var baseVariants = map[string]string{ "arm64": "v8", } -// WantedPlatforms returns all compatible platforms with the platform specifics possibly overriden by user, +// WantedPlatforms returns all compatible platforms with the platform specifics possibly overridden by user, // the most compatible platform is first. // If some option (arch, os, variant) is not present, a value from current platform is detected. func WantedPlatforms(ctx *types.SystemContext) ([]imgspecv1.Platform, error) { diff --git a/vendor/github.com/containers/image/v5/internal/rootless/rootless.go b/vendor/github.com/containers/image/v5/internal/rootless/rootless.go new file mode 100644 index 000000000..80623bfbc --- /dev/null +++ b/vendor/github.com/containers/image/v5/internal/rootless/rootless.go @@ -0,0 +1,25 @@ +package rootless + +import ( + "os" + "strconv" +) + +// GetRootlessEUID returns the UID of the current user (in the parent userNS, if any) +// +// Podman and similar software, in “rootless” configuration, when run as a non-root +// user, very early switches to a user namespace, where Geteuid() == 0 (but does not +// switch to a limited mount namespace); so, code relying on Geteuid() would use +// system-wide paths in e.g. /var, when the user is actually not privileged to write to +// them, and expects state to be stored in the home directory. +// +// If Podman is setting up such a user namespace, it records the original UID in an +// environment variable, allowing us to make choices based on the actual user’s identity. +func GetRootlessEUID() int { + euidEnv := os.Getenv("_CONTAINERS_ROOTLESS_UID") + if euidEnv != "" { + euid, _ := strconv.Atoi(euidEnv) + return euid + } + return os.Geteuid() +} diff --git a/vendor/github.com/containers/image/v5/oci/archive/oci_dest.go b/vendor/github.com/containers/image/v5/oci/archive/oci_dest.go index 0509eaa83..23d471325 100644 --- a/vendor/github.com/containers/image/v5/oci/archive/oci_dest.go +++ b/vendor/github.com/containers/image/v5/oci/archive/oci_dest.go @@ -103,7 +103,7 @@ func (d *ociArchiveImageDestination) PutBlob(ctx context.Context, stream io.Read // (e.g. if the blob is a filesystem layer, this signifies that the changes it describes need to be applied again when composing a filesystem tree). // info.Digest must not be empty. // If canSubstitute, TryReusingBlob can use an equivalent equivalent of the desired blob; in that case the returned info may not match the input. -// If the blob has been succesfully reused, returns (true, info, nil); info must contain at least a digest and size. +// If the blob has been successfully reused, returns (true, info, nil); info must contain at least a digest and size. // If the transport can not reuse the requested blob, TryReusingBlob returns (false, {}, nil); it returns a non-nil error only on an unexpected failure. // May use and/or update cache. func (d *ociArchiveImageDestination) TryReusingBlob(ctx context.Context, info types.BlobInfo, cache types.BlobInfoCache, canSubstitute bool) (bool, types.BlobInfo, error) { diff --git a/vendor/github.com/containers/image/v5/oci/layout/oci_dest.go b/vendor/github.com/containers/image/v5/oci/layout/oci_dest.go index 48a32315b..0c88e1ef0 100644 --- a/vendor/github.com/containers/image/v5/oci/layout/oci_dest.go +++ b/vendor/github.com/containers/image/v5/oci/layout/oci_dest.go @@ -186,7 +186,7 @@ func (d *ociImageDestination) PutBlob(ctx context.Context, stream io.Reader, inp // (e.g. if the blob is a filesystem layer, this signifies that the changes it describes need to be applied again when composing a filesystem tree). // info.Digest must not be empty. // If canSubstitute, TryReusingBlob can use an equivalent equivalent of the desired blob; in that case the returned info may not match the input. -// If the blob has been succesfully reused, returns (true, info, nil); info must contain at least a digest and size. +// If the blob has been successfully reused, returns (true, info, nil); info must contain at least a digest and size. // If the transport can not reuse the requested blob, TryReusingBlob returns (false, {}, nil); it returns a non-nil error only on an unexpected failure. // May use and/or update cache. func (d *ociImageDestination) TryReusingBlob(ctx context.Context, info types.BlobInfo, cache types.BlobInfoCache, canSubstitute bool) (bool, types.BlobInfo, error) { diff --git a/vendor/github.com/containers/image/v5/openshift/openshift-copies.go b/vendor/github.com/containers/image/v5/openshift/openshift-copies.go index 4b081bb29..d87f748e6 100644 --- a/vendor/github.com/containers/image/v5/openshift/openshift-copies.go +++ b/vendor/github.com/containers/image/v5/openshift/openshift-copies.go @@ -251,7 +251,7 @@ func getServerIdentificationPartialConfig(configAuthInfo clientcmdAuthInfo, conf // getUserIdentificationPartialConfig is a modified copy of k8s.io/kubernetes/pkg/client/unversioned/clientcmd.getUserIdentificationPartialConfig. // clientauth.Info object contain both user identification and server identification. We want different precedence orders for // both, so we have to split the objects and merge them separately -// we want this order of precedence for user identifcation +// we want this order of precedence for user identification // 1. configAuthInfo minus auth-path (the final result of command line flags and merged .kubeconfig files) // 2. configAuthInfo.auth-path (this file can contain information that conflicts with #1, and we want #1 to win the priority) // 3. if there is not enough information to idenfity the user, load try the ~/.kubernetes_auth file diff --git a/vendor/github.com/containers/image/v5/openshift/openshift.go b/vendor/github.com/containers/image/v5/openshift/openshift.go index 28bfc456d..c4c84dd54 100644 --- a/vendor/github.com/containers/image/v5/openshift/openshift.go +++ b/vendor/github.com/containers/image/v5/openshift/openshift.go @@ -410,7 +410,7 @@ func (d *openshiftImageDestination) PutBlob(ctx context.Context, stream io.Reade // (e.g. if the blob is a filesystem layer, this signifies that the changes it describes need to be applied again when composing a filesystem tree). // info.Digest must not be empty. // If canSubstitute, TryReusingBlob can use an equivalent equivalent of the desired blob; in that case the returned info may not match the input. -// If the blob has been succesfully reused, returns (true, info, nil); info must contain at least a digest and size. +// If the blob has been successfully reused, returns (true, info, nil); info must contain at least a digest and size. // If the transport can not reuse the requested blob, TryReusingBlob returns (false, {}, nil); it returns a non-nil error only on an unexpected failure. // May use and/or update cache. func (d *openshiftImageDestination) TryReusingBlob(ctx context.Context, info types.BlobInfo, cache types.BlobInfoCache, canSubstitute bool) (bool, types.BlobInfo, error) { diff --git a/vendor/github.com/containers/image/v5/ostree/ostree_dest.go b/vendor/github.com/containers/image/v5/ostree/ostree_dest.go index 115097055..b518122e2 100644 --- a/vendor/github.com/containers/image/v5/ostree/ostree_dest.go +++ b/vendor/github.com/containers/image/v5/ostree/ostree_dest.go @@ -339,7 +339,7 @@ func (d *ostreeImageDestination) importConfig(repo *otbuiltin.Repo, blob *blobTo // (e.g. if the blob is a filesystem layer, this signifies that the changes it describes need to be applied again when composing a filesystem tree). // info.Digest must not be empty. // If canSubstitute, TryReusingBlob can use an equivalent equivalent of the desired blob; in that case the returned info may not match the input. -// If the blob has been succesfully reused, returns (true, info, nil); info must contain at least a digest and size. +// If the blob has been successfully reused, returns (true, info, nil); info must contain at least a digest and size. // If the transport can not reuse the requested blob, TryReusingBlob returns (false, {}, nil); it returns a non-nil error only on an unexpected failure. // May use and/or update cache. func (d *ostreeImageDestination) TryReusingBlob(ctx context.Context, info types.BlobInfo, cache types.BlobInfoCache, canSubstitute bool) (bool, types.BlobInfo, error) { diff --git a/vendor/github.com/containers/image/v5/pkg/blobinfocache/default.go b/vendor/github.com/containers/image/v5/pkg/blobinfocache/default.go index 952bcf5a1..01abb8d1e 100644 --- a/vendor/github.com/containers/image/v5/pkg/blobinfocache/default.go +++ b/vendor/github.com/containers/image/v5/pkg/blobinfocache/default.go @@ -4,8 +4,8 @@ import ( "fmt" "os" "path/filepath" - "strconv" + "github.com/containers/image/v5/internal/rootless" "github.com/containers/image/v5/pkg/blobinfocache/boltdb" "github.com/containers/image/v5/pkg/blobinfocache/memory" "github.com/containers/image/v5/types" @@ -48,18 +48,9 @@ func blobInfoCacheDir(sys *types.SystemContext, euid int) (string, error) { return filepath.Join(dataDir, "containers", "cache"), nil } -func getRootlessUID() int { - uidEnv := os.Getenv("_CONTAINERS_ROOTLESS_UID") - if uidEnv != "" { - u, _ := strconv.Atoi(uidEnv) - return u - } - return os.Geteuid() -} - // DefaultCache returns the default BlobInfoCache implementation appropriate for sys. func DefaultCache(sys *types.SystemContext) types.BlobInfoCache { - dir, err := blobInfoCacheDir(sys, getRootlessUID()) + dir, err := blobInfoCacheDir(sys, rootless.GetRootlessEUID()) if err != nil { logrus.Debugf("Error determining a location for %s, using a memory-only cache", blobInfoCacheFilename) return memory.New() diff --git a/vendor/github.com/containers/image/v5/pkg/docker/config/config.go b/vendor/github.com/containers/image/v5/pkg/docker/config/config.go index 5d7598648..cf82ee861 100644 --- a/vendor/github.com/containers/image/v5/pkg/docker/config/config.go +++ b/vendor/github.com/containers/image/v5/pkg/docker/config/config.go @@ -35,6 +35,7 @@ type authPath struct { var ( defaultPerUIDPathFormat = filepath.FromSlash("/run/containers/%d/auth.json") + xdgConfigHomePath = filepath.FromSlash("containers/auth.json") xdgRuntimeDirPath = filepath.FromSlash("containers/auth.json") dockerHomePath = filepath.FromSlash(".docker/config.json") dockerLegacyHomePath = ".dockercfg" @@ -117,7 +118,7 @@ func GetAllCredentials(sys *types.SystemContext) (map[string]types.DockerAuthCon } } - // TODO(keyring): if we ever reenable the keyring support, we had to + // TODO(keyring): if we ever re-enable the keyring support, we had to // query all credentials from the keyring here. return authConfigs, nil @@ -136,8 +137,21 @@ func getAuthFilePaths(sys *types.SystemContext) []authPath { // Logging the error as a warning instead and moving on to pulling the image logrus.Warnf("%v: Trying to pull image in the event that it is a public image.", err) } + xdgCfgHome := os.Getenv("XDG_CONFIG_HOME") + if xdgCfgHome == "" { + xdgCfgHome = filepath.Join(homedir.Get(), ".config") + } + paths = append(paths, authPath{path: filepath.Join(xdgCfgHome, xdgConfigHomePath), legacyFormat: false}) + if dockerConfig := os.Getenv("DOCKER_CONFIG"); dockerConfig != "" { + paths = append(paths, + authPath{path: filepath.Join(dockerConfig, "config.json"), legacyFormat: false}, + ) + } else { + paths = append(paths, + authPath{path: filepath.Join(homedir.Get(), dockerHomePath), legacyFormat: false}, + ) + } paths = append(paths, - authPath{path: filepath.Join(homedir.Get(), dockerHomePath), legacyFormat: false}, authPath{path: filepath.Join(homedir.Get(), dockerLegacyHomePath), legacyFormat: true}, ) return paths @@ -245,7 +259,7 @@ func RemoveAllAuthentication(sys *types.SystemContext) error { }) } -// getPathToAuth gets the path of the auth.json file used for reading and writting credentials +// getPathToAuth gets the path of the auth.json file used for reading and writing credentials // returns the path, and a bool specifies whether the file is in legacy format func getPathToAuth(sys *types.SystemContext) (string, bool, error) { if sys != nil { diff --git a/vendor/github.com/containers/image/v5/pkg/docker/config/config_linux.go b/vendor/github.com/containers/image/v5/pkg/docker/config/config_linux.go index 43f2d5a85..1531d6943 100644 --- a/vendor/github.com/containers/image/v5/pkg/docker/config/config_linux.go +++ b/vendor/github.com/containers/image/v5/pkg/docker/config/config_linux.go @@ -63,7 +63,7 @@ func removeAllAuthFromKernelKeyring() error { // split string "type;uid;gid;perm;description" keyAttrs := strings.SplitN(keyAttr, ";", 5) if len(keyAttrs) < 5 { - return errors.Errorf("Key attributes of %d are not avaliable", k.ID()) + return errors.Errorf("Key attributes of %d are not available", k.ID()) } keyDescribe := keyAttrs[4] if strings.HasPrefix(keyDescribe, keyDescribePrefix) { diff --git a/vendor/github.com/containers/image/v5/pkg/sysregistriesv2/system_registries_v2.go b/vendor/github.com/containers/image/v5/pkg/sysregistriesv2/system_registries_v2.go index 9a5712654..ea2b21575 100644 --- a/vendor/github.com/containers/image/v5/pkg/sysregistriesv2/system_registries_v2.go +++ b/vendor/github.com/containers/image/v5/pkg/sysregistriesv2/system_registries_v2.go @@ -363,7 +363,7 @@ type configWrapper struct { configPath string // path to system-wide registries.conf.d directory, or "" if not used configDirPath string - // path to user specificed registries.conf.d directory, or "" if not used + // path to user specified registries.conf.d directory, or "" if not used userConfigDirPath string } diff --git a/vendor/github.com/containers/image/v5/signature/mechanism.go b/vendor/github.com/containers/image/v5/signature/mechanism.go index bdf26c531..2c08c231e 100644 --- a/vendor/github.com/containers/image/v5/signature/mechanism.go +++ b/vendor/github.com/containers/image/v5/signature/mechanism.go @@ -28,8 +28,8 @@ type SigningMechanism interface { Verify(unverifiedSignature []byte) (contents []byte, keyIdentity string, err error) // UntrustedSignatureContents returns UNTRUSTED contents of the signature WITHOUT ANY VERIFICATION, // along with a short identifier of the key used for signing. - // WARNING: The short key identifier (which correponds to "Key ID" for OpenPGP keys) - // is NOT the same as a "key identity" used in other calls ot this interface, and + // WARNING: The short key identifier (which corresponds to "Key ID" for OpenPGP keys) + // is NOT the same as a "key identity" used in other calls to this interface, and // the values may have no recognizable relationship if the public key is not available. UntrustedSignatureContents(untrustedSignature []byte) (untrustedContents []byte, shortKeyIdentifier string, err error) } @@ -58,8 +58,8 @@ func NewEphemeralGPGSigningMechanism(blob []byte) (SigningMechanism, []string, e // gpgUntrustedSignatureContents returns UNTRUSTED contents of the signature WITHOUT ANY VERIFICATION, // along with a short identifier of the key used for signing. -// WARNING: The short key identifier (which correponds to "Key ID" for OpenPGP keys) -// is NOT the same as a "key identity" used in other calls ot this interface, and +// WARNING: The short key identifier (which corresponds to "Key ID" for OpenPGP keys) +// is NOT the same as a "key identity" used in other calls to this interface, and // the values may have no recognizable relationship if the public key is not available. func gpgUntrustedSignatureContents(untrustedSignature []byte) (untrustedContents []byte, shortKeyIdentifier string, err error) { // This uses the Golang-native OpenPGP implementation instead of gpgme because we are not doing any cryptography. @@ -75,7 +75,7 @@ func gpgUntrustedSignatureContents(untrustedSignature []byte) (untrustedContents // Coverage: An error during reading the body can happen only if // 1) the message is encrypted, which is not our case (and we don’t give ReadMessage the key // to decrypt the contents anyway), or - // 2) the message is signed AND we give ReadMessage a correspnding public key, which we don’t. + // 2) the message is signed AND we give ReadMessage a corresponding public key, which we don’t. return nil, "", err } diff --git a/vendor/github.com/containers/image/v5/signature/mechanism_gpgme.go b/vendor/github.com/containers/image/v5/signature/mechanism_gpgme.go index 277fba16b..a0afc34b4 100644 --- a/vendor/github.com/containers/image/v5/signature/mechanism_gpgme.go +++ b/vendor/github.com/containers/image/v5/signature/mechanism_gpgme.go @@ -167,8 +167,8 @@ func (m *gpgmeSigningMechanism) Verify(unverifiedSignature []byte) (contents []b // UntrustedSignatureContents returns UNTRUSTED contents of the signature WITHOUT ANY VERIFICATION, // along with a short identifier of the key used for signing. -// WARNING: The short key identifier (which correponds to "Key ID" for OpenPGP keys) -// is NOT the same as a "key identity" used in other calls ot this interface, and +// WARNING: The short key identifier (which corresponds to "Key ID" for OpenPGP keys) +// is NOT the same as a "key identity" used in other calls to this interface, and // the values may have no recognizable relationship if the public key is not available. func (m *gpgmeSigningMechanism) UntrustedSignatureContents(untrustedSignature []byte) (untrustedContents []byte, shortKeyIdentifier string, err error) { return gpgUntrustedSignatureContents(untrustedSignature) diff --git a/vendor/github.com/containers/image/v5/signature/mechanism_openpgp.go b/vendor/github.com/containers/image/v5/signature/mechanism_openpgp.go index 51f20f310..a05760284 100644 --- a/vendor/github.com/containers/image/v5/signature/mechanism_openpgp.go +++ b/vendor/github.com/containers/image/v5/signature/mechanism_openpgp.go @@ -151,8 +151,8 @@ func (m *openpgpSigningMechanism) Verify(unverifiedSignature []byte) (contents [ // UntrustedSignatureContents returns UNTRUSTED contents of the signature WITHOUT ANY VERIFICATION, // along with a short identifier of the key used for signing. -// WARNING: The short key identifier (which correponds to "Key ID" for OpenPGP keys) -// is NOT the same as a "key identity" used in other calls ot this interface, and +// WARNING: The short key identifier (which corresponds to "Key ID" for OpenPGP keys) +// is NOT the same as a "key identity" used in other calls to this interface, and // the values may have no recognizable relationship if the public key is not available. func (m *openpgpSigningMechanism) UntrustedSignatureContents(untrustedSignature []byte) (untrustedContents []byte, shortKeyIdentifier string, err error) { return gpgUntrustedSignatureContents(untrustedSignature) diff --git a/vendor/github.com/containers/image/v5/signature/policy_config.go b/vendor/github.com/containers/image/v5/signature/policy_config.go index 72fcf618a..a4873e9fa 100644 --- a/vendor/github.com/containers/image/v5/signature/policy_config.go +++ b/vendor/github.com/containers/image/v5/signature/policy_config.go @@ -1,4 +1,4 @@ -// policy_config.go hanles creation of policy objects, either by parsing JSON +// policy_config.go handles creation of policy objects, either by parsing JSON // or by programs building them programmatically. // The New* constructors are intended to be a stable API. FIXME: after an independent review. @@ -516,7 +516,7 @@ func newPolicyReferenceMatchFromJSON(data []byte) (PolicyReferenceMatch, error) return res, nil } -// newPRMMatchExact is NewPRMMatchExact, except it resturns the private type. +// newPRMMatchExact is NewPRMMatchExact, except it returns the private type. func newPRMMatchExact() *prmMatchExact { return &prmMatchExact{prmCommon{Type: prmTypeMatchExact}} } @@ -546,7 +546,7 @@ func (prm *prmMatchExact) UnmarshalJSON(data []byte) error { return nil } -// newPRMMatchRepoDigestOrExact is NewPRMMatchRepoDigestOrExact, except it resturns the private type. +// newPRMMatchRepoDigestOrExact is NewPRMMatchRepoDigestOrExact, except it returns the private type. func newPRMMatchRepoDigestOrExact() *prmMatchRepoDigestOrExact { return &prmMatchRepoDigestOrExact{prmCommon{Type: prmTypeMatchRepoDigestOrExact}} } @@ -576,7 +576,7 @@ func (prm *prmMatchRepoDigestOrExact) UnmarshalJSON(data []byte) error { return nil } -// newPRMMatchRepository is NewPRMMatchRepository, except it resturns the private type. +// newPRMMatchRepository is NewPRMMatchRepository, except it returns the private type. func newPRMMatchRepository() *prmMatchRepository { return &prmMatchRepository{prmCommon{Type: prmTypeMatchRepository}} } @@ -606,7 +606,7 @@ func (prm *prmMatchRepository) UnmarshalJSON(data []byte) error { return nil } -// newPRMExactReference is NewPRMExactReference, except it resturns the private type. +// newPRMExactReference is NewPRMExactReference, except it returns the private type. func newPRMExactReference(dockerReference string) (*prmExactReference, error) { ref, err := reference.ParseNormalizedNamed(dockerReference) if err != nil { @@ -652,7 +652,7 @@ func (prm *prmExactReference) UnmarshalJSON(data []byte) error { return nil } -// newPRMExactRepository is NewPRMExactRepository, except it resturns the private type. +// newPRMExactRepository is NewPRMExactRepository, except it returns the private type. func newPRMExactRepository(dockerRepository string) (*prmExactRepository, error) { if _, err := reference.ParseNormalizedNamed(dockerRepository); err != nil { return nil, InvalidPolicyFormatError(fmt.Sprintf("Invalid format of dockerRepository %s: %s", dockerRepository, err.Error())) diff --git a/vendor/github.com/containers/image/v5/signature/policy_eval.go b/vendor/github.com/containers/image/v5/signature/policy_eval.go index a1fb1eebb..edcbf52f4 100644 --- a/vendor/github.com/containers/image/v5/signature/policy_eval.go +++ b/vendor/github.com/containers/image/v5/signature/policy_eval.go @@ -1,5 +1,5 @@ // This defines the top-level policy evaluation API. -// To the extent possible, the interface of the fuctions provided +// To the extent possible, the interface of the functions provided // here is intended to be completely unambiguous, and stable for users // to rely on. @@ -47,7 +47,7 @@ type PolicyRequirement interface { // - sarUnknown if if this PolicyRequirement does not deal with signatures. // NOTE: sarUnknown should not be returned if this PolicyRequirement should make a decision but something failed. // Returning sarUnknown and a non-nil error value is invalid. - // WARNING: This makes the signature contents acceptable for futher processing, + // WARNING: This makes the signature contents acceptable for further processing, // but it does not necessarily mean that the contents of the signature are // consistent with local policy. // For example: @@ -166,7 +166,7 @@ func (pc *PolicyContext) requirementsForImageRef(ref types.ImageReference) Polic // verified). // NOTE: This may legitimately return an empty list and no error, if the image // has no signatures or only invalid signatures. -// WARNING: This makes the signature contents acceptable for futher processing, +// WARNING: This makes the signature contents acceptable for further processing, // but it does not necessarily mean that the contents of the signature are // consistent with local policy. // For example: diff --git a/vendor/github.com/containers/image/v5/signature/policy_reference_match.go b/vendor/github.com/containers/image/v5/signature/policy_reference_match.go index a148ede52..e2a21f01d 100644 --- a/vendor/github.com/containers/image/v5/signature/policy_reference_match.go +++ b/vendor/github.com/containers/image/v5/signature/policy_reference_match.go @@ -51,7 +51,7 @@ func (prm *prmMatchRepoDigestOrExact) matchesDockerReference(image types.Unparse return signature.String() == intended.String() case reference.Canonical: // We don’t actually compare the manifest digest against the signature here; that happens prSignedBy.in UnparsedImage.Manifest. - // Becase UnparsedImage.Manifest verifies the intended.Digest() against the manifest, and prSignedBy verifies the signature digest against the manifest, + // Because UnparsedImage.Manifest verifies the intended.Digest() against the manifest, and prSignedBy verifies the signature digest against the manifest, // we know that signature digest matches intended.Digest() (but intended.Digest() and signature digest may use different algorithms) return signature.Name() == intended.Name() default: // !reference.IsNameOnly(intended) diff --git a/vendor/github.com/containers/image/v5/signature/signature.go b/vendor/github.com/containers/image/v5/signature/signature.go index bc1c0e575..09f4f85e0 100644 --- a/vendor/github.com/containers/image/v5/signature/signature.go +++ b/vendor/github.com/containers/image/v5/signature/signature.go @@ -210,7 +210,7 @@ type signatureAcceptanceRules struct { validateSignedDockerManifestDigest func(digest.Digest) error } -// verifyAndExtractSignature verifies that unverifiedSignature has been signed, and that its principial components +// verifyAndExtractSignature verifies that unverifiedSignature has been signed, and that its principal components // match expected values, both as specified by rules, and returns it func verifyAndExtractSignature(mech SigningMechanism, unverifiedSignature []byte, rules signatureAcceptanceRules) (*Signature, error) { signed, keyIdentity, err := mech.Verify(unverifiedSignature) @@ -248,7 +248,7 @@ func verifyAndExtractSignature(mech SigningMechanism, unverifiedSignature []byte // There is NO REASON to expect the values to be correct, or not intentionally misleading // (including things like “✅ Verified by $authority”) func GetUntrustedSignatureInformationWithoutVerifying(untrustedSignatureBytes []byte) (*UntrustedSignatureInformation, error) { - // NOTE: This should eventualy do format autodetection. + // NOTE: This should eventually do format autodetection. mech, _, err := NewEphemeralGPGSigningMechanism([]byte{}) if err != nil { return nil, err diff --git a/vendor/github.com/containers/image/v5/storage/storage_image.go b/vendor/github.com/containers/image/v5/storage/storage_image.go index df4b67c7a..d24f8bbee 100644 --- a/vendor/github.com/containers/image/v5/storage/storage_image.go +++ b/vendor/github.com/containers/image/v5/storage/storage_image.go @@ -463,7 +463,7 @@ func (s *storageImageDestination) PutBlob(ctx context.Context, stream io.Reader, // (e.g. if the blob is a filesystem layer, this signifies that the changes it describes need to be applied again when composing a filesystem tree). // info.Digest must not be empty. // If canSubstitute, TryReusingBlob can use an equivalent equivalent of the desired blob; in that case the returned info may not match the input. -// If the blob has been succesfully reused, returns (true, info, nil); info must contain at least a digest and size. +// If the blob has been successfully reused, returns (true, info, nil); info must contain at least a digest and size. // If the transport can not reuse the requested blob, TryReusingBlob returns (false, {}, nil); it returns a non-nil error only on an unexpected failure. // May use and/or update cache. func (s *storageImageDestination) TryReusingBlob(ctx context.Context, blobinfo types.BlobInfo, cache types.BlobInfoCache, canSubstitute bool) (bool, types.BlobInfo, error) { @@ -657,7 +657,7 @@ func (s *storageImageDestination) Commit(ctx context.Context, unparsedToplevel t // Check if it's elsewhere and the caller just forgot to pass it to us in a PutBlob(), // or to even check if we had it. // Use none.NoCache to avoid a repeated DiffID lookup in the BlobInfoCache; a caller - // that relies on using a blob digest that has never been seeen by the store had better call + // that relies on using a blob digest that has never been seen by the store had better call // TryReusingBlob; not calling PutBlob already violates the documented API, so there’s only // so far we are going to accommodate that (if we should be doing that at all). logrus.Debugf("looking for diffID for blob %+v", blob.Digest) diff --git a/vendor/github.com/containers/image/v5/types/types.go b/vendor/github.com/containers/image/v5/types/types.go index 4f624cf33..5a91f0096 100644 --- a/vendor/github.com/containers/image/v5/types/types.go +++ b/vendor/github.com/containers/image/v5/types/types.go @@ -170,7 +170,7 @@ type BICReplacementCandidate struct { Location BICLocationReference } -// BlobInfoCache records data useful for reusing blobs, or substituing equivalent ones, to avoid unnecessary blob copies. +// BlobInfoCache records data useful for reusing blobs, or substituting equivalent ones, to avoid unnecessary blob copies. // // It records two kinds of data: // - Sets of corresponding digest vs. uncompressed digest ("DiffID") pairs: @@ -193,7 +193,7 @@ type BICReplacementCandidate struct { // can be directly reused within a registry, or mounted across registries within a registry server.) // // None of the methods return an error indication: errors when neither reading from, nor writing to, the cache, should be fatal; -// users of the cahce should just fall back to copying the blobs the usual way. +// users of the cache should just fall back to copying the blobs the usual way. type BlobInfoCache interface { // UncompressedDigest returns an uncompressed digest corresponding to anyDigest. // May return anyDigest if it is known to be uncompressed. @@ -306,7 +306,7 @@ type ImageDestination interface { // (e.g. if the blob is a filesystem layer, this signifies that the changes it describes need to be applied again when composing a filesystem tree). // info.Digest must not be empty. // If canSubstitute, TryReusingBlob can use an equivalent equivalent of the desired blob; in that case the returned info may not match the input. - // If the blob has been succesfully reused, returns (true, info, nil); info must contain at least a digest and size. + // If the blob has been successfully reused, returns (true, info, nil); info must contain at least a digest and size. // If the transport can not reuse the requested blob, TryReusingBlob returns (false, {}, nil); it returns a non-nil error only on an unexpected failure. // May use and/or update cache. TryReusingBlob(ctx context.Context, info BlobInfo, cache BlobInfoCache, canSubstitute bool) (bool, BlobInfo, error) diff --git a/vendor/github.com/containers/image/v5/version/version.go b/vendor/github.com/containers/image/v5/version/version.go index 9c0ddcaed..b6b79f26c 100644 --- a/vendor/github.com/containers/image/v5/version/version.go +++ b/vendor/github.com/containers/image/v5/version/version.go @@ -6,7 +6,7 @@ const ( // VersionMajor is for an API incompatible changes VersionMajor = 5 // VersionMinor is for functionality in a backwards-compatible manner - VersionMinor = 6 + VersionMinor = 7 // VersionPatch is for backwards-compatible bug fixes VersionPatch = 0 diff --git a/vendor/github.com/containers/storage/.cirrus.yml b/vendor/github.com/containers/storage/.cirrus.yml index 6ba46ebbb..539bfeabb 100644 --- a/vendor/github.com/containers/storage/.cirrus.yml +++ b/vendor/github.com/containers/storage/.cirrus.yml @@ -17,14 +17,15 @@ env: #### #### Cache-image names to test with (double-quotes around names are critical) ### - FEDORA_NAME: "fedora-32" - PRIOR_FEDORA_NAME: "fedora-31" - UBUNTU_NAME: "ubuntu-20" - PRIOR_UBUNTU_NAME: "ubuntu-19" + FEDORA_NAME: "fedora" + PRIOR_FEDORA_NAME: "prior-fedora" + UBUNTU_NAME: "ubuntu" + PRIOR_UBUNTU_NAME: "prior-ubuntu" # GCE project where images live IMAGE_PROJECT: "libpod-218412" - _BUILT_IMAGE_SUFFIX: "libpod-6508632441356288" + # VM Image built in containers/automation_images + _BUILT_IMAGE_SUFFIX: "c4948709391728640" FEDORA_CACHE_IMAGE_NAME: "${FEDORA_NAME}-${_BUILT_IMAGE_SUFFIX}" PRIOR_FEDORA_CACHE_IMAGE_NAME: "${PRIOR_FEDORA_NAME}-${_BUILT_IMAGE_SUFFIX}" UBUNTU_CACHE_IMAGE_NAME: "${UBUNTU_NAME}-${_BUILT_IMAGE_SUFFIX}" diff --git a/vendor/github.com/containers/storage/.gitignore b/vendor/github.com/containers/storage/.gitignore index 64b9a98bd..99b40fbde 100644 --- a/vendor/github.com/containers/storage/.gitignore +++ b/vendor/github.com/containers/storage/.gitignore @@ -9,6 +9,7 @@ *.test .*.swp .DS_Store +.idea* # a .bashrc may be added to customize the build environment .bashrc .gopath/ diff --git a/vendor/github.com/containers/storage/VERSION b/vendor/github.com/containers/storage/VERSION index ca8ec414e..2d27ccba1 100644 --- a/vendor/github.com/containers/storage/VERSION +++ b/vendor/github.com/containers/storage/VERSION @@ -1 +1 @@ -1.23.5 +1.23.7 diff --git a/vendor/github.com/containers/storage/drivers/btrfs/btrfs.go b/vendor/github.com/containers/storage/drivers/btrfs/btrfs.go index be4362dc0..e71ad69e2 100644 --- a/vendor/github.com/containers/storage/drivers/btrfs/btrfs.go +++ b/vendor/github.com/containers/storage/drivers/btrfs/btrfs.go @@ -422,7 +422,7 @@ func subvolLimitQgroup(path string, size uint64) error { // subvolQgroupStatus performs a BTRFS_IOC_TREE_SEARCH on the root path // with search key of BTRFS_QGROUP_STATUS_KEY. -// In case qgroup is enabled, the retuned key type will match BTRFS_QGROUP_STATUS_KEY. +// In case qgroup is enabled, the returned key type will match BTRFS_QGROUP_STATUS_KEY. // For more details please see https://github.com/kdave/btrfs-progs/blob/v4.9/qgroup.c#L1035 func subvolQgroupStatus(path string) error { dir, err := openDir(path) diff --git a/vendor/github.com/containers/storage/drivers/chown_unix.go b/vendor/github.com/containers/storage/drivers/chown_unix.go index 3a3978b71..1cadb089f 100644 --- a/vendor/github.com/containers/storage/drivers/chown_unix.go +++ b/vendor/github.com/containers/storage/drivers/chown_unix.go @@ -54,7 +54,7 @@ func platformLChown(path string, info os.FileInfo, toHost, toContainer *idtools. } // Make the change. - if err := syscall.Lchown(path, uid, gid); err != nil { + if err := os.Lchown(path, uid, gid); err != nil { return fmt.Errorf("%s: chown(%q): %v", os.Args[0], path, err) } // Restore the SUID and SGID bits if they were originally set. diff --git a/vendor/github.com/containers/storage/drivers/devmapper/deviceset.go b/vendor/github.com/containers/storage/drivers/devmapper/deviceset.go index cba3d05ea..775dc1685 100644 --- a/vendor/github.com/containers/storage/drivers/devmapper/deviceset.go +++ b/vendor/github.com/containers/storage/drivers/devmapper/deviceset.go @@ -1213,7 +1213,11 @@ func (devices *DeviceSet) growFS(info *devInfo) error { return errors.Wrapf(err, "Failed to mount; dmesg: %s", string(dmesg.Dmesg(256))) } - defer unix.Unmount(fsMountPoint, unix.MNT_DETACH) + defer func() { + if err := mount.Unmount(fsMountPoint); err != nil { + logrus.Warnf("devmapper.growFS cleanup error: %v", err) + } + }() switch devices.BaseDeviceFilesystem { case ext4: @@ -2257,6 +2261,38 @@ func (devices *DeviceSet) cancelDeferredRemoval(info *devInfo) error { return err } +func (devices *DeviceSet) unmountAndDeactivateAll(dir string) { + files, err := ioutil.ReadDir(dir) + if err != nil { + logrus.Warnf("devmapper: unmountAndDeactivate: %s", err) + return + } + + for _, d := range files { + if !d.IsDir() { + continue + } + + name := d.Name() + fullname := path.Join(dir, name) + + // We use MNT_DETACH here in case it is still busy in some running + // container. This means it'll go away from the global scope directly, + // and the device will be released when that container dies. + if err := mount.Unmount(fullname); err != nil { + logrus.Warnf("devmapper.Shutdown error: %s", err) + } + + if devInfo, err := devices.lookupDevice(name); err != nil { + logrus.Debugf("devmapper: Shutdown lookup device %s, error: %s", name, err) + } else { + if err := devices.deactivateDevice(devInfo); err != nil { + logrus.Debugf("devmapper: Shutdown deactivate %s, error: %s", devInfo.Hash, err) + } + } + } +} + // Shutdown shuts down the device by unmounting the root. func (devices *DeviceSet) Shutdown(home string) error { logrus.Debugf("devmapper: [deviceset %s] Shutdown()", devices.devicePrefix) @@ -2278,45 +2314,7 @@ func (devices *DeviceSet) Shutdown(home string) error { // will be killed and we will not get a chance to save deviceset // metadata. Hence save this early before trying to deactivate devices. devices.saveDeviceSetMetaData() - - // ignore the error since it's just a best effort to not try to unmount something that's mounted - mounts, _ := mount.GetMounts() - mounted := make(map[string]bool, len(mounts)) - for _, mnt := range mounts { - mounted[mnt.Mountpoint] = true - } - - if err := filepath.Walk(path.Join(home, "mnt"), func(p string, info os.FileInfo, err error) error { - if err != nil { - return err - } - if !info.IsDir() { - return nil - } - - if mounted[p] { - // We use MNT_DETACH here in case it is still busy in some running - // container. This means it'll go away from the global scope directly, - // and the device will be released when that container dies. - if err := unix.Unmount(p, unix.MNT_DETACH); err != nil { - logrus.Debugf("devmapper: Shutdown unmounting %s, error: %s", p, err) - } - } - - if devInfo, err := devices.lookupDevice(path.Base(p)); err != nil { - logrus.Debugf("devmapper: Shutdown lookup device %s, error: %s", path.Base(p), err) - } else { - if err := devices.deactivateDevice(devInfo); err != nil { - logrus.Debugf("devmapper: Shutdown deactivate %s , error: %s", devInfo.Hash, err) - } - } - - return nil - }); err != nil && !os.IsNotExist(err) { - devices.Unlock() - return err - } - + devices.unmountAndDeactivateAll(path.Join(home, "mnt")) devices.Unlock() info, _ := devices.lookupDeviceWithLock("") @@ -2420,7 +2418,9 @@ func (devices *DeviceSet) MountDevice(hash, path string, moptions graphdriver.Mo if fstype == xfs && devices.xfsNospaceRetries != "" { if err := devices.xfsSetNospaceRetries(info); err != nil { - unix.Unmount(path, unix.MNT_DETACH) + if err := mount.Unmount(path); err != nil { + logrus.Warnf("devmapper.MountDevice cleanup error: %v", err) + } devices.deactivateDevice(info) return err } @@ -2446,11 +2446,23 @@ func (devices *DeviceSet) UnmountDevice(hash, mountPath string) error { defer devices.Unlock() logrus.Debugf("devmapper: Unmount(%s)", mountPath) - if err := unix.Unmount(mountPath, unix.MNT_DETACH); err != nil { + if err := mount.Unmount(mountPath); err != nil { return err } logrus.Debug("devmapper: Unmount done") + // Remove the mountpoint here. Removing the mountpoint (in newer kernels) + // will cause all other instances of this mount in other mount namespaces + // to be killed (this is an anti-DoS measure that is necessary for things + // like devicemapper). This is necessary to avoid cases where a libdm mount + // that is present in another namespace will cause subsequent RemoveDevice + // operations to fail. We ignore any errors here because this may fail on + // older kernels which don't have + // torvalds/linux@8ed936b5671bfb33d89bc60bdcc7cf0470ba52fe applied. + if err := os.Remove(mountPath); err != nil { + logrus.Debugf("devmapper: error doing a remove on unmounted device %s: %v", mountPath, err) + } + return devices.deactivateDevice(info) } diff --git a/vendor/github.com/containers/storage/drivers/devmapper/driver.go b/vendor/github.com/containers/storage/drivers/devmapper/driver.go index 0afa6c84d..4149979a5 100644 --- a/vendor/github.com/containers/storage/drivers/devmapper/driver.go +++ b/vendor/github.com/containers/storage/drivers/devmapper/driver.go @@ -14,9 +14,9 @@ import ( "github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/locker" "github.com/containers/storage/pkg/mount" - "github.com/containers/storage/pkg/system" units "github.com/docker/go-units" "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" ) func init() { @@ -116,11 +116,13 @@ func (d *Driver) Metadata(id string) (map[string]string, error) { func (d *Driver) Cleanup() error { err := d.DeviceSet.Shutdown(d.home) - if err2 := mount.Unmount(d.home); err == nil { - err = err2 + umountErr := mount.Unmount(d.home) + // in case we have two errors, prefer the one from Shutdown() + if err != nil { + return err } - return err + return umountErr } // CreateFromTemplate creates a layer with the same contents and parent as another layer. @@ -148,7 +150,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error { return nil } -// Remove removes a device with a given id, unmounts the filesystem. +// Remove removes a device with a given id, unmounts the filesystem, and removes the mount point. func (d *Driver) Remove(id string) error { d.locker.Lock(id) defer d.locker.Unlock(id) @@ -163,7 +165,21 @@ func (d *Driver) Remove(id string) error { if err := d.DeviceSet.DeleteDevice(id, false); err != nil { return fmt.Errorf("failed to remove device %s: %v", id, err) } - return system.EnsureRemoveAll(path.Join(d.home, "mnt", id)) + + // Most probably the mount point is already removed on Put() + // (see DeviceSet.UnmountDevice()), but just in case it was not + // let's try to remove it here as well, ignoring errors as + // an older kernel can return EBUSY if e.g. the mount was leaked + // to other mount namespaces. A failure to remove the container's + // mount point is not important and should not be treated + // as a failure to remove the container. + mp := path.Join(d.home, "mnt", id) + err := unix.Rmdir(mp) + if err != nil && !os.IsNotExist(err) { + logrus.WithField("storage-driver", "devicemapper").Warnf("unable to remove mount point %q: %s", mp, err) + } + + return nil } // Get mounts a device with given id into the root filesystem @@ -226,10 +242,12 @@ func (d *Driver) Put(id string) error { if count := d.ctr.Decrement(mp); count > 0 { return nil } + err := d.DeviceSet.UnmountDevice(id, mp) if err != nil { - logrus.Errorf("devmapper: Error unmounting device %s: %s", id, err) + logrus.Errorf("devmapper: Error unmounting device %s: %v", id, err) } + return err } diff --git a/vendor/github.com/containers/storage/drivers/overlay/overlay.go b/vendor/github.com/containers/storage/drivers/overlay/overlay.go index 1de771bb0..398fe6531 100644 --- a/vendor/github.com/containers/storage/drivers/overlay/overlay.go +++ b/vendor/github.com/containers/storage/drivers/overlay/overlay.go @@ -75,7 +75,7 @@ const ( maxDepth = 128 // idLength represents the number of random characters - // which can be used to create the unique link identifer + // which can be used to create the unique link identifier // for every layer. If this value is too long then the // page size limit for the mount command may be exceeded. // The idLength should be selected such that following equation @@ -219,7 +219,7 @@ func Init(home string, options graphdriver.Options) (graphdriver.Driver, error) return nil, errors.Wrap(err, "error recording metacopy-being-used status") } } else { - logrus.Warnf("overlay test mount did not indicate whether or not metacopy is being used: %v", err) + logrus.Infof("overlay test mount did not indicate whether or not metacopy is being used: %v", err) return nil, err } } @@ -280,7 +280,7 @@ func parseOptions(options []string) (*overlayOptions, error) { trimkey = strings.TrimPrefix(trimkey, ".") switch trimkey { case "override_kernel_check": - logrus.Warnf("overlay: override_kernel_check option was specified, but is no longer necessary") + logrus.Debugf("overlay: override_kernel_check option was specified, but is no longer necessary") case "mountopt": o.mountOptions = val case "size": @@ -444,14 +444,14 @@ func (d *Driver) useNaiveDiff() bool { logrus.Debugf("cached value indicated that native-diff is usable") } else { logrus.Debugf("cached value indicated that native-diff is not being used") - logrus.Warn(nativeDiffCacheText) + logrus.Info(nativeDiffCacheText) } useNaiveDiffOnly = !nativeDiffCacheResult return } if err := doesSupportNativeDiff(d.home, d.options.mountOptions); err != nil { nativeDiffCacheText = fmt.Sprintf("Not using native diff for overlay, this may cause degraded performance for building images: %v", err) - logrus.Warn(nativeDiffCacheText) + logrus.Info(nativeDiffCacheText) useNaiveDiffOnly = true } cachedFeatureRecord(d.runhome, feature, !useNaiveDiffOnly, nativeDiffCacheText) diff --git a/vendor/github.com/containers/storage/drivers/zfs/zfs.go b/vendor/github.com/containers/storage/drivers/zfs/zfs.go index 3e850d136..4e7290efc 100644 --- a/vendor/github.com/containers/storage/drivers/zfs/zfs.go +++ b/vendor/github.com/containers/storage/drivers/zfs/zfs.go @@ -160,7 +160,7 @@ func lookupZfsDataset(rootdir string) (string, error) { continue // may fail on fuse file systems } - if stat.Dev == wantedDev && m.Fstype == "zfs" { + if stat.Dev == wantedDev && m.FSType == "zfs" { return m.Source, nil } } diff --git a/vendor/github.com/containers/storage/go.mod b/vendor/github.com/containers/storage/go.mod index 39db66641..f38266a16 100644 --- a/vendor/github.com/containers/storage/go.mod +++ b/vendor/github.com/containers/storage/go.mod @@ -1,4 +1,4 @@ -go 1.15 +go 1.14 module github.com/containers/storage @@ -8,23 +8,23 @@ require ( github.com/Microsoft/hcsshim v0.8.9 github.com/docker/go-units v0.4.0 github.com/hashicorp/go-multierror v1.1.0 - github.com/klauspost/compress v1.11.0 + github.com/klauspost/compress v1.11.1 github.com/klauspost/pgzip v1.2.5 github.com/mattn/go-shellwords v1.0.10 github.com/mistifyio/go-zfs v2.1.1+incompatible - github.com/moby/sys/mountinfo v0.1.3 + github.com/moby/sys/mountinfo v0.4.0 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/runc v1.0.0-rc91 github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2 github.com/opencontainers/selinux v1.6.0 github.com/pkg/errors v0.9.1 github.com/pquerna/ffjson v0.0.0-20181028064349-e517b90714f7 - github.com/sirupsen/logrus v1.6.0 + github.com/sirupsen/logrus v1.7.0 github.com/stretchr/testify v1.6.1 github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 github.com/tchap/go-patricia v2.3.0+incompatible github.com/vbatts/tar-split v0.11.1 golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 - golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775 + golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 gotest.tools v2.2.0+incompatible ) diff --git a/vendor/github.com/containers/storage/go.sum b/vendor/github.com/containers/storage/go.sum index d1fb711b1..2aad798d8 100644 --- a/vendor/github.com/containers/storage/go.sum +++ b/vendor/github.com/containers/storage/go.sum @@ -62,8 +62,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.11.0 h1:wJbzvpYMVGG9iTI9VxpnNZfd4DzMPoCWze3GgSqz8yg= -github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.1 h1:bPb7nMRdOZYDrpPMTA3EInUQrdgoBinqUuSwlGdKDdE= +github.com/klauspost/compress v1.11.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -80,6 +80,8 @@ github.com/mistifyio/go-zfs v2.1.1+incompatible h1:gAMO1HM9xBRONLHHYnu5iFsOJUiJd github.com/mistifyio/go-zfs v2.1.1+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/moby/sys/mountinfo v0.1.3 h1:KIrhRO14+AkwKvG/g2yIpNMOUVZ02xNhOw8KY1WsLOI= github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o= +github.com/moby/sys/mountinfo v0.4.0 h1:1KInV3Huv18akCu58V7lzNlt+jFmqlu1EaErnEHE/VM= +github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618 h1:7InQ7/zrOh6SlFjaXFubv0xX0HsuC9qJsdqm7bNQpYM= github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -113,6 +115,8 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -163,12 +167,15 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775 h1:TC0v2RSO1u2kn1ZugjrFXkRZAEaqMN/RW+OTZkBzmLE= golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 h1:W0lCpv29Hv0UaM1LXb9QlBHLNP8UFfcKjblhVCWftOM= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/vendor/github.com/containers/storage/layers.go b/vendor/github.com/containers/storage/layers.go index 52577299c..a70806f40 100644 --- a/vendor/github.com/containers/storage/layers.go +++ b/vendor/github.com/containers/storage/layers.go @@ -907,11 +907,11 @@ func (r *layerStore) ParentOwners(id string) (uids, gids []int, err error) { for dir := filepath.Dir(layer.MountPoint); dir != "" && dir != string(os.PathSeparator); dir = filepath.Dir(dir) { st, err := system.Stat(dir) if err != nil { - return nil, nil, errors.Wrapf(err, "error reading ownership of directory %q", dir) + return nil, nil, errors.Wrap(err, "read directory ownership") } lst, err := system.Lstat(dir) if err != nil { - return nil, nil, errors.Wrapf(err, "error reading ownership of directory-in-case-it's-a-symlink %q", dir) + return nil, nil, err } fsuid := int(st.UID()) fsgid := int(st.GID()) diff --git a/vendor/github.com/containers/storage/pkg/archive/archive.go b/vendor/github.com/containers/storage/pkg/archive/archive.go index ac0f5f336..4472511a2 100644 --- a/vendor/github.com/containers/storage/pkg/archive/archive.go +++ b/vendor/github.com/containers/storage/pkg/archive/archive.go @@ -694,29 +694,6 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L } } - var errors []string - for key, value := range hdr.Xattrs { - if err := system.Lsetxattr(path, key, []byte(value), 0); err != nil { - if err == syscall.ENOTSUP || (err == syscall.EPERM && inUserns) { - // We ignore errors here because not all graphdrivers support - // xattrs *cough* old versions of AUFS *cough*. However only - // ENOTSUP should be emitted in that case, otherwise we still - // bail. We also ignore EPERM errors if we are running in a - // user namespace. - errors = append(errors, err.Error()) - continue - } - return err - } - - } - - if len(errors) > 0 { - logrus.WithFields(logrus.Fields{ - "errors": errors, - }).Warn("ignored xattrs in archive: underlying filesystem doesn't support them") - } - // There is no LChmod, so ignore mode for symlink. Also, this // must happen after chown, as that can modify the file mode if err := handleLChmod(hdr, path, hdrInfo); err != nil { @@ -746,6 +723,30 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L return err } } + + var errors []string + for key, value := range hdr.Xattrs { + if err := system.Lsetxattr(path, key, []byte(value), 0); err != nil { + if err == syscall.ENOTSUP || (err == syscall.EPERM && inUserns) { + // We ignore errors here because not all graphdrivers support + // xattrs *cough* old versions of AUFS *cough*. However only + // ENOTSUP should be emitted in that case, otherwise we still + // bail. We also ignore EPERM errors if we are running in a + // user namespace. + errors = append(errors, err.Error()) + continue + } + return err + } + + } + + if len(errors) > 0 { + logrus.WithFields(logrus.Fields{ + "errors": errors, + }).Warn("ignored xattrs in archive: underlying filesystem doesn't support them") + } + return nil } @@ -1247,10 +1248,11 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) { }() options := &TarOptions{ - UIDMaps: archiver.UntarIDMappings.UIDs(), - GIDMaps: archiver.UntarIDMappings.GIDs(), - ChownOpts: archiver.ChownOpts, - InUserNS: rsystem.RunningInUserNS(), + UIDMaps: archiver.UntarIDMappings.UIDs(), + GIDMaps: archiver.UntarIDMappings.GIDs(), + ChownOpts: archiver.ChownOpts, + InUserNS: rsystem.RunningInUserNS(), + NoOverwriteDirNonDir: true, } err = archiver.Untar(r, filepath.Dir(dst), options) if err != nil { diff --git a/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_log.go b/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_log.go index b540281fa..082fb1ba3 100644 --- a/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_log.go +++ b/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_log.go @@ -12,7 +12,7 @@ import ( ) // DevmapperLogger defines methods required to register as a callback for -// logging events recieved from devicemapper. Note that devicemapper will send +// logging events received from devicemapper. Note that devicemapper will send // *all* logs regardless to callbacks (including debug logs) so it's // recommended to not spam the console with the outputs. type DevmapperLogger interface { diff --git a/vendor/github.com/containers/storage/pkg/fileutils/fileutils.go b/vendor/github.com/containers/storage/pkg/fileutils/fileutils.go index e5faf9aad..b3998fb35 100644 --- a/vendor/github.com/containers/storage/pkg/fileutils/fileutils.go +++ b/vendor/github.com/containers/storage/pkg/fileutils/fileutils.go @@ -13,7 +13,7 @@ import ( "github.com/sirupsen/logrus" ) -// PatternMatcher allows checking paths agaist a list of patterns +// PatternMatcher allows checking paths against a list of patterns type PatternMatcher struct { patterns []*Pattern exclusions bool diff --git a/vendor/github.com/containers/storage/pkg/idtools/idtools.go b/vendor/github.com/containers/storage/pkg/idtools/idtools.go index 5105720ba..0958c0c5d 100644 --- a/vendor/github.com/containers/storage/pkg/idtools/idtools.go +++ b/vendor/github.com/containers/storage/pkg/idtools/idtools.go @@ -291,7 +291,7 @@ func parseSubidFile(path, username string) (ranges, error) { func checkChownErr(err error, name string, uid, gid int) error { if e, ok := err.(*os.PathError); ok && e.Err == syscall.EINVAL { - return errors.Wrapf(err, "there might not be enough IDs available in the namespace (requested %d:%d for %s)", uid, gid, name) + return errors.Wrapf(err, "potentially insufficient UIDs or GIDs available in user namespace (requested %d:%d for %s): Check /etc/subuid and /etc/subgid", uid, gid, name) } return err } diff --git a/vendor/github.com/containers/storage/pkg/idtools/parser.go b/vendor/github.com/containers/storage/pkg/idtools/parser.go index 86f98f16e..1c819a1f9 100644 --- a/vendor/github.com/containers/storage/pkg/idtools/parser.go +++ b/vendor/github.com/containers/storage/pkg/idtools/parser.go @@ -8,13 +8,6 @@ import ( "strings" ) -func nonDigitsToWhitespace(r rune) rune { - if !strings.ContainsRune("0123456789", r) { - return ' ' - } - return r -} - func parseTriple(spec []string) (container, host, size uint32, err error) { cid, err := strconv.ParseUint(spec[0], 10, 32) if err != nil { @@ -33,9 +26,12 @@ func parseTriple(spec []string) (container, host, size uint32, err error) { // ParseIDMap parses idmap triples from string. func ParseIDMap(mapSpec []string, mapSetting string) (idmap []IDMap, err error) { - stdErr := fmt.Errorf("error initializing ID mappings: %s setting is malformed", mapSetting) + stdErr := fmt.Errorf("error initializing ID mappings: %s setting is malformed expected [\"uint32:uint32:uint32\"]: %q", mapSetting, mapSpec) for _, idMapSpec := range mapSpec { - idSpec := strings.Fields(strings.Map(nonDigitsToWhitespace, idMapSpec)) + if idMapSpec == "" { + continue + } + idSpec := strings.Split(idMapSpec, ":") if len(idSpec)%3 != 0 { return nil, stdErr } diff --git a/vendor/github.com/containers/storage/pkg/mount/mount.go b/vendor/github.com/containers/storage/pkg/mount/mount.go index 8273ab5a9..cd4bacd66 100644 --- a/vendor/github.com/containers/storage/pkg/mount/mount.go +++ b/vendor/github.com/containers/storage/pkg/mount/mount.go @@ -90,7 +90,7 @@ func RecursiveUnmount(target string) error { if err := Unmount(m.Mountpoint); err != nil && i == len(mounts)-1 { return err // Ignore errors for submounts and continue trying to unmount others - // The final unmount should fail if there ane any submounts remaining + // The final unmount should fail if there are any submounts remaining } } return nil diff --git a/vendor/github.com/containers/storage/pkg/mount/mountinfo.go b/vendor/github.com/containers/storage/pkg/mount/mountinfo.go index efc6c406e..bb2da474f 100644 --- a/vendor/github.com/containers/storage/pkg/mount/mountinfo.go +++ b/vendor/github.com/containers/storage/pkg/mount/mountinfo.go @@ -1,21 +1,13 @@ package mount import ( - "github.com/containers/storage/pkg/fileutils" "github.com/moby/sys/mountinfo" ) type Info = mountinfo.Info +var Mounted = mountinfo.Mounted + func GetMounts() ([]*Info, error) { return mountinfo.GetMounts(nil) } - -// Mounted determines if a specified mountpoint has been mounted. -func Mounted(mountpoint string) (bool, error) { - mountpoint, err := fileutils.ReadSymlinkedPath(mountpoint) - if err != nil { - return false, err - } - return mountinfo.Mounted(mountpoint) -} diff --git a/vendor/github.com/containers/storage/storage.conf b/vendor/github.com/containers/storage/storage.conf index a8ec9d98b..64e02f327 100644 --- a/vendor/github.com/containers/storage/storage.conf +++ b/vendor/github.com/containers/storage/storage.conf @@ -47,7 +47,7 @@ additionalimagestores = [ # remap-group = "containers" # Root-auto-userns-user is a user name which can be used to look up one or more UID/GID -# ranges in the /etc/subuid and /etc/subgid file. These ranges will be partioned +# ranges in the /etc/subuid and /etc/subgid file. These ranges will be partitioned # to containers configured to create automatically a user namespace. Containers # configured to automatically create a user namespace can still overlap with containers # having an explicit mapping set. diff --git a/vendor/github.com/containers/storage/store.go b/vendor/github.com/containers/storage/store.go index 2a34c84cc..6b51b405d 100644 --- a/vendor/github.com/containers/storage/store.go +++ b/vendor/github.com/containers/storage/store.go @@ -613,14 +613,14 @@ func GetStore(options StoreOptions) (Store, error) { if options.GraphRoot != "" { dir, err := filepath.Abs(options.GraphRoot) if err != nil { - return nil, errors.Wrapf(err, "error deriving an absolute path from %q", options.GraphRoot) + return nil, err } options.GraphRoot = dir } if options.RunRoot != "" { dir, err := filepath.Abs(options.RunRoot) if err != nil { - return nil, errors.Wrapf(err, "error deriving an absolute path from %q", options.RunRoot) + return nil, err } options.RunRoot = dir } @@ -2677,21 +2677,16 @@ func (s *store) MountImage(id string, mountOpts []string, mountLabel string) (st } func (s *store) Mount(id, mountLabel string) (string, error) { - container, err := s.Container(id) - var ( - uidMap, gidMap []idtools.IDMap - mountOpts []string - ) - if err == nil { - uidMap, gidMap = container.UIDMap, container.GIDMap - id = container.LayerID - mountOpts = container.MountOpts() - } options := drivers.MountOpts{ MountLabel: mountLabel, - UidMaps: uidMap, - GidMaps: gidMap, - Options: mountOpts, + } + // check if `id` is a container, then grab the LayerID, uidmap and gidmap, along with + // otherwise we assume the id is a LayerID and attempt to mount it. + if container, err := s.Container(id); err == nil { + id = container.LayerID + options.UidMaps = container.UIDMap + options.GidMaps = container.GIDMap + options.Options = container.MountOpts() } return s.mount(id, options) } @@ -3460,7 +3455,10 @@ func copyStringInterfaceMap(m map[string]interface{}) map[string]interface{} { } // defaultConfigFile path to the system wide storage.conf file -var defaultConfigFile = "/etc/containers/storage.conf" +var ( + defaultConfigFile = "/etc/containers/storage.conf" + defaultConfigFileSet = false +) // AutoUserNsMinSize is the minimum size for automatically created user namespaces const AutoUserNsMinSize = 1024 @@ -3475,21 +3473,24 @@ const RootAutoUserNsUser = "containers" // SetDefaultConfigFilePath sets the default configuration to the specified path func SetDefaultConfigFilePath(path string) { defaultConfigFile = path + defaultConfigFileSet = true + reloadConfigurationFileIfNeeded(defaultConfigFile, &defaultStoreOptions) } // DefaultConfigFile returns the path to the storage config file used func DefaultConfigFile(rootless bool) (string, error) { - if rootless { - if configHome := os.Getenv("XDG_CONFIG_HOME"); configHome != "" { - return filepath.Join(configHome, "containers/storage.conf"), nil - } - home := homedir.Get() - if home == "" { - return "", errors.New("cannot determine user's homedir") - } - return filepath.Join(home, ".config/containers/storage.conf"), nil + if defaultConfigFileSet || !rootless { + return defaultConfigFile, nil + } + + if configHome := os.Getenv("XDG_CONFIG_HOME"); configHome != "" { + return filepath.Join(configHome, "containers/storage.conf"), nil + } + home := homedir.Get() + if home == "" { + return "", errors.New("cannot determine user's homedir") } - return defaultConfigFile, nil + return filepath.Join(home, ".config/containers/storage.conf"), nil } // TOML-friendly explicit tables used for conversions. diff --git a/vendor/github.com/containers/storage/utils.go b/vendor/github.com/containers/storage/utils.go index b22263fe4..762c3a00d 100644 --- a/vendor/github.com/containers/storage/utils.go +++ b/vendor/github.com/containers/storage/utils.go @@ -76,7 +76,7 @@ func GetRootlessRuntimeDir(rootlessUID int) (string, error) { } path = filepath.Join(path, "containers") if err := os.MkdirAll(path, 0700); err != nil { - return "", errors.Wrapf(err, "unable to make rootless runtime dir %s", path) + return "", errors.Wrapf(err, "unable to make rootless runtime") } return path, nil } @@ -154,7 +154,7 @@ func getRootlessRuntimeDirIsolated(env rootlessRuntimeDirEnvironment) (string, e } resolvedHomeDir, err := filepath.EvalSymlinks(homeDir) if err != nil { - return "", errors.Wrapf(err, "cannot resolve %s", homeDir) + return "", err } return filepath.Join(resolvedHomeDir, "rundir"), nil } @@ -190,7 +190,7 @@ func getRootlessDirInfo(rootlessUID int) (string, string, error) { // on CoreOS /home is a symlink to /var/home, so resolve any symlink. resolvedHome, err := filepath.EvalSymlinks(home) if err != nil { - return "", "", errors.Wrapf(err, "cannot resolve %s", home) + return "", "", err } dataDir = filepath.Join(resolvedHome, ".local", "share") @@ -206,11 +206,10 @@ func getRootlessStorageOpts(rootlessUID int, systemOpts StoreOptions) (StoreOpti return opts, err } opts.RunRoot = rootlessRuntime - opts.GraphRoot = filepath.Join(dataDir, "containers", "storage") if systemOpts.RootlessStoragePath != "" { - opts.RootlessStoragePath = systemOpts.RootlessStoragePath + opts.GraphRoot = systemOpts.RootlessStoragePath } else { - opts.RootlessStoragePath = opts.GraphRoot + opts.GraphRoot = filepath.Join(dataDir, "containers", "storage") } if path, err := exec.LookPath("fuse-overlayfs"); err == nil { opts.GraphDriverName = "overlay" @@ -259,13 +258,24 @@ func defaultStoreOptionsIsolated(rootless bool, rootlessUID int, storageConf str } _, err = os.Stat(storageConf) if err != nil && !os.IsNotExist(err) { - return storageOpts, errors.Wrapf(err, "cannot stat %s", storageConf) + return storageOpts, err } - if err == nil { + if err == nil && !defaultConfigFileSet { defaultRootlessRunRoot = storageOpts.RunRoot defaultRootlessGraphRoot = storageOpts.GraphRoot storageOpts = StoreOptions{} reloadConfigurationFileIfNeeded(storageConf, &storageOpts) + if rootless && rootlessUID != 0 { + // If the file did not specify a graphroot or runroot, + // set sane defaults so we don't try and use root-owned + // directories + if storageOpts.RunRoot == "" { + storageOpts.RunRoot = defaultRootlessRunRoot + } + if storageOpts.GraphRoot == "" { + storageOpts.GraphRoot = defaultRootlessGraphRoot + } + } } if storageOpts.RunRoot != "" { runRoot, err := expandEnvPath(storageOpts.RunRoot, rootlessUID) @@ -282,26 +292,6 @@ func defaultStoreOptionsIsolated(rootless bool, rootlessUID int, storageConf str storageOpts.GraphRoot = graphRoot } - if rootless && rootlessUID != 0 { - if err == nil { - // If the file did not specify a graphroot or runroot, - // set sane defaults so we don't try and use root-owned - // directories - if storageOpts.RunRoot == "" { - storageOpts.RunRoot = defaultRootlessRunRoot - } - if storageOpts.GraphRoot == "" { - storageOpts.GraphRoot = defaultRootlessGraphRoot - } - if storageOpts.RootlessStoragePath != "" { - rootlessStoragePath, err := expandEnvPath(storageOpts.RootlessStoragePath, rootlessUID) - if err != nil { - return storageOpts, err - } - storageOpts.GraphRoot = rootlessStoragePath - } - } - } return storageOpts, nil } diff --git a/vendor/github.com/klauspost/compress/zstd/encoder_options.go b/vendor/github.com/klauspost/compress/zstd/encoder_options.go index 579206163..1209915bc 100644 --- a/vendor/github.com/klauspost/compress/zstd/encoder_options.go +++ b/vendor/github.com/klauspost/compress/zstd/encoder_options.go @@ -30,12 +30,13 @@ type encoderOptions struct { func (o *encoderOptions) setDefault() { *o = encoderOptions{ // use less ram: true for now, but may change. - concurrent: runtime.GOMAXPROCS(0), - crc: true, - single: nil, - blockSize: 1 << 16, - windowSize: 8 << 20, - level: SpeedDefault, + concurrent: runtime.GOMAXPROCS(0), + crc: true, + single: nil, + blockSize: 1 << 16, + windowSize: 8 << 20, + level: SpeedDefault, + allLitEntropy: true, } } diff --git a/vendor/github.com/moby/sys/mountinfo/doc.go b/vendor/github.com/moby/sys/mountinfo/doc.go index 21aa8dd59..b80e05efd 100644 --- a/vendor/github.com/moby/sys/mountinfo/doc.go +++ b/vendor/github.com/moby/sys/mountinfo/doc.go @@ -1,5 +1,6 @@ // Package mountinfo provides a set of functions to retrieve information about OS mounts. -// Currently it supports Linux. For historical reasons, there is also some support for FreeBSD, +// +// Currently it supports Linux. For historical reasons, there is also some support for FreeBSD and OpenBSD, // and a shallow implementation for Windows, but in general this is Linux-only package, so // the rest of the document only applies to Linux, unless explicitly specified otherwise. // @@ -8,17 +9,14 @@ // see different mounts. A per-process mountinfo table is available from /proc/<PID>/mountinfo, // where <PID> is a numerical process identifier. // -// In general, /proc is not a very effective interface, and mountinfo is not an exception. +// In general, /proc is not a very efficient interface, and mountinfo is not an exception. // For example, there is no way to get information about a specific mount point (i.e. it // is all-or-nothing). This package tries to hide the /proc ineffectiveness by using // parse filters while reading mountinfo. A filter can skip some entries, or stop // processing the rest of the file once the needed information is found. // -// For mountinfo filters that accept path as an argument, the path must be: -// - absolute; -// - having all symlinks resolved; -// - being cleaned. -// +// For mountinfo filters that accept path as an argument, the path must be absolute, +// having all symlinks resolved, and being cleaned (i.e. no extra slashes or dots). // One way to achieve all of the above is to employ filepath.Abs followed by // filepath.EvalSymlinks (the latter calls filepath.Clean on the result so // there is no need to explicitly call filepath.Clean). @@ -27,21 +25,20 @@ // of the cases where mountinfo should not be parsed: // // 1. Before performing a mount. Usually, this is not needed, but if required (say to -// prevent overmounts), to check whether a directory is mounted, call os.Lstat -// on it and its parent directory, and compare their st.Sys().(*syscall.Stat_t).Dev -// fields -- if they differ, then the directory is the mount point. NOTE this does -// not work for bind mounts. Optionally, the filesystem type can also be checked -// by calling unix.Statfs and checking the Type field (i.e. filesystem type). +// prevent over-mounts), to check whether a directory is mounted, call os.Lstat +// on it and its parent directory, and compare their st.Sys().(*syscall.Stat_t).Dev +// fields -- if they differ, then the directory is the mount point. NOTE this does +// not work for bind mounts. Optionally, the filesystem type can also be checked +// by calling unix.Statfs and checking the Type field (i.e. filesystem type). // // 2. After performing a mount. If there is no error returned, the mount succeeded; -// checking the mount table for a new mount is redundant and expensive. +// checking the mount table for a new mount is redundant and expensive. // // 3. Before performing an unmount. It is more efficient to do an unmount and ignore -// a specific error (EINVAL) which tells the directory is not mounted. +// a specific error (EINVAL) which tells the directory is not mounted. // // 4. After performing an unmount. If there is no error returned, the unmount succeeded. // // 5. To find the mount point root of a specific directory. You can perform os.Stat() -// on the directory and traverse up until the Dev field of a parent directory differs. - +// on the directory and traverse up until the Dev field of a parent directory differs. package mountinfo diff --git a/vendor/github.com/moby/sys/mountinfo/mounted_unix.go b/vendor/github.com/moby/sys/mountinfo/mounted_unix.go index c4d66b2f4..efb03978b 100644 --- a/vendor/github.com/moby/sys/mountinfo/mounted_unix.go +++ b/vendor/github.com/moby/sys/mountinfo/mounted_unix.go @@ -1,4 +1,4 @@ -// +build linux freebsd,cgo +// +build linux freebsd,cgo openbsd,cgo package mountinfo diff --git a/vendor/github.com/moby/sys/mountinfo/mountinfo.go b/vendor/github.com/moby/sys/mountinfo/mountinfo.go index 1987fcbb2..fe828c8f5 100644 --- a/vendor/github.com/moby/sys/mountinfo/mountinfo.go +++ b/vendor/github.com/moby/sys/mountinfo/mountinfo.go @@ -1,7 +1,6 @@ package mountinfo import ( - "io" "os" ) @@ -11,14 +10,6 @@ func GetMounts(f FilterFunc) ([]*Info, error) { return parseMountTable(f) } -// GetMountsFromReader retrieves a list of mounts from the -// reader provided, with an optional filter applied (use nil -// for no filter). This can be useful in tests or benchmarks -// that provide a fake mountinfo data. -func GetMountsFromReader(reader io.Reader, f FilterFunc) ([]*Info, error) { - return parseInfoFile(reader, f) -} - // Mounted determines if a specified path is a mount point. // // The argument must be an absolute path, with all symlinks resolved, and clean. @@ -55,18 +46,18 @@ type Info struct { // Mountpoint indicates the mount point relative to the process's root. Mountpoint string - // Opts represents mount-specific options. - Opts string + // Options represents mount-specific options. + Options string // Optional represents optional fields. Optional string - // Fstype indicates the type of filesystem, such as EXT3. - Fstype string + // FSType indicates the type of filesystem, such as EXT3. + FSType string // Source indicates filesystem specific information or "none". Source string - // VfsOpts represents per super block options. - VfsOpts string + // VFSOptions represents per super block options. + VFSOptions string } diff --git a/vendor/github.com/moby/sys/mountinfo/mountinfo_freebsd.go b/vendor/github.com/moby/sys/mountinfo/mountinfo_bsd.go index b30dc1625..b1c12d02b 100644 --- a/vendor/github.com/moby/sys/mountinfo/mountinfo_freebsd.go +++ b/vendor/github.com/moby/sys/mountinfo/mountinfo_bsd.go @@ -1,3 +1,5 @@ +// +build freebsd,cgo openbsd,cgo + package mountinfo /* @@ -33,7 +35,7 @@ func parseMountTable(filter FilterFunc) ([]*Info, error) { var mountinfo Info var skip, stop bool mountinfo.Mountpoint = C.GoString(&entry.f_mntonname[0]) - mountinfo.Fstype = C.GoString(&entry.f_fstypename[0]) + mountinfo.FSType = C.GoString(&entry.f_fstypename[0]) mountinfo.Source = C.GoString(&entry.f_mntfromname[0]) if filter != nil { @@ -54,7 +56,7 @@ func parseMountTable(filter FilterFunc) ([]*Info, error) { func mounted(path string) (bool, error) { // Fast path: compare st.st_dev fields. - // This should always work for FreeBSD. + // This should always work for FreeBSD and OpenBSD. mounted, err := mountedByStat(path) if err == nil { return mounted, nil diff --git a/vendor/github.com/moby/sys/mountinfo/mountinfo_filters.go b/vendor/github.com/moby/sys/mountinfo/mountinfo_filters.go index 8aebe1ad4..5869b2cee 100644 --- a/vendor/github.com/moby/sys/mountinfo/mountinfo_filters.go +++ b/vendor/github.com/moby/sys/mountinfo/mountinfo_filters.go @@ -6,12 +6,12 @@ import "strings" // used to filter out mountinfo entries we're not interested in, // and/or stop further processing if we found what we wanted. // -// It takes a pointer to the Info struct (not fully populated, -// currently only Mountpoint, Fstype, Source, and (on Linux) -// VfsOpts are filled in), and returns two booleans: +// It takes a pointer to the Info struct (fully populated with all available +// fields on the GOOS platform), and returns two booleans: // -// - skip: true if the entry should be skipped -// - stop: true if parsing should be stopped after the entry +// skip: true if the entry should be skipped; +// +// stop: true if parsing should be stopped after the entry. type FilterFunc func(*Info) (skip, stop bool) // PrefixFilter discards all entries whose mount points @@ -36,8 +36,8 @@ func SingleEntryFilter(mp string) FilterFunc { // ParentsFilter returns all entries whose mount points // can be parents of a path specified, discarding others. // -// For example, given `/var/lib/docker/something`, entries -// like `/var/lib/docker`, `/var` and `/` are returned. +// For example, given /var/lib/docker/something, entries +// like /var/lib/docker, /var and / are returned. func ParentsFilter(path string) FilterFunc { return func(m *Info) (bool, bool) { skip := !strings.HasPrefix(path, m.Mountpoint) @@ -45,12 +45,12 @@ func ParentsFilter(path string) FilterFunc { } } -// FstypeFilter returns all entries that match provided fstype(s). -func FstypeFilter(fstype ...string) FilterFunc { +// FSTypeFilter returns all entries that match provided fstype(s). +func FSTypeFilter(fstype ...string) FilterFunc { return func(m *Info) (bool, bool) { for _, t := range fstype { - if m.Fstype == t { - return false, false // don't skeep, keep going + if m.FSType == t { + return false, false // don't skip, keep going } } return true, false // skip, keep going diff --git a/vendor/github.com/moby/sys/mountinfo/mountinfo_linux.go b/vendor/github.com/moby/sys/mountinfo/mountinfo_linux.go index cdfd37da5..e591c8365 100644 --- a/vendor/github.com/moby/sys/mountinfo/mountinfo_linux.go +++ b/vendor/github.com/moby/sys/mountinfo/mountinfo_linux.go @@ -1,5 +1,3 @@ -// +build go1.13 - package mountinfo import ( @@ -11,14 +9,18 @@ import ( "strings" ) -func parseInfoFile(r io.Reader, filter FilterFunc) ([]*Info, error) { +// GetMountsFromReader retrieves a list of mounts from the +// reader provided, with an optional filter applied (use nil +// for no filter). This can be useful in tests or benchmarks +// that provide a fake mountinfo data. +// +// This function is Linux-specific. +func GetMountsFromReader(r io.Reader, filter FilterFunc) ([]*Info, error) { s := bufio.NewScanner(r) out := []*Info{} - var err error for s.Scan() { - if err = s.Err(); err != nil { - return nil, err - } + var err error + /* See http://man7.org/linux/man-pages/man5/proc.5.html @@ -70,12 +72,11 @@ func parseInfoFile(r io.Reader, filter FilterFunc) ([]*Info, error) { p := &Info{} - // Fill in the fields that a filter might check p.Mountpoint, err = unescape(fields[4]) if err != nil { return nil, fmt.Errorf("Parsing '%s' failed: mount point: %w", fields[4], err) } - p.Fstype, err = unescape(fields[sepIdx+1]) + p.FSType, err = unescape(fields[sepIdx+1]) if err != nil { return nil, fmt.Errorf("Parsing '%s' failed: fstype: %w", fields[sepIdx+1], err) } @@ -83,19 +84,7 @@ func parseInfoFile(r io.Reader, filter FilterFunc) ([]*Info, error) { if err != nil { return nil, fmt.Errorf("Parsing '%s' failed: source: %w", fields[sepIdx+2], err) } - p.VfsOpts = fields[sepIdx+3] - - // Run a filter soon so we can skip parsing/adding entries - // the caller is not interested in - var skip, stop bool - if filter != nil { - skip, stop = filter(p) - if skip { - continue - } - } - - // Fill in the rest of the fields + p.VFSOptions = fields[sepIdx+3] // ignore any numbers parsing errors, as there should not be any p.ID, _ = strconv.Atoi(fields[0]) @@ -112,7 +101,7 @@ func parseInfoFile(r io.Reader, filter FilterFunc) ([]*Info, error) { return nil, fmt.Errorf("Parsing '%s' failed: root: %w", fields[3], err) } - p.Opts = fields[5] + p.Options = fields[5] // zero or more optional fields switch { @@ -124,11 +113,23 @@ func parseInfoFile(r io.Reader, filter FilterFunc) ([]*Info, error) { p.Optional = strings.Join(fields[6:sepIdx-1], " ") } + // Run the filter after parsing all of the fields. + var skip, stop bool + if filter != nil { + skip, stop = filter(p) + if skip { + continue + } + } + out = append(out, p) if stop { break } } + if err := s.Err(); err != nil { + return nil, err + } return out, nil } @@ -141,12 +142,17 @@ func parseMountTable(filter FilterFunc) ([]*Info, error) { } defer f.Close() - return parseInfoFile(f, filter) + return GetMountsFromReader(f, filter) } -// PidMountInfo collects the mounts for a specific process ID. If the process -// ID is unknown, it is better to use `GetMounts` which will inspect -// "/proc/self/mountinfo" instead. +// PidMountInfo retrieves the list of mounts from a given process' mount +// namespace. Unless there is a need to get mounts from a mount namespace +// different from that of a calling process, use GetMounts. +// +// This function is Linux-specific. +// +// Deprecated: this will be removed before v1; use GetMountsFromReader with +// opened /proc/<pid>/mountinfo as an argument instead. func PidMountInfo(pid int) ([]*Info, error) { f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid)) if err != nil { @@ -154,7 +160,7 @@ func PidMountInfo(pid int) ([]*Info, error) { } defer f.Close() - return parseInfoFile(f, nil) + return GetMountsFromReader(f, nil) } // A few specific characters in mountinfo path entries (root and mountpoint) @@ -173,7 +179,7 @@ func unescape(path string) (string, error) { } // The following code is UTF-8 transparent as it only looks for some - // specific characters (backslach and 0..7) with values < utf8.RuneSelf, + // specific characters (backslash and 0..7) with values < utf8.RuneSelf, // and everything else is passed through as is. buf := make([]byte, len(path)) bufLen := 0 diff --git a/vendor/github.com/moby/sys/mountinfo/mountinfo_unsupported.go b/vendor/github.com/moby/sys/mountinfo/mountinfo_unsupported.go index 1eb8558c8..d33ebca09 100644 --- a/vendor/github.com/moby/sys/mountinfo/mountinfo_unsupported.go +++ b/vendor/github.com/moby/sys/mountinfo/mountinfo_unsupported.go @@ -1,10 +1,9 @@ -// +build !windows,!linux,!freebsd freebsd,!cgo +// +build !windows,!linux,!freebsd,!openbsd freebsd,!cgo openbsd,!cgo package mountinfo import ( "fmt" - "io" "runtime" ) @@ -14,10 +13,6 @@ func parseMountTable(_ FilterFunc) ([]*Info, error) { return nil, errNotImplemented } -func parseInfoFile(_ io.Reader, f FilterFunc) ([]*Info, error) { - return parseMountTable(f) -} - func mounted(path string) (bool, error) { return false, errNotImplemented } diff --git a/vendor/github.com/moby/sys/mountinfo/mountinfo_windows.go b/vendor/github.com/moby/sys/mountinfo/mountinfo_windows.go index 5659c1b0f..13fad165e 100644 --- a/vendor/github.com/moby/sys/mountinfo/mountinfo_windows.go +++ b/vendor/github.com/moby/sys/mountinfo/mountinfo_windows.go @@ -1,16 +1,10 @@ package mountinfo -import "io" - func parseMountTable(_ FilterFunc) ([]*Info, error) { // Do NOT return an error! return nil, nil } -func parseInfoFile(_ io.Reader, f FilterFunc) ([]*Info, error) { - return parseMountTable(f) -} - func mounted(_ string) (bool, error) { return false, nil } diff --git a/vendor/github.com/openshift/imagebuilder/README.md b/vendor/github.com/openshift/imagebuilder/README.md index 772747bce..748bff971 100644 --- a/vendor/github.com/openshift/imagebuilder/README.md +++ b/vendor/github.com/openshift/imagebuilder/README.md @@ -102,5 +102,6 @@ Example of usage from OpenShift's experimental `dockerbuild` [command with mount ## Run conformance tests (very slow): ``` -go test ./dockerclient/conformance_test.go -tags conformance +chmod -R go-w ./dockerclient/testdata +go test ./dockerclient/conformance_test.go -tags conformance -timeout 30m ``` diff --git a/vendor/github.com/openshift/imagebuilder/builder.go b/vendor/github.com/openshift/imagebuilder/builder.go index 583c303c0..22dc548b9 100644 --- a/vendor/github.com/openshift/imagebuilder/builder.go +++ b/vendor/github.com/openshift/imagebuilder/builder.go @@ -332,20 +332,10 @@ func ParseFile(path string) (*parser.Node, error) { // Step creates a new step from the current state. func (b *Builder) Step() *Step { - argsMap := make(map[string]string) - for _, argsVal := range b.Arguments() { - val := strings.SplitN(argsVal, "=", 2) - if len(val) > 1 { - argsMap[val[0]] = val[1] - } - } - - userArgs := makeUserArgs(b.Env, argsMap) - dst := make([]string, len(userArgs)+len(b.RunConfig.Env)) - copy(dst, userArgs) - dst = append(dst, b.RunConfig.Env...) - - return &Step{Env: dst} + // Include build arguments in the table of variables that we'll use in + // Resolve(), but override them with values from the actual + // environment in case there's any conflict. + return &Step{Env: mergeEnv(b.Arguments(), mergeEnv(b.Env, b.RunConfig.Env))} } // Run executes a step, transforming the current builder and @@ -473,7 +463,7 @@ func (b *Builder) FromImage(image *docker.Image, node *parser.Node) error { SplitChildren(node, command.From) b.RunConfig = *image.Config - b.Env = append(b.Env, b.RunConfig.Env...) + b.Env = mergeEnv(b.Env, b.RunConfig.Env) b.RunConfig.Env = nil // Check to see if we have a default PATH, note that windows won't @@ -573,14 +563,21 @@ var builtinAllowedBuildArgs = map[string]bool{ } // ParseDockerIgnore returns a list of the excludes in the .dockerignore file. -// extracted from fsouza/go-dockerclient. +// extracted from fsouza/go-dockerclient and modified to drop comments and +// empty lines. func ParseDockerignore(root string) ([]string, error) { var excludes []string ignore, err := ioutil.ReadFile(filepath.Join(root, ".dockerignore")) if err != nil && !os.IsNotExist(err) { return excludes, fmt.Errorf("error reading .dockerignore: '%s'", err) } - return strings.Split(string(ignore), "\n"), nil + for _, e := range strings.Split(string(ignore), "\n") { + if len(e) == 0 || e[0] == '#' { + continue + } + excludes = append(excludes, e) + } + return excludes, nil } // ExportEnv creates an export statement for a shell that contains all of the diff --git a/vendor/github.com/openshift/imagebuilder/dispatchers.go b/vendor/github.com/openshift/imagebuilder/dispatchers.go index 3a350fbee..ea3df04d3 100644 --- a/vendor/github.com/openshift/imagebuilder/dispatchers.go +++ b/vendor/github.com/openshift/imagebuilder/dispatchers.go @@ -83,21 +83,9 @@ func env(b *Builder, args []string, attributes map[string]bool, flagArgs []strin for j := 0; j < len(args); j++ { // name ==> args[j] // value ==> args[j+1] - newVar := args[j] + "=" + args[j+1] + "" - gotOne := false - for i, envVar := range b.RunConfig.Env { - envParts := strings.SplitN(envVar, "=", 2) - if envParts[0] == args[j] { - b.RunConfig.Env[i] = newVar - b.Env = append([]string{newVar}, b.Env...) - gotOne = true - break - } - } - if !gotOne { - b.RunConfig.Env = append(b.RunConfig.Env, newVar) - b.Env = append([]string{newVar}, b.Env...) - } + newVar := []string{args[j] + "=" + args[j+1]} + b.RunConfig.Env = mergeEnv(b.RunConfig.Env, newVar) + b.Env = mergeEnv(b.Env, newVar) j++ } @@ -153,7 +141,7 @@ func add(b *Builder, args []string, attributes map[string]bool, flagArgs []strin var chown string last := len(args) - 1 dest := makeAbsolute(args[last], b.RunConfig.WorkingDir) - userArgs := makeUserArgs(b.Env, b.Args) + userArgs := mergeEnv(envMapAsSlice(b.Args), b.Env) for _, a := range flagArgs { arg, err := ProcessWord(a, userArgs) if err != nil { @@ -182,7 +170,7 @@ func dispatchCopy(b *Builder, args []string, attributes map[string]bool, flagArg dest := makeAbsolute(args[last], b.RunConfig.WorkingDir) var chown string var from string - userArgs := makeUserArgs(b.Env, b.Args) + userArgs := mergeEnv(envMapAsSlice(b.Args), b.Env) for _, a := range flagArgs { arg, err := ProcessWord(a, userArgs) if err != nil { diff --git a/vendor/github.com/openshift/imagebuilder/dockerfile/parser/parser.go b/vendor/github.com/openshift/imagebuilder/dockerfile/parser/parser.go index 0223963e1..b3f4ff4f6 100644 --- a/vendor/github.com/openshift/imagebuilder/dockerfile/parser/parser.go +++ b/vendor/github.com/openshift/imagebuilder/dockerfile/parser/parser.go @@ -12,8 +12,8 @@ import ( "strings" "unicode" - "github.com/openshift/imagebuilder/dockerfile/command" "github.com/docker/docker/pkg/system" + "github.com/openshift/imagebuilder/dockerfile/command" "github.com/pkg/errors" ) @@ -37,7 +37,7 @@ type Node struct { Original string // original line used before parsing Flags []string // only top Node should have this set StartLine int // the line in the original dockerfile where the node begins - endLine int // the line in the original dockerfile where the node ends + EndLine int // the line in the original dockerfile where the node ends } // Dump dumps the AST defined by `node` as a list of sexps. @@ -67,7 +67,7 @@ func (node *Node) Dump() string { func (node *Node) lines(start, end int) { node.StartLine = start - node.endLine = end + node.EndLine = end } // AddChild adds a new child node, and updates line information @@ -76,7 +76,7 @@ func (node *Node) AddChild(child *Node, startLine, endLine int) { if node.StartLine < 0 { node.StartLine = startLine } - node.endLine = endLine + node.EndLine = endLine node.Children = append(node.Children, child) } diff --git a/vendor/github.com/openshift/imagebuilder/imagebuilder.spec b/vendor/github.com/openshift/imagebuilder/imagebuilder.spec index b8680bd10..89951fcec 100644 --- a/vendor/github.com/openshift/imagebuilder/imagebuilder.spec +++ b/vendor/github.com/openshift/imagebuilder/imagebuilder.spec @@ -12,7 +12,7 @@ # %global golang_version 1.8.1 -%{!?version: %global version 1.1.6} +%{!?version: %global version 1.1.8} %{!?release: %global release 1} %global package_name imagebuilder %global product_name Container Image Builder diff --git a/vendor/github.com/openshift/imagebuilder/internals.go b/vendor/github.com/openshift/imagebuilder/internals.go index 5dc174bf7..f33dc70bb 100644 --- a/vendor/github.com/openshift/imagebuilder/internals.go +++ b/vendor/github.com/openshift/imagebuilder/internals.go @@ -93,27 +93,28 @@ func parseOptInterval(f *flag.Flag) (time.Duration, error) { return d, nil } -// makeUserArgs - Package the variables from the Dockerfile defined by -// the ENV aand the ARG statements into one slice so the values -// defined by both can later be evaluated when resolving variables -// such as ${MY_USER}. If the variable is defined by both ARG and ENV -// don't include the definition of the ARG variable. -func makeUserArgs(bEnv []string, bArgs map[string]string) (userArgs []string) { - - userArgs = bEnv - envMap := make(map[string]string) - for _, envVal := range bEnv { - val := strings.SplitN(envVal, "=", 2) - if len(val) > 1 { - envMap[val[0]] = val[1] +// mergeEnv merges two lists of environment variables, avoiding duplicates. +func mergeEnv(defaults, overrides []string) []string { + s := make([]string, 0, len(defaults)+len(overrides)) + index := make(map[string]int) + for _, envSpec := range append(defaults, overrides...) { + envVar := strings.SplitN(envSpec, "=", 2) + if i, ok := index[envVar[0]]; ok { + s[i] = envSpec + continue } + s = append(s, envSpec) + index[envVar[0]] = len(s) - 1 } + return s +} - for key, value := range bArgs { - if _, ok := envMap[key]; ok { - continue - } - userArgs = append(userArgs, key+"="+value) +// envMapAsSlice returns the contents of a map[string]string as a slice of keys +// and values joined with "=". +func envMapAsSlice(m map[string]string) []string { + s := make([]string, 0, len(m)) + for k, v := range m { + s = append(s, k+"="+v) } - return userArgs + return s } diff --git a/vendor/github.com/openshift/imagebuilder/shell_parser.go b/vendor/github.com/openshift/imagebuilder/shell_parser.go index 65f1db6dc..5c461a34a 100644 --- a/vendor/github.com/openshift/imagebuilder/shell_parser.go +++ b/vendor/github.com/openshift/imagebuilder/shell_parser.go @@ -7,6 +7,7 @@ package imagebuilder // be added by adding code to the "special ${} format processing" section import ( + "errors" "fmt" "strings" "text/scanner" @@ -119,7 +120,7 @@ func (sw *shellWord) processStopOn(stopChar rune) (string, []string, error) { if stopChar != scanner.EOF && ch == stopChar { sw.scanner.Next() - break + return result, words.getWords(), nil } if fn, ok := charFuncMapping[ch]; ok { // Call special processing func for certain chars @@ -156,6 +157,10 @@ func (sw *shellWord) processStopOn(stopChar rune) (string, []string, error) { } } + if stopChar != scanner.EOF { + return "", []string{}, fmt.Errorf("unexpected end of statement while looking for matching %s", string(stopChar)) + } + return result, words.getWords(), nil } @@ -168,9 +173,12 @@ func (sw *shellWord) processSingleQuote() (string, error) { for { ch := sw.scanner.Next() - if ch == '\'' || ch == scanner.EOF { + if ch == '\'' { break } + if ch == scanner.EOF { + return "", errors.New("unexpected end of statement while looking for matching single-quote") + } result += string(ch) } @@ -184,12 +192,15 @@ func (sw *shellWord) processDoubleQuote() (string, error) { sw.scanner.Next() - for sw.scanner.Peek() != scanner.EOF { + for { ch := sw.scanner.Peek() if ch == '"' { sw.scanner.Next() break } + if ch == scanner.EOF { + return "", errors.New("unexpected end of statement while looking for matching double-quote") + } if ch == '$' { tmp, err := sw.processDollar() if err != nil { @@ -206,8 +217,8 @@ func (sw *shellWord) processDoubleQuote() (string, error) { continue } - if chNext == '"' || chNext == '$' { - // \" and \$ can be escaped, all other \'s are left as-is + if chNext == '"' || chNext == '$' || chNext == '\\' { + // \" and \$ and \\ can be escaped, all other \'s are left as-is ch = sw.scanner.Next() } } diff --git a/vendor/github.com/spf13/cobra/go.mod b/vendor/github.com/spf13/cobra/go.mod index 18ff78048..57e3244d5 100644 --- a/vendor/github.com/spf13/cobra/go.mod +++ b/vendor/github.com/spf13/cobra/go.mod @@ -8,5 +8,5 @@ require ( github.com/mitchellh/go-homedir v1.1.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.7.0 - gopkg.in/yaml.v2 v2.3.0 + gopkg.in/yaml.v2 v2.2.8 ) diff --git a/vendor/github.com/spf13/cobra/go.sum b/vendor/github.com/spf13/cobra/go.sum index e8b69c60b..0aae73863 100644 --- a/vendor/github.com/spf13/cobra/go.sum +++ b/vendor/github.com/spf13/cobra/go.sum @@ -304,8 +304,8 @@ gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/vendor/modules.txt b/vendor/modules.txt index 2f8a79aa8..73c7402c9 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.16.4 +# github.com/containers/buildah v1.16.5 github.com/containers/buildah github.com/containers/buildah/bind github.com/containers/buildah/chroot @@ -86,7 +86,7 @@ github.com/containers/buildah/pkg/secrets github.com/containers/buildah/pkg/supplemented github.com/containers/buildah/pkg/umask github.com/containers/buildah/util -# github.com/containers/common v0.26.0 +# github.com/containers/common v0.26.3 github.com/containers/common/pkg/apparmor github.com/containers/common/pkg/apparmor/internal/supported github.com/containers/common/pkg/auth @@ -94,13 +94,15 @@ github.com/containers/common/pkg/capabilities github.com/containers/common/pkg/cgroupv2 github.com/containers/common/pkg/completion github.com/containers/common/pkg/config +github.com/containers/common/pkg/report +github.com/containers/common/pkg/report/camelcase github.com/containers/common/pkg/retry github.com/containers/common/pkg/seccomp github.com/containers/common/pkg/sysinfo github.com/containers/common/version # github.com/containers/conmon v2.0.20+incompatible github.com/containers/conmon/runner/config -# github.com/containers/image/v5 v5.6.0 +# github.com/containers/image/v5 v5.7.0 github.com/containers/image/v5/copy github.com/containers/image/v5/directory github.com/containers/image/v5/directory/explicitfilepath @@ -115,6 +117,7 @@ github.com/containers/image/v5/image github.com/containers/image/v5/internal/iolimits github.com/containers/image/v5/internal/pkg/keyctl github.com/containers/image/v5/internal/pkg/platform +github.com/containers/image/v5/internal/rootless github.com/containers/image/v5/internal/tmpdir github.com/containers/image/v5/internal/uploadreader github.com/containers/image/v5/manifest @@ -162,7 +165,7 @@ github.com/containers/psgo/internal/dev github.com/containers/psgo/internal/host github.com/containers/psgo/internal/proc github.com/containers/psgo/internal/process -# github.com/containers/storage v1.23.5 +# github.com/containers/storage v1.23.7 github.com/containers/storage github.com/containers/storage/drivers github.com/containers/storage/drivers/aufs @@ -330,7 +333,7 @@ github.com/inconshreveable/mousetrap github.com/ishidawataru/sctp # github.com/json-iterator/go v1.1.10 github.com/json-iterator/go -# github.com/klauspost/compress v1.11.0 +# github.com/klauspost/compress v1.11.1 github.com/klauspost/compress/flate github.com/klauspost/compress/fse github.com/klauspost/compress/huff0 @@ -349,7 +352,7 @@ github.com/matttproud/golang_protobuf_extensions/pbutil github.com/mistifyio/go-zfs # github.com/moby/sys/mount v0.1.1 github.com/moby/sys/mount -# github.com/moby/sys/mountinfo v0.2.0 +# github.com/moby/sys/mountinfo v0.4.0 github.com/moby/sys/mountinfo # github.com/moby/term v0.0.0-20200915141129-7f0af18e79f2 github.com/moby/term @@ -439,7 +442,7 @@ github.com/opencontainers/runtime-tools/validate github.com/opencontainers/selinux/go-selinux github.com/opencontainers/selinux/go-selinux/label github.com/opencontainers/selinux/pkg/pwalk -# github.com/openshift/imagebuilder v1.1.6 +# github.com/openshift/imagebuilder v1.1.8 github.com/openshift/imagebuilder github.com/openshift/imagebuilder/dockerfile/command github.com/openshift/imagebuilder/dockerfile/parser @@ -494,7 +497,7 @@ github.com/seccomp/libseccomp-golang # github.com/sirupsen/logrus v1.7.0 github.com/sirupsen/logrus github.com/sirupsen/logrus/hooks/syslog -# github.com/spf13/cobra v1.1.0 +# github.com/spf13/cobra v1.1.1 github.com/spf13/cobra # github.com/spf13/pflag v1.0.5 github.com/spf13/pflag @@ -701,7 +704,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.19.2 +# k8s.io/apimachinery v0.19.3 k8s.io/apimachinery/pkg/api/errors k8s.io/apimachinery/pkg/api/resource k8s.io/apimachinery/pkg/apis/meta/v1 |