diff options
43 files changed, 904 insertions, 213 deletions
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go index 6200592b4..a969e17e9 100644 --- a/cmd/podman/common/create.go +++ b/cmd/podman/common/create.go @@ -15,6 +15,18 @@ const sizeWithUnitFormat = "(format: `<number>[<unit>]`, where unit = b (bytes), var containerConfig = registry.PodmanConfig() +// ContainerToPodOptions takes the Container and Pod Create options, assigning the matching values back to podCreate for the purpose of the libpod API +// For this function to succeed, the JSON tags in PodCreateOptions and ContainerCreateOptions need to match due to the Marshaling and Unmarshaling done. +// The types of the options also need to match or else the unmarshaling will fail even if the tags match +func ContainerToPodOptions(containerCreate *entities.ContainerCreateOptions, podCreate *entities.PodCreateOptions) error { + contMarshal, err := json.Marshal(containerCreate) + if err != nil { + return err + } + return json.Unmarshal(contMarshal, podCreate) +} + +// DefineCreateFlags declares and instantiates the container create flags func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, isInfra bool) { createFlags := cmd.Flags() @@ -144,14 +156,6 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, ) _ = cmd.RegisterFlagCompletionFunc(cpusetMemsFlagName, completion.AutocompleteNone) - deviceFlagName := "device" - createFlags.StringSliceVar( - &cf.Devices, - deviceFlagName, devices(), - "Add a host device to the container", - ) - _ = cmd.RegisterFlagCompletionFunc(deviceFlagName, completion.AutocompleteDefault) - deviceCgroupRuleFlagName := "device-cgroup-rule" createFlags.StringSliceVar( &cf.DeviceCGroupRule, @@ -865,4 +869,11 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, volumeDesciption, ) _ = cmd.RegisterFlagCompletionFunc(volumeFlagName, AutocompleteVolumeFlag) + deviceFlagName := "device" + createFlags.StringSliceVar( + &cf.Devices, + deviceFlagName, devices(), + "Add a host device to the container", + ) + _ = cmd.RegisterFlagCompletionFunc(deviceFlagName, completion.AutocompleteDefault) } diff --git a/cmd/podman/common/create_test.go b/cmd/podman/common/create_test.go new file mode 100644 index 000000000..17b47dd16 --- /dev/null +++ b/cmd/podman/common/create_test.go @@ -0,0 +1,53 @@ +package common_test + +import ( + "reflect" + "strings" + "testing" + + "github.com/containers/podman/v3/cmd/podman/common" + "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/stretchr/testify/assert" +) + +func TestPodOptions(t *testing.T) { + entry := "/test1" + exampleOptions := entities.ContainerCreateOptions{CPUS: 5.5, CPUSetCPUs: "0-4", Entrypoint: &entry, Hostname: "foo", Name: "testing123", Volume: []string{"/fakeVol1", "/fakeVol2"}, Net: &entities.NetOptions{CNINetworks: []string{"FakeNetwork"}}, PID: "ns:/proc/self/ns"} + + podOptions := entities.PodCreateOptions{} + err := common.ContainerToPodOptions(&exampleOptions, &podOptions) + assert.Nil(t, err) + + cc := reflect.ValueOf(&exampleOptions).Elem() + pc := reflect.ValueOf(&podOptions).Elem() + + pcType := reflect.TypeOf(podOptions) + for i := 0; i < pc.NumField(); i++ { + podField := pc.FieldByIndex([]int{i}) + podType := pcType.Field(i) + for j := 0; j < cc.NumField(); j++ { + containerField := cc.FieldByIndex([]int{j}) + containerType := reflect.TypeOf(exampleOptions).Field(j) + tagPod := strings.Split(string(podType.Tag.Get("json")), ",")[0] + tagContainer := strings.Split(string(containerType.Tag.Get("json")), ",")[0] + if tagPod == tagContainer && (tagPod != "" && tagContainer != "") { + areEqual := true + if containerField.Kind() == podField.Kind() { + switch containerField.Kind() { + case reflect.Slice: + for i, w := range containerField.Interface().([]string) { + areEqual = podField.Interface().([]string)[i] == w + } + case reflect.String: + areEqual = (podField.String() == containerField.String()) + case reflect.Bool: + areEqual = (podField.Bool() == containerField.Bool()) + case reflect.Ptr: + areEqual = (reflect.DeepEqual(podField.Elem().Interface(), containerField.Elem().Interface())) + } + } + assert.True(t, areEqual) + } + } + } +} diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go index ff792b78b..afb8edd91 100644 --- a/cmd/podman/containers/ps.go +++ b/cmd/podman/containers/ps.go @@ -221,7 +221,10 @@ func ps(cmd *cobra.Command, _ []string) error { } hdrs, format := createPsOut() + + noHeading, _ := cmd.Flags().GetBool("noheading") if cmd.Flags().Changed("format") { + noHeading = noHeading || !report.HasTable(listOpts.Format) format = report.NormalizeFormat(listOpts.Format) format = report.EnforceRange(format) } @@ -240,8 +243,7 @@ func ps(cmd *cobra.Command, _ []string) error { defer w.Flush() headers := func() error { return nil } - noHeading, _ := cmd.Flags().GetBool("noheading") - if !(noHeading || listOpts.Quiet || cmd.Flags().Changed("format")) { + if !noHeading { headers = func() error { return tmpl.Execute(w, hdrs) } @@ -298,9 +300,11 @@ func createPsOut() ([]map[string]string, string) { "IPC": "ipc", "MNT": "mnt", "NET": "net", + "Networks": "networks", "PIDNS": "pidns", "Pod": "pod id", "PodName": "podname", // undo camelcase space break + "RunningFor": "running for", "UTS": "uts", "User": "userns", }) diff --git a/cmd/podman/images/inspect.go b/cmd/podman/images/inspect.go index 35c173a60..dd8cf8056 100644 --- a/cmd/podman/images/inspect.go +++ b/cmd/podman/images/inspect.go @@ -17,9 +17,9 @@ var ( Long: `Displays the low-level information of an image identified by name or ID.`, RunE: inspectExec, ValidArgsFunction: common.AutocompleteImages, - Example: `podman inspect alpine - podman inspect --format "imageId: {{.Id}} size: {{.Size}}" alpine - podman inspect --format "image: {{.ImageName}} driver: {{.Driver}}" myctr`, + Example: `podman image inspect alpine + podman image inspect --format "imageId: {{.Id}} size: {{.Size}}" alpine + podman image inspect --format "image: {{.ImageName}} driver: {{.Driver}}" myctr`, } inspectOpts *entities.InspectOptions ) diff --git a/cmd/podman/machine/init.go b/cmd/podman/machine/init.go index ec44a707d..19f31d1a6 100644 --- a/cmd/podman/machine/init.go +++ b/cmd/podman/machine/init.go @@ -3,6 +3,8 @@ package machine import ( + "fmt" + "github.com/containers/common/pkg/completion" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/pkg/machine" @@ -26,6 +28,7 @@ var ( var ( initOpts = machine.InitOptions{} defaultMachineName = "podman-machine-default" + now bool ) func init() { @@ -61,6 +64,12 @@ func init() { ) _ = initCmd.RegisterFlagCompletionFunc(memoryFlagName, completion.AutocompleteNone) + flags.BoolVar( + &now, + "now", false, + "Start machine now", + ) + ImagePathFlagName := "image-path" flags.StringVar(&initOpts.ImagePath, ImagePathFlagName, cfg.Engine.MachineImage, "Path to qcow image") _ = initCmd.RegisterFlagCompletionFunc(ImagePathFlagName, completion.AutocompleteDefault) @@ -91,5 +100,15 @@ func initMachine(cmd *cobra.Command, args []string) error { if err != nil { return err } - return vm.Init(initOpts) + err = vm.Init(initOpts) + if err != nil { + return err + } + if now { + err = vm.Start(initOpts.Name, machine.StartOptions{}) + if err == nil { + fmt.Printf("Machine %q started successfully\n", initOpts.Name) + } + } + return err } diff --git a/cmd/podman/networks/inspect.go b/cmd/podman/networks/inspect.go index c0e5b9720..4f3e86fc9 100644 --- a/cmd/podman/networks/inspect.go +++ b/cmd/podman/networks/inspect.go @@ -12,7 +12,7 @@ var ( networkinspectDescription = `Inspect network` networkinspectCommand = &cobra.Command{ Use: "inspect [options] NETWORK [NETWORK...]", - Short: "network inspect", + Short: "Displays the raw CNI network configuration for one or more networks.", Long: networkinspectDescription, RunE: networkInspect, Example: `podman network inspect podman`, diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go index 7000c92c8..ca73a8356 100644 --- a/cmd/podman/pods/create.go +++ b/cmd/podman/pods/create.go @@ -132,7 +132,6 @@ func create(cmd *cobra.Command, args []string) error { createOptions.Share = nil } else { // reassign certain optios for lbpod api, these need to be populated in spec - MapOptions() flags := cmd.Flags() infraOptions.Net, err = common.NetFlagsToNetOptions(nil, *flags, false) if err != nil { @@ -142,13 +141,11 @@ func create(cmd *cobra.Command, args []string) error { if err != nil { return err } - createOptions.Net = infraOptions.Net createOptions.Share = strings.Split(share, ",") if cmd.Flag("infra-command").Changed { // Only send content to server side if user changed defaults cmdIn, err := cmd.Flags().GetString("infra-command") infraOptions.Entrypoint = &cmdIn - createOptions.InfraCommand = cmdIn if err != nil { return err } @@ -161,6 +158,10 @@ func create(cmd *cobra.Command, args []string) error { return err } } + err = common.ContainerToPodOptions(&infraOptions, &createOptions) + if err != nil { + return err + } } if cmd.Flag("pod-id-file").Changed { @@ -196,8 +197,8 @@ func create(cmd *cobra.Command, args []string) error { if createOptions.Cpus > float64(numCPU) { createOptions.Cpus = float64(numCPU) } - copy := createOptions.CpusetCpus - cpuSet := createOptions.Cpus + copy := infraOptions.CPUSetCPUs + cpuSet := infraOptions.CPUS if cpuSet == 0 { cpuSet = float64(sysinfo.NumCPU()) } @@ -217,10 +218,10 @@ func create(cmd *cobra.Command, args []string) error { if core > int(cpuSet) { if copy == "" { copy = "0-" + strconv.Itoa(int(cpuSet)) - createOptions.CpusetCpus = copy + infraOptions.CPUSetCPUs = copy break } else { - createOptions.CpusetCpus = copy + infraOptions.CPUSetCPUs = copy break } } else if ind != 0 { @@ -229,6 +230,8 @@ func create(cmd *cobra.Command, args []string) error { copy = "" + strconv.Itoa(core) } } + createOptions.Cpus = infraOptions.CPUS + createOptions.CpusetCpus = infraOptions.CPUSetCPUs podSpec := specgen.NewPodSpecGenerator() podSpec, err = entities.ToPodSpecGen(*podSpec, &createOptions) if err != nil { @@ -248,11 +251,8 @@ func create(cmd *cobra.Command, args []string) error { } podSpec.InfraImage = imageName if infraOptions.Entrypoint != nil { - createOptions.InfraCommand = *infraOptions.Entrypoint + createOptions.InfraCommand = infraOptions.Entrypoint } - infraOptions.CPUS = createOptions.Cpus - infraOptions.CPUSetCPUs = createOptions.CpusetCpus - infraOptions.PID = createOptions.Pid podSpec.InfraContainerSpec = specgen.NewSpecGenerator(imageName, false) podSpec.InfraContainerSpec.RawImageName = rawImageName podSpec.InfraContainerSpec.NetworkOptions = podSpec.NetworkOptions @@ -290,13 +290,3 @@ func replacePod(name string) error { } return removePods([]string{name}, rmOptions, false) } - -func MapOptions() { - createOptions.Cpus = infraOptions.CPUS - createOptions.CpusetCpus = infraOptions.CPUSetCPUs - createOptions.Hostname = infraOptions.Hostname - createOptions.InfraConmonPidFile = infraOptions.ConmonPIDFile - createOptions.InfraName = infraOptions.Name - createOptions.Pid = infraOptions.PID - createOptions.Volume = infraOptions.Volume -} diff --git a/cmd/podman/system/service.go b/cmd/podman/system/service.go index a30f43839..d6fe8837b 100644 --- a/cmd/podman/system/service.go +++ b/cmd/podman/system/service.go @@ -52,8 +52,9 @@ func init() { flags := srvCmd.Flags() + cfg := registry.PodmanConfig() timeFlagName := "time" - flags.Int64VarP(&srvArgs.Timeout, timeFlagName, "t", 5, "Time until the service session expires in seconds. Use 0 to disable the timeout") + flags.Int64VarP(&srvArgs.Timeout, timeFlagName, "t", int64(cfg.Engine.ServiceTimeout), "Time until the service session expires in seconds. Use 0 to disable the timeout") _ = srvCmd.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone) flags.StringVarP(&srvArgs.CorsHeaders, "cors", "", "", "Set CORS Headers") _ = srvCmd.RegisterFlagCompletionFunc("cors", completion.AutocompleteNone) diff --git a/docs/source/markdown/links/podman-container-inspect.1 b/docs/source/markdown/links/podman-container-inspect.1 deleted file mode 100644 index 261043845..000000000 --- a/docs/source/markdown/links/podman-container-inspect.1 +++ /dev/null @@ -1 +0,0 @@ -.so man1/podman-inspect.1 diff --git a/docs/source/markdown/links/podman-image-inspect.1 b/docs/source/markdown/links/podman-image-inspect.1 deleted file mode 100644 index 261043845..000000000 --- a/docs/source/markdown/links/podman-image-inspect.1 +++ /dev/null @@ -1 +0,0 @@ -.so man1/podman-inspect.1 diff --git a/docs/source/markdown/podman-container-inspect.1.md b/docs/source/markdown/podman-container-inspect.1.md new file mode 100644 index 000000000..72b7cef3b --- /dev/null +++ b/docs/source/markdown/podman-container-inspect.1.md @@ -0,0 +1,318 @@ +% podman-container-inspect(1) + +## NAME +podman\-container\-inspect - Display a container's configuration + +## SYNOPSIS +**podman container inspect** [*options*] *container* [*container* ...] + +## DESCRIPTION + +This displays the low-level information on containers identified by name or ID. By default, this will render +all results in a JSON array. If a format is specified, the given template will be executed for each result. + +## OPTIONS + +#### **--format**, **-f**=*format* + +Format the output using the given Go template. +The keys of the returned JSON can be used as the values for the --format flag (see examples below). + +#### **--latest**, **-l** + +Instead of providing the container name or ID, use the last created container. If you use methods other than Podman +to run containers such as CRI-O, the last started container could be from either of those methods. + +(This option is not available with the remote Podman client.) + +#### **--size**, **-s** + +In addition to normal output, display the total file size if the type is a container. + + +## EXAMPLE + +``` +$ podman container inspect foobar +[ + { + "Id": "99f66530fe9c7249f7cf29f78e8661669d5831cbe4ee80ea757d5e922dd6a8a6", + "Created": "2021-09-16T06:09:08.936623325-04:00", + "Path": "echo", + "Args": [ + "hi" + ], + "State": { + "OciVersion": "1.0.2-dev", + "Status": "exited", + "Running": false, + "Paused": false, + "Restarting": false, + "OOMKilled": false, + "Dead": false, + "Pid": 0, + "ExitCode": 0, + "Error": "", + "StartedAt": "2021-09-16T06:09:09.033564436-04:00", + "FinishedAt": "2021-09-16T06:09:09.036184314-04:00", + "Healthcheck": { + "Status": "", + "FailingStreak": 0, + "Log": null + } + }, + "Image": "14119a10abf4669e8cdbdff324a9f9605d99697215a0d21c360fe8dfa8471bab", + "ImageName": "docker.io/library/alpine:latest", + "Rootfs": "", + "Pod": "", + "ResolvConfPath": "/run/user/3267/containers/overlay-containers/99f66530fe9c7249f7cf29f78e8661669d5831cbe4ee80ea757d5e922dd6a8a6/userdata/resolv.conf", + "HostnamePath": "/run/user/3267/containers/overlay-containers/99f66530fe9c7249f7cf29f78e8661669d5831cbe4ee80ea757d5e922dd6a8a6/userdata/hostname", + "HostsPath": "/run/user/3267/containers/overlay-containers/99f66530fe9c7249f7cf29f78e8661669d5831cbe4ee80ea757d5e922dd6a8a6/userdata/hosts", + "StaticDir": "/home/dwalsh/.local/share/containers/storage/overlay-containers/99f66530fe9c7249f7cf29f78e8661669d5831cbe4ee80ea757d5e922dd6a8a6/userdata", + "OCIConfigPath": "/home/dwalsh/.local/share/containers/storage/overlay-containers/99f66530fe9c7249f7cf29f78e8661669d5831cbe4ee80ea757d5e922dd6a8a6/userdata/config.json", + "OCIRuntime": "crun", + "ConmonPidFile": "/run/user/3267/containers/overlay-containers/99f66530fe9c7249f7cf29f78e8661669d5831cbe4ee80ea757d5e922dd6a8a6/userdata/conmon.pid", + "PidFile": "/run/user/3267/containers/overlay-containers/99f66530fe9c7249f7cf29f78e8661669d5831cbe4ee80ea757d5e922dd6a8a6/userdata/pidfile", + "Name": "foobar", + "RestartCount": 0, + "Driver": "overlay", + "MountLabel": "system_u:object_r:container_file_t:s0:c25,c695", + "ProcessLabel": "system_u:system_r:container_t:s0:c25,c695", + "AppArmorProfile": "", + "EffectiveCaps": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FOWNER", + "CAP_FSETID", + "CAP_KILL", + "CAP_NET_BIND_SERVICE", + "CAP_SETFCAP", + "CAP_SETGID", + "CAP_SETPCAP", + "CAP_SETUID", + "CAP_SYS_CHROOT" + ], + "BoundingCaps": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FOWNER", + "CAP_FSETID", + "CAP_KILL", + "CAP_NET_BIND_SERVICE", + "CAP_SETFCAP", + "CAP_SETGID", + "CAP_SETPCAP", + "CAP_SETUID", + "CAP_SYS_CHROOT" + ], + "ExecIDs": [], + "GraphDriver": { + "Name": "overlay", + "Data": { + "LowerDir": "/home/dwalsh/.local/share/containers/storage/overlay/e2eb06d8af8218cfec8210147357a68b7e13f7c485b991c288c2d01dc228bb68/diff", + "UpperDir": "/home/dwalsh/.local/share/containers/storage/overlay/8f3d70434a3db17410ec4710caf4f251f3e4ed0a96a08124e4b3d4af0a0ea300/diff", + "WorkDir": "/home/dwalsh/.local/share/containers/storage/overlay/8f3d70434a3db17410ec4710caf4f251f3e4ed0a96a08124e4b3d4af0a0ea300/work" + } + }, + "Mounts": [], + "Dependencies": [], + "NetworkSettings": { + "EndpointID": "", + "Gateway": "", + "IPAddress": "", + "IPPrefixLen": 0, + "IPv6Gateway": "", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "MacAddress": "", + "Bridge": "", + "SandboxID": "", + "HairpinMode": false, + "LinkLocalIPv6Address": "", + "LinkLocalIPv6PrefixLen": 0, + "Ports": {}, + "SandboxKey": "" + }, + "ExitCommand": [ + "/usr/bin/podman", + "--root", + "/home/dwalsh/.local/share/containers/storage", + "--runroot", + "/run/user/3267/containers", + "--log-level", + "warning", + "--cgroup-manager", + "systemd", + "--tmpdir", + "/run/user/3267/libpod/tmp", + "--runtime", + "crun", + "--storage-driver", + "overlay", + "--events-backend", + "journald", + "container", + "cleanup", + "99f66530fe9c7249f7cf29f78e8661669d5831cbe4ee80ea757d5e922dd6a8a6" + ], + "Namespace": "", + "IsInfra": false, + "Config": { + "Hostname": "99f66530fe9c", + "Domainname": "", + "User": "", + "AttachStdin": false, + "AttachStdout": false, + "AttachStderr": false, + "Tty": false, + "OpenStdin": false, + "StdinOnce": false, + "Env": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "TERM=xterm", + "container=podman", + "HOME=/root", + "HOSTNAME=99f66530fe9c" + ], + "Cmd": [ + "echo", + "hi" + ], + "Image": "docker.io/library/alpine:latest", + "Volumes": null, + "WorkingDir": "/", + "Entrypoint": "", + "OnBuild": null, + "Labels": null, + "Annotations": { + "io.container.manager": "libpod", + "io.kubernetes.cri-o.Created": "2021-09-16T06:09:08.936623325-04:00", + "io.kubernetes.cri-o.TTY": "false", + "io.podman.annotations.autoremove": "FALSE", + "io.podman.annotations.init": "FALSE", + "io.podman.annotations.privileged": "FALSE", + "io.podman.annotations.publish-all": "FALSE", + "org.opencontainers.image.stopSignal": "15" + }, + "StopSignal": 15, + "CreateCommand": [ + "podman", + "run", + "--name", + "foobar", + "alpine", + "echo", + "hi" + ], + "Timezone": "local", + "Umask": "0022", + "Timeout": 0, + "StopTimeout": 10 + }, + "HostConfig": { + "Binds": [], + "CgroupManager": "systemd", + "CgroupMode": "private", + "ContainerIDFile": "", + "LogConfig": { + "Type": "journald", + "Config": null, + "Path": "", + "Tag": "", + "Size": "0B" + }, + "NetworkMode": "slirp4netns", + "PortBindings": {}, + "RestartPolicy": { + "Name": "", + "MaximumRetryCount": 0 + }, + "AutoRemove": false, + "VolumeDriver": "", + "VolumesFrom": null, + "CapAdd": [], + "CapDrop": [ + "CAP_AUDIT_WRITE", + "CAP_MKNOD", + "CAP_NET_RAW" + ], + "Dns": [], + "DnsOptions": [], + "DnsSearch": [], + "ExtraHosts": [], + "GroupAdd": [], + "IpcMode": "private", + "Cgroup": "", + "Cgroups": "default", + "Links": null, + "OomScoreAdj": 0, + "PidMode": "private", + "Privileged": false, + "PublishAllPorts": false, + "ReadonlyRootfs": false, + "SecurityOpt": [], + "Tmpfs": {}, + "UTSMode": "private", + "UsernsMode": "", + "ShmSize": 65536000, + "Runtime": "oci", + "ConsoleSize": [ + 0, + 0 + ], + "Isolation": "", + "CpuShares": 0, + "Memory": 0, + "NanoCpus": 0, + "CgroupParent": "user.slice", + "BlkioWeight": 0, + "BlkioWeightDevice": null, + "BlkioDeviceReadBps": null, + "BlkioDeviceWriteBps": null, + "BlkioDeviceReadIOps": null, + "BlkioDeviceWriteIOps": null, + "CpuPeriod": 0, + "CpuQuota": 0, + "CpuRealtimePeriod": 0, + "CpuRealtimeRuntime": 0, + "CpusetCpus": "", + "CpusetMems": "", + "Devices": [], + "DiskQuota": 0, + "KernelMemory": 0, + "MemoryReservation": 0, + "MemorySwap": 0, + "MemorySwappiness": 0, + "OomKillDisable": false, + "PidsLimit": 2048, + "Ulimits": [], + "CpuCount": 0, + "CpuPercent": 0, + "IOMaximumIOps": 0, + "IOMaximumBandwidth": 0, + "CgroupConf": null + } + } +] +``` + +``` +$ podman container inspect nervous_fermi --format "{{.ImageName}}" +registry.access.redhat.com/ubi8:latest +``` + +``` +$ podman container inspect foobar --format "{{.GraphDriver.Name}}" +overlay +``` + +``` +$ podman container inspect --latest --format {{.EffectiveCaps}} +[CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER CAP_FSETID CAP_KILL CAP_NET_BIND_SERVICE CAP_SETFCAP CAP_SETGID CAP_SETPCAP CAP_SETUID CAP_SYS_CHROOT] +``` + +## SEE ALSO +**[podman(1)](podman.1.md)**,**[podman-container(1)](podman-container.1.md)**, **[podman-inspect(1)](podman-inspect.1.md)** + +## HISTORY +Sep 2021, Originally compiled by Dan Walsh <dwalsh@redhat.com> diff --git a/docs/source/markdown/podman-container.1.md b/docs/source/markdown/podman-container.1.md index 3cc90d9ec..c950347a9 100644 --- a/docs/source/markdown/podman-container.1.md +++ b/docs/source/markdown/podman-container.1.md @@ -24,7 +24,7 @@ The container command allows you to manage containers | exists | [podman-container-exists(1)](podman-container-exists.1.md) | Check if a container exists in local storage | | export | [podman-export(1)](podman-export.1.md) | Export a container's filesystem contents as a tar archive. | | init | [podman-init(1)](podman-init.1.md) | Initialize a container | -| inspect | [podman-inspect(1)](podman-inspect.1.md) | Display a container or image's configuration. | +| inspect | [podman-container-inspect(1)](podman-container-inspect.1.md)| Display a container's configuration. | | kill | [podman-kill(1)](podman-kill.1.md) | Kill the main process in one or more containers. | | list | [podman-ps(1)](podman-ps.1.md) | List the containers on the system.(alias ls) | | logs | [podman-logs(1)](podman-logs.1.md) | Display the logs of a container. | diff --git a/docs/source/markdown/podman-image-inspect.1.md b/docs/source/markdown/podman-image-inspect.1.md new file mode 100644 index 000000000..a4f81dfc0 --- /dev/null +++ b/docs/source/markdown/podman-image-inspect.1.md @@ -0,0 +1,105 @@ +% podman-image-inspect(1) + +## NAME +podman\-image\-inspect - Display an image's configuration + +## SYNOPSIS +**podman image inspect** [*options*] *image* [*image* ...] + +## DESCRIPTION + +This displays the low-level information on images identified by name or ID. By default, this will render +all results in a JSON array. If a format is specified, the given template will be executed for each result. + +## OPTIONS + +#### **--format**, **-f**=*format* + +Format the output using the given Go template. +The keys of the returned JSON can be used as the values for the --format flag (see examples below). + +## EXAMPLE + +``` +$ podman image inspect fedora +[ + { + "Id": "37e5619f4a8ca9dbc4d6c0ae7890625674a10dbcfb76201399e2aaddb40da17d", + "Digest": "sha256:1b0d4ddd99b1a8c8a80e885aafe6034c95f266da44ead992aab388e6aa91611a", + "RepoTags": [ + "registry.fedoraproject.org/fedora:latest" + ], + "RepoDigests": [ + "registry.fedoraproject.org/fedora@sha256:1b0d4ddd99b1a8c8a80e885aafe6034c95f266da44ead992aab388e6aa91611a", + "registry.fedoraproject.org/fedora@sha256:b5290db40008aae9272ad3a6bd8070ef7ecd547c3bef014b894c327960acc582" + ], + "Parent": "", + "Comment": "Created by Image Factory", + "Created": "2021-08-09T05:48:47Z", + "Config": { + "Env": [ + "DISTTAG=f34container", + "FGC=f34", + "container=oci" + ], + "Cmd": [ + "/bin/bash" + ], + "Labels": { + "license": "MIT", + "name": "fedora", + "vendor": "Fedora Project", + "version": "34" + } + }, + "Version": "1.10.1", + "Author": "", + "Architecture": "amd64", + "Os": "linux", + "Size": 183852302, + "VirtualSize": 183852302, + "GraphDriver": { + "Name": "overlay", + "Data": { + "UpperDir": "/home/dwalsh/.local/share/containers/storage/overlay/0203e243f1ca4b6bb49371ecd21363212467ec6d7d3fa9f324cd4e78cc6b5fa2/diff", + "WorkDir": "/home/dwalsh/.local/share/containers/storage/overlay/0203e243f1ca4b6bb49371ecd21363212467ec6d7d3fa9f324cd4e78cc6b5fa2/work" + } + }, + "RootFS": { + "Type": "layers", + "Layers": [ + "sha256:0203e243f1ca4b6bb49371ecd21363212467ec6d7d3fa9f324cd4e78cc6b5fa2" + ] + }, + "Labels": { + "license": "MIT", + "name": "fedora", + "vendor": "Fedora Project", + "version": "34" + }, + "Annotations": {}, + "ManifestType": "application/vnd.docker.distribution.manifest.v2+json", + "User": "", + "History": [ + { + "created": "2021-08-09T05:48:47Z", + "comment": "Created by Image Factory" + } + ], + "NamesHistory": [ + "registry.fedoraproject.org/fedora:latest" + ] + } +] +``` + +``` +$ podman image inspect --format '{{ .Id }}' fedora +37e5619f4a8ca9dbc4d6c0ae7890625674a10dbcfb76201399e2aaddb40da17d +``` + +## SEE ALSO +**[podman(1)](podman.1.md)**,**[podman-image(1)](podman-image.1.md)**, **[podman-inspect(1)](podman-inspect.1.md)** + +## HISTORY +Sep 2021, Originally compiled by Dan Walsh <dwalsh@redhat.com> diff --git a/docs/source/markdown/podman-image.1.md b/docs/source/markdown/podman-image.1.md index 3e6050d99..01024519f 100644 --- a/docs/source/markdown/podman-image.1.md +++ b/docs/source/markdown/podman-image.1.md @@ -11,30 +11,30 @@ The image command allows you to manage images ## COMMANDS -| Command | Man Page | Description | -| -------- | ----------------------------------------------- | --------------------------------------------------------------------------- | -| build | [podman-build(1)](podman-build.1.md) | Build a container using a Dockerfile. | -| diff | [podman-image-diff(1)](podman-image-diff.1.md) | Inspect changes on an image's filesystem. | -| exists | [podman-image-exists(1)](podman-image-exists.1.md) | Check if an image exists in local storage. | -| history | [podman-history(1)](podman-history.1.md) | Show the history of an image. | -| import | [podman-import(1)](podman-import.1.md) | Import a tarball and save it as a filesystem image. | -| inspect | [podman-inspect(1)](podman-inspect.1.md) | Display an image or image's configuration. | -| list | [podman-images(1)](podman-images.1.md) | List the container images on the system.(alias ls) | -| load | [podman-load(1)](podman-load.1.md) | Load an image from the docker archive. | -| mount | [podman-image-mount(1)](podman-image-mount.1.md) | Mount an image's root filesystem. | -| prune | [podman-image-prune(1)](podman-image-prune.1.md) | Remove all unused images from the local store. | -| pull | [podman-pull(1)](podman-pull.1.md) | Pull an image from a registry. | -| push | [podman-push(1)](podman-push.1.md) | Push an image from local storage to elsewhere. | -| rm | [podman-rmi(1)](podman-rmi.1.md) | Removes one or more locally stored images. | -| save | [podman-save(1)](podman-save.1.md) | Save an image to docker-archive or oci. | -| scp | [podman-image-scp(1)](podman-image-scp.1.md) | Securely copy an image from one host to another. | -| search | [podman-search(1)](podman-search.1.md) | Search a registry for an image. | -| sign | [podman-image-sign(1)](podman-image-sign.1.md) | Create a signature for an image. | -| tag | [podman-tag(1)](podman-tag.1.md) | Add an additional name to a local image. | -| tree | [podman-image-tree(1)](podman-image-tree.1.md) | Prints layer hierarchy of an image in a tree format. | -| trust | [podman-image-trust(1)](podman-image-trust.1.md) | Manage container registry image trust policy. | -| unmount | [podman-image-unmount(1)](podman-image-unmount.1.md) | Unmount an image's root filesystem. | -| untag | [podman-untag(1)](podman-untag.1.md) | Removes one or more names from a locally-stored image. | +| Command | Man Page | Description | +| -------- | --------------------------------------------------- | ----------------------------------------------------------------------- | +| build | [podman-build(1)](podman-build.1.md) | Build a container using a Dockerfile. | +| diff | [podman-image-diff(1)](podman-image-diff.1.md) | Inspect changes on an image's filesystem. | +| exists | [podman-image-exists(1)](podman-image-exists.1.md) | Check if an image exists in local storage. | +| history | [podman-history(1)](podman-history.1.md) | Show the history of an image. | +| import | [podman-import(1)](podman-import.1.md) | Import a tarball and save it as a filesystem image. | +| inspect | [podman-image-inspect(1)](podman-image-inspect.1.md)| Display an image's configuration. | +| list | [podman-images(1)](podman-images.1.md) | List the container images on the system.(alias ls) | +| load | [podman-load(1)](podman-load.1.md) | Load an image from the docker archive. | +| mount | [podman-image-mount(1)](podman-image-mount.1.md) | Mount an image's root filesystem. | +| prune | [podman-image-prune(1)](podman-image-prune.1.md) | Remove all unused images from the local store. | +| pull | [podman-pull(1)](podman-pull.1.md) | Pull an image from a registry. | +| push | [podman-push(1)](podman-push.1.md) | Push an image from local storage to elsewhere. | +| rm | [podman-rmi(1)](podman-rmi.1.md) | Removes one or more locally stored images. | +| save | [podman-save(1)](podman-save.1.md) | Save an image to docker-archive or oci. | +| scp | [podman-image-scp(1)](podman-image-scp.1.md) | Securely copy an image from one host to another. | +| search | [podman-search(1)](podman-search.1.md) | Search a registry for an image. | +| sign | [podman-image-sign(1)](podman-image-sign.1.md) | Create a signature for an image. | +| tag | [podman-tag(1)](podman-tag.1.md) | Add an additional name to a local image. | +| tree | [podman-image-tree(1)](podman-image-tree.1.md) | Prints layer hierarchy of an image in a tree format. | +| trust | [podman-image-trust(1)](podman-image-trust.1.md) | Manage container registry image trust policy. | +| unmount | [podman-image-unmount(1)](podman-image-unmount.1.md) | Unmount an image's root filesystem. | +| untag | [podman-untag(1)](podman-untag.1.md) | Removes one or more names from a locally-stored image. | ## SEE ALSO podman diff --git a/docs/source/markdown/podman-inspect.1.md b/docs/source/markdown/podman-inspect.1.md index ae26c1bbb..83dc5cbbe 100644 --- a/docs/source/markdown/podman-inspect.1.md +++ b/docs/source/markdown/podman-inspect.1.md @@ -14,6 +14,8 @@ all results in a JSON array. If the inspect type is all, the order of inspection If a format is specified, the given template will be executed for each result. For more inspection options, see also +[podman-container-inspect(1)](podman-container-inspect.1.md), +[podman-image-inspect(1)](podman-image-inspect.1.md), [podman-network-inspect(1)](podman-network-inspect.1.md), [podman-pod-inspect(1)](podman-pod-inspect.1.md), and [podman-volume-inspect(1)](podman-volume-inspect.1.md). @@ -160,7 +162,7 @@ myNetwork ``` ## SEE ALSO -podman(1) +**[podman(1)](podman.1.md)**,**[podman-container-inspect(1)](podman-container-inspect.1.md)**,**[podman-image-inspect(1)](podman-image-inspect.1.md)**,**[podman-network-inspect(1)](podman-network-inspect.1.md)**,**[podman-pod-inspect(1)](podman-pod-inspect.1.md)**,**[podman-volume-inspect(1)](podman-volume-inspect.1.md)**. ## HISTORY July 2017, Originally compiled by Dan Walsh <dwalsh@redhat.com> diff --git a/docs/source/markdown/podman-machine-init.1.md b/docs/source/markdown/podman-machine-init.1.md index c864a87ef..1236db602 100644 --- a/docs/source/markdown/podman-machine-init.1.md +++ b/docs/source/markdown/podman-machine-init.1.md @@ -47,6 +47,10 @@ Defaults to `testing`. Memory (in MB). +#### **--now** + +Start the virtual machine immediately after it has been initialized. + #### **--help** Print usage statement. diff --git a/docs/source/markdown/podman-pod-create.1.md b/docs/source/markdown/podman-pod-create.1.md index b4c3fc2eb..a1540672c 100644 --- a/docs/source/markdown/podman-pod-create.1.md +++ b/docs/source/markdown/podman-pod-create.1.md @@ -38,6 +38,22 @@ Examples of the List Format: 0-4,9 # bits 0, 1, 2, 3, 4, and 9 set 0-2,7,12-14 # bits 0, 1, 2, 7, 12, 13, and 14 set +#### **--device**=_host-device_[**:**_container-device_][**:**_permissions_] + +Add a host device to the pod. Optional *permissions* parameter +can be used to specify device permissions It is a combination of +**r** for read, **w** for write, and **m** for **mknod**(2). + +Example: **--device=/dev/sdc:/dev/xvdc:rwm**. + +Note: if _host_device_ is a symbolic link then it will be resolved first. +The pod will only store the major and minor numbers of the host device. + +Note: the pod implements devices by storing the initial configuration passed by the user and recreating the device on each container added to the pod. + +Podman may load kernel modules required for using the specified +device. The devices that Podman will load modules for when necessary are: +/dev/fuse. #### **--dns**=*ipaddr* diff --git a/docs/source/markdown/podman-pod-inspect.1.md b/docs/source/markdown/podman-pod-inspect.1.md index 1f4e6cb06..0c58b099e 100644 --- a/docs/source/markdown/podman-pod-inspect.1.md +++ b/docs/source/markdown/podman-pod-inspect.1.md @@ -70,7 +70,7 @@ Valid placeholders for the Go template are listed below: ``` ## SEE ALSO -podman-pod(1), podman-pod-ps(1) +**[podman(1)](podman.1.md)**,**[podman-pod(1)](podman-pod.1.md)**, **[podman-inspect(1)](podman-inspect.1.md)** ## HISTORY August 2018, Originally compiled by Brent Baude <bbaude@redhat.com> diff --git a/docs/source/markdown/podman-system-service.1.md b/docs/source/markdown/podman-system-service.1.md index dfb026de1..3bc4fc7f1 100644 --- a/docs/source/markdown/podman-system-service.1.md +++ b/docs/source/markdown/podman-system-service.1.md @@ -30,6 +30,9 @@ Note: The default systemd unit files (system and user) change the log-level opti The time until the session expires in _seconds_. The default is 5 seconds. A value of `0` means no timeout, therefore the session will not expire. +The default timeout can be changed via the `service_timeout=VALUE` field in containers.conf. +See **[containers.conf(5)](https://github.com/containers/common/blob/master/docs/containers.conf.5.md)** for more information. + #### **--cors** CORS headers to inject to the HTTP response. The default value is empty string which disables CORS headers. @@ -46,7 +49,7 @@ podman system service --time 5 ``` ## SEE ALSO -podman(1), podman-system-service(1), podman-system-connection(1) +**[podman(1)](podman.1.md)**, **[podman-system-connection(1)](podman-system-connection.1.md)**, **[containers.conf(5)](https://github.com/containers/common/blob/master/docs/containers.conf.5.md)** ## HISTORY January 2020, Originally compiled by Brent Baude `<bbaude@redhat.com>` diff --git a/docs/source/markdown/podman-volume-inspect.1.md b/docs/source/markdown/podman-volume-inspect.1.md index ea0ee91b4..4595ccda6 100644 --- a/docs/source/markdown/podman-volume-inspect.1.md +++ b/docs/source/markdown/podman-volume-inspect.1.md @@ -40,7 +40,7 @@ $ podman volume inspect --format "{{.Driver}} {{.Scope}}" myvol ``` ## SEE ALSO -podman-volume(1) +**[podman(1)](podman.1.md)**,**[podman-volume(1)](podman-volume.1.md)**, **[podman-inspect(1)](podman-inspect.1.md)** ## HISTORY November 2018, Originally compiled by Urvashi Mohnani <umohnani@redhat.com> diff --git a/libpod/container.go b/libpod/container.go index cf727926c..7d602326e 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -278,6 +278,11 @@ func (c *Container) Config() *ContainerConfig { return returnConfig } +// DeviceHostSrc returns the user supplied device to be passed down in the pod +func (c *Container) DeviceHostSrc() []spec.LinuxDevice { + return c.config.DeviceHostSrc +} + // Runtime returns the container's Runtime. func (c *Container) Runtime() *Runtime { return c.runtime diff --git a/libpod/container_config.go b/libpod/container_config.go index 0374c25fe..54d102a71 100644 --- a/libpod/container_config.go +++ b/libpod/container_config.go @@ -381,6 +381,8 @@ type ContainerMiscConfig struct { PidFile string `json:"pid_file,omitempty"` // CDIDevices contains devices that use the CDI CDIDevices []string `json:"cdiDevices,omitempty"` + // DeviceHostSrc contains the original source on the host + DeviceHostSrc []spec.LinuxDevice `json:"device_host_src,omitempty"` // EnvSecrets are secrets that are set as environment variables EnvSecrets map[string]*secrets.Secret `json:"secret_env,omitempty"` // InitContainerType specifies if the container is an initcontainer diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go index 530160b2d..e65c86cef 100644 --- a/libpod/container_inspect.go +++ b/libpod/container_inspect.go @@ -819,27 +819,10 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named // Devices // Do not include if privileged - assumed that all devices will be // included. - hostConfig.Devices = []define.InspectDevice{} - if ctrSpec.Linux != nil && !hostConfig.Privileged { - for _, dev := range ctrSpec.Linux.Devices { - key := fmt.Sprintf("%d:%d", dev.Major, dev.Minor) - if deviceNodes == nil { - nodes, err := util.FindDeviceNodes() - if err != nil { - return nil, err - } - deviceNodes = nodes - } - path, ok := deviceNodes[key] - if !ok { - logrus.Warnf("Could not locate device %s on host", key) - continue - } - newDev := define.InspectDevice{} - newDev.PathOnHost = path - newDev.PathInContainer = dev.Path - hostConfig.Devices = append(hostConfig.Devices, newDev) - } + var err error + hostConfig.Devices, err = c.GetDevices(*&hostConfig.Privileged, *ctrSpec, deviceNodes) + if err != nil { + return nil, err } // Ulimits @@ -885,3 +868,29 @@ func (c *Container) inHostPidNS() (bool, error) { } return true, nil } + +func (c *Container) GetDevices(priv bool, ctrSpec spec.Spec, deviceNodes map[string]string) ([]define.InspectDevice, error) { + devices := []define.InspectDevice{} + if ctrSpec.Linux != nil && !priv { + for _, dev := range ctrSpec.Linux.Devices { + key := fmt.Sprintf("%d:%d", dev.Major, dev.Minor) + if deviceNodes == nil { + nodes, err := util.FindDeviceNodes() + if err != nil { + return nil, err + } + deviceNodes = nodes + } + path, ok := deviceNodes[key] + if !ok { + logrus.Warnf("Could not locate device %s on host", key) + continue + } + newDev := define.InspectDevice{} + newDev.PathOnHost = path + newDev.PathInContainer = dev.Path + devices = append(devices, newDev) + } + } + return devices, nil +} diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 6ebbfd1f3..0a663200a 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -369,13 +369,46 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { if err != nil { return nil, err } - volMount := spec.Mount{ - Type: "bind", - Source: mountPoint, - Destination: namedVol.Dest, - Options: namedVol.Options, + + overlayFlag := false + for _, o := range namedVol.Options { + if o == "O" { + overlayFlag = true + } + } + + if overlayFlag { + contentDir, err := overlay.TempDir(c.config.StaticDir, c.RootUID(), c.RootGID()) + if err != nil { + return nil, err + } + overlayMount, err := overlay.Mount(contentDir, mountPoint, namedVol.Dest, c.RootUID(), c.RootGID(), c.runtime.store.GraphOptions()) + if err != nil { + return nil, errors.Wrapf(err, "mounting overlay failed %q", mountPoint) + } + + for _, o := range namedVol.Options { + switch o { + case "U": + if err := chown.ChangeHostPathOwnership(mountPoint, true, int(hostUID), int(hostGID)); err != nil { + return nil, err + } + + if err := chown.ChangeHostPathOwnership(contentDir, true, int(hostUID), int(hostGID)); err != nil { + return nil, err + } + } + } + g.AddMount(overlayMount) + } else { + volMount := spec.Mount{ + Type: "bind", + Source: mountPoint, + Destination: namedVol.Dest, + Options: namedVol.Options, + } + g.AddMount(volMount) } - g.AddMount(volMount) } // Check if the spec file mounts contain the options z, Z or U. @@ -2050,35 +2083,39 @@ func (c *Container) getHosts() string { } } - // Add gateway entry - var depCtr *Container - netStatus := c.getNetworkStatus() - if c.config.NetNsCtr != "" { - // ignoring the error because there isn't anything to do - depCtr, _ = c.getRootNetNsDepCtr() - } else if len(netStatus) != 0 { - depCtr = c - } - - if depCtr != nil { - for _, status := range depCtr.getNetworkStatus() { - for _, netInt := range status.Interfaces { - for _, netAddress := range netInt.Networks { - if netAddress.Gateway != nil { - hosts += fmt.Sprintf("%s host.containers.internal\n", netAddress.Gateway.String()) + // Add gateway entry if we are not in a machine. If we use podman machine + // the gvproxy dns server will take care of host.containers.internal. + // https://github.com/containers/gvisor-tap-vsock/commit/1108ea45162281046d239047a6db9bc187e64b08 + if !c.runtime.config.Engine.MachineEnabled { + var depCtr *Container + netStatus := c.getNetworkStatus() + if c.config.NetNsCtr != "" { + // ignoring the error because there isn't anything to do + depCtr, _ = c.getRootNetNsDepCtr() + } else if len(netStatus) != 0 { + depCtr = c + } + + if depCtr != nil { + for _, status := range depCtr.getNetworkStatus() { + for _, netInt := range status.Interfaces { + for _, netAddress := range netInt.Networks { + if netAddress.Gateway != nil { + hosts += fmt.Sprintf("%s host.containers.internal\n", netAddress.Gateway.String()) + } } } } - } - } else if c.config.NetMode.IsSlirp4netns() { - gatewayIP, err := GetSlirp4netnsGateway(c.slirp4netnsSubnet) - if err != nil { - logrus.Warn("failed to determine gatewayIP: ", err.Error()) + } else if c.config.NetMode.IsSlirp4netns() { + gatewayIP, err := GetSlirp4netnsGateway(c.slirp4netnsSubnet) + if err != nil { + logrus.Warn("failed to determine gatewayIP: ", err.Error()) + } else { + hosts += fmt.Sprintf("%s host.containers.internal\n", gatewayIP.String()) + } } else { - hosts += fmt.Sprintf("%s host.containers.internal\n", gatewayIP.String()) + logrus.Debug("network configuration does not support host.containers.internal address") } - } else { - logrus.Debug("network configuration does not support host.containers.internal address") } return hosts diff --git a/libpod/define/pod_inspect.go b/libpod/define/pod_inspect.go index b7a6e76b5..e78d97850 100644 --- a/libpod/define/pod_inspect.go +++ b/libpod/define/pod_inspect.go @@ -59,6 +59,8 @@ type InspectPodData struct { CPUSetCPUs string `json:"cpuset_cpus,omitempty"` // Mounts contains volume related information for the pod Mounts []InspectMount `json:"mounts,omitempty"` + // Devices contains the specified host devices + Devices []InspectDevice `json:"devices,omitempty"` } // InspectPodInfraConfig contains the configuration of the pod's infra diff --git a/libpod/kube.go b/libpod/kube.go index 54e8a7c50..9b96dd99d 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -11,6 +11,7 @@ import ( "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/network/types" + "github.com/containers/podman/v3/pkg/env" "github.com/containers/podman/v3/pkg/lookup" "github.com/containers/podman/v3/pkg/namespaces" "github.com/containers/podman/v3/pkg/specgen" @@ -570,12 +571,16 @@ func ocicniPortMappingToContainerPort(portMappings []types.OCICNIPortMapping) ([ // libpodEnvVarsToKubeEnvVars converts a key=value string slice to []v1.EnvVar func libpodEnvVarsToKubeEnvVars(envs []string) ([]v1.EnvVar, error) { + defaultEnv := env.DefaultEnvVariables() envVars := make([]v1.EnvVar, 0, len(envs)) for _, e := range envs { split := strings.SplitN(e, "=", 2) if len(split) != 2 { return envVars, errors.Errorf("environment variable %s is malformed; should be key=value", e) } + if defaultEnv[split[0]] == split[1] { + continue + } ev := v1.EnvVar{ Name: split[0], Value: split[1], diff --git a/libpod/options.go b/libpod/options.go index 3f6ccf1cb..a80f51c6a 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -21,6 +21,7 @@ import ( "github.com/containers/podman/v3/pkg/util" "github.com/containers/storage" "github.com/containers/storage/pkg/idtools" + "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -1809,6 +1810,17 @@ func WithInitCtrType(containerType string) CtrCreateOption { } } +// WithHostDevice adds the original host src to the config +func WithHostDevice(dev []specs.LinuxDevice) CtrCreateOption { + return func(ctr *Container) error { + if ctr.valid { + return define.ErrCtrFinalized + } + ctr.config.DeviceHostSrc = dev + return nil + } +} + // Pod Creation Options // WithPodCreateCommand adds the full command plus arguments of the current diff --git a/libpod/pod_api.go b/libpod/pod_api.go index 4e0acf950..ff818edc2 100644 --- a/libpod/pod_api.go +++ b/libpod/pod_api.go @@ -583,6 +583,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) { // container. var infraConfig *define.InspectPodInfraConfig var inspectMounts []define.InspectMount + var devices []define.InspectDevice if p.state.InfraContainerID != "" { infra, err := p.runtime.GetContainer(p.state.InfraContainerID) if err != nil { @@ -604,6 +605,12 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) { return nil, err } + var nodes map[string]string + devices, err = infra.GetDevices(false, *infra.config.Spec, nodes) + if err != nil { + return nil, err + } + if len(infra.Config().ContainerNetworkConfig.DNSServer) > 0 { infraConfig.DNSServer = make([]string, 0, len(infra.Config().ContainerNetworkConfig.DNSServer)) for _, entry := range infra.Config().ContainerNetworkConfig.DNSServer { @@ -652,6 +659,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) { CPUPeriod: p.CPUPeriod(), CPUQuota: p.CPUQuota(), Mounts: inspectMounts, + Devices: devices, } return &inspectData, nil diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go index 1f03e121e..9f46ecc52 100644 --- a/pkg/api/handlers/libpod/pods.go +++ b/pkg/api/handlers/libpod/pods.go @@ -41,8 +41,8 @@ func PodCreate(w http.ResponseWriter, r *http.Request) { return } if !psg.NoInfra { - infraOptions := &entities.ContainerCreateOptions{ImageVolume: "bind", IsInfra: true, Net: &entities.NetOptions{}} // options for pulling the image and FillOutSpec - err = specgenutil.FillOutSpecGen(psg.InfraContainerSpec, infraOptions, []string{}) // necessary for default values in many cases (userns, idmappings) + infraOptions := &entities.ContainerCreateOptions{ImageVolume: "bind", IsInfra: true, Net: &entities.NetOptions{}, Devices: psg.Devices} // options for pulling the image and FillOutSpec + err = specgenutil.FillOutSpecGen(psg.InfraContainerSpec, infraOptions, []string{}) // necessary for default values in many cases (userns, idmappings) if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error filling out specgen")) return diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go index a74725c63..f0c88d77e 100644 --- a/pkg/domain/entities/pods.go +++ b/pkg/domain/entities/pods.go @@ -112,26 +112,28 @@ type PodSpec struct { PodSpecGen specgen.PodSpecGenerator } -// PodCreateOptions provides all possible options for creating a pod and its infra container +// PodCreateOptions provides all possible options for creating a pod and its infra container. +// The JSON tags below are made to match the respective field in ContainerCreateOptions for the purpose of mapping. // swagger:model PodCreateOptions type PodCreateOptions struct { - CGroupParent string - CreateCommand []string - Hostname string - Infra bool - InfraImage string - InfraName string - InfraCommand string - InfraConmonPidFile string - Labels map[string]string - Name string - Net *NetOptions - Share []string - Pid string - Cpus float64 - CpusetCpus string - Userns specgen.Namespace - Volume []string + CGroupParent string `json:"cgroup_parent,omitempty"` + CreateCommand []string `json:"create_command,omitempty"` + Devices []string `json:"devices,omitempty"` + Hostname string `json:"hostname,omitempty"` + Infra bool `json:"infra,omitempty"` + InfraImage string `json:"infra_image,omitempty"` + InfraName string `json:"container_name,omitempty"` + InfraCommand *string `json:"container_command,omitempty"` + InfraConmonPidFile string `json:"container_conmon_pidfile,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + Name string `json:"name,omitempty"` + Net *NetOptions `json:"net,omitempty"` + Share []string `json:"share,omitempty"` + Pid string `json:"pid,omitempty"` + Cpus float64 `json:"cpus,omitempty"` + CpusetCpus string `json:"cpuset_cpus,omitempty"` + Userns specgen.Namespace `json:"-"` + Volume []string `json:"volume,omitempty"` } // PodLogsOptions describes the options to extract pod logs. @@ -152,24 +154,24 @@ type ContainerCreateOptions struct { CapDrop []string CgroupNS string CGroupsMode string - CGroupParent string + CGroupParent string `json:"cgroup_parent,omitempty"` CIDFile string - ConmonPIDFile string + ConmonPIDFile string `json:"container_conmon_pidfile,omitempty"` CPUPeriod uint64 CPUQuota int64 CPURTPeriod uint64 CPURTRuntime int64 CPUShares uint64 - CPUS float64 - CPUSetCPUs string + CPUS float64 `json:"cpus,omitempty"` + CPUSetCPUs string `json:"cpuset_cpus,omitempty"` CPUSetMems string - Devices []string + Devices []string `json:"devices,omitempty"` DeviceCGroupRule []string DeviceReadBPs []string DeviceReadIOPs []string DeviceWriteBPs []string DeviceWriteIOPs []string - Entrypoint *string + Entrypoint *string `json:"container_command,omitempty"` Env []string EnvHost bool EnvFile []string @@ -181,7 +183,7 @@ type ContainerCreateOptions struct { HealthRetries uint HealthStartPeriod string HealthTimeout string - Hostname string + Hostname string `json:"hostname,omitempty"` HTTPProxy bool ImageVolume string Init bool @@ -198,14 +200,14 @@ type ContainerCreateOptions struct { MemoryReservation string MemorySwap string MemorySwappiness int64 - Name string + Name string `json:"container_name,omitempty"` NoHealthCheck bool OOMKillDisable bool OOMScoreAdj int Arch string OS string Variant string - PID string + PID string `json:"pid,omitempty"` PIDsLimit *int64 Platform string Pod string @@ -244,17 +246,17 @@ type ContainerCreateOptions struct { UIDMap []string Ulimit []string User string - UserNS string + UserNS string `json:"-"` UTS string Mount []string - Volume []string + Volume []string `json:"volume,omitempty"` VolumesFrom []string Workdir string SeccompPolicy string PidFile string IsInfra bool - Net *NetOptions + Net *NetOptions `json:"net,omitempty"` CgroupConf []string } @@ -294,9 +296,10 @@ func ToPodSpecGen(s specgen.PodSpecGenerator, p *PodCreateOptions) (*specgen.Pod s.Pid = out s.Hostname = p.Hostname s.Labels = p.Labels + s.Devices = p.Devices s.NoInfra = !p.Infra - if len(p.InfraCommand) > 0 { - s.InfraCommand = strings.Split(p.InfraCommand, " ") + if p.InfraCommand != nil && len(*p.InfraCommand) > 0 { + s.InfraCommand = strings.Split(*p.InfraCommand, " ") } if len(p.InfraConmonPidFile) > 0 { s.InfraConmonPidFile = p.InfraConmonPidFile diff --git a/pkg/env/env.go b/pkg/env/env.go index 0d55e5560..ecd2d62a5 100644 --- a/pkg/env/env.go +++ b/pkg/env/env.go @@ -17,8 +17,9 @@ const whiteSpaces = " \t" // DefaultEnvVariables returns a default environment, with $PATH and $TERM set. func DefaultEnvVariables() map[string]string { return map[string]string{ - "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "TERM": "xterm", + "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "TERM": "xterm", + "container": "podman", } } diff --git a/pkg/specgen/generate/config_linux.go b/pkg/specgen/generate/config_linux.go index 6b9e9c4bf..2d1e2b288 100644 --- a/pkg/specgen/generate/config_linux.go +++ b/pkg/specgen/generate/config_linux.go @@ -132,7 +132,6 @@ func DevicesFromPath(g *generate.Generator, devicePath string) error { } return nil } - return addDevice(g, strings.Join(append([]string{resolvedDevicePath}, devs[1:]...), ":")) } diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index 91230338e..fefa9b4a9 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -30,24 +30,27 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener // If joining a pod, retrieve the pod for use, and its infra container var pod *libpod.Pod - var cont *libpod.Container - var config *libpod.ContainerConfig + var infraConfig *libpod.ContainerConfig if s.Pod != "" { pod, err = rt.LookupPod(s.Pod) if err != nil { return nil, nil, nil, errors.Wrapf(err, "error retrieving pod %s", s.Pod) } if pod.HasInfraContainer() { - cont, err = pod.InfraContainer() + infra, err := pod.InfraContainer() if err != nil { return nil, nil, nil, err } - config = cont.Config() + infraConfig = infra.Config() } } - if config != nil && (len(config.NamedVolumes) > 0 || len(config.UserVolumes) > 0 || len(config.ImageVolumes) > 0 || len(config.OverlayVolumes) > 0) { - s.VolumesFrom = append(s.VolumesFrom, config.ID) + if infraConfig != nil && (len(infraConfig.NamedVolumes) > 0 || len(infraConfig.UserVolumes) > 0 || len(infraConfig.ImageVolumes) > 0 || len(infraConfig.OverlayVolumes) > 0) { + s.VolumesFrom = append(s.VolumesFrom, infraConfig.ID) + } + + if infraConfig != nil && len(infraConfig.Spec.Linux.Devices) > 0 { + s.DevicesFrom = append(s.DevicesFrom, infraConfig.ID) } // Set defaults for unset namespaces if s.PidNS.IsDefault() { @@ -166,6 +169,16 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener logrus.Debugf("setting container name %s", s.Name) options = append(options, libpod.WithName(s.Name)) } + if len(s.DevicesFrom) > 0 { + for _, dev := range s.DevicesFrom { + ctr, err := rt.GetContainer(dev) + if err != nil { + return nil, nil, nil, err + } + devices := ctr.DeviceHostSrc() + s.Devices = append(s.Devices, devices...) + } + } if len(s.Devices) > 0 { opts = extractCDIDevices(s) options = append(options, opts...) @@ -174,6 +187,9 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener if err != nil { return nil, nil, nil, err } + if len(s.HostDeviceList) > 0 { + options = append(options, libpod.WithHostDevice(s.HostDeviceList)) + } return runtimeSpec, s, options, err } func ExecuteCreate(ctx context.Context, rt *libpod.Runtime, runtimeSpec *spec.Spec, s *specgen.SpecGenerator, infra bool, options ...libpod.CtrCreateOption) (*libpod.Container, error) { diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index 80c7f112f..55010f716 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -301,8 +301,8 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt g.AddProcessEnv("container", "podman") g.Config.Linux.Resources = s.ResourceLimits - // Devices + if s.Privileged { // If privileged, we need to add all the host devices to the // spec. We do not add the user provided ones because we are @@ -313,17 +313,18 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt } else { // add default devices from containers.conf for _, device := range rtc.Containers.Devices { - if err := DevicesFromPath(&g, device); err != nil { + if err = DevicesFromPath(&g, device); err != nil { return nil, err } } // add default devices specified by caller for _, device := range s.Devices { - if err := DevicesFromPath(&g, device.Path); err != nil { + if err = DevicesFromPath(&g, device.Path); err != nil { return nil, err } } } + s.HostDeviceList = s.Devices for _, dev := range s.DeviceCGroupRule { g.AddLinuxResourcesDevice(true, dev.Type, dev.Major, dev.Minor, dev.Access) diff --git a/pkg/specgen/podspecgen.go b/pkg/specgen/podspecgen.go index 5f72fc47d..83fa9426c 100644 --- a/pkg/specgen/podspecgen.go +++ b/pkg/specgen/podspecgen.go @@ -88,6 +88,8 @@ type PodBasicConfig struct { // Image volumes bind-mount a container-image mount into the pod's infra container. // Optional. ImageVolumes []*ImageVolume `json:"image_volumes,omitempty"` + // Devices contains user specified Devices to be added to the Pod + Devices []string `json:"pod_devices,omitempty"` } // PodNetworkConfig contains networking configuration for a pod. diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index e0609c5bc..7aa27487a 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -254,6 +254,10 @@ type ContainerStorageConfig struct { // DeviceCGroupRule are device cgroup rules that allow containers // to use additional types of devices. DeviceCGroupRule []spec.LinuxDeviceCgroup `json:"device_cgroup_rule,omitempty"` + // DevicesFrom is a way to ensure your container inherits device specific information from another container + DevicesFrom []string `json:"devices_from,omitempty"` + // HostDeviceList is used to recreate the mounted device on inherited containers + HostDeviceList []spec.LinuxDevice `json:"host_device_list,omitempty"` // IpcNS is the container's IPC namespace. // Default is private. // Conflicts with ShmSize if not set to private. diff --git a/test/e2e/config/containers.conf b/test/e2e/config/containers.conf index bbd712254..c33f32ab4 100644 --- a/test/e2e/config/containers.conf +++ b/test/e2e/config/containers.conf @@ -59,6 +59,7 @@ no_hosts=true [engine] network_cmd_options=["allow_host_loopback=true"] +service_timeout=1234 # We need to ensure each test runs on a separate plugin instance... # For now, let's just make a bunch of plugin paths and have each test use one. diff --git a/test/e2e/containers_conf_test.go b/test/e2e/containers_conf_test.go index f5e85e723..fac200c3c 100644 --- a/test/e2e/containers_conf_test.go +++ b/test/e2e/containers_conf_test.go @@ -444,4 +444,12 @@ var _ = Describe("Podman run", func() { Expect(session).Should(Exit(0)) Expect(session.ErrorToString()).To(ContainSubstring("invalid image_copy_tmp_dir")) }) + + It("podman system sevice --help shows (default 20)", func() { + SkipIfRemote("this test is only for local") + result := podmanTest.Podman([]string{"system", "service", "--help"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(ContainSubstring("(default 1234)")) + }) }) diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index fcda89fbc..0d5b9d52c 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -63,12 +63,6 @@ spec: - -d - "1.5" env: - - name: PATH - value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin - - name: TERM - value: xterm - - name: container - value: podman - name: HOSTNAME value: label-pod image: quay.io/libpod/alpine:latest @@ -171,12 +165,6 @@ spec: - -d - "1.5" env: - - name: PATH - value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin - - name: TERM - value: xterm - - name: container - value: podman - name: HOSTNAME value: label-pod image: quay.io/libpod/alpine:latest @@ -287,13 +275,7 @@ spec: - {{.}} {{ end }} env: - - name: PATH - value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin - - name: TERM - value: xterm - name: HOSTNAME - - name: container - value: podman {{ range .Env }} - name: {{ .Name }} {{ if (eq .ValueFrom "configmap") }} @@ -453,13 +435,7 @@ spec: - {{.}} {{ end }} env: - - name: PATH - value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin - - name: TERM - value: xterm - name: HOSTNAME - - name: container - value: podman image: {{ .Image }} name: {{ .Name }} imagePullPolicy: {{ .PullPolicy }} diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index 7d40d36dd..76a05fa0f 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -881,6 +881,25 @@ ENTRYPOINT ["sleep","99999"] ctr3 := podmanTest.Podman([]string{"run", "--pod", podName, ALPINE, "cat", "/tmp1/test"}) ctr3.WaitWithDefaultTimeout() Expect(ctr3.OutputToString()).To(ContainSubstring("hello")) + }) + + It("podman pod create --device", func() { + SkipIfRootless("Cannot create devices in /dev in rootless mode") + Expect(os.MkdirAll("/dev/foodevdir", os.ModePerm)).To(BeNil()) + defer os.RemoveAll("/dev/foodevdir") + + mknod := SystemExec("mknod", []string{"/dev/foodevdir/null", "c", "1", "3"}) + mknod.WaitWithDefaultTimeout() + Expect(mknod).Should(Exit(0)) + + podName := "testPod" + session := podmanTest.Podman([]string{"pod", "create", "--device", "/dev/foodevdir:/dev/bar", "--name", podName}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + session = podmanTest.Podman([]string{"run", "-q", "--pod", podName, ALPINE, "stat", "-c%t:%T", "/dev/bar/null"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(Equal("1:3")) }) diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go index aeb88e481..010885dd5 100644 --- a/test/e2e/ps_test.go +++ b/test/e2e/ps_test.go @@ -6,7 +6,6 @@ import ( "regexp" "sort" "strconv" - "strings" . "github.com/containers/podman/v3/test/utils" "github.com/containers/storage/pkg/stringid" @@ -187,7 +186,10 @@ var _ = Describe("Podman ps", func() { result.WaitWithDefaultTimeout() result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) - Expect(result.OutputToString()).To(ContainSubstring("bravo")) + + actual := result.OutputToString() + Expect(actual).To(ContainSubstring("bravo")) + Expect(actual).To(ContainSubstring("NAMES")) }) It("podman ps --filter network=container:<id>", func() { @@ -206,7 +208,9 @@ var _ = Describe("Podman ps", func() { result.WaitWithDefaultTimeout() result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) - Expect(result.OutputToString()).To(ContainSubstring("second")) + actual := result.OutputToString() + Expect(actual).To(ContainSubstring("second")) + Expect(actual).ToNot(ContainSubstring("table")) }) It("podman ps namespace flag", func() { @@ -228,7 +232,7 @@ var _ = Describe("Podman ps", func() { result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) // it must contains `::` when some ns is null. If it works normally, it should be "$num1:$num2:$num3" - Expect(result.OutputToString()).To(Not(ContainSubstring(`::`))) + Expect(result.OutputToString()).ToNot(ContainSubstring(`::`)) }) It("podman ps with no containers is valid json format", func() { @@ -285,11 +289,14 @@ var _ = Describe("Podman ps", func() { result := podmanTest.Podman([]string{"ps", "-a", "--format", "table {{.ID}} {{.Image}} {{.ImageID}} {{.Labels}}"}) result.WaitWithDefaultTimeout() - - Expect(result.OutputToStringArray()[0]).ToNot(ContainSubstring("table")) - Expect(result.OutputToStringArray()[0]).ToNot(ContainSubstring("ImageID")) - Expect(result.OutputToStringArray()[0]).To(ContainSubstring("alpine:latest")) Expect(result).Should(Exit(0)) + + Expect(result.OutputToString()).ToNot(ContainSubstring("table")) + + actual := result.OutputToStringArray() + Expect(actual[0]).To(ContainSubstring("CONTAINER ID")) + Expect(actual[0]).ToNot(ContainSubstring("ImageID")) + Expect(actual[1]).To(ContainSubstring("alpine:latest")) }) It("podman ps ancestor filter flag", func() { @@ -380,7 +387,9 @@ var _ = Describe("Podman ps", func() { psFilter.WaitWithDefaultTimeout() Expect(psFilter).Should(Exit(0)) - Expect(strings.Contains(psFilter.OutputToString(), ctrName)).To(BeFalse()) + actual := psFilter.OutputToString() + Expect(actual).ToNot(ContainSubstring(ctrName)) + Expect(actual).ToNot(ContainSubstring("NAMES")) }) It("podman ps mutually exclusive flags", func() { @@ -453,14 +462,13 @@ var _ = Describe("Podman ps", func() { Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"ps", "-a", "--sort=command", "--format", "{{.Command}}"}) - session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - sortedArr := session.OutputToStringArray() + Expect(session.OutputToString()).ToNot(ContainSubstring("COMMAND")) + sortedArr := session.OutputToStringArray() Expect(sort.SliceIsSorted(sortedArr, func(i, j int) bool { return sortedArr[i] < sortedArr[j] })).To(BeTrue()) - }) It("podman --pod", func() { @@ -474,7 +482,7 @@ var _ = Describe("Podman ps", func() { session = podmanTest.Podman([]string{"ps", "--no-trunc"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.OutputToString()).To(Not(ContainSubstring(podid))) + Expect(session.OutputToString()).ToNot(ContainSubstring(podid)) session = podmanTest.Podman([]string{"ps", "--pod", "--no-trunc"}) session.WaitWithDefaultTimeout() @@ -510,7 +518,11 @@ var _ = Describe("Podman ps", func() { session = podmanTest.Podman([]string{"ps", "--format", "{{.Ports}}"}) session.WaitWithDefaultTimeout() - Expect(session.OutputToString()).To(ContainSubstring("0.0.0.0:2000-2006")) + Expect(session).To(Exit(0)) + + actual := session.OutputToString() + Expect(actual).To(ContainSubstring("0.0.0.0:2000-2006")) + Expect(actual).ToNot(ContainSubstring("PORT")) }) It("podman ps test with invalid port range", func() { @@ -628,7 +640,10 @@ var _ = Describe("Podman ps", func() { result := podmanTest.Podman([]string{"ps", "-a", "--format", "{{.RunningFor}}"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) - Expect(result.OutputToString()).To(ContainSubstring("ago")) + + actual := result.OutputToString() + Expect(actual).To(ContainSubstring("ago")) + Expect(actual).ToNot(ContainSubstring("RUNNING FOR")) }) It("podman ps filter test", func() { @@ -823,8 +838,9 @@ var _ = Describe("Podman ps", func() { session = podmanTest.Podman([]string{"ps", "--all", "--no-trunc", "--filter", "network=" + net}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.OutputToString()).To(ContainSubstring(ctrWithNet)) - Expect(session.OutputToString()).To(Not(ContainSubstring(ctrWithoutNet))) + actual := session.OutputToString() + Expect(actual).To(ContainSubstring(ctrWithNet)) + Expect(actual).ToNot(ContainSubstring(ctrWithoutNet)) }) It("podman ps --format networks", func() { @@ -835,12 +851,15 @@ var _ = Describe("Podman ps", func() { session = podmanTest.Podman([]string{"ps", "--all", "--format", "{{ .Networks }}"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) + + actual := session.OutputToString() + Expect(actual).ToNot(ContainSubstring("NETWORKS")) if isRootless() { // rootless container don't have a network by default - Expect(session.OutputToString()).To(Equal("")) + Expect(actual).To(BeEmpty()) } else { // default network name is podman - Expect(session.OutputToString()).To(Equal("podman")) + Expect(actual).To(Equal("podman")) } net1 := stringid.GenerateNonCryptoID() diff --git a/test/e2e/run_volume_test.go b/test/e2e/run_volume_test.go index 59937b6c0..4264e1efe 100644 --- a/test/e2e/run_volume_test.go +++ b/test/e2e/run_volume_test.go @@ -242,6 +242,39 @@ var _ = Describe("Podman run with volumes", func() { Expect(session).Should(Exit(0)) }) + It("podman support overlay on named volume", func() { + SkipIfRemote("Overlay volumes only work locally") + if os.Getenv("container") != "" { + Skip("Overlay mounts not supported when running in a container") + } + if rootless.IsRootless() { + if _, err := exec.LookPath("fuse-overlayfs"); err != nil { + Skip("Fuse-Overlayfs required for rootless overlay mount test") + } + } + session := podmanTest.Podman([]string{"volume", "create", "myvolume"}) + session.WaitWithDefaultTimeout() + volName := session.OutputToString() + Expect(session).Should(Exit(0)) + + // create file on actual volume + session = podmanTest.Podman([]string{"run", "--volume", volName + ":/data", ALPINE, "sh", "-c", "echo hello >> " + "/data/test"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + // create file on overlayed volume + session = podmanTest.Podman([]string{"run", "--volume", volName + ":/data:O", ALPINE, "sh", "-c", "echo hello >> " + "/data/overlayed"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + // volume should contain only `test` not `overlayed` + session = podmanTest.Podman([]string{"run", "--volume", volName + ":/data", ALPINE, "sh", "-c", "ls /data"}) + session.WaitWithDefaultTimeout() + Expect(session.OutputToString()).To(Not(ContainSubstring("overlayed"))) + Expect(session.OutputToString()).To(ContainSubstring("test")) + + }) + It("podman run with noexec can't exec", func() { session := podmanTest.Podman([]string{"run", "--rm", "-v", "/bin:/hostbin:noexec", ALPINE, "/hostbin/ls", "/"}) session.WaitWithDefaultTimeout() diff --git a/test/utils/utils.go b/test/utils/utils.go index 80af7fb7c..bfefc58ec 100644 --- a/test/utils/utils.go +++ b/test/utils/utils.go @@ -19,7 +19,7 @@ import ( ) var ( - defaultWaitTimeout = 90 + DefaultWaitTimeout = 90 OSReleasePath = "/etc/os-release" ProcessOneCgroupPath = "/proc/1/cgroup" ) @@ -317,15 +317,20 @@ func (s *PodmanSession) IsJSONOutputValid() bool { return true } -// WaitWithDefaultTimeout waits for process finished with defaultWaitTimeout +// WaitWithDefaultTimeout waits for process finished with DefaultWaitTimeout func (s *PodmanSession) WaitWithDefaultTimeout() { - Eventually(s, defaultWaitTimeout).Should(Exit()) + s.WaitWithTimeout(DefaultWaitTimeout) +} + +// WaitWithTimeout waits for process finished with DefaultWaitTimeout +func (s *PodmanSession) WaitWithTimeout(timeout int) { + Eventually(s, timeout).Should(Exit()) os.Stdout.Sync() os.Stderr.Sync() fmt.Println("output:", s.OutputToString()) } -// CreateTempDirinTempDir create a temp dir with prefix podman_test +// CreateTempDirInTempDir create a temp dir with prefix podman_test func CreateTempDirInTempDir() (string, error) { return ioutil.TempDir("", "podman_test") } @@ -337,7 +342,7 @@ func SystemExec(command string, args []string) *PodmanSession { if err != nil { Fail(fmt.Sprintf("unable to run command: %s %s", command, strings.Join(args, " "))) } - session.Wait(defaultWaitTimeout) + session.Wait(DefaultWaitTimeout) return &PodmanSession{session} } |