summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/inspect.go42
-rw-r--r--cmd/podman/shared/container_inspect.go211
-rw-r--r--cmd/podman/shared/create.go9
-rw-r--r--go.mod2
-rw-r--r--libpod/container_inspect.go1011
-rw-r--r--pkg/spec/spec.go37
-rw-r--r--pkg/util/utils_linux.go43
-rw-r--r--pkg/util/utils_unsupported.go12
-rw-r--r--pkg/varlinkapi/containers.go24
-rw-r--r--test/e2e/common_test.go7
-rw-r--r--vendor/modules.txt2
11 files changed, 1069 insertions, 331 deletions
diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go
index df597c868..12d89764c 100644
--- a/cmd/podman/inspect.go
+++ b/cmd/podman/inspect.go
@@ -6,9 +6,7 @@ import (
"github.com/containers/buildah/pkg/formats"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/pkg/adapter"
- cc "github.com/containers/libpod/pkg/spec"
"github.com/containers/libpod/pkg/util"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -148,19 +146,9 @@ func iterateInput(ctx context.Context, size bool, args []string, runtime *adapte
inspectError = errors.Wrapf(err, "error looking up container %q", input)
break
}
- libpodInspectData, err := ctr.Inspect(size)
+ data, err = ctr.Inspect(size)
if err != nil {
- inspectError = errors.Wrapf(err, "error getting libpod container inspect data %s", ctr.ID())
- break
- }
- artifact, err := getArtifact(ctr)
- if inspectError != nil {
- inspectError = err
- break
- }
- data, err = shared.GetCtrInspectInfo(ctr.Config(), libpodInspectData, artifact)
- if err != nil {
- inspectError = errors.Wrapf(err, "error parsing container data %q", ctr.ID())
+ inspectError = errors.Wrapf(err, "error inspecting container %s", ctr.ID())
break
}
case inspectTypeImage:
@@ -188,19 +176,9 @@ func iterateInput(ctx context.Context, size bool, args []string, runtime *adapte
break
}
} else {
- libpodInspectData, err := ctr.Inspect(size)
- if err != nil {
- inspectError = errors.Wrapf(err, "error getting libpod container inspect data %s", ctr.ID())
- break
- }
- artifact, err := getArtifact(ctr)
+ data, err = ctr.Inspect(size)
if err != nil {
- inspectError = err
- break
- }
- data, err = shared.GetCtrInspectInfo(ctr.Config(), libpodInspectData, artifact)
- if err != nil {
- inspectError = errors.Wrapf(err, "error parsing container data %s", ctr.ID())
+ inspectError = errors.Wrapf(err, "error inspecting container %s", ctr.ID())
break
}
}
@@ -211,15 +189,3 @@ func iterateInput(ctx context.Context, size bool, args []string, runtime *adapte
}
return inspectedItems, inspectError
}
-
-func getArtifact(ctr *adapter.Container) (*cc.CreateConfig, error) {
- var createArtifact cc.CreateConfig
- artifact, err := ctr.GetArtifact("create-config")
- if err != nil {
- return nil, err
- }
- if err := json.Unmarshal(artifact, &createArtifact); err != nil {
- return nil, err
- }
- return &createArtifact, nil
-}
diff --git a/cmd/podman/shared/container_inspect.go b/cmd/podman/shared/container_inspect.go
deleted file mode 100644
index a8094466e..000000000
--- a/cmd/podman/shared/container_inspect.go
+++ /dev/null
@@ -1,211 +0,0 @@
-package shared
-
-import (
- "github.com/containers/libpod/libpod"
- cc "github.com/containers/libpod/pkg/spec"
- "github.com/docker/go-connections/nat"
- "github.com/opencontainers/runtime-spec/specs-go"
-)
-
-// InspectContainer holds all inspect data for a container.
-// The format of individual components is fixed so the overall structure, when
-// JSON encoded, matches the output of `docker inspect`.
-// It combines Libpod-source inspect data with Podman-specific inspect data.
-type InspectContainer struct {
- *libpod.InspectContainerData
- HostConfig *InspectContainerHostConfig `json:"HostConfig"`
-}
-
-// InspectContainerHostConfig holds Container configuration that is not specific
-// to Libpod. This information is (mostly) stored by Podman as an artifact.
-// This struct is matched to the output of `docker inspect`.
-type InspectContainerHostConfig struct {
- ContainerIDFile string `json:"ContainerIDFile"`
- LogConfig *InspectLogConfig `json:"LogConfig"` //TODO
- NetworkMode string `json:"NetworkMode"`
- PortBindings nat.PortMap `json:"PortBindings"` //TODO
- AutoRemove bool `json:"AutoRemove"`
- CapAdd []string `json:"CapAdd"`
- CapDrop []string `json:"CapDrop"`
- DNS []string `json:"DNS"`
- DNSOptions []string `json:"DNSOptions"`
- DNSSearch []string `json:"DNSSearch"`
- ExtraHosts []string `json:"ExtraHosts"`
- GroupAdd []uint32 `json:"GroupAdd"`
- IpcMode string `json:"IpcMode"`
- Cgroup string `json:"Cgroup"`
- OomScoreAdj *int `json:"OomScoreAdj"`
- PidMode string `json:"PidMode"`
- Privileged bool `json:"Privileged"`
- PublishAllPorts bool `json:"PublishAllPorts"` //TODO
- ReadOnlyRootfs bool `json:"ReadonlyRootfs"`
- ReadOnlyTmpfs bool `json:"ReadonlyTmpfs"`
- SecurityOpt []string `json:"SecurityOpt"`
- UTSMode string `json:"UTSMode"`
- UsernsMode string `json:"UsernsMode"`
- ShmSize int64 `json:"ShmSize"`
- Runtime string `json:"Runtime"`
- ConsoleSize *specs.Box `json:"ConsoleSize"`
- CPUShares *uint64 `json:"CpuShares"`
- Memory int64 `json:"Memory"`
- NanoCPUs int `json:"NanoCpus"`
- CgroupParent string `json:"CgroupParent"`
- BlkioWeight *uint16 `json:"BlkioWeight"`
- BlkioWeightDevice []specs.LinuxWeightDevice `json:"BlkioWeightDevice"`
- BlkioDeviceReadBps []specs.LinuxThrottleDevice `json:"BlkioDeviceReadBps"`
- BlkioDeviceWriteBps []specs.LinuxThrottleDevice `json:"BlkioDeviceWriteBps"`
- BlkioDeviceReadIOps []specs.LinuxThrottleDevice `json:"BlkioDeviceReadIOps"`
- BlkioDeviceWriteIOps []specs.LinuxThrottleDevice `json:"BlkioDeviceWriteIOps"`
- CPUPeriod *uint64 `json:"CpuPeriod"`
- CPUQuota *int64 `json:"CpuQuota"`
- CPURealtimePeriod *uint64 `json:"CpuRealtimePeriod"`
- CPURealtimeRuntime *int64 `json:"CpuRealtimeRuntime"`
- CPUSetCPUs string `json:"CpuSetCpus"`
- CPUSetMems string `json:"CpuSetMems"`
- Devices []specs.LinuxDevice `json:"Devices"`
- DiskQuota int `json:"DiskQuota"` //check type, TODO
- KernelMemory *int64 `json:"KernelMemory"`
- MemoryReservation *int64 `json:"MemoryReservation"`
- MemorySwap *int64 `json:"MemorySwap"`
- MemorySwappiness *uint64 `json:"MemorySwappiness"`
- OomKillDisable *bool `json:"OomKillDisable"`
- PidsLimit *int64 `json:"PidsLimit"`
- Ulimits []string `json:"Ulimits"`
- CPUCount int `json:"CpuCount"`
- CPUPercent int `json:"CpuPercent"`
- IOMaximumIOps int `json:"IOMaximumIOps"` //check type, TODO
- IOMaximumBandwidth int `json:"IOMaximumBandwidth"` //check type, TODO
- Tmpfs []string `json:"Tmpfs"`
-}
-
-// InspectLogConfig holds information about a container's configured log driver
-// and is presently unused. It is retained for Docker compatibility.
-type InspectLogConfig struct {
- Type string `json:"Type"`
- Config map[string]string `json:"Config"` //idk type, TODO
-}
-
-// GetCtrInspectInfo inspects a container, combining Libpod inspect information
-// with other information not stored in Libpod and returning a struct that, when
-// formatted for JSON output, is compatible with `docker inspect`.
-func GetCtrInspectInfo(config *libpod.ContainerConfig, ctrInspectData *libpod.InspectContainerData, createArtifact *cc.CreateConfig) (*InspectContainer, error) {
- spec := config.Spec
-
- cpus, mems, period, quota, realtimePeriod, realtimeRuntime, shares := getCPUInfo(spec)
- blkioWeight, blkioWeightDevice, blkioReadBps, blkioWriteBps, blkioReadIOPS, blkioeWriteIOPS := getBLKIOInfo(spec)
- memKernel, memReservation, memSwap, memSwappiness, memDisableOOMKiller := getMemoryInfo(spec)
- pidsLimit := getPidsInfo(spec)
- cgroup := getCgroup(spec)
- logConfig := InspectLogConfig{
- config.LogDriver,
- make(map[string]string),
- }
-
- data := &InspectContainer{
- ctrInspectData,
- &InspectContainerHostConfig{
- ConsoleSize: spec.Process.ConsoleSize,
- OomScoreAdj: spec.Process.OOMScoreAdj,
- CPUShares: shares,
- BlkioWeight: blkioWeight,
- BlkioWeightDevice: blkioWeightDevice,
- BlkioDeviceReadBps: blkioReadBps,
- BlkioDeviceWriteBps: blkioWriteBps,
- BlkioDeviceReadIOps: blkioReadIOPS,
- BlkioDeviceWriteIOps: blkioeWriteIOPS,
- CPUPeriod: period,
- CPUQuota: quota,
- CPURealtimePeriod: realtimePeriod,
- CPURealtimeRuntime: realtimeRuntime,
- CPUSetCPUs: cpus,
- CPUSetMems: mems,
- Devices: spec.Linux.Devices,
- KernelMemory: memKernel,
- LogConfig: &logConfig,
- MemoryReservation: memReservation,
- MemorySwap: memSwap,
- MemorySwappiness: memSwappiness,
- OomKillDisable: memDisableOOMKiller,
- PidsLimit: pidsLimit,
- Privileged: config.Privileged,
- ReadOnlyRootfs: spec.Root.Readonly,
- ReadOnlyTmpfs: createArtifact.ReadOnlyTmpfs,
- Runtime: config.OCIRuntime,
- NetworkMode: string(createArtifact.NetMode),
- IpcMode: string(createArtifact.IpcMode),
- Cgroup: cgroup,
- UTSMode: string(createArtifact.UtsMode),
- UsernsMode: string(createArtifact.UsernsMode),
- GroupAdd: spec.Process.User.AdditionalGids,
- ContainerIDFile: createArtifact.CidFile,
- AutoRemove: createArtifact.Rm,
- CapAdd: createArtifact.CapAdd,
- CapDrop: createArtifact.CapDrop,
- DNS: createArtifact.DNSServers,
- DNSOptions: createArtifact.DNSOpt,
- DNSSearch: createArtifact.DNSSearch,
- PidMode: string(createArtifact.PidMode),
- CgroupParent: createArtifact.CgroupParent,
- ShmSize: createArtifact.Resources.ShmSize,
- Memory: createArtifact.Resources.Memory,
- Ulimits: createArtifact.Resources.Ulimit,
- SecurityOpt: createArtifact.SecurityOpts,
- Tmpfs: createArtifact.Tmpfs,
- },
- }
- return data, nil
-}
-
-func getCPUInfo(spec *specs.Spec) (string, string, *uint64, *int64, *uint64, *int64, *uint64) {
- if spec.Linux.Resources == nil {
- return "", "", nil, nil, nil, nil, nil
- }
- cpu := spec.Linux.Resources.CPU
- if cpu == nil {
- return "", "", nil, nil, nil, nil, nil
- }
- return cpu.Cpus, cpu.Mems, cpu.Period, cpu.Quota, cpu.RealtimePeriod, cpu.RealtimeRuntime, cpu.Shares
-}
-
-func getBLKIOInfo(spec *specs.Spec) (*uint16, []specs.LinuxWeightDevice, []specs.LinuxThrottleDevice, []specs.LinuxThrottleDevice, []specs.LinuxThrottleDevice, []specs.LinuxThrottleDevice) {
- if spec.Linux.Resources == nil {
- return nil, nil, nil, nil, nil, nil
- }
- blkio := spec.Linux.Resources.BlockIO
- if blkio == nil {
- return nil, nil, nil, nil, nil, nil
- }
- return blkio.Weight, blkio.WeightDevice, blkio.ThrottleReadBpsDevice, blkio.ThrottleWriteBpsDevice, blkio.ThrottleReadIOPSDevice, blkio.ThrottleWriteIOPSDevice
-}
-
-func getMemoryInfo(spec *specs.Spec) (*int64, *int64, *int64, *uint64, *bool) {
- if spec.Linux.Resources == nil {
- return nil, nil, nil, nil, nil
- }
- memory := spec.Linux.Resources.Memory
- if memory == nil {
- return nil, nil, nil, nil, nil
- }
- return memory.Kernel, memory.Reservation, memory.Swap, memory.Swappiness, memory.DisableOOMKiller
-}
-
-func getPidsInfo(spec *specs.Spec) *int64 {
- if spec.Linux.Resources == nil {
- return nil
- }
- pids := spec.Linux.Resources.Pids
- if pids == nil {
- return nil
- }
- return &pids.Limit
-}
-
-func getCgroup(spec *specs.Spec) string {
- cgroup := "host"
- for _, ns := range spec.Linux.Namespaces {
- if ns.Type == specs.CgroupNamespace && ns.Path != "" {
- cgroup = "container"
- }
- }
- return cgroup
-}
diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go
index fd319e215..5c37a1875 100644
--- a/cmd/podman/shared/create.go
+++ b/cmd/podman/shared/create.go
@@ -651,6 +651,7 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
ImageVolumeType: c.String("image-volume"),
CapAdd: c.StringSlice("cap-add"),
CapDrop: c.StringSlice("cap-drop"),
+ CidFile: c.String("cidfile"),
CgroupParent: c.String("cgroup-parent"),
Command: command,
Detach: c.Bool("detach"),
@@ -766,14 +767,6 @@ func CreateContainerFromCreateConfig(r *libpod.Runtime, createConfig *cc.CreateC
if err != nil {
return nil, err
}
-
- createConfigJSON, err := json.Marshal(createConfig)
- if err != nil {
- return nil, err
- }
- if err := ctr.AddArtifact("create-config", createConfigJSON); err != nil {
- return nil, err
- }
return ctr, nil
}
diff --git a/go.mod b/go.mod
index 6fb743847..b6c400f18 100644
--- a/go.mod
+++ b/go.mod
@@ -101,7 +101,7 @@ require (
github.com/spf13/pflag v1.0.3
github.com/spf13/viper v1.4.0 // indirect
github.com/stretchr/testify v1.3.0
- github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 // indirect
+ github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2
github.com/tchap/go-patricia v2.3.0+incompatible // indirect
github.com/uber/jaeger-client-go v2.16.0+incompatible
github.com/uber/jaeger-lib v0.0.0-20190122222657-d036253de8f5 // indirect
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index de0027414..7273a9005 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -1,16 +1,70 @@
package libpod
import (
+ "fmt"
"strings"
"time"
"github.com/containers/image/manifest"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/driver"
+ "github.com/containers/libpod/pkg/util"
"github.com/cri-o/ocicni/pkg/ocicni"
spec "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/opencontainers/runtime-tools/generate"
+ "github.com/opencontainers/runtime-tools/validate"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
+ "github.com/syndtr/gocapability/capability"
+)
+
+const (
+ // InspectAnnotationCIDFile is used by Inspect to determine if a
+ // container ID file was created for the container.
+ // If an annotation with this key is found in the OCI spec, it will be
+ // used in the output of Inspect().
+ InspectAnnotationCIDFile = "io.podman.annotations.cid-file"
+ // InspectAnnotationAutoremove is used by Inspect to determine if a
+ // container will be automatically removed on exit.
+ // If an annotation with this key is found in the OCI spec and is one of
+ // the two supported boolean values (InspectResponseTrue and
+ // InspectResponseFalse) it will be used in the output of Inspect().
+ InspectAnnotationAutoremove = "io.podman.annotations.autoremove"
+ // InspectAnnotationVolumesFrom is used by Inspect to identify
+ // containers whose volumes are are being used by this container.
+ // It is expected to be a comma-separated list of container names and/or
+ // IDs.
+ // If an annotation with this key is found in the OCI spec, it will be
+ // used in the output of Inspect().
+ InspectAnnotationVolumesFrom = "io.podman.annotations.volumes-from"
+ // InspectAnnotationPrivileged is used by Inspect to identify containers
+ // which are privileged (IE, running with elevated privileges).
+ // It is expected to be a boolean, populated by one of
+ // InspectResponseTrue or InspectResponseFalse.
+ // If an annotation with this key is found in the OCI spec, it will be
+ // used in the output of Inspect().
+ InspectAnnotationPrivileged = "io.podman.annotations.privileged"
+ // InspectAnnotationPublishAll is used by Inspect to identify containers
+ // which have all the ports from their image published.
+ // It is expected to be a boolean, populated by one of
+ // InspectResponseTrue or InspectResponseFalse.
+ // If an annotation with this key is found in the OCI spec, it will be
+ // used in the output of Inspect().
+ InspectAnnotationPublishAll = "io.podman.annotations.publish-all"
+ // InspectAnnotationInit is used by Inspect to identify containers that
+ // mount an init binary in.
+ // It is expected to be a boolean, populated by one of
+ // InspectResponseTrue or InspectResponseFalse.
+ // If an annotation with this key is found in the OCI spec, it will be
+ // used in the output of Inspect().
+ InspectAnnotationInit = "io.podman.annotations.init"
+
+ // InspectResponseTrue is a boolean True response for an inspect
+ // annotation.
+ InspectResponseTrue = "TRUE"
+ // InspectResponseFalse is a boolean False response for an inspect
+ // annotation.
+ InspectResponseFalse = "FALSE"
)
// InspectContainerData provides a detailed record of a container's configuration
@@ -19,41 +73,42 @@ import (
// compatible with `docker inspect` JSON, but additional fields have been added
// as required to share information not in the original output.
type InspectContainerData struct {
- ID string `json:"Id"`
- Created time.Time `json:"Created"`
- Path string `json:"Path"`
- Args []string `json:"Args"`
- State *InspectContainerState `json:"State"`
- ImageID string `json:"Image"`
- ImageName string `json:"ImageName"`
- Rootfs string `json:"Rootfs"`
- ResolvConfPath string `json:"ResolvConfPath"`
- HostnamePath string `json:"HostnamePath"`
- HostsPath string `json:"HostsPath"`
- StaticDir string `json:"StaticDir"`
- OCIConfigPath string `json:"OCIConfigPath,omitempty"`
- OCIRuntime string `json:"OCIRuntime,omitempty"`
- LogPath string `json:"LogPath"`
- ConmonPidFile string `json:"ConmonPidFile"`
- Name string `json:"Name"`
- RestartCount int32 `json:"RestartCount"`
- Driver string `json:"Driver"`
- MountLabel string `json:"MountLabel"`
- ProcessLabel string `json:"ProcessLabel"`
- AppArmorProfile string `json:"AppArmorProfile"`
- EffectiveCaps []string `json:"EffectiveCaps"`
- BoundingCaps []string `json:"BoundingCaps"`
- ExecIDs []string `json:"ExecIDs"`
- GraphDriver *driver.Data `json:"GraphDriver"`
- SizeRw int64 `json:"SizeRw,omitempty"`
- SizeRootFs int64 `json:"SizeRootFs,omitempty"`
- Mounts []InspectMount `json:"Mounts"`
- Dependencies []string `json:"Dependencies"`
- NetworkSettings *InspectNetworkSettings `json:"NetworkSettings"` //TODO
- ExitCommand []string `json:"ExitCommand"`
- Namespace string `json:"Namespace"`
- IsInfra bool `json:"IsInfra"`
- Config *InspectContainerConfig `json:"Config"`
+ ID string `json:"Id"`
+ Created time.Time `json:"Created"`
+ Path string `json:"Path"`
+ Args []string `json:"Args"`
+ State *InspectContainerState `json:"State"`
+ ImageID string `json:"Image"`
+ ImageName string `json:"ImageName"`
+ Rootfs string `json:"Rootfs"`
+ ResolvConfPath string `json:"ResolvConfPath"`
+ HostnamePath string `json:"HostnamePath"`
+ HostsPath string `json:"HostsPath"`
+ StaticDir string `json:"StaticDir"`
+ OCIConfigPath string `json:"OCIConfigPath,omitempty"`
+ OCIRuntime string `json:"OCIRuntime,omitempty"`
+ LogPath string `json:"LogPath"`
+ ConmonPidFile string `json:"ConmonPidFile"`
+ Name string `json:"Name"`
+ RestartCount int32 `json:"RestartCount"`
+ Driver string `json:"Driver"`
+ MountLabel string `json:"MountLabel"`
+ ProcessLabel string `json:"ProcessLabel"`
+ AppArmorProfile string `json:"AppArmorProfile"`
+ EffectiveCaps []string `json:"EffectiveCaps"`
+ BoundingCaps []string `json:"BoundingCaps"`
+ ExecIDs []string `json:"ExecIDs"`
+ GraphDriver *driver.Data `json:"GraphDriver"`
+ SizeRw int64 `json:"SizeRw,omitempty"`
+ SizeRootFs int64 `json:"SizeRootFs,omitempty"`
+ Mounts []InspectMount `json:"Mounts"`
+ Dependencies []string `json:"Dependencies"`
+ NetworkSettings *InspectNetworkSettings `json:"NetworkSettings"` //TODO
+ ExitCommand []string `json:"ExitCommand"`
+ Namespace string `json:"Namespace"`
+ IsInfra bool `json:"IsInfra"`
+ Config *InspectContainerConfig `json:"Config"`
+ HostConfig *InspectContainerHostConfig `json:"HostConfig"`
}
// InspectContainerConfig holds further data about how a container was initially
@@ -102,6 +157,354 @@ type InspectContainerConfig struct {
Healthcheck *manifest.Schema2HealthConfig `json:"Healthcheck,omitempty"`
}
+// InspectContainerHostConfig holds information used when the container was
+// created.
+// It's very much a Docker-specific struct, retained (mostly) as-is for
+// compatibility. We fill individual fields as best as we can, inferring as much
+// as possible from the spec and container config.
+// Some things cannot be inferred. These will be populated by spec annotations
+// (if available).
+// Field names are fixed for compatibility and cannot be changed.
+// As such, silence lint warnings about them.
+//nolint
+type InspectContainerHostConfig struct {
+ // Binds contains an array of user-added mounts.
+ // Both volume mounts and named volumes are included.
+ // Tmpfs mounts are NOT included.
+ // In 'docker inspect' this is separated into 'Binds' and 'Mounts' based
+ // on how a mount was added. We do not make this distinction and do not
+ // include a Mounts field in inspect.
+ // Format: <src>:<destination>[:<comma-separated options>]
+ Binds []string `json:"Binds"`
+ // ContainerIDFile is a file created during container creation to hold
+ // the ID of the created container.
+ // This is not handled within libpod and is stored in an annotation.
+ ContainerIDFile string `json:"ContainerIDFile"`
+ // LogConfig contains information on the container's logging backend
+ LogConfig *InspectLogConfig `json:"LogConfig"`
+ // NetworkMode is the configuration of the container's network
+ // namespace.
+ // Populated as follows:
+ // default - A network namespace is being created and configured via CNI
+ // none - A network namespace is being created, not configured via CNI
+ // host - No network namespace created
+ // container:<id> - Using another container's network namespace
+ // ns:<path> - A path to a network namespace has been specified
+ NetworkMode string `json:"NetworkMode"`
+ // PortBindings contains the container's port bindings.
+ // It is formatted as map[string][]InspectHostPort.
+ // The string key here is formatted as <integer port number>/<protocol>
+ // and represents the container port. A single container port may be
+ // bound to multiple host ports (on different IPs).
+ PortBindings map[string][]InspectHostPort `json:"PortBindings"`
+ // RestartPolicy contains the container's restart policy.
+ RestartPolicy *InspectRestartPolicy `json:"RestartPolicy"`
+ // AutoRemove is whether the container will be automatically removed on
+ // exiting.
+ // It is not handled directly within libpod and is stored in an
+ // annotation.
+ AutoRemove bool `json:"AutoRemove"`
+ // VolumeDriver is presently unused and is retained for Docker
+ // compatibility.
+ VolumeDriver string `json:"VolumeDriver"`
+ // VolumesFrom is a list of containers which this container uses volumes
+ // from. This is not handled directly within libpod and is stored in an
+ // annotation.
+ // It is formatted as an array of container names and IDs.
+ VolumesFrom []string `json:"VolumesFrom"`
+ // CapAdd is a list of capabilities added to the container.
+ // It is not directly stored by Libpod, and instead computed from the
+ // capabilities listed in the container's spec, compared against a set
+ // of default capabilities.
+ CapAdd []string `json:"CapAdd"`
+ // CapDrop is a list of capabilities removed from the container.
+ // It is not directly stored by libpod, and instead computed from the
+ // capabilities listed in the container's spec, compared against a set
+ // of default capabilities.
+ CapDrop []string `json:"CapDrop"`
+ // Dns is a list of DNS nameservers that will be added to the
+ // container's resolv.conf
+ Dns []string `json:"Dns"`
+ // DnsOptions is a list of DNS options that will be set in the
+ // container's resolv.conf
+ DnsOptions []string `json:"DnsOptions"`
+ // DnsSearch is a list of DNS search domains that will be set in the
+ // container's resolv.conf
+ DnsSearch []string `json:"DnsSearch"`
+ // ExtraHosts contains hosts that will be aded to the container's
+ // /etc/hosts.
+ ExtraHosts []string `json:"ExtraHosts"`
+ // GroupAdd contains groups that the user inside the container will be
+ // added to.
+ GroupAdd []string `json:"GroupAdd"`
+ // IpcMode represents the configuration of the container's IPC
+ // namespace.
+ // Populated as follows:
+ // "" (empty string) - Default, an IPC namespace will be created
+ // host - No IPC namespace created
+ // container:<id> - Using another container's IPC namespace
+ // ns:<path> - A path to an IPC namespace has been specified
+ IpcMode string `json:"IpcMode"`
+ // Cgroup contains the container's cgroup. It is presently not
+ // populated.
+ // TODO.
+ Cgroup string `json:"Cgroup"`
+ // Links is unused, and provided purely for Docker compatibility.
+ Links []string `json:"Links"`
+ // OOMScoreAdj is an adjustment that will be made to the container's OOM
+ // score.
+ OomScoreAdj int `json:"OomScoreAdj"`
+ // PidMode represents the configuration of the container's PID
+ // namespace.
+ // Populated as follows:
+ // "" (empty string) - Default, a PID namespace will be created
+ // host - No PID namespace created
+ // container:<id> - Using another container's PID namespace
+ // ns:<path> - A path to a PID namespace has been specified
+ PidMode string `json:"PidMode"`
+ // Privileged indicates whether the container is running with elevated
+ // privileges.
+ // This has a very specific meaning in the Docker sense, so it's very
+ // difficult to decode from the spec and config, and so is stored as an
+ // annotation.
+ Privileged bool `json:"Privileged"`
+ // PublishAllPorts indicates whether image ports are being published.
+ // This is not directly stored in libpod and is saved as an annotation.
+ PublishAllPorts bool `json:"PublishAllPorts"`
+ // ReadonlyRootfs is whether the container will be mounted read-only.
+ ReadonlyRootfs bool `json:"ReadonlyRootfs"`
+ // SecurityOpt is a list of security-related options that are set in the
+ // container.
+ // TODO.
+ SecurityOpt []string `json:"SecurityOpt"`
+ // Tmpfs is a list of tmpfs filesystems that will be mounted into the
+ // container.
+ // It is a map of destination path to options for the mount.
+ Tmpfs map[string]string `json:"Tmpfs"`
+ // UTSMode represents the configuration of the container's UID
+ // namespace.
+ // Populated as follows:
+ // "" (empty string) - Default, a UTS namespace will be created
+ // host - no UTS namespace created
+ // container:<id> - Using another container's UTS namespace
+ // ns:<path> - A path to a UTS namespace has been specified
+ UTSMode string `json:"UTSMode"`
+ // UsernsMode represents the configuration of the container's user
+ // namespace.
+ // When running rootless, a user namespace is created outside of libpod
+ // to allow some privileged operations. This will not be reflected here.
+ // Populated as follows:
+ // "" (empty string) - No user namespace will be created
+ // private - The container will be run in a user namespace
+ // container:<id> - Using another container's user namespace
+ // ns:<path> - A path to a user namespace has been specified
+ // TODO Rootless has an additional 'keep-id' option, presently not
+ // reflected here.
+ UsernsMode string `json:"UsernsMode"`
+ // ShmSize is the size of the container's SHM device.
+ ShmSize int64 `json:"ShmSize"`
+ // Runtime is provided purely for Docker compatibility.
+ // It is set unconditionally to "oci" as Podman does not presently
+ // support non-OCI runtimes.
+ Runtime string `json:"Runtime"`
+ // ConsoleSize is an array of 2 integers showing the size of the
+ // container's console.
+ // It is only set if the container is creating a terminal.
+ // TODO.
+ ConsoleSize []uint `json:"ConsoleSize"`
+ // Isolation is presently unused and provided solely for Docker
+ // compatibility.
+ Isolation string `json:"Isolation"`
+ // CpuShares indicates the CPU resources allocated to the container.
+ // It is a relative weight in the scheduler for assigning CPU time
+ // versus other CGroups.
+ CpuShares uint64 `json:"CpuShares"`
+ // Memory indicates the memory resources allocated to the container.
+ // This is the limit (in bytes) of RAM the container may use.
+ Memory int64 `json:"Memory"`
+ // NanoCpus indicates number of CPUs allocated to the container.
+ // It is an integer where one full CPU is indicated by 1000000000 (one
+ // billion).
+ // Thus, 2.5 CPUs (fractional portions of CPUs are allowed) would be
+ // 2500000000 (2.5 billion).
+ // In 'docker inspect' this is set exclusively of two further options in
+ // the output (CpuPeriod and CpuQuota) which are both used to implement
+ // this functionality.
+ // We can't distinguish here, so if CpuQuota is set to the default of
+ // 100000, we will set both CpuQuota, CpuPeriod, and NanoCpus. If
+ // CpuQuota is not the default, we will not set NanoCpus.
+ NanoCpus int64 `json:"NanoCpus"`
+ // CgroupParent is the CGroup parent of the container.
+ // Only set if not default.
+ CgroupParent string `json:"CgroupParent"`
+ // BlkioWeight indicates the I/O resources allocated to the container.
+ // It is a relative weight in the scheduler for assigning I/O time
+ // versus other CGroups.
+ BlkioWeight uint16 `json:"BlkioWeight"`
+ // BlkioWeightDevice is an array of I/O resource priorities for
+ // individual device nodes.
+ // Unfortunately, the spec only stores the device's Major/Minor numbers
+ // and not the path, which is used here.
+ // Fortunately, the kernel provides an interface for retrieving the path
+ // of a given node by major:minor at /sys/dev/. However, the exact path
+ // in use may not be what was used in the original CLI invocation -
+ // though it is guaranteed that the device node will be the same, and
+ // using the given path will be functionally identical.
+ BlkioWeightDevice []InspectBlkioWeightDevice `json:"BlkioWeightDevice"`
+ // BlkioDeviceReadBps is an array of I/O throttle parameters for
+ // individual device nodes.
+ // This specifically sets read rate cap in bytes per second for device
+ // nodes.
+ // As with BlkioWeightDevice, we pull the path from /sys/dev, and we
+ // don't guarantee the path will be identical to the original (though
+ // the node will be).
+ BlkioDeviceReadBps []InspectBlkioThrottleDevice `json:"BlkioDeviceReadBps"`
+ // BlkioDeviceWriteBps is an array of I/O throttle parameters for
+ // individual device nodes.
+ // this specifically sets write rate cap in bytes per second for device
+ // nodes.
+ // as with BlkioWeightDevice, we pull the path from /sys/dev, and we
+ // don't guarantee the path will be identical to the original (though
+ // the node will be).
+ BlkioDeviceWriteBps []InspectBlkioThrottleDevice `json:"BlkioDeviceWriteBps"`
+ // BlkioDeviceReadIOps is an array of I/O throttle parameters for
+ // individual device nodes.
+ // This specifically sets the read rate cap in iops per second for
+ // device nodes.
+ // As with BlkioWeightDevice, we pull the path from /sys/dev, and we
+ // don't guarantee the path will be identical to the original (though
+ // the node will be).
+ BlkioDeviceReadIOps []InspectBlkioThrottleDevice `json:"BlkioDeviceReadIOps"`
+ // BlkioDeviceWriteIOps is an array of I/O throttle parameters for
+ // individual device nodes.
+ // This specifically sets the write rate cap in iops per second for
+ // device nodes.
+ // As with BlkioWeightDevice, we pull the path from /sys/dev, and we
+ // don't guarantee the path will be identical to the original (though
+ // the node will be).
+ BlkioDeviceWriteIOps []InspectBlkioThrottleDevice `json:"BlkioDeviceWriteIOps"`
+ // CpuPeriod is the length of a CPU period in microseconds.
+ // It relates directly to CpuQuota.
+ CpuPeriod uint64 `json:"CpuPeriod"`
+ // CpuPeriod is the amount of time (in microseconds) that a container
+ // can use the CPU in every CpuPeriod.
+ CpuQuota int64 `json:"CpuQuota"`
+ // CpuRealtimePeriod is the length of time (in microseconds) of the CPU
+ // realtime period. If set to 0, no time will be allocated to realtime
+ // tasks.
+ CpuRealtimePeriod uint64 `json:"CpuRealtimePeriod"`
+ // CpuRealtimeRuntime is the length of time (in microseconds) allocated
+ // for realtime tasks within every CpuRealtimePeriod.
+ CpuRealtimeRuntime int64 `json:"CpuRealtimeRuntime"`
+ // CpusetCpus is the is the set of CPUs that the container will execute
+ // on. Formatted as `0-3` or `0,2`. Default (if unset) is all CPUs.
+ CpusetCpus string `json:"CpusetCpus"`
+ // CpusetMems is the set of memory nodes the container will use.
+ // Formatted as `0-3` or `0,2`. Default (if unset) is all memory nodes.
+ CpusetMems string `json:"CpusetMems"`
+ // Devices is a list of device nodes that will be added to the
+ // container.
+ // These are stored in the OCI spec only as type, major, minor while we
+ // display the host path. We convert this with /sys/dev, but we cannot
+ // guarantee that the host path will be identical - only that the actual
+ // device will be.
+ Devices []InspectDevice `json:"Devices"`
+ // DiskQuota is the maximum amount of disk space the container may use
+ // (in bytes).
+ // Presently not populated.
+ // TODO.
+ DiskQuota uint64 `json:"DiskQuota"`
+ // KernelMemory is the maximum amount of memory the kernel will devote
+ // to the container.
+ KernelMemory int64 `json:"KernelMemory"`
+ // MemoryReservation is the reservation (soft limit) of memory available
+ // to the container. Soft limits are warnings only and can be exceeded.
+ MemoryReservation int64 `json:"MemoryReservation"`
+ // MemorySwap is the total limit for all memory available to the
+ // container, including swap. 0 indicates that there is no limit to the
+ // amount of memory available.
+ MemorySwap int64 `json:"MemorySwap"`
+ // MemorySwappiness is the willingness of the kernel to page container
+ // memory to swap. It is an integer from 0 to 100, with low numbers
+ // being more likely to be put into swap.
+ // -1, the default, will not set swappiness and use the system defaults.
+ MemorySwappiness int64 `json:"MemorySwappiness"`
+ // OomKillDisable indicates whether the kernel OOM killer is disabled
+ // for the container.
+ OomKillDisable bool `json:"OomKillDisable"`
+ // Init indicates whether the container has an init mounted into it.
+ Init bool `json:"Init,omitempty"`
+ // PidsLimit is the maximum number of PIDs what may be created within
+ // the container. 0, the default, indicates no limit.
+ PidsLimit int64 `json:"PidsLimit"`
+ // Ulimits is a set of ulimits that will be set within the container.
+ Ulimits []InspectUlimit `json:"Ulimits"`
+ // CpuCount is Windows-only and not presently implemented.
+ CpuCount uint64 `json:"CpuCount"`
+ // CpuPercent is Windows-only and not presently implemented.
+ CpuPercent uint64 `json:"CpuPercent"`
+ // IOMaximumIOps is Windows-only and not presently implemented.
+ IOMaximumIOps uint64 `json:"IOMaximumIOps"`
+ // IOMaximumBandwidth is Windows-only and not presently implemented.
+ IOMaximumBandwidth uint64 `json:"IOMaximumBandwidth"`
+}
+
+// InspectLogConfig holds information about a container's configured log driver
+// and is presently unused. It is retained for Docker compatibility.
+type InspectLogConfig struct {
+ Type string `json:"Type"`
+ Config map[string]string `json:"Config"` //idk type, TODO
+}
+
+// InspectRestartPolicy holds information about the container's restart policy.
+type InspectRestartPolicy struct {
+ // Name contains the container's restart policy.
+ // Allowable values are "no" or "" (take no action),
+ // "on-failure" (restart on non-zero exit code, with an optional max
+ // retry count), and "always" (always restart on container stop, unless
+ // explicitly requested by API).
+ // Note that this is NOT actually a name of any sort - the poor naming
+ // is for Docker compatibility.
+ Name string `json:"Name"`
+ // MaximumRetryCount is the maximum number of retries allowed if the
+ // "on-failure" restart policy is in use. Not used if "on-failure" is
+ // not set.
+ MaximumRetryCount uint `json:"MaximumRetryCount"`
+}
+
+// InspectBlkioWeightDevice holds information about the relative weight
+// of an individual device node. Weights are used in the I/O scheduler to give
+// relative priority to some accesses.
+type InspectBlkioWeightDevice struct {
+ // Path is the path to the device this applies to.
+ Path string `json:"Path"`
+ // Weight is the relative weight the scheduler will use when scheduling
+ // I/O.
+ Weight uint16 `json:"Weight"`
+}
+
+// InspectBlkioThrottleDevice holds information about a speed cap for a device
+// node. This cap applies to a specific operation (read, write, etc) on the given
+// node.
+type InspectBlkioThrottleDevice struct {
+ // Path is the path to the device this applies to.
+ Path string `json:"Path"`
+ // Rate is the maximum rate. It is in either bytes per second or iops
+ // per second, determined by where it is used - documentation will
+ // indicate which is appropriate.
+ Rate uint64 `json:"Rate"`
+}
+
+// InspectUlimit is a ulimit that will be applied to the container.
+type InspectUlimit struct {
+ // Name is the name (type) of the ulimit.
+ Name string `json:"Name"`
+ // Soft is the soft limit that will be applied.
+ Soft uint64 `json:"Soft"`
+ // Hard is the hard limit that will be applied.
+ Hard uint64 `json:"Hard"`
+}
+
// InspectMount provides a record of a single mount in a container. It contains
// fields for both named and normal volumes. Only user-specified volumes will be
// included, and tmpfs volumes are not included even if the user specified them.
@@ -131,6 +534,29 @@ type InspectMount struct {
Propagation string `json:"Propagation"`
}
+// InspectDevice is a single device that will be mounted into the container.
+type InspectDevice struct {
+ // PathOnHost is the path of the device on the host.
+ PathOnHost string `json:"PathOnHost"`
+ // PathInContainer is the path of the device within the container.
+ PathInContainer string `json:"PathInContainer"`
+ // CgroupPermissions is the permissions of the mounted device.
+ // Presently not populated.
+ // TODO.
+ CgroupPermissions string `json:"CgroupPermissions"`
+}
+
+// InspectHostPort provides information on a port on the host that a container's
+// port is bound to.
+type InspectHostPort struct {
+ // IP on the host we are bound to. "" if not specified (binding to all
+ // IPs).
+ HostIP string `json:"HostIp"`
+ // Port on the host we are bound to. No special formatting - just an
+ // integer stuffed into a string.
+ HostPort string `json:"HostPort"`
+}
+
// InspectContainerState provides a detailed record of a container's current
// state. It is returned as part of InspectContainerData.
// As with InspectContainerData, many portions of this struct are matched to
@@ -206,7 +632,7 @@ func (c *Container) Inspect(size bool) (*InspectContainerData, error) {
func (c *Container) getContainerInspectData(size bool, driverData *driver.Data) (*InspectContainerData, error) {
config := c.config
runtimeInfo := c.state
- stateSpec, err := c.specFromState()
+ ctrSpec, err := c.specFromState()
if err != nil {
return nil, err
}
@@ -244,7 +670,8 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data)
}
}
- mounts, err := c.getInspectMounts(stateSpec)
+ namedVolumes, mounts := c.sortUserVolumes(ctrSpec)
+ inspectMounts, err := c.getInspectMounts(ctrSpec, namedVolumes, mounts)
if err != nil {
return nil, err
}
@@ -255,7 +682,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data)
Path: path,
Args: args,
State: &InspectContainerState{
- OciVersion: stateSpec.Version,
+ OciVersion: ctrSpec.Version,
Status: runtimeInfo.State.String(),
Running: runtimeInfo.State == define.ContainerStateRunning,
Paused: runtimeInfo.State == define.ContainerStatePaused,
@@ -285,12 +712,12 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data)
Driver: driverData.Name,
MountLabel: config.MountLabel,
ProcessLabel: config.ProcessLabel,
- EffectiveCaps: stateSpec.Process.Capabilities.Effective,
- BoundingCaps: stateSpec.Process.Capabilities.Bounding,
- AppArmorProfile: stateSpec.Process.ApparmorProfile,
+ EffectiveCaps: ctrSpec.Process.Capabilities.Effective,
+ BoundingCaps: ctrSpec.Process.Capabilities.Bounding,
+ AppArmorProfile: ctrSpec.Process.ApparmorProfile,
ExecIDs: execIDs,
GraphDriver: driverData,
- Mounts: mounts,
+ Mounts: inspectMounts,
Dependencies: c.Dependencies(),
NetworkSettings: &InspectNetworkSettings{
Bridge: "", // TODO
@@ -338,12 +765,18 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data)
// Get information on the container's network namespace (if present)
data = c.getContainerNetworkInfo(data)
- inspectConfig, err := c.generateInspectContainerConfig(stateSpec)
+ inspectConfig, err := c.generateInspectContainerConfig(ctrSpec)
if err != nil {
return nil, err
}
data.Config = inspectConfig
+ hostConfig, err := c.generateInspectContainerHostConfig(ctrSpec, namedVolumes, mounts)
+ if err != nil {
+ return nil, err
+ }
+ data.HostConfig = hostConfig
+
if size {
rootFsSize, err := c.rootFsSize()
if err != nil {
@@ -362,7 +795,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data)
// Get inspect-formatted mounts list.
// Only includes user-specified mounts. Only includes bind mounts and named
// volumes, not tmpfs volumes.
-func (c *Container) getInspectMounts(ctrSpec *spec.Spec) ([]InspectMount, error) {
+func (c *Container) getInspectMounts(ctrSpec *spec.Spec, namedVolumes []*ContainerNamedVolume, mounts []spec.Mount) ([]InspectMount, error) {
inspectMounts := []InspectMount{}
// No mounts, return early
@@ -370,7 +803,6 @@ func (c *Container) getInspectMounts(ctrSpec *spec.Spec) ([]InspectMount, error)
return inspectMounts, nil
}
- namedVolumes, mounts := c.sortUserVolumes(ctrSpec)
for _, volume := range namedVolumes {
mountStruct := InspectMount{}
mountStruct.Type = "volume"
@@ -493,3 +925,492 @@ func (c *Container) generateInspectContainerConfig(spec *spec.Spec) (*InspectCon
return ctrConfig, nil
}
+
+// Generate the InspectContainerHostConfig struct for the HostConfig field of
+// Inspect.
+func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, namedVolumes []*ContainerNamedVolume, mounts []spec.Mount) (*InspectContainerHostConfig, error) {
+ hostConfig := new(InspectContainerHostConfig)
+
+ logConfig := new(InspectLogConfig)
+ logConfig.Type = c.config.LogDriver
+ hostConfig.LogConfig = logConfig
+
+ restartPolicy := new(InspectRestartPolicy)
+ restartPolicy.Name = c.config.RestartPolicy
+ restartPolicy.MaximumRetryCount = c.config.RestartRetries
+ hostConfig.RestartPolicy = restartPolicy
+
+ hostConfig.Dns = make([]string, 0, len(c.config.DNSServer))
+ for _, dns := range c.config.DNSServer {
+ hostConfig.Dns = append(hostConfig.Dns, dns.String())
+ }
+
+ hostConfig.DnsOptions = make([]string, 0, len(c.config.DNSOption))
+ for _, opt := range c.config.DNSOption {
+ hostConfig.DnsOptions = append(hostConfig.DnsOptions, opt)
+ }
+
+ hostConfig.DnsSearch = make([]string, 0, len(c.config.DNSSearch))
+ for _, search := range c.config.DNSSearch {
+ hostConfig.DnsSearch = append(hostConfig.DnsSearch, search)
+ }
+
+ hostConfig.ExtraHosts = make([]string, 0, len(c.config.HostAdd))
+ for _, host := range c.config.HostAdd {
+ hostConfig.ExtraHosts = append(hostConfig.ExtraHosts, host)
+ }
+
+ hostConfig.GroupAdd = make([]string, 0, len(c.config.Groups))
+ for _, group := range c.config.Groups {
+ hostConfig.GroupAdd = append(hostConfig.GroupAdd, group)
+ }
+
+ if ctrSpec.Process != nil {
+ if ctrSpec.Process.OOMScoreAdj != nil {
+ hostConfig.OomScoreAdj = *ctrSpec.Process.OOMScoreAdj
+ }
+ }
+
+ hostConfig.ReadonlyRootfs = ctrSpec.Root.Readonly
+ hostConfig.ShmSize = c.config.ShmSize
+ hostConfig.Runtime = "oci"
+
+ // This is very expensive to initialize.
+ // So we don't want to initialize it unless we absolutely have to - IE,
+ // there are things that require a major:minor to path translation.
+ var deviceNodes map[string]string
+
+ // Annotations
+ if ctrSpec.Annotations != nil {
+ hostConfig.ContainerIDFile = ctrSpec.Annotations[InspectAnnotationCIDFile]
+ if ctrSpec.Annotations[InspectAnnotationAutoremove] == InspectResponseTrue {
+ hostConfig.AutoRemove = true
+ }
+ if ctrs, ok := ctrSpec.Annotations[InspectAnnotationVolumesFrom]; ok {
+ hostConfig.VolumesFrom = strings.Split(ctrs, ",")
+ }
+ if ctrSpec.Annotations[InspectAnnotationPrivileged] == InspectResponseTrue {
+ hostConfig.Privileged = true
+ }
+ if ctrSpec.Annotations[InspectAnnotationInit] == InspectResponseTrue {
+ hostConfig.Init = true
+ }
+ }
+
+ // Resource limits
+ if ctrSpec.Linux != nil {
+ if ctrSpec.Linux.Resources != nil {
+ if ctrSpec.Linux.Resources.CPU != nil {
+ if ctrSpec.Linux.Resources.CPU.Shares != nil {
+ hostConfig.CpuShares = *ctrSpec.Linux.Resources.CPU.Shares
+ }
+ if ctrSpec.Linux.Resources.CPU.Period != nil {
+ hostConfig.CpuPeriod = *ctrSpec.Linux.Resources.CPU.Period
+ }
+ if ctrSpec.Linux.Resources.CPU.Quota != nil {
+ hostConfig.CpuQuota = *ctrSpec.Linux.Resources.CPU.Quota
+ }
+ if ctrSpec.Linux.Resources.CPU.RealtimePeriod != nil {
+ hostConfig.CpuRealtimePeriod = *ctrSpec.Linux.Resources.CPU.RealtimePeriod
+ }
+ if ctrSpec.Linux.Resources.CPU.RealtimeRuntime != nil {
+ hostConfig.CpuRealtimeRuntime = *ctrSpec.Linux.Resources.CPU.RealtimeRuntime
+ }
+ hostConfig.CpusetCpus = ctrSpec.Linux.Resources.CPU.Cpus
+ hostConfig.CpusetMems = ctrSpec.Linux.Resources.CPU.Mems
+ }
+ if ctrSpec.Linux.Resources.Memory != nil {
+ if ctrSpec.Linux.Resources.Memory.Limit != nil {
+ hostConfig.Memory = *ctrSpec.Linux.Resources.Memory.Limit
+ }
+ if ctrSpec.Linux.Resources.Memory.Kernel != nil {
+ hostConfig.KernelMemory = *ctrSpec.Linux.Resources.Memory.Kernel
+ }
+ if ctrSpec.Linux.Resources.Memory.Reservation != nil {
+ hostConfig.MemoryReservation = *ctrSpec.Linux.Resources.Memory.Reservation
+ }
+ if ctrSpec.Linux.Resources.Memory.Swap != nil {
+ hostConfig.MemorySwap = *ctrSpec.Linux.Resources.Memory.Swap
+ }
+ if ctrSpec.Linux.Resources.Memory.Swappiness != nil {
+ hostConfig.MemorySwappiness = int64(*ctrSpec.Linux.Resources.Memory.Swappiness)
+ } else {
+ // Swappiness has a default of -1
+ hostConfig.MemorySwappiness = -1
+ }
+ if ctrSpec.Linux.Resources.Memory.DisableOOMKiller != nil {
+ hostConfig.OomKillDisable = *ctrSpec.Linux.Resources.Memory.DisableOOMKiller
+ }
+ }
+ if ctrSpec.Linux.Resources.Pids != nil {
+ hostConfig.PidsLimit = ctrSpec.Linux.Resources.Pids.Limit
+ }
+ if ctrSpec.Linux.Resources.BlockIO != nil {
+ if ctrSpec.Linux.Resources.BlockIO.Weight != nil {
+ hostConfig.BlkioWeight = *ctrSpec.Linux.Resources.BlockIO.Weight
+ }
+ hostConfig.BlkioWeightDevice = []InspectBlkioWeightDevice{}
+ for _, dev := range ctrSpec.Linux.Resources.BlockIO.WeightDevice {
+ key := fmt.Sprintf("%d:%d", dev.Major, dev.Minor)
+ // TODO: how do we handle LeafWeight vs
+ // Weight? For now, ignore anything
+ // without Weight set.
+ if dev.Weight == nil {
+ logrus.Warnf("Ignoring weight device %s as it lacks a weight", key)
+ continue
+ }
+ 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 weight device %s in system devices", key)
+ continue
+ }
+ weightDev := InspectBlkioWeightDevice{}
+ weightDev.Path = path
+ weightDev.Weight = *dev.Weight
+ hostConfig.BlkioWeightDevice = append(hostConfig.BlkioWeightDevice, weightDev)
+ }
+
+ handleThrottleDevice := func(devs []spec.LinuxThrottleDevice) ([]InspectBlkioThrottleDevice, error) {
+ out := []InspectBlkioThrottleDevice{}
+ for _, dev := range devs {
+ 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 throttle device %s in system devices", key)
+ continue
+ }
+ throttleDev := InspectBlkioThrottleDevice{}
+ throttleDev.Path = path
+ throttleDev.Rate = dev.Rate
+ out = append(out, throttleDev)
+ }
+ return out, nil
+ }
+
+ readBps, err := handleThrottleDevice(ctrSpec.Linux.Resources.BlockIO.ThrottleReadBpsDevice)
+ if err != nil {
+ return nil, err
+ }
+ hostConfig.BlkioDeviceReadBps = readBps
+
+ writeBps, err := handleThrottleDevice(ctrSpec.Linux.Resources.BlockIO.ThrottleWriteBpsDevice)
+ if err != nil {
+ return nil, err
+ }
+ hostConfig.BlkioDeviceWriteBps = writeBps
+
+ readIops, err := handleThrottleDevice(ctrSpec.Linux.Resources.BlockIO.ThrottleReadIOPSDevice)
+ if err != nil {
+ return nil, err
+ }
+ hostConfig.BlkioDeviceReadIOps = readIops
+
+ writeIops, err := handleThrottleDevice(ctrSpec.Linux.Resources.BlockIO.ThrottleWriteIOPSDevice)
+ if err != nil {
+ return nil, err
+ }
+ hostConfig.BlkioDeviceWriteIOps = writeIops
+ }
+ }
+ }
+
+ // NanoCPUs.
+ // This is only calculated if CpuPeriod == 100000.
+ // It is given in nanoseconds, versus the microseconds used elsewhere -
+ // so multiply by 10000 (not sure why, but 1000 is off by 10).
+ if hostConfig.CpuPeriod == 100000 {
+ hostConfig.NanoCpus = 10000 * hostConfig.CpuQuota
+ }
+
+ // Bind mounts, formatted as src:dst.
+ // We'll be appending some options that aren't necessarily in the
+ // original command line... but no helping that from inside libpod.
+ binds := []string{}
+ tmpfs := make(map[string]string)
+ for _, namedVol := range namedVolumes {
+ if len(namedVol.Options) > 0 {
+ binds = append(binds, fmt.Sprintf("%s:%s:%s", namedVol.Name, namedVol.Dest, strings.Join(namedVol.Options, ",")))
+ } else {
+ binds = append(binds, fmt.Sprintf("%s:%s", namedVol.Name, namedVol.Dest))
+ }
+ }
+ for _, mount := range mounts {
+ if mount.Type == "tmpfs" {
+ tmpfs[mount.Destination] = strings.Join(mount.Options, ",")
+ } else {
+ // TODO - maybe we should parse for empty source/destination
+ // here. Would be confusing if we print just a bare colon.
+ if len(mount.Options) > 0 {
+ binds = append(binds, fmt.Sprintf("%s:%s:%s", mount.Source, mount.Destination, strings.Join(mount.Options, ",")))
+ } else {
+ binds = append(binds, fmt.Sprintf("%s:%s", mount.Source, mount.Destination))
+ }
+ }
+ }
+ hostConfig.Binds = binds
+ hostConfig.Tmpfs = tmpfs
+
+ // Network mode parsing.
+ networkMode := ""
+ if c.config.CreateNetNS {
+ networkMode = "default"
+ } else if c.config.NetNsCtr != "" {
+ networkMode = fmt.Sprintf("container:%s", c.config.NetNsCtr)
+ } else {
+ // Find the spec's network namespace.
+ // If there is none, it's host networking.
+ // If there is one and it has a path, it's "ns:".
+ foundNetNS := false
+ for _, ns := range ctrSpec.Linux.Namespaces {
+ if ns.Type == spec.NetworkNamespace {
+ foundNetNS = true
+ if ns.Path != "" {
+ networkMode = fmt.Sprintf("ns:%s", ns.Path)
+ } else {
+ networkMode = "none"
+ }
+ break
+ }
+ }
+ if !foundNetNS {
+ networkMode = "host"
+ }
+ }
+ hostConfig.NetworkMode = networkMode
+
+ // Port bindings.
+ // Only populate if we're using CNI to configure the network.
+ portBindings := make(map[string][]InspectHostPort)
+ if c.config.CreateNetNS {
+ for _, port := range c.config.PortMappings {
+ key := fmt.Sprintf("%d/%s", port.ContainerPort, port.Protocol)
+ hostPorts := portBindings[key]
+ if hostPorts == nil {
+ hostPorts = []InspectHostPort{}
+ }
+ hostPorts = append(hostPorts, InspectHostPort{
+ HostIP: port.HostIP,
+ HostPort: fmt.Sprintf("%d", port.HostPort),
+ })
+ portBindings[key] = hostPorts
+ }
+ }
+ hostConfig.PortBindings = portBindings
+
+ // Cap add and cap drop.
+ // We need a default set of capabilities to compare against.
+ // The OCI generate package has one, and is commonly used, so we'll
+ // use it.
+ // Problem: there are 5 sets of capabilities.
+ // Use the bounding set for this computation, it's the most encompassing
+ // (but still not perfect).
+ capAdd := []string{}
+ capDrop := []string{}
+ // No point in continuing if we got a spec without a Process block...
+ if ctrSpec.Process != nil {
+ // Max an O(1) lookup table for default bounding caps.
+ boundingCaps := make(map[string]bool)
+ g, err := generate.New("linux")
+ if err != nil {
+ return nil, err
+ }
+ if !hostConfig.Privileged {
+ for _, cap := range g.Config.Process.Capabilities.Bounding {
+ boundingCaps[cap] = true
+ }
+ } else {
+ // If we are privileged, use all caps.
+ for _, cap := range capability.List() {
+ if g.HostSpecific && cap > validate.LastCap() {
+ continue
+ }
+ boundingCaps[fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String()))] = true
+ }
+ }
+ // Iterate through spec caps.
+ // If it's not in default bounding caps, it was added.
+ // If it is, delete from the default set. Whatever remains after
+ // we finish are the dropped caps.
+ for _, cap := range ctrSpec.Process.Capabilities.Bounding {
+ if _, ok := boundingCaps[cap]; ok {
+ delete(boundingCaps, cap)
+ } else {
+ capAdd = append(capAdd, cap)
+ }
+ }
+ for cap := range boundingCaps {
+ capDrop = append(capDrop, cap)
+ }
+ }
+ hostConfig.CapAdd = capAdd
+ hostConfig.CapDrop = capDrop
+
+ // IPC Namespace mode
+ ipcMode := ""
+ if c.config.IPCNsCtr != "" {
+ ipcMode = fmt.Sprintf("container:%s", c.config.IPCNsCtr)
+ } else {
+ // Locate the spec's IPC namespace.
+ // If there is none, it's ipc=host.
+ // If there is one and it has a path, it's "ns:".
+ // If no path, it's default - the empty string.
+ foundIPCNS := false
+ for _, ns := range ctrSpec.Linux.Namespaces {
+ if ns.Type == spec.IPCNamespace {
+ foundIPCNS = true
+ if ns.Path != "" {
+ ipcMode = fmt.Sprintf("ns:%s", ns.Path)
+ }
+ break
+ }
+ }
+ if !foundIPCNS {
+ ipcMode = "host"
+ }
+ }
+ hostConfig.IpcMode = ipcMode
+
+ // CGroup parent
+ // Need to check if it's the default, and not print if so.
+ defaultCgroupParent := ""
+ switch c.runtime.config.CgroupManager {
+ case CgroupfsCgroupsManager:
+ defaultCgroupParent = CgroupfsDefaultCgroupParent
+ case SystemdCgroupsManager:
+ defaultCgroupParent = SystemdDefaultCgroupParent
+ }
+ if c.config.CgroupParent != defaultCgroupParent {
+ hostConfig.CgroupParent = c.config.CgroupParent
+ }
+
+ // PID namespace mode
+ pidMode := ""
+ if c.config.PIDNsCtr != "" {
+ pidMode = fmt.Sprintf("container:%s", c.config.PIDNsCtr)
+ } else {
+ // Locate the spec's PID namespace.
+ // If there is none, it's pid=host.
+ // If there is one and it has a path, it's "ns:".
+ // If there is no path, it's default - the empty string.
+ foundPIDNS := false
+ for _, ns := range ctrSpec.Linux.Namespaces {
+ if ns.Type == spec.PIDNamespace {
+ foundPIDNS = true
+ if ns.Path != "" {
+ pidMode = fmt.Sprintf("ns:%s", ns.Path)
+ }
+ break
+ }
+ }
+ if !foundPIDNS {
+ pidMode = "host"
+ }
+ }
+ hostConfig.PidMode = pidMode
+
+ // UTS namespace mode
+ utsMode := ""
+ if c.config.UTSNsCtr != "" {
+ utsMode = fmt.Sprintf("container:%s", c.config.UTSNsCtr)
+ } else {
+ // Locate the spec's UTS namespace.
+ // If there is none, it's uts=host.
+ // If there is one and it has a path, it's "ns:".
+ // If there is no path, it's default - the empty string.
+ foundUTSNS := false
+ for _, ns := range ctrSpec.Linux.Namespaces {
+ if ns.Type == spec.UTSNamespace {
+ foundUTSNS = true
+ if ns.Path != "" {
+ utsMode = fmt.Sprintf("ns:%s", ns.Path)
+ }
+ break
+ }
+ }
+ if !foundUTSNS {
+ utsMode = "host"
+ }
+ }
+ hostConfig.UTSMode = utsMode
+
+ // User namespace mode
+ usernsMode := ""
+ if c.config.UserNsCtr != "" {
+ usernsMode = fmt.Sprintf("container:%s", c.config.UserNsCtr)
+ } else {
+ // Locate the spec's user namespace.
+ // If there is none, it's default - the empty string.
+ // If there is one, it's "private" if no path, or "ns:" if
+ // there's a path.
+ for _, ns := range ctrSpec.Linux.Namespaces {
+ if ns.Type == spec.UserNamespace {
+ if ns.Path != "" {
+ usernsMode = fmt.Sprintf("ns:%s", ns.Path)
+ } else {
+ usernsMode = "private"
+ }
+ }
+ }
+ }
+ hostConfig.UsernsMode = usernsMode
+
+ // Devices
+ // Do not include if privileged - assumed that all devices will be
+ // included.
+ hostConfig.Devices = []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 := InspectDevice{}
+ newDev.PathOnHost = path
+ newDev.PathInContainer = dev.Path
+ hostConfig.Devices = append(hostConfig.Devices, newDev)
+ }
+ }
+
+ // Ulimits
+ hostConfig.Ulimits = []InspectUlimit{}
+ if ctrSpec.Process != nil {
+ for _, limit := range ctrSpec.Process.Rlimits {
+ newLimit := InspectUlimit{}
+ newLimit.Name = limit.Type
+ newLimit.Soft = limit.Soft
+ newLimit.Hard = limit.Hard
+ hostConfig.Ulimits = append(hostConfig.Ulimits, newLimit)
+ }
+ }
+
+ // Terminal size
+ // We can't actually get this for now...
+ // So default to something sane.
+ // TODO: Populate this.
+ hostConfig.ConsoleSize = []uint{0, 0}
+
+ return hostConfig, nil
+}
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index 6d8d399f4..ca627f3aa 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -418,6 +418,43 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
}
}
+ // Add annotations
+ if configSpec.Annotations == nil {
+ configSpec.Annotations = make(map[string]string)
+ }
+
+ if config.CidFile != "" {
+ configSpec.Annotations[libpod.InspectAnnotationCIDFile] = config.CidFile
+ }
+
+ if config.Rm {
+ configSpec.Annotations[libpod.InspectAnnotationAutoremove] = libpod.InspectResponseTrue
+ } else {
+ configSpec.Annotations[libpod.InspectAnnotationAutoremove] = libpod.InspectResponseFalse
+ }
+
+ if len(config.VolumesFrom) > 0 {
+ configSpec.Annotations[libpod.InspectAnnotationVolumesFrom] = strings.Join(config.VolumesFrom, ",")
+ }
+
+ if config.Privileged {
+ configSpec.Annotations[libpod.InspectAnnotationPrivileged] = libpod.InspectResponseTrue
+ } else {
+ configSpec.Annotations[libpod.InspectAnnotationPrivileged] = libpod.InspectResponseFalse
+ }
+
+ if config.PublishAll {
+ configSpec.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseTrue
+ } else {
+ configSpec.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseFalse
+ }
+
+ if config.Init {
+ configSpec.Annotations[libpod.InspectAnnotationInit] = libpod.InspectResponseTrue
+ } else {
+ configSpec.Annotations[libpod.InspectAnnotationInit] = libpod.InspectResponseFalse
+ }
+
return configSpec, nil
}
diff --git a/pkg/util/utils_linux.go b/pkg/util/utils_linux.go
index 47fa1031f..318bd2b1b 100644
--- a/pkg/util/utils_linux.go
+++ b/pkg/util/utils_linux.go
@@ -1,7 +1,14 @@
package util
import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "syscall"
+
"github.com/containers/psgo"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
// GetContainerPidInformationDescriptors returns a string slice of all supported
@@ -9,3 +16,39 @@ import (
func GetContainerPidInformationDescriptors() ([]string, error) {
return psgo.ListDescriptors(), nil
}
+
+// FindDeviceNodes parses /dev/ into a set of major:minor -> path, where
+// [major:minor] is the device's major and minor numbers formatted as, for
+// example, 2:0 and path is the path to the device node.
+// Symlinks to nodes are ignored.
+func FindDeviceNodes() (map[string]string, error) {
+ nodes := make(map[string]string)
+ err := filepath.Walk("/dev", func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ logrus.Warnf("Error descending into path %s: %v", path, err)
+ return filepath.SkipDir
+ }
+
+ // If we aren't a device node, do nothing.
+ if info.Mode()&(os.ModeDevice|os.ModeCharDevice) == 0 {
+ return nil
+ }
+
+ // We are a device node. Get major/minor.
+ sysstat, ok := info.Sys().(*syscall.Stat_t)
+ if !ok {
+ return errors.Errorf("Could not convert stat output for use")
+ }
+ major := uint64(sysstat.Rdev / 256)
+ minor := uint64(sysstat.Rdev % 256)
+
+ nodes[fmt.Sprintf("%d:%d", major, minor)] = path
+
+ return nil
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ return nodes, nil
+}
diff --git a/pkg/util/utils_unsupported.go b/pkg/util/utils_unsupported.go
new file mode 100644
index 000000000..62805d7c8
--- /dev/null
+++ b/pkg/util/utils_unsupported.go
@@ -0,0 +1,12 @@
+// +build darwin windows
+
+package util
+
+import (
+ "github.com/pkg/errors"
+)
+
+// FindDeviceNodes is not implemented anywhere except Linux.
+func FindDeviceNodes() (map[string]string, error) {
+ return nil, errors.Errorf("not supported on non-Linux OSes")
+}
diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go
index 6855a7231..700e02b0c 100644
--- a/pkg/varlinkapi/containers.go
+++ b/pkg/varlinkapi/containers.go
@@ -19,7 +19,6 @@ import (
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/logs"
"github.com/containers/libpod/pkg/adapter/shortcuts"
- cc "github.com/containers/libpod/pkg/spec"
"github.com/containers/storage/pkg/archive"
"github.com/pkg/errors"
)
@@ -170,16 +169,7 @@ func (i *LibpodAPI) InspectContainer(call iopodman.VarlinkCall, name string) err
if err != nil {
return call.ReplyContainerNotFound(name, err.Error())
}
- inspectInfo, err := ctr.Inspect(true)
- if err != nil {
- return call.ReplyErrorOccurred(err.Error())
- }
- artifact, err := getArtifact(ctr)
- if err != nil {
- return call.ReplyErrorOccurred(err.Error())
- }
-
- data, err := shared.GetCtrInspectInfo(ctr.Config(), inspectInfo, artifact)
+ data, err := ctr.Inspect(true)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
@@ -587,18 +577,6 @@ func (i *LibpodAPI) ContainerRestore(call iopodman.VarlinkCall, name string, kee
return call.ReplyContainerRestore(ctr.ID())
}
-func getArtifact(ctr *libpod.Container) (*cc.CreateConfig, error) {
- var createArtifact cc.CreateConfig
- artifact, err := ctr.GetArtifact("create-config")
- if err != nil {
- return nil, err
- }
- if err := json.Unmarshal(artifact, &createArtifact); err != nil {
- return nil, err
- }
- return &createArtifact, nil
-}
-
// ContainerConfig returns just the container.config struct
func (i *LibpodAPI) ContainerConfig(call iopodman.VarlinkCall, name string) error {
ctr, err := i.Runtime.LookupContainer(name)
diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go
index abaf2cccf..ef1c85518 100644
--- a/test/e2e/common_test.go
+++ b/test/e2e/common_test.go
@@ -12,7 +12,6 @@ import (
"testing"
"time"
- "github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/inspect"
"github.com/containers/libpod/pkg/rootless"
@@ -322,7 +321,7 @@ func (s *PodmanSessionIntegration) InspectImageJSON() []inspect.ImageData {
}
// InspectContainer returns a container's inspect data in JSON format
-func (p *PodmanTestIntegration) InspectContainer(name string) []shared.InspectContainer {
+func (p *PodmanTestIntegration) InspectContainer(name string) []libpod.InspectContainerData {
cmd := []string{"inspect", name}
session := p.Podman(cmd)
session.WaitWithDefaultTimeout()
@@ -481,8 +480,8 @@ func (p *PodmanTestIntegration) PullImage(image string) error {
// InspectContainerToJSON takes the session output of an inspect
// container and returns json
-func (s *PodmanSessionIntegration) InspectContainerToJSON() []shared.InspectContainer {
- var i []shared.InspectContainer
+func (s *PodmanSessionIntegration) InspectContainerToJSON() []libpod.InspectContainerData {
+ var i []libpod.InspectContainerData
err := json.Unmarshal(s.Out.Contents(), &i)
Expect(err).To(BeNil())
return i
diff --git a/vendor/modules.txt b/vendor/modules.txt
index ad2f69976..462ba1408 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -355,8 +355,8 @@ github.com/opencontainers/runc/libcontainer/system
github.com/opencontainers/runtime-spec/specs-go
# github.com/opencontainers/runtime-tools v0.9.0
github.com/opencontainers/runtime-tools/generate
-github.com/opencontainers/runtime-tools/generate/seccomp
github.com/opencontainers/runtime-tools/validate
+github.com/opencontainers/runtime-tools/generate/seccomp
github.com/opencontainers/runtime-tools/filepath
github.com/opencontainers/runtime-tools/specerror
github.com/opencontainers/runtime-tools/error