summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/kpod/create.go363
-rw-r--r--cmd/kpod/create_cli.go106
-rw-r--r--cmd/kpod/images.go33
-rw-r--r--cmd/kpod/inspect.go308
-rw-r--r--cmd/kpod/run.go12
-rw-r--r--cmd/kpod/spec.go162
-rw-r--r--cmd/kpod/spec_test.go4
-rw-r--r--docs/kpod-inspect.1.md163
-rw-r--r--libkpod/container_data.go210
-rw-r--r--libpod/container.go90
-rw-r--r--libpod/container_inspect.go70
-rw-r--r--libpod/driver/driver.go20
-rw-r--r--libpod/image_inspect.go81
-rw-r--r--libpod/images/image_data.go202
-rw-r--r--libpod/inspect_data.go103
-rw-r--r--libpod/runtime_img.go69
-rw-r--r--test/kpod_inspect.bats10
17 files changed, 1050 insertions, 956 deletions
diff --git a/cmd/kpod/create.go b/cmd/kpod/create.go
index fc6e519fa..3548ad7df 100644
--- a/cmd/kpod/create.go
+++ b/cmd/kpod/create.go
@@ -1,6 +1,7 @@
package main
import (
+ "encoding/json"
"fmt"
"os"
"strconv"
@@ -36,91 +37,92 @@ var (
)
type createResourceConfig struct {
- blkioWeight uint16 // blkio-weight
- blkioWeightDevice []string // blkio-weight-device
- cpuPeriod uint64 // cpu-period
- cpuQuota int64 // cpu-quota
- cpuRtPeriod uint64 // cpu-rt-period
- cpuRtRuntime int64 // cpu-rt-runtime
- cpuShares uint64 // cpu-shares
- cpus string // cpus
- cpusetCpus string
- cpusetMems string // cpuset-mems
- deviceReadBps []string // device-read-bps
- deviceReadIOps []string // device-read-iops
- deviceWriteBps []string // device-write-bps
- deviceWriteIOps []string // device-write-iops
- disableOomKiller bool // oom-kill-disable
- kernelMemory int64 // kernel-memory
- memory int64 //memory
- memoryReservation int64 // memory-reservation
- memorySwap int64 //memory-swap
- memorySwappiness int // memory-swappiness
- oomScoreAdj int //oom-score-adj
- pidsLimit int64 // pids-limit
- shmSize string
- ulimit []string //ulimit
+ BlkioWeight uint16 // blkio-weight
+ BlkioWeightDevice []string // blkio-weight-device
+ CpuPeriod uint64 // cpu-period
+ CpuQuota int64 // cpu-quota
+ CpuRtPeriod uint64 // cpu-rt-period
+ CpuRtRuntime int64 // cpu-rt-runtime
+ CpuShares uint64 // cpu-shares
+ Cpus string // cpus
+ CpusetCpus string
+ CpusetMems string // cpuset-mems
+ DeviceReadBps []string // device-read-bps
+ DeviceReadIOps []string // device-read-iops
+ DeviceWriteBps []string // device-write-bps
+ DeviceWriteIOps []string // device-write-iops
+ DisableOomKiller bool // oom-kill-disable
+ KernelMemory int64 // kernel-memory
+ Memory int64 //memory
+ MemoryReservation int64 // memory-reservation
+ MemorySwap int64 //memory-swap
+ MemorySwappiness int // memory-swappiness
+ OomScoreAdj int //oom-score-adj
+ PidsLimit int64 // pids-limit
+ ShmSize string
+ Ulimit []string //ulimit
}
type createConfig struct {
- runtime *libpod.Runtime
- args []string
- capAdd []string // cap-add
- capDrop []string // cap-drop
- cidFile string
- cgroupParent string // cgroup-parent
- command []string
- detach bool // detach
- devices []*pb.Device // device
- dnsOpt []string //dns-opt
- dnsSearch []string //dns-search
- dnsServers []string //dns
- entrypoint string //entrypoint
- env map[string]string //env
- expose []string //expose
- groupAdd []uint32 // group-add
- hostname string //hostname
- image string
- interactive bool //interactive
- ipcMode container.IpcMode //ipc
- ip6Address string //ipv6
- ipAddress string //ip
- labels map[string]string //label
- linkLocalIP []string // link-local-ip
- logDriver string // log-driver
- logDriverOpt []string // log-opt
- macAddress string //mac-address
- name string //name
- netMode container.NetworkMode //net
- network string //network
- networkAlias []string //network-alias
- pidMode container.PidMode //pid
- nsUser string
- pod string //pod
- privileged bool //privileged
- publish []string //publish
- publishAll bool //publish-all
- readOnlyRootfs bool //read-only
- resources createResourceConfig
- rm bool //rm
- shmDir string
- sigProxy bool //sig-proxy
- stopSignal string // stop-signal
- stopTimeout int64 // stop-timeout
- storageOpts []string //storage-opt
- sysctl map[string]string //sysctl
- tmpfs []string // tmpfs
- tty bool //tty
- user uint32 //user
- group uint32 // group
- utsMode container.UTSMode //uts
- volumes []string //volume
- workDir string //workdir
- mountLabel string //SecurityOpts
- processLabel string //SecurityOpts
- noNewPrivileges bool //SecurityOpts
- apparmorProfile string //SecurityOpts
- seccompProfilePath string //SecurityOpts
+ Runtime *libpod.Runtime
+ Args []string
+ CapAdd []string // cap-add
+ CapDrop []string // cap-drop
+ CidFile string
+ CgroupParent string // cgroup-parent
+ Command []string
+ Detach bool // detach
+ Devices []*pb.Device // device
+ DnsOpt []string //dns-opt
+ DnsSearch []string //dns-search
+ DnsServers []string //dns
+ Entrypoint string //entrypoint
+ Env map[string]string //env
+ Expose []string //expose
+ GroupAdd []uint32 // group-add
+ Hostname string //hostname
+ Image string
+ Interactive bool //interactive
+ IpcMode container.IpcMode //ipc
+ Ip6Address string //ipv6
+ IpAddress string //ip
+ Labels map[string]string //label
+ LinkLocalIP []string // link-local-ip
+ LogDriver string // log-driver
+ LogDriverOpt []string // log-opt
+ MacAddress string //mac-address
+ Name string //name
+ NetMode container.NetworkMode //net
+ Network string //network
+ NetworkAlias []string //network-alias
+ PidMode container.PidMode //pid
+ NsUser string
+ Pod string //pod
+ Privileged bool //privileged
+ Publish []string //publish
+ PublishAll bool //publish-all
+ ReadOnlyRootfs bool //read-only
+ Resources createResourceConfig
+ Rm bool //rm
+ ShmDir string
+ SigProxy bool //sig-proxy
+ StopSignal string // stop-signal
+ StopTimeout int64 // stop-timeout
+ StorageOpts []string //storage-opt
+ Sysctl map[string]string //sysctl
+ Tmpfs []string // tmpfs
+ Tty bool //tty
+ User uint32 //user
+ Group uint32 // group
+ UtsMode container.UTSMode //uts
+ Volumes []string //volume
+ WorkDir string //workdir
+ MountLabel string //SecurityOpts
+ ProcessLabel string //SecurityOpts
+ NoNewPrivileges bool //SecurityOpts
+ ApparmorProfile string //SecurityOpts
+ SeccompProfilePath string //SecurityOpts
+ SecurityOpts []string
}
var createDescription = "Creates a new container from the given image or" +
@@ -160,7 +162,7 @@ func createCmd(c *cli.Context) error {
}
// Deal with the image after all the args have been checked
- createImage := runtime.NewImage(createConfig.image)
+ createImage := runtime.NewImage(createConfig.Image)
createImage.LocalName, _ = createImage.GetLocalImageName()
if createImage.LocalName == "" {
// The image wasnt found by the user input'd name or its fqname
@@ -203,13 +205,21 @@ func createCmd(c *cli.Context) error {
}
// Gather up the options for NewContainer which consist of With... funcs
options = append(options, libpod.WithRootFSFromImage(imageID, imageName, false))
- options = append(options, libpod.WithSELinuxLabels(createConfig.processLabel, createConfig.mountLabel))
- options = append(options, libpod.WithShmDir(createConfig.shmDir))
+ options = append(options, libpod.WithSELinuxLabels(createConfig.ProcessLabel, createConfig.MountLabel))
+ options = append(options, libpod.WithShmDir(createConfig.ShmDir))
ctr, err := runtime.NewContainer(runtimeSpec, options...)
if err != nil {
return err
}
+ createConfigJSON, err := json.Marshal(createConfig)
+ if err != nil {
+ return err
+ }
+ if err := ctr.AddArtifact("create-config", createConfigJSON); err != nil {
+ return err
+ }
+
logrus.Debug("new container created ", ctr.ID())
if c.String("cidfile") != "" {
@@ -229,29 +239,29 @@ func parseSecurityOpt(config *createConfig, securityOpts []string) error {
err error
)
- if config.pidMode.IsHost() {
+ if config.PidMode.IsHost() {
labelOpts = append(labelOpts, label.DisableSecOpt()...)
- } else if config.pidMode.IsContainer() {
- ctr, err := config.runtime.LookupContainer(config.pidMode.Container())
+ } else if config.PidMode.IsContainer() {
+ ctr, err := config.Runtime.LookupContainer(config.PidMode.Container())
if err != nil {
- return errors.Wrapf(err, "container %q not found", config.pidMode.Container())
+ return errors.Wrapf(err, "container %q not found", config.PidMode.Container())
}
labelOpts = append(labelOpts, label.DupSecOpt(ctr.ProcessLabel())...)
}
- if config.ipcMode.IsHost() {
+ if config.IpcMode.IsHost() {
labelOpts = append(labelOpts, label.DisableSecOpt()...)
- } else if config.ipcMode.IsContainer() {
- ctr, err := config.runtime.LookupContainer(config.ipcMode.Container())
+ } else if config.IpcMode.IsContainer() {
+ ctr, err := config.Runtime.LookupContainer(config.IpcMode.Container())
if err != nil {
- return errors.Wrapf(err, "container %q not found", config.ipcMode.Container())
+ return errors.Wrapf(err, "container %q not found", config.IpcMode.Container())
}
labelOpts = append(labelOpts, label.DupSecOpt(ctr.ProcessLabel())...)
}
for _, opt := range securityOpts {
if opt == "no-new-privileges" {
- config.noNewPrivileges = true
+ config.NoNewPrivileges = true
} else {
con := strings.SplitN(opt, "=", 2)
if len(con) != 2 {
@@ -262,25 +272,25 @@ func parseSecurityOpt(config *createConfig, securityOpts []string) error {
case "label":
labelOpts = append(labelOpts, con[1])
case "apparmor":
- config.apparmorProfile = con[1]
+ config.ApparmorProfile = con[1]
case "seccomp":
- config.seccompProfilePath = con[1]
+ config.SeccompProfilePath = con[1]
default:
return fmt.Errorf("Invalid --security-opt 2: %q", opt)
}
}
}
- if config.seccompProfilePath == "" {
+ if config.SeccompProfilePath == "" {
if _, err := os.Stat(seccompDefaultPath); err != nil {
if !os.IsNotExist(err) {
return errors.Wrapf(err, "can't check if %q exists", seccompDefaultPath)
}
} else {
- config.seccompProfilePath = seccompDefaultPath
+ config.SeccompProfilePath = seccompDefaultPath
}
}
- config.processLabel, config.mountLabel, err = label.InitLabels(labelOpts)
+ config.ProcessLabel, config.MountLabel, err = label.InitLabels(labelOpts)
return err
}
@@ -403,88 +413,89 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime) (*createConfig, er
}
config := &createConfig{
- runtime: runtime,
- capAdd: c.StringSlice("cap-add"),
- capDrop: c.StringSlice("cap-drop"),
- cgroupParent: c.String("cgroup-parent"),
- command: command,
- detach: c.Bool("detach"),
- dnsOpt: c.StringSlice("dns-opt"),
- dnsSearch: c.StringSlice("dns-search"),
- dnsServers: c.StringSlice("dns"),
- entrypoint: c.String("entrypoint"),
- env: env,
- expose: c.StringSlice("expose"),
- groupAdd: groupAdd,
- hostname: c.String("hostname"),
- image: image,
- interactive: c.Bool("interactive"),
- ip6Address: c.String("ipv6"),
- ipAddress: c.String("ip"),
- labels: labels,
- linkLocalIP: c.StringSlice("link-local-ip"),
- logDriver: c.String("log-driver"),
- logDriverOpt: c.StringSlice("log-opt"),
- macAddress: c.String("mac-address"),
- name: c.String("name"),
- network: c.String("network"),
- networkAlias: c.StringSlice("network-alias"),
- ipcMode: ipcMode,
- netMode: container.NetworkMode(c.String("network")),
- utsMode: utsMode,
- pidMode: pidMode,
- pod: c.String("pod"),
- privileged: c.Bool("privileged"),
- publish: c.StringSlice("publish"),
- publishAll: c.Bool("publish-all"),
- readOnlyRootfs: c.Bool("read-only"),
- resources: createResourceConfig{
- blkioWeight: blkioWeight,
- blkioWeightDevice: c.StringSlice("blkio-weight-device"),
- cpuShares: c.Uint64("cpu-shares"),
- cpuPeriod: c.Uint64("cpu-period"),
- cpusetCpus: c.String("cpu-period"),
- cpusetMems: c.String("cpuset-mems"),
- cpuQuota: c.Int64("cpu-quota"),
- cpuRtPeriod: c.Uint64("cpu-rt-period"),
- cpuRtRuntime: c.Int64("cpu-rt-runtime"),
- cpus: c.String("cpus"),
- deviceReadBps: c.StringSlice("device-read-bps"),
- deviceReadIOps: c.StringSlice("device-read-iops"),
- deviceWriteBps: c.StringSlice("device-write-bps"),
- deviceWriteIOps: c.StringSlice("device-write-iops"),
- disableOomKiller: c.Bool("oom-kill-disable"),
- shmSize: c.String("shm-size"),
- memory: memoryLimit,
- memoryReservation: memoryReservation,
- memorySwap: memorySwap,
- memorySwappiness: c.Int("memory-swappiness"),
- kernelMemory: memoryKernel,
- oomScoreAdj: c.Int("oom-score-adj"),
-
- pidsLimit: c.Int64("pids-limit"),
- ulimit: c.StringSlice("ulimit"),
+ Runtime: runtime,
+ CapAdd: c.StringSlice("cap-add"),
+ CapDrop: c.StringSlice("cap-drop"),
+ CgroupParent: c.String("cgroup-parent"),
+ Command: command,
+ Detach: c.Bool("detach"),
+ DnsOpt: c.StringSlice("dns-opt"),
+ DnsSearch: c.StringSlice("dns-search"),
+ DnsServers: c.StringSlice("dns"),
+ Entrypoint: c.String("entrypoint"),
+ Env: env,
+ Expose: c.StringSlice("expose"),
+ GroupAdd: groupAdd,
+ Hostname: c.String("hostname"),
+ Image: image,
+ Interactive: c.Bool("interactive"),
+ Ip6Address: c.String("ipv6"),
+ IpAddress: c.String("ip"),
+ Labels: labels,
+ LinkLocalIP: c.StringSlice("link-local-ip"),
+ LogDriver: c.String("log-driver"),
+ LogDriverOpt: c.StringSlice("log-opt"),
+ MacAddress: c.String("mac-address"),
+ Name: c.String("name"),
+ Network: c.String("network"),
+ NetworkAlias: c.StringSlice("network-alias"),
+ IpcMode: ipcMode,
+ NetMode: container.NetworkMode(c.String("network")),
+ UtsMode: utsMode,
+ PidMode: pidMode,
+ Pod: c.String("pod"),
+ Privileged: c.Bool("privileged"),
+ Publish: c.StringSlice("publish"),
+ PublishAll: c.Bool("publish-all"),
+ ReadOnlyRootfs: c.Bool("read-only"),
+ Resources: createResourceConfig{
+ BlkioWeight: blkioWeight,
+ BlkioWeightDevice: c.StringSlice("blkio-weight-device"),
+ CpuShares: c.Uint64("cpu-shares"),
+ CpuPeriod: c.Uint64("cpu-period"),
+ CpusetCpus: c.String("cpu-period"),
+ CpusetMems: c.String("cpuset-mems"),
+ CpuQuota: c.Int64("cpu-quota"),
+ CpuRtPeriod: c.Uint64("cpu-rt-period"),
+ CpuRtRuntime: c.Int64("cpu-rt-runtime"),
+ Cpus: c.String("cpus"),
+ DeviceReadBps: c.StringSlice("device-read-bps"),
+ DeviceReadIOps: c.StringSlice("device-read-iops"),
+ DeviceWriteBps: c.StringSlice("device-write-bps"),
+ DeviceWriteIOps: c.StringSlice("device-write-iops"),
+ DisableOomKiller: c.Bool("oom-kill-disable"),
+ ShmSize: c.String("shm-size"),
+ Memory: memoryLimit,
+ MemoryReservation: memoryReservation,
+ MemorySwap: memorySwap,
+ MemorySwappiness: c.Int("memory-swappiness"),
+ KernelMemory: memoryKernel,
+ OomScoreAdj: c.Int("oom-score-adj"),
+
+ PidsLimit: c.Int64("pids-limit"),
+ Ulimit: c.StringSlice("ulimit"),
},
- rm: c.Bool("rm"),
- shmDir: shmDir,
- sigProxy: c.Bool("sig-proxy"),
- stopSignal: c.String("stop-signal"),
- stopTimeout: c.Int64("stop-timeout"),
- storageOpts: c.StringSlice("storage-opt"),
- sysctl: sysctl,
- tmpfs: c.StringSlice("tmpfs"),
- tty: tty,
- user: uid,
- group: gid,
- volumes: c.StringSlice("volume"),
- workDir: c.String("workdir"),
- }
-
- if !config.privileged {
+ Rm: c.Bool("rm"),
+ ShmDir: shmDir,
+ SigProxy: c.Bool("sig-proxy"),
+ StopSignal: c.String("stop-signal"),
+ StopTimeout: c.Int64("stop-timeout"),
+ StorageOpts: c.StringSlice("storage-opt"),
+ Sysctl: sysctl,
+ Tmpfs: c.StringSlice("tmpfs"),
+ Tty: tty,
+ User: uid,
+ Group: gid,
+ Volumes: c.StringSlice("volume"),
+ WorkDir: c.String("workdir"),
+ }
+
+ if !config.Privileged {
if err := parseSecurityOpt(config, c.StringSlice("security-opt")); err != nil {
return nil, err
}
}
+ config.SecurityOpts = c.StringSlice("security-opt")
warnings, err := verifyContainerResources(config, false)
if err != nil {
return nil, err
diff --git a/cmd/kpod/create_cli.go b/cmd/kpod/create_cli.go
index 9686b89a7..a162cb319 100644
--- a/cmd/kpod/create_cli.go
+++ b/cmd/kpod/create_cli.go
@@ -111,131 +111,131 @@ func verifyContainerResources(config *createConfig, update bool) ([]string, erro
sysInfo := sysinfo.New(true)
// memory subsystem checks and adjustments
- if config.resources.memory != 0 && config.resources.memory < linuxMinMemory {
+ if config.Resources.Memory != 0 && config.Resources.Memory < linuxMinMemory {
return warnings, fmt.Errorf("minimum memory limit allowed is 4MB")
}
- if config.resources.memory > 0 && !sysInfo.MemoryLimit {
+ if config.Resources.Memory > 0 && !sysInfo.MemoryLimit {
warnings = addWarning(warnings, "Your kernel does not support memory limit capabilities or the cgroup is not mounted. Limitation discarded.")
- config.resources.memory = 0
- config.resources.memorySwap = -1
+ config.Resources.Memory = 0
+ config.Resources.MemorySwap = -1
}
- if config.resources.memory > 0 && config.resources.memorySwap != -1 && !sysInfo.SwapLimit {
+ if config.Resources.Memory > 0 && config.Resources.MemorySwap != -1 && !sysInfo.SwapLimit {
warnings = addWarning(warnings, "Your kernel does not support swap limit capabilities,or the cgroup is not mounted. Memory limited without swap.")
- config.resources.memorySwap = -1
+ config.Resources.MemorySwap = -1
}
- if config.resources.memory > 0 && config.resources.memorySwap > 0 && config.resources.memorySwap < config.resources.memory {
+ if config.Resources.Memory > 0 && config.Resources.MemorySwap > 0 && config.Resources.MemorySwap < config.Resources.Memory {
return warnings, fmt.Errorf("minimum memoryswap limit should be larger than memory limit, see usage")
}
- if config.resources.memory == 0 && config.resources.memorySwap > 0 && !update {
- return warnings, fmt.Errorf("you should always set the Memory limit when using Memoryswap limit, see usage")
+ if config.Resources.Memory == 0 && config.Resources.MemorySwap > 0 && !update {
+ return warnings, fmt.Errorf("you should always set the memory limit when using memoryswap limit, see usage")
}
- if config.resources.memorySwappiness != -1 {
+ if config.Resources.MemorySwappiness != -1 {
if !sysInfo.MemorySwappiness {
msg := "Your kernel does not support memory swappiness capabilities, or the cgroup is not mounted. Memory swappiness discarded."
warnings = addWarning(warnings, msg)
- config.resources.memorySwappiness = -1
+ config.Resources.MemorySwappiness = -1
} else {
- swappiness := config.resources.memorySwappiness
+ swappiness := config.Resources.MemorySwappiness
if swappiness < -1 || swappiness > 100 {
return warnings, fmt.Errorf("invalid value: %v, valid memory swappiness range is 0-100", swappiness)
}
}
}
- if config.resources.memoryReservation > 0 && !sysInfo.MemoryReservation {
+ if config.Resources.MemoryReservation > 0 && !sysInfo.MemoryReservation {
warnings = addWarning(warnings, "Your kernel does not support memory soft limit capabilities or the cgroup is not mounted. Limitation discarded.")
- config.resources.memoryReservation = 0
+ config.Resources.MemoryReservation = 0
}
- if config.resources.memoryReservation > 0 && config.resources.memoryReservation < linuxMinMemory {
+ if config.Resources.MemoryReservation > 0 && config.Resources.MemoryReservation < linuxMinMemory {
return warnings, fmt.Errorf("minimum memory reservation allowed is 4MB")
}
- if config.resources.memory > 0 && config.resources.memoryReservation > 0 && config.resources.memory < config.resources.memoryReservation {
+ if config.Resources.Memory > 0 && config.Resources.MemoryReservation > 0 && config.Resources.Memory < config.Resources.MemoryReservation {
return warnings, fmt.Errorf("minimum memory limit can not be less than memory reservation limit, see usage")
}
- if config.resources.kernelMemory > 0 && !sysInfo.KernelMemory {
+ if config.Resources.KernelMemory > 0 && !sysInfo.KernelMemory {
warnings = addWarning(warnings, "Your kernel does not support kernel memory limit capabilities or the cgroup is not mounted. Limitation discarded.")
- config.resources.kernelMemory = 0
+ config.Resources.KernelMemory = 0
}
- if config.resources.kernelMemory > 0 && config.resources.kernelMemory < linuxMinMemory {
+ if config.Resources.KernelMemory > 0 && config.Resources.KernelMemory < linuxMinMemory {
return warnings, fmt.Errorf("minimum kernel memory limit allowed is 4MB")
}
- if config.resources.disableOomKiller == true && !sysInfo.OomKillDisable {
+ if config.Resources.DisableOomKiller == true && !sysInfo.OomKillDisable {
// only produce warnings if the setting wasn't to *disable* the OOM Kill; no point
// warning the caller if they already wanted the feature to be off
warnings = addWarning(warnings, "Your kernel does not support OomKillDisable. OomKillDisable discarded.")
- config.resources.disableOomKiller = false
+ config.Resources.DisableOomKiller = false
}
- if config.resources.pidsLimit != 0 && !sysInfo.PidsLimit {
+ if config.Resources.PidsLimit != 0 && !sysInfo.PidsLimit {
warnings = addWarning(warnings, "Your kernel does not support pids limit capabilities or the cgroup is not mounted. PIDs limit discarded.")
- config.resources.pidsLimit = 0
+ config.Resources.PidsLimit = 0
}
- if config.resources.cpuShares > 0 && !sysInfo.CPUShares {
+ if config.Resources.CpuShares > 0 && !sysInfo.CPUShares {
warnings = addWarning(warnings, "Your kernel does not support CPU shares or the cgroup is not mounted. Shares discarded.")
- config.resources.cpuShares = 0
+ config.Resources.CpuShares = 0
}
- if config.resources.cpuPeriod > 0 && !sysInfo.CPUCfsPeriod {
+ if config.Resources.CpuPeriod > 0 && !sysInfo.CPUCfsPeriod {
warnings = addWarning(warnings, "Your kernel does not support CPU cfs period or the cgroup is not mounted. Period discarded.")
- config.resources.cpuPeriod = 0
+ config.Resources.CpuPeriod = 0
}
- if config.resources.cpuPeriod != 0 && (config.resources.cpuPeriod < 1000 || config.resources.cpuPeriod > 1000000) {
+ if config.Resources.CpuPeriod != 0 && (config.Resources.CpuPeriod < 1000 || config.Resources.CpuPeriod > 1000000) {
return warnings, fmt.Errorf("CPU cfs period can not be less than 1ms (i.e. 1000) or larger than 1s (i.e. 1000000)")
}
- if config.resources.cpuQuota > 0 && !sysInfo.CPUCfsQuota {
+ if config.Resources.CpuQuota > 0 && !sysInfo.CPUCfsQuota {
warnings = addWarning(warnings, "Your kernel does not support CPU cfs quota or the cgroup is not mounted. Quota discarded.")
- config.resources.cpuQuota = 0
+ config.Resources.CpuQuota = 0
}
- if config.resources.cpuQuota > 0 && config.resources.cpuQuota < 1000 {
+ if config.Resources.CpuQuota > 0 && config.Resources.CpuQuota < 1000 {
return warnings, fmt.Errorf("CPU cfs quota can not be less than 1ms (i.e. 1000)")
}
// cpuset subsystem checks and adjustments
- if (config.resources.cpusetCpus != "" || config.resources.cpusetMems != "") && !sysInfo.Cpuset {
+ if (config.Resources.CpusetCpus != "" || config.Resources.CpusetMems != "") && !sysInfo.Cpuset {
warnings = addWarning(warnings, "Your kernel does not support cpuset or the cgroup is not mounted. Cpuset discarded.")
- config.resources.cpusetCpus = ""
- config.resources.cpusetMems = ""
+ config.Resources.CpusetCpus = ""
+ config.Resources.CpusetMems = ""
}
- cpusAvailable, err := sysInfo.IsCpusetCpusAvailable(config.resources.cpusetCpus)
+ cpusAvailable, err := sysInfo.IsCpusetCpusAvailable(config.Resources.CpusetCpus)
if err != nil {
- return warnings, fmt.Errorf("invalid value %s for cpuset cpus", config.resources.cpusetCpus)
+ return warnings, fmt.Errorf("invalid value %s for cpuset cpus", config.Resources.CpusetCpus)
}
if !cpusAvailable {
- return warnings, fmt.Errorf("requested CPUs are not available - requested %s, available: %s", config.resources.cpusetCpus, sysInfo.Cpus)
+ return warnings, fmt.Errorf("requested CPUs are not available - requested %s, available: %s", config.Resources.CpusetCpus, sysInfo.Cpus)
}
- memsAvailable, err := sysInfo.IsCpusetMemsAvailable(config.resources.cpusetMems)
+ memsAvailable, err := sysInfo.IsCpusetMemsAvailable(config.Resources.CpusetMems)
if err != nil {
- return warnings, fmt.Errorf("invalid value %s for cpuset mems", config.resources.cpusetMems)
+ return warnings, fmt.Errorf("invalid value %s for cpuset mems", config.Resources.CpusetMems)
}
if !memsAvailable {
- return warnings, fmt.Errorf("requested memory nodes are not available - requested %s, available: %s", config.resources.cpusetMems, sysInfo.Mems)
+ return warnings, fmt.Errorf("requested memory nodes are not available - requested %s, available: %s", config.Resources.CpusetMems, sysInfo.Mems)
}
// blkio subsystem checks and adjustments
- if config.resources.blkioWeight > 0 && !sysInfo.BlkioWeight {
+ if config.Resources.BlkioWeight > 0 && !sysInfo.BlkioWeight {
warnings = addWarning(warnings, "Your kernel does not support Block I/O weight or the cgroup is not mounted. Weight discarded.")
- config.resources.blkioWeight = 0
+ config.Resources.BlkioWeight = 0
}
- if config.resources.blkioWeight > 0 && (config.resources.blkioWeight < 10 || config.resources.blkioWeight > 1000) {
+ if config.Resources.BlkioWeight > 0 && (config.Resources.BlkioWeight < 10 || config.Resources.BlkioWeight > 1000) {
return warnings, fmt.Errorf("range of blkio weight is from 10 to 1000")
}
- if len(config.resources.blkioWeightDevice) > 0 && !sysInfo.BlkioWeightDevice {
+ if len(config.Resources.BlkioWeightDevice) > 0 && !sysInfo.BlkioWeightDevice {
warnings = addWarning(warnings, "Your kernel does not support Block I/O weight_device or the cgroup is not mounted. Weight-device discarded.")
- config.resources.blkioWeightDevice = []string{}
+ config.Resources.BlkioWeightDevice = []string{}
}
- if len(config.resources.deviceReadBps) > 0 && !sysInfo.BlkioReadBpsDevice {
+ if len(config.Resources.DeviceReadBps) > 0 && !sysInfo.BlkioReadBpsDevice {
warnings = addWarning(warnings, "Your kernel does not support BPS Block I/O read limit or the cgroup is not mounted. Block I/O BPS read limit discarded")
- config.resources.deviceReadBps = []string{}
+ config.Resources.DeviceReadBps = []string{}
}
- if len(config.resources.deviceWriteBps) > 0 && !sysInfo.BlkioWriteBpsDevice {
+ if len(config.Resources.DeviceWriteBps) > 0 && !sysInfo.BlkioWriteBpsDevice {
warnings = addWarning(warnings, "Your kernel does not support BPS Block I/O write limit or the cgroup is not mounted. Block I/O BPS write limit discarded.")
- config.resources.deviceWriteBps = []string{}
+ config.Resources.DeviceWriteBps = []string{}
}
- if len(config.resources.deviceReadIOps) > 0 && !sysInfo.BlkioReadIOpsDevice {
+ if len(config.Resources.DeviceReadIOps) > 0 && !sysInfo.BlkioReadIOpsDevice {
warnings = addWarning(warnings, "Your kernel does not support IOPS Block read limit or the cgroup is not mounted. Block I/O IOPS read limit discarded.")
- config.resources.deviceReadIOps = []string{}
+ config.Resources.DeviceReadIOps = []string{}
}
- if len(config.resources.deviceWriteIOps) > 0 && !sysInfo.BlkioWriteIOpsDevice {
+ if len(config.Resources.DeviceWriteIOps) > 0 && !sysInfo.BlkioWriteIOpsDevice {
warnings = addWarning(warnings, "Your kernel does not support IOPS Block I/O write limit or the cgroup is not mounted. Block I/O IOPS write limit discarded.")
- config.resources.deviceWriteIOps = []string{}
+ config.Resources.DeviceWriteIOps = []string{}
}
return warnings, nil
diff --git a/cmd/kpod/images.go b/cmd/kpod/images.go
index 76f5ca69a..2b1003ebd 100644
--- a/cmd/kpod/images.go
+++ b/cmd/kpod/images.go
@@ -6,7 +6,6 @@ import (
"strings"
"time"
- "github.com/containers/image/types"
"github.com/containers/storage"
"github.com/docker/go-units"
digest "github.com/opencontainers/go-digest"
@@ -208,18 +207,18 @@ func getImagesTemplateOutput(runtime *libpod.Runtime, images []*storage.Image, o
}
}
- info, imageDigest, size, _ := runtime.InfoAndDigestAndSize(*img)
- if info != nil {
- createdTime = info.Created
+ imgData, _ := runtime.GetImageInspectInfo(*img)
+ if imgData != nil {
+ createdTime = *imgData.Created
}
params := imagesTemplateParams{
Repository: repository,
Tag: tag,
ID: imageID,
- Digest: imageDigest,
+ Digest: imgData.Digest,
Created: units.HumanDuration(time.Since((createdTime))) + " ago",
- Size: units.HumanSizeWithPrecision(float64(size), 3),
+ Size: units.HumanSizeWithPrecision(float64(imgData.Size), 3),
}
imagesOutput = append(imagesOutput, params)
}
@@ -231,17 +230,17 @@ func getImagesJSONOutput(runtime *libpod.Runtime, images []*storage.Image) (imag
for _, img := range images {
createdTime := img.Created
- info, imageDigest, size, _ := runtime.InfoAndDigestAndSize(*img)
- if info != nil {
- createdTime = info.Created
+ imgData, _ := runtime.GetImageInspectInfo(*img)
+ if imgData != nil {
+ createdTime = *imgData.Created
}
params := imagesJSONParams{
ID: img.ID,
Name: img.Names,
- Digest: imageDigest,
+ Digest: imgData.Digest,
Created: createdTime,
- Size: size,
+ Size: imgData.Size,
}
imagesOutput = append(imagesOutput, params)
}
@@ -274,7 +273,7 @@ func generateImagesOutput(runtime *libpod.Runtime, images []*storage.Image, opts
func generateImagesFilter(params *libpod.ImageFilterParams, filterType string) libpod.ImageFilter {
switch filterType {
case "label":
- return func(image *storage.Image, info *types.ImageInspectInfo) bool {
+ return func(image *storage.Image, info *libpod.ImageData) bool {
if params == nil || params.Label == "" {
return true
}
@@ -291,21 +290,21 @@ func generateImagesFilter(params *libpod.ImageFilterParams, filterType string) l
return false
}
case "before-image":
- return func(image *storage.Image, info *types.ImageInspectInfo) bool {
+ return func(image *storage.Image, info *libpod.ImageData) bool {
if params == nil || params.BeforeImage.IsZero() {
return true
}
return info.Created.Before(params.BeforeImage)
}
case "since-image":
- return func(image *storage.Image, info *types.ImageInspectInfo) bool {
+ return func(image *storage.Image, info *libpod.ImageData) bool {
if params == nil || params.SinceImage.IsZero() {
return true
}
return info.Created.After(params.SinceImage)
}
case "dangling":
- return func(image *storage.Image, info *types.ImageInspectInfo) bool {
+ return func(image *storage.Image, info *libpod.ImageData) bool {
if params == nil || params.Dangling == "" {
return true
}
@@ -318,14 +317,14 @@ func generateImagesFilter(params *libpod.ImageFilterParams, filterType string) l
return false
}
case "reference":
- return func(image *storage.Image, info *types.ImageInspectInfo) bool {
+ return func(image *storage.Image, info *libpod.ImageData) bool {
if params == nil || params.ReferencePattern == "" {
return true
}
return libpod.MatchesReference(params.ImageName, params.ReferencePattern)
}
case "image-input":
- return func(image *storage.Image, info *types.ImageInspectInfo) bool {
+ return func(image *storage.Image, info *libpod.ImageData) bool {
if params == nil || params.ImageInput == "" {
return true
}
diff --git a/cmd/kpod/inspect.go b/cmd/kpod/inspect.go
index a70e285ac..e2f9ec97e 100644
--- a/cmd/kpod/inspect.go
+++ b/cmd/kpod/inspect.go
@@ -1,10 +1,12 @@
package main
import (
+ "encoding/json"
+
+ specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/projectatomic/libpod/cmd/kpod/formats"
- "github.com/projectatomic/libpod/libkpod"
- "github.com/projectatomic/libpod/libpod/images"
+ "github.com/projectatomic/libpod/libpod"
"github.com/urfave/cli"
)
@@ -53,56 +55,63 @@ func inspectCmd(c *cli.Context) error {
return err
}
- itemType := c.String("type")
- size := c.Bool("size")
+ runtime, err := getRuntime(c)
+ if err != nil {
+ return errors.Wrapf(err, "error creating libpod runtime")
+ }
+ defer runtime.Shutdown(false)
- switch itemType {
- case inspectTypeContainer:
- case inspectTypeImage:
- case inspectAll:
- default:
+ if c.String("type") != inspectTypeContainer && c.String("type") != inspectTypeImage && c.String("type") != inspectAll {
return errors.Errorf("the only recognized types are %q, %q, and %q", inspectTypeContainer, inspectTypeImage, inspectAll)
}
name := args[0]
- config, err := getConfig(c)
- if err != nil {
- return errors.Wrapf(err, "Could not get config")
- }
- server, err := libkpod.New(config)
- if err != nil {
- return errors.Wrapf(err, "could not get container server")
- }
- defer server.Shutdown()
- if err = server.Update(); err != nil {
- return errors.Wrapf(err, "could not update list of containers")
- }
-
outputFormat := c.String("format")
var data interface{}
- switch itemType {
+ switch c.String("type") {
case inspectTypeContainer:
- data, err = server.GetContainerData(name, size)
+ ctr, err := runtime.LookupContainer(name)
+ if err != nil {
+ return errors.Wrapf(err, "error looking up container %q", name)
+ }
+ libpodInspectData, err := ctr.Inspect(c.Bool("size"))
+ if err != nil {
+ return errors.Wrapf(err, "error getting libpod container inspect data %q", ctr.ID)
+ }
+ data, err = getCtrInspectInfo(ctr, libpodInspectData)
if err != nil {
- return errors.Wrapf(err, "error parsing container data")
+ return errors.Wrapf(err, "error parsing container data %q", ctr.ID())
}
case inspectTypeImage:
- data, err = images.GetData(server.Store(), name)
+ image, err := runtime.GetImage(name)
if err != nil {
- return errors.Wrapf(err, "error parsing image data")
+ return errors.Wrapf(err, "error getting image %q", name)
+ }
+ data, err = runtime.GetImageInspectInfo(*image)
+ if err != nil {
+ return errors.Wrapf(err, "error parsing image data %q", image.ID)
}
case inspectAll:
- ctrData, err := server.GetContainerData(name, size)
+ ctr, err := runtime.LookupContainer(name)
if err != nil {
- imgData, err := images.GetData(server.Store(), name)
+ image, err := runtime.GetImage(name)
if err != nil {
- return errors.Wrapf(err, "error parsing container or image data")
+ return errors.Wrapf(err, "error getting image %q", name)
+ }
+ data, err = runtime.GetImageInspectInfo(*image)
+ if err != nil {
+ return errors.Wrapf(err, "error parsing image data %q", image.ID)
}
- data = imgData
-
} else {
- data = ctrData
+ libpodInspectData, err := ctr.Inspect(c.Bool("size"))
+ if err != nil {
+ return errors.Wrapf(err, "error getting libpod container inspect data %q", ctr.ID)
+ }
+ data, err = getCtrInspectInfo(ctr, libpodInspectData)
+ if err != nil {
+ return errors.Wrapf(err, "error parsing container data %q", ctr.ID)
+ }
}
}
@@ -118,3 +127,236 @@ func inspectCmd(c *cli.Context) error {
formats.Writer(out).Out()
return nil
}
+
+func getCtrInspectInfo(ctr *libpod.Container, ctrInspectData *libpod.ContainerInspectData) (*ContainerData, error) {
+ config := ctr.Config()
+ 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)
+
+ artifact, err := ctr.GetArtifact("create-config")
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting artifact %q", ctr.ID())
+ }
+ var createArtifact createConfig
+ if err := json.Unmarshal(artifact, &createArtifact); err != nil {
+ return nil, err
+ }
+
+ data := &ContainerData{
+ CtrInspectData: ctrInspectData,
+ HostConfig: &HostConfig{
+ 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,
+ MemoryReservation: memReservation,
+ MemorySwap: memSwap,
+ MemorySwappiness: memSwappiness,
+ OomKillDisable: memDisableOOMKiller,
+ PidsLimit: pidsLimit,
+ Privileged: spec.Process.NoNewPrivileges,
+ ReadonlyRootfs: spec.Root.Readonly,
+ Runtime: ctr.RuntimeName(),
+ NetworkMode: string(createArtifact.NetMode),
+ IpcMode: string(createArtifact.IpcMode),
+ Cgroup: cgroup,
+ UTSMode: string(createArtifact.UtsMode),
+ UsernsMode: createArtifact.NsUser,
+ 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,
+ },
+ Config: &CtrConfig{
+ Hostname: spec.Hostname,
+ User: spec.Process.User,
+ Env: spec.Process.Env,
+ Image: config.RootfsImageName,
+ WorkingDir: spec.Process.Cwd,
+ Labels: config.Labels,
+ Annotations: spec.Annotations,
+ Tty: spec.Process.Terminal,
+ OpenStdin: config.Stdin,
+ StopSignal: config.StopSignal,
+ Cmd: config.Spec.Process.Args,
+ Entrypoint: createArtifact.Entrypoint,
+ },
+ }
+ 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
+}
+
+// ContainerData holds the kpod inspect data for a container
+type ContainerData struct {
+ CtrInspectData *libpod.ContainerInspectData `json:"CtrInspectData"`
+ HostConfig *HostConfig `json:"HostConfig"`
+ Config *CtrConfig `json:"Config"`
+}
+
+// LogConfig holds the log information for a container
+type LogConfig struct {
+ Type string `json:"Type"` // TODO
+ Config map[string]string `json:"Config"` //idk type, TODO
+}
+
+// HostConfig represents the host configuration for the container
+type HostConfig struct {
+ ContainerIDFile string `json:"ContainerIDFile"`
+ LogConfig *LogConfig `json:"LogConfig"` //TODO
+ NetworkMode string `json:"NetworkMode"`
+ PortBindings map[string]struct{} `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"`
+ SecurityOpt []string `json:"SecurityOpt"`
+ UTSMode string `json:"UTSMode"`
+ UsernsMode string `json:"UsernsMode"`
+ ShmSize string `json:"ShmSize"`
+ Runtime string `json:"Runtime"`
+ ConsoleSize *specs.Box `json:"ConsoleSize"`
+ Isolation string `json:"Isolation"` //TODO
+ CPUShares *uint64 `json:"CPUSShares"`
+ Memory int64 `json:"Memory"`
+ NanoCpus int `json:"NanoCpus"` //check type, TODO
+ 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"` //check type, TODO
+ CPUPercent int `json:"CPUPercent"` //check type, TODO
+ IOMaximumIOps int `json:"IOMaximumIOps"` //check type, TODO
+ IOMaximumBandwidth int `json:"IOMaximumBandwidth"` //check type, TODO
+}
+
+// CtrConfig holds information about the container configuration
+type CtrConfig struct {
+ Hostname string `json:"Hostname"`
+ DomainName string `json:"Domainname"` //TODO
+ User specs.User `json:"User"`
+ AttachStdin bool `json:"AttachStdin"` //TODO
+ AttachStdout bool `json:"AttachStdout"` //TODO
+ AttachStderr bool `json:"AttachStderr"` //TODO
+ Tty bool `json:"Tty"`
+ OpenStdin bool `json:"OpenStdin"`
+ StdinOnce bool `json:"StdinOnce"` //TODO
+ Env []string `json:"Env"`
+ Cmd []string `json:"Cmd"`
+ Image string `json:"Image"`
+ Volumes map[string]struct{} `json:"Volumes"`
+ WorkingDir string `json:"WorkingDir"`
+ Entrypoint string `json:"Entrypoint"`
+ Labels map[string]string `json:"Labels"`
+ Annotations map[string]string `json:"Annotations"`
+ StopSignal uint `json:"StopSignal"`
+}
diff --git a/cmd/kpod/run.go b/cmd/kpod/run.go
index 7e078f66a..6142983ad 100644
--- a/cmd/kpod/run.go
+++ b/cmd/kpod/run.go
@@ -39,7 +39,7 @@ func runCmd(c *cli.Context) error {
return err
}
- createImage := runtime.NewImage(createConfig.image)
+ createImage := runtime.NewImage(createConfig.Image)
createImage.LocalName, _ = createImage.GetLocalImageName()
if createImage.LocalName == "" {
// The image wasnt found by the user input'd name or its fqname
@@ -89,8 +89,8 @@ func runCmd(c *cli.Context) error {
// Gather up the options for NewContainer which consist of With... funcs
options = append(options, libpod.WithRootFSFromImage(imageID, imageName, false))
- options = append(options, libpod.WithSELinuxLabels(createConfig.processLabel, createConfig.mountLabel))
- options = append(options, libpod.WithShmDir(createConfig.shmDir))
+ options = append(options, libpod.WithSELinuxLabels(createConfig.ProcessLabel, createConfig.MountLabel))
+ options = append(options, libpod.WithShmDir(createConfig.ShmDir))
ctr, err := runtime.NewContainer(runtimeSpec, options...)
if err != nil {
return err
@@ -114,7 +114,7 @@ func runCmd(c *cli.Context) error {
// to finish before exiting main
var wg sync.WaitGroup
- if !createConfig.detach {
+ if !createConfig.Detach {
// We increment the wg counter because we need to do the attach
wg.Add(1)
// Attach to the running container
@@ -133,13 +133,13 @@ func runCmd(c *cli.Context) error {
if err := ctr.Start(); err != nil {
return errors.Wrapf(err, "unable to start container %q", ctr.ID())
}
- if createConfig.detach {
+ if createConfig.Detach {
fmt.Printf("%s\n", ctr.ID())
return nil
}
wg.Wait()
- if createConfig.rm {
+ if createConfig.Rm {
return runtime.RemoveContainer(ctr, true)
}
return ctr.CleanupStorage()
diff --git a/cmd/kpod/spec.go b/cmd/kpod/spec.go
index b200ed77a..4e00f04ff 100644
--- a/cmd/kpod/spec.go
+++ b/cmd/kpod/spec.go
@@ -20,7 +20,7 @@ import (
)
func blockAccessToKernelFilesystems(config *createConfig, g *generate.Generator) {
- if !config.privileged {
+ if !config.Privileged {
for _, mp := range []string{
"/proc/kcore",
"/proc/latency_stats",
@@ -47,12 +47,12 @@ func blockAccessToKernelFilesystems(config *createConfig, g *generate.Generator)
}
func addPidNS(config *createConfig, g *generate.Generator) error {
- pidMode := config.pidMode
+ pidMode := config.PidMode
if pidMode.IsHost() {
return g.RemoveLinuxNamespace(libpod.PIDNamespace)
}
if pidMode.IsContainer() {
- ctr, err := config.runtime.LookupContainer(pidMode.Container())
+ ctr, err := config.Runtime.LookupContainer(pidMode.Container())
if err != nil {
return errors.Wrapf(err, "container %q not found", pidMode.Container())
}
@@ -69,7 +69,7 @@ func addPidNS(config *createConfig, g *generate.Generator) error {
}
func addNetNS(config *createConfig, g *generate.Generator) error {
- netMode := config.netMode
+ netMode := config.NetMode
if netMode.IsHost() {
return g.RemoveLinuxNamespace(libpod.NetNamespace)
}
@@ -80,7 +80,7 @@ func addNetNS(config *createConfig, g *generate.Generator) error {
return libpod.ErrNotImplemented
}
if netMode.IsContainer() {
- ctr, err := config.runtime.LookupContainer(netMode.ConnectedContainer())
+ ctr, err := config.Runtime.LookupContainer(netMode.ConnectedContainer())
if err != nil {
return errors.Wrapf(err, "container %q not found", netMode.ConnectedContainer())
}
@@ -97,7 +97,7 @@ func addNetNS(config *createConfig, g *generate.Generator) error {
}
func addUTSNS(config *createConfig, g *generate.Generator) error {
- utsMode := config.utsMode
+ utsMode := config.UtsMode
if utsMode.IsHost() {
return g.RemoveLinuxNamespace(libpod.UTSNamespace)
}
@@ -105,12 +105,12 @@ func addUTSNS(config *createConfig, g *generate.Generator) error {
}
func addIpcNS(config *createConfig, g *generate.Generator) error {
- ipcMode := config.ipcMode
+ ipcMode := config.IpcMode
if ipcMode.IsHost() {
return g.RemoveLinuxNamespace(libpod.IPCNamespace)
}
if ipcMode.IsContainer() {
- ctr, err := config.runtime.LookupContainer(ipcMode.Container())
+ ctr, err := config.Runtime.LookupContainer(ipcMode.Container())
if err != nil {
return errors.Wrapf(err, "container %q not found", ipcMode.Container())
}
@@ -133,7 +133,7 @@ func addRlimits(config *createConfig, g *generate.Generator) error {
err error
)
- for _, u := range config.resources.ulimit {
+ for _, u := range config.Resources.Ulimit {
if ul, err = units.ParseUlimit(u); err != nil {
return errors.Wrapf(err, "ulimit option %q requires name=SOFT:HARD, failed to be parsed", u)
}
@@ -146,10 +146,10 @@ func addRlimits(config *createConfig, g *generate.Generator) error {
func setupCapabilities(config *createConfig, configSpec *spec.Spec) error {
var err error
var caplist []string
- if config.privileged {
+ if config.Privileged {
caplist = caps.GetAllCapabilities()
} else {
- caplist, err = caps.TweakCapabilities(configSpec.Process.Capabilities.Bounding, config.capAdd, config.capDrop)
+ caplist, err = caps.TweakCapabilities(configSpec.Process.Capabilities.Bounding, config.CapAdd, config.CapDrop)
if err != nil {
return err
}
@@ -166,85 +166,85 @@ func setupCapabilities(config *createConfig, configSpec *spec.Spec) error {
func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) {
g := generate.New()
g.AddCgroupsMount("ro")
- g.SetProcessCwd(config.workDir)
- g.SetProcessArgs(config.command)
- g.SetProcessTerminal(config.tty)
+ g.SetProcessCwd(config.WorkDir)
+ g.SetProcessArgs(config.Command)
+ g.SetProcessTerminal(config.Tty)
// User and Group must go together
- g.SetProcessUID(config.user)
- g.SetProcessGID(config.group)
- for _, gid := range config.groupAdd {
+ g.SetProcessUID(config.User)
+ g.SetProcessGID(config.Group)
+ for _, gid := range config.GroupAdd {
g.AddProcessAdditionalGid(gid)
}
for key, val := range config.GetAnnotations() {
g.AddAnnotation(key, val)
}
- g.SetRootReadonly(config.readOnlyRootfs)
- g.SetHostname(config.hostname)
- if config.hostname != "" {
- g.AddProcessEnv("HOSTNAME", config.hostname)
+ g.SetRootReadonly(config.ReadOnlyRootfs)
+ g.SetHostname(config.Hostname)
+ if config.Hostname != "" {
+ g.AddProcessEnv("HOSTNAME", config.Hostname)
}
- for _, sysctl := range config.sysctl {
+ for _, sysctl := range config.Sysctl {
s := strings.SplitN(sysctl, "=", 2)
g.AddLinuxSysctl(s[0], s[1])
}
// RESOURCES - MEMORY
- if config.resources.memory != 0 {
- g.SetLinuxResourcesMemoryLimit(config.resources.memory)
+ if config.Resources.Memory != 0 {
+ g.SetLinuxResourcesMemoryLimit(config.Resources.Memory)
}
- if config.resources.memoryReservation != 0 {
- g.SetLinuxResourcesMemoryReservation(config.resources.memoryReservation)
+ if config.Resources.MemoryReservation != 0 {
+ g.SetLinuxResourcesMemoryReservation(config.Resources.MemoryReservation)
}
- if config.resources.memorySwap != 0 {
- g.SetLinuxResourcesMemorySwap(config.resources.memorySwap)
+ if config.Resources.MemorySwap != 0 {
+ g.SetLinuxResourcesMemorySwap(config.Resources.MemorySwap)
}
- if config.resources.kernelMemory != 0 {
- g.SetLinuxResourcesMemoryKernel(config.resources.kernelMemory)
+ if config.Resources.KernelMemory != 0 {
+ g.SetLinuxResourcesMemoryKernel(config.Resources.KernelMemory)
}
- if config.resources.memorySwappiness != -1 {
- g.SetLinuxResourcesMemorySwappiness(uint64(config.resources.memorySwappiness))
+ if config.Resources.MemorySwappiness != -1 {
+ g.SetLinuxResourcesMemorySwappiness(uint64(config.Resources.MemorySwappiness))
}
- g.SetLinuxResourcesMemoryDisableOOMKiller(config.resources.disableOomKiller)
- g.SetProcessOOMScoreAdj(config.resources.oomScoreAdj)
+ g.SetLinuxResourcesMemoryDisableOOMKiller(config.Resources.DisableOomKiller)
+ g.SetProcessOOMScoreAdj(config.Resources.OomScoreAdj)
// RESOURCES - CPU
- if config.resources.cpuShares != 0 {
- g.SetLinuxResourcesCPUShares(config.resources.cpuShares)
+ if config.Resources.CpuShares != 0 {
+ g.SetLinuxResourcesCPUShares(config.Resources.CpuShares)
}
- if config.resources.cpuQuota != 0 {
- g.SetLinuxResourcesCPUQuota(config.resources.cpuQuota)
+ if config.Resources.CpuQuota != 0 {
+ g.SetLinuxResourcesCPUQuota(config.Resources.CpuQuota)
}
- if config.resources.cpuPeriod != 0 {
- g.SetLinuxResourcesCPUPeriod(config.resources.cpuPeriod)
+ if config.Resources.CpuPeriod != 0 {
+ g.SetLinuxResourcesCPUPeriod(config.Resources.CpuPeriod)
}
- if config.resources.cpuRtRuntime != 0 {
- g.SetLinuxResourcesCPURealtimeRuntime(config.resources.cpuRtRuntime)
+ if config.Resources.CpuRtRuntime != 0 {
+ g.SetLinuxResourcesCPURealtimeRuntime(config.Resources.CpuRtRuntime)
}
- if config.resources.cpuRtPeriod != 0 {
- g.SetLinuxResourcesCPURealtimePeriod(config.resources.cpuRtPeriod)
+ if config.Resources.CpuRtPeriod != 0 {
+ g.SetLinuxResourcesCPURealtimePeriod(config.Resources.CpuRtPeriod)
}
- if config.resources.cpus != "" {
- g.SetLinuxResourcesCPUCpus(config.resources.cpus)
+ if config.Resources.Cpus != "" {
+ g.SetLinuxResourcesCPUCpus(config.Resources.Cpus)
}
- if config.resources.cpusetMems != "" {
- g.SetLinuxResourcesCPUMems(config.resources.cpusetMems)
+ if config.Resources.CpusetMems != "" {
+ g.SetLinuxResourcesCPUMems(config.Resources.CpusetMems)
}
// SECURITY OPTS
- g.SetProcessNoNewPrivileges(config.noNewPrivileges)
- g.SetProcessApparmorProfile(config.apparmorProfile)
- g.SetProcessSelinuxLabel(config.processLabel)
- g.SetLinuxMountLabel(config.mountLabel)
+ g.SetProcessNoNewPrivileges(config.NoNewPrivileges)
+ g.SetProcessApparmorProfile(config.ApparmorProfile)
+ g.SetProcessSelinuxLabel(config.ProcessLabel)
+ g.SetLinuxMountLabel(config.MountLabel)
blockAccessToKernelFilesystems(config, &g)
// RESOURCES - PIDS
- if config.resources.pidsLimit != 0 {
- g.SetLinuxResourcesPidsLimit(config.resources.pidsLimit)
+ if config.Resources.PidsLimit != 0 {
+ g.SetLinuxResourcesPidsLimit(config.Resources.PidsLimit)
}
- for _, i := range config.tmpfs {
+ for _, i := range config.Tmpfs {
options := []string{"rw", "noexec", "nosuid", "nodev", "size=65536k"}
spliti := strings.SplitN(i, ":", 2)
if len(spliti) > 1 {
@@ -257,7 +257,7 @@ func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) {
g.AddTmpfsMount(spliti[0], options)
}
- for name, val := range config.env {
+ for name, val := range config.Env {
g.AddProcessEnv(name, val)
}
@@ -282,14 +282,14 @@ func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) {
}
configSpec := g.Spec()
- if config.seccompProfilePath != "" && config.seccompProfilePath != "unconfined" {
- seccompProfile, err := ioutil.ReadFile(config.seccompProfilePath)
+ if config.SeccompProfilePath != "" && config.SeccompProfilePath != "unconfined" {
+ seccompProfile, err := ioutil.ReadFile(config.SeccompProfilePath)
if err != nil {
- return nil, errors.Wrapf(err, "opening seccomp profile (%s) failed", config.seccompProfilePath)
+ return nil, errors.Wrapf(err, "opening seccomp profile (%s) failed", config.SeccompProfilePath)
}
var seccompConfig spec.LinuxSeccomp
if err := json.Unmarshal(seccompProfile, &seccompConfig); err != nil {
- return nil, errors.Wrapf(err, "decoding seccomp profile (%s) failed", config.seccompProfilePath)
+ return nil, errors.Wrapf(err, "decoding seccomp profile (%s) failed", config.SeccompProfilePath)
}
configSpec.Linux.Seccomp = &seccompConfig
}
@@ -347,10 +347,10 @@ func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) {
func (c *createConfig) CreateBlockIO() (spec.LinuxBlockIO, error) {
bio := spec.LinuxBlockIO{}
- bio.Weight = &c.resources.blkioWeight
- if len(c.resources.blkioWeightDevice) > 0 {
+ bio.Weight = &c.Resources.BlkioWeight
+ if len(c.Resources.BlkioWeightDevice) > 0 {
var lwds []spec.LinuxWeightDevice
- for _, i := range c.resources.blkioWeightDevice {
+ for _, i := range c.Resources.BlkioWeightDevice {
wd, err := validateweightDevice(i)
if err != nil {
return bio, errors.Wrapf(err, "invalid values for blkio-weight-device")
@@ -364,29 +364,29 @@ func (c *createConfig) CreateBlockIO() (spec.LinuxBlockIO, error) {
lwds = append(lwds, lwd)
}
}
- if len(c.resources.deviceReadBps) > 0 {
- readBps, err := makeThrottleArray(c.resources.deviceReadBps)
+ if len(c.Resources.DeviceReadBps) > 0 {
+ readBps, err := makeThrottleArray(c.Resources.DeviceReadBps)
if err != nil {
return bio, err
}
bio.ThrottleReadBpsDevice = readBps
}
- if len(c.resources.deviceWriteBps) > 0 {
- writeBpds, err := makeThrottleArray(c.resources.deviceWriteBps)
+ if len(c.Resources.DeviceWriteBps) > 0 {
+ writeBpds, err := makeThrottleArray(c.Resources.DeviceWriteBps)
if err != nil {
return bio, err
}
bio.ThrottleWriteBpsDevice = writeBpds
}
- if len(c.resources.deviceReadIOps) > 0 {
- readIOps, err := makeThrottleArray(c.resources.deviceReadIOps)
+ if len(c.Resources.DeviceReadIOps) > 0 {
+ readIOps, err := makeThrottleArray(c.Resources.DeviceReadIOps)
if err != nil {
return bio, err
}
bio.ThrottleReadIOPSDevice = readIOps
}
- if len(c.resources.deviceWriteIOps) > 0 {
- writeIOps, err := makeThrottleArray(c.resources.deviceWriteIOps)
+ if len(c.Resources.DeviceWriteIOps) > 0 {
+ writeIOps, err := makeThrottleArray(c.Resources.DeviceWriteIOps)
if err != nil {
return bio, err
}
@@ -401,7 +401,7 @@ func (c *createConfig) GetAnnotations() map[string]string {
a := getDefaultAnnotations()
// TODO - Which annotations do we want added by default
// TODO - This should be added to the DB long term
- if c.tty {
+ if c.Tty {
a["io.kubernetes.cri-o.TTY"] = "true"
}
return a
@@ -445,7 +445,7 @@ func getDefaultAnnotations() map[string]string {
func (c *createConfig) GetVolumeMounts() ([]spec.Mount, error) {
var m []spec.Mount
var options []string
- for _, i := range c.volumes {
+ for _, i := range c.Volumes {
// We need to handle SELinux options better here, specifically :Z
spliti := strings.Split(i, ":")
if len(spliti) > 2 {
@@ -472,12 +472,12 @@ func (c *createConfig) GetVolumeMounts() ([]spec.Mount, error) {
options = append(options, "rw")
}
if foundz {
- if err := label.Relabel(spliti[0], c.mountLabel, true); err != nil {
+ if err := label.Relabel(spliti[0], c.MountLabel, true); err != nil {
return nil, errors.Wrapf(err, "relabel failed %q", spliti[0])
}
}
if foundZ {
- if err := label.Relabel(spliti[0], c.mountLabel, false); err != nil {
+ if err := label.Relabel(spliti[0], c.MountLabel, false); err != nil {
return nil, errors.Wrapf(err, "relabel failed %q", spliti[0])
}
}
@@ -495,10 +495,10 @@ func (c *createConfig) GetVolumeMounts() ([]spec.Mount, error) {
return m, nil
}
-//GetTmpfsMounts takes user provided input for tmpfs mounts and creates Mount structs
+//GetTmpfsMounts takes user provided input for Tmpfs mounts and creates Mount structs
func (c *createConfig) GetTmpfsMounts() []spec.Mount {
var m []spec.Mount
- for _, i := range c.tmpfs {
+ for _, i := range c.Tmpfs {
// Default options if nothing passed
options := []string{"rw", "noexec", "nosuid", "nodev", "size=65536k"}
spliti := strings.Split(i, ":")
@@ -522,12 +522,12 @@ func (c *createConfig) GetContainerCreateOptions() ([]libpod.CtrCreateOption, er
// Uncomment after talking to mheon about unimplemented funcs
// options = append(options, libpod.WithLabels(c.labels))
- if c.interactive {
+ if c.Interactive {
options = append(options, libpod.WithStdin())
}
- if c.name != "" {
- logrus.Debugf("appending name %s", c.name)
- options = append(options, libpod.WithName(c.name))
+ if c.Name != "" {
+ logrus.Debugf("appending name %s", c.Name)
+ options = append(options, libpod.WithName(c.Name))
}
return options, nil
diff --git a/cmd/kpod/spec_test.go b/cmd/kpod/spec_test.go
index 799d6b235..01e1a4ad3 100644
--- a/cmd/kpod/spec_test.go
+++ b/cmd/kpod/spec_test.go
@@ -16,7 +16,7 @@ func TestCreateConfig_GetVolumeMounts(t *testing.T) {
Options: []string{"ro", "rbind", "rprivate"},
}
config := createConfig{
- volumes: []string{"foobar:/foobar:ro"},
+ Volumes: []string{"foobar:/foobar:ro"},
}
specMount, err := config.GetVolumeMounts()
assert.NoError(t, err)
@@ -31,7 +31,7 @@ func TestCreateConfig_GetTmpfsMounts(t *testing.T) {
Options: []string{"rw", "size=787448k", "mode=1777"},
}
config := createConfig{
- tmpfs: []string{"/homer:rw,size=787448k,mode=1777"},
+ Tmpfs: []string{"/homer:rw,size=787448k,mode=1777"},
}
tmpfsMount := config.GetTmpfsMounts()
assert.True(t, reflect.DeepEqual(data, tmpfsMount[0]))
diff --git a/docs/kpod-inspect.1.md b/docs/kpod-inspect.1.md
index 1baa46f13..d3927cd37 100644
--- a/docs/kpod-inspect.1.md
+++ b/docs/kpod-inspect.1.md
@@ -28,144 +28,55 @@ Display the total file size if the type is a container
## EXAMPLE
-kpod inspect redis:alpine
-
-{
- "ArgsEscaped": true,
- "AttachStderr": false,
- "AttachStdin": false,
- "AttachStdout": false,
- "Cmd": [
- "/bin/sh",
- "-c",
- "#(nop) ",
- "CMD [\"redis-server\"]"
- ],
- "Domainname": "",
- "Entrypoint": [
- "entrypoint.sh"
- ],
- "Env": [
- "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
- "REDIS_VERSION=3.2.9",
- "REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-3.2.9.tar.gz",
- "REDIS_DOWNLOAD_SHA=6eaacfa983b287e440d0839ead20c2231749d5d6b78bbe0e0ffa3a890c59ff26"
- ],
- "ExposedPorts": {
- "6379/tcp": {}
- },
- "Hostname": "e1ede117fb1e",
- "Image": "sha256:75e877aa15b534396de82d385386cc4dda7819d5cbb018b9f97b77aeb8f4b55a",
- "Labels": {},
- "OnBuild": [],
- "OpenStdin": false,
- "StdinOnce": false,
- "Tty": false,
- "User": "",
- "Volumes": {
- "/data": {}
- },
- "WorkingDir": "/data"
-}
+```
+# kpod inspect fedora
{
- "ID": "b3f2436bdb978c1d33b1387afb5d7ba7e3243ed2ce908db431ac0069da86cb45",
- "Names": [
- "docker.io/library/redis:alpine"
+ "Id": "422dc563ca3260ad9ef5c47a1c246f5065d7f177ce51f4dd208efd82967ff182",
+ "Digest": "sha256:1b9bfb4e634dc1e5c19d0fa1eb2e5a28a5c2b498e3d3e4ac742bd7f5dae08611",
+ "RepoTags": [
+ "docker.io/library/fedora:latest"
],
- "Digests": [
- "sha256:88286f41530e93dffd4b964e1db22ce4939fffa4a4c665dab8591fbab03d4926",
- "sha256:07b1ac6c7a5068201d8b63a09bb15358ec1616b813ef3942eb8cc12ae191227f",
- "sha256:91e2e140ea27b3e89f359cd9fab4ec45647dda2a8e5fb0c78633217d9dca87b5",
- "sha256:08957ceaa2b3be874cde8d7fa15c274300f47185acd62bca812a2ffb6228482d",
- "sha256:acd3d12a6a79f772961a771f678c1a39e1f370e7baeb9e606ad8f1b92572f4ab",
- "sha256:4ad88df090801e8faa8cf0be1f403b77613d13e11dad73f561461d482f79256c",
- "sha256:159ac12c79e1a8d85dfe61afff8c64b96881719139730012a9697f432d6b739a"
+ "RepoDigests": [
+ "docker.io/library/fedora@sha256:1b9bfb4e634dc1e5c19d0fa1eb2e5a28a5c2b498e3d3e4ac742bd7f5dae08611"
],
"Parent": "",
"Comment": "",
- "Created": "2017-06-28T22:14:36.35280993Z",
- "Container": "ba8d6c6b0d7fdd201fce404236136b44f3bfdda883466531a3d1a1f87906770b",
- "ContainerConfig": {
- "Hostname": "e1ede117fb1e",
- "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",
- "REDIS_VERSION=3.2.9",
- "REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-3.2.9.tar.gz",
- "REDIS_DOWNLOAD_SHA=6eaacfa983b287e440d0839ead20c2231749d5d6b78bbe0e0ffa3a890c59ff26"
- ],
- "Cmd": [
- "/bin/sh",
- "-c",
- "#(nop) ",
- "CMD [\"redis-server\"]"
- ],
- "ArgsEscaped": true,
- "Image": "sha256:75e877aa15b534396de82d385386cc4dda7819d5cbb018b9f97b77aeb8f4b55a",
- "Volumes": {
- "/data": {}
- },
- "WorkingDir": "/data",
- "Entrypoint": [
- "entrypoint.sh"
- ],
- "Labels": {},
- "OnBuild": []
- },
- "Author": "",
+ "Created": "2017-11-14T21:07:08.475840838Z",
"Config": {
- "ExposedPorts": {
- "6379/tcp": {}
- },
- "Env": [
- "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
- "REDIS_VERSION=3.2.9",
- "REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-3.2.9.tar.gz",
- "REDIS_DOWNLOAD_SHA=6eaacfa983b287e440d0839ead20c2231749d5d6b78bbe0e0ffa3a890c59ff26"
- ],
- "Entrypoint": [
- "entrypoint.sh"
- ],
- "Cmd": [
- "redis-server"
- ],
- "Volumes": {
- "/data": {}
- },
- "WorkingDir": "/data"
+ "Env": [
+ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+ "DISTTAG=f27container",
+ "FGC=f27",
+ "FBR=f27"
+ ]
},
+ "Version": "17.06.2-ce",
+ "Author": "[Adam Miller \u003cmaxamillion@fedoraproject.org\u003e] [Patrick Uiterwijk \u003cpatrick@puiterwijk.org\u003e]",
"Architecture": "amd64",
- "OS": "linux",
- "Size": 3965955,
- "VirtualSize": 19808086,
+ "Os": "linux",
+ "Size": 251722732,
+ "VirtualSize": 514895140,
"GraphDriver": {
- "Name": "overlay",
- "Data": {
- "MergedDir": "/var/lib/containers/storage/overlay/2059d805c90e034cb773d9722232ef018a72143dd31113b470fb876baeccd700/merged",
- "UpperDir": "/var/lib/containers/storage/overlay/2059d805c90e034cb773d9722232ef018a72143dd31113b470fb876baeccd700/diff",
- "WorkDir": "/var/lib/containers/storage/overlay/2059d805c90e034cb773d9722232ef018a72143dd31113b470fb876baeccd700/work"
- }
+ "Name": "overlay",
+ "Data": {
+ "MergedDir": "/var/lib/containers/storage/overlay/d32459d9ce237564fb93573b85cbc707600d43fbe5e46e8eeef22cad914bb516/merged",
+ "UpperDir": "/var/lib/containers/storage/overlay/d32459d9ce237564fb93573b85cbc707600d43fbe5e46e8eeef22cad914bb516/diff",
+ "WorkDir": "/var/lib/containers/storage/overlay/d32459d9ce237564fb93573b85cbc707600d43fbe5e46e8eeef22cad914bb516/work"
+ }
},
"RootFS": {
- "type": "layers",
- "diff_ids": [
- "sha256:5bef08742407efd622d243692b79ba0055383bbce12900324f75e56f589aedb0",
- "sha256:c92a8fc997217611d0bfc9ff14d7ec00350ca564aef0ecbf726624561d7872d7",
- "sha256:d4c406dea37a107b0cccb845611266a146725598be3e82ba31c55c08d1583b5a",
- "sha256:8b4fa064e2b6c03a6c37089b0203f167375a8b49259c0ad7cb47c8c1e58b3fa0",
- "sha256:c393e3d0b00ddf6b4166f1e2ad68245e08e9e3be0a0567a36d0a43854f03bfd6",
- "sha256:38047b4117cb8bb3bba82991daf9a4e14ba01f9f66c1434d4895a7e96f67d8ba"
- ]
- }
+ "Type": "layers",
+ "Layers": [
+ "sha256:d32459d9ce237564fb93573b85cbc707600d43fbe5e46e8eeef22cad914bb516"
+ ]
+ },
+ "Labels": null,
+ "Annotations": {}
}
-
+```
## SEE ALSO
kpod(1)
+
+## HISTORY
+July 2017, Originally compiled by Dan Walsh <dwalsh@redhat.com>
diff --git a/libkpod/container_data.go b/libkpod/container_data.go
deleted file mode 100644
index eb84aa42d..000000000
--- a/libkpod/container_data.go
+++ /dev/null
@@ -1,210 +0,0 @@
-package libkpod
-
-import (
- "encoding/json"
- "os"
- "time"
-
- "k8s.io/apimachinery/pkg/fields"
- pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
-
- "github.com/opencontainers/image-spec/specs-go/v1"
- specs "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/pkg/errors"
- "github.com/projectatomic/libpod/libpod/driver"
- "github.com/projectatomic/libpod/libpod/images"
- "github.com/projectatomic/libpod/oci"
-)
-
-// ContainerData handles the data used when inspecting a container
-type ContainerData struct {
- ID string
- Name string
- LogPath string
- Labels fields.Set
- Annotations fields.Set
- State *ContainerState
- Metadata *pb.ContainerMetadata
- BundlePath string
- StopSignal string
- FromImage string `json:"Image,omitempty"`
- FromImageID string `json:"ImageID"`
- MountPoint string `json:"Mountpoint,omitempty"`
- MountLabel string
- Mounts []specs.Mount
- AppArmorProfile string
- ImageAnnotations map[string]string `json:"Annotations,omitempty"`
- ImageCreatedBy string `json:"CreatedBy,omitempty"`
- Config v1.ImageConfig `json:"Config,omitempty"`
- SizeRw uint `json:"SizeRw,omitempty"`
- SizeRootFs uint `json:"SizeRootFs,omitempty"`
- Args []string
- ResolvConfPath string
- HostnamePath string
- HostsPath string
- GraphDriver driverData
-}
-
-type driverData struct {
- Name string
- Data map[string]string
-}
-
-// ContainerState represents the status of a container.
-type ContainerState struct {
- specs.State
- Created time.Time `json:"created"`
- Started time.Time `json:"started,omitempty"`
- Finished time.Time `json:"finished,omitempty"`
- ExitCode int32 `json:"exitCode"`
- OOMKilled bool `json:"oomKilled,omitempty"`
- Error string `json:"error,omitempty"`
-}
-
-// GetContainerData gets the ContainerData for a container with the given name in the given store.
-// If size is set to true, it will also determine the size of the container
-func (c *ContainerServer) GetContainerData(name string, size bool) (*ContainerData, error) {
- ctr, err := c.inspectContainer(name)
- if err != nil {
- return nil, errors.Wrapf(err, "error reading build container %q", name)
- }
- container, err := c.store.Container(name)
- if err != nil {
- return nil, errors.Wrapf(err, "error reading container data")
- }
-
- // The runtime configuration won't exist if the container has never been started by cri-o or kpod,
- // so treat a not-exist error as non-fatal.
- m := getBlankSpec()
- config, err := c.store.FromContainerDirectory(ctr.ID(), "config.json")
- if err != nil && !os.IsNotExist(errors.Cause(err)) {
- return nil, err
- }
- if len(config) > 0 {
- if err = json.Unmarshal(config, &m); err != nil {
- return nil, err
- }
- }
-
- if container.ImageID == "" {
- return nil, errors.Errorf("error reading container image data: container is not based on an image")
- }
- imageData, err := images.GetData(c.store, container.ImageID)
- if err != nil {
- return nil, errors.Wrapf(err, "error reading container image data")
- }
-
- driverName, err := driver.GetDriverName(c.store)
- if err != nil {
- return nil, err
- }
- topLayer, err := c.GetContainerTopLayerID(ctr.ID())
- if err != nil {
- return nil, err
- }
- layer, err := c.store.Layer(topLayer)
- if err != nil {
- return nil, err
- }
- driverMetadata, err := driver.GetDriverMetadata(c.store, topLayer)
- if err != nil {
- return nil, err
- }
- imageName := ""
- if len(imageData.Tags) > 0 {
- imageName = imageData.Tags[0]
- } else if len(imageData.Digests) > 0 {
- imageName = imageData.Digests[0]
- }
- data := &ContainerData{
- ID: ctr.ID(),
- Name: ctr.Name(),
- LogPath: ctr.LogPath(),
- Labels: ctr.Labels(),
- Annotations: ctr.Annotations(),
- State: c.State(ctr),
- Metadata: ctr.Metadata(),
- BundlePath: ctr.BundlePath(),
- StopSignal: ctr.GetStopSignal(),
- Args: m.Process.Args,
- FromImage: imageName,
- FromImageID: container.ImageID,
- MountPoint: layer.MountPoint,
- ImageAnnotations: imageData.Annotations,
- ImageCreatedBy: imageData.CreatedBy,
- Config: imageData.Config,
- GraphDriver: driverData{
- Name: driverName,
- Data: driverMetadata,
- },
- MountLabel: m.Linux.MountLabel,
- Mounts: m.Mounts,
- AppArmorProfile: m.Process.ApparmorProfile,
- ResolvConfPath: "",
- HostnamePath: "",
- HostsPath: "",
- }
-
- if size {
- sizeRootFs, err := c.GetContainerRootFsSize(data.ID)
- if err != nil {
-
- return nil, errors.Wrapf(err, "error reading size for container %q", name)
- }
- data.SizeRootFs = uint(sizeRootFs)
- sizeRw, err := c.GetContainerRwSize(data.ID)
- if err != nil {
- return nil, errors.Wrapf(err, "error reading RWSize for container %q", name)
- }
- data.SizeRw = uint(sizeRw)
- }
-
- return data, nil
-}
-
-// Get an oci.Container and update its status
-func (c *ContainerServer) inspectContainer(container string) (*oci.Container, error) {
- ociCtr, err := c.LookupContainer(container)
- if err != nil {
- return nil, err
- }
- // call runtime.UpdateStatus()
- err = c.Runtime().UpdateStatus(ociCtr)
- if err != nil {
- return nil, err
- }
- return ociCtr, nil
-}
-
-func getBlankSpec() specs.Spec {
- return specs.Spec{
- Process: &specs.Process{},
- Root: &specs.Root{},
- Mounts: []specs.Mount{},
- Hooks: &specs.Hooks{},
- Annotations: make(map[string]string),
- Linux: &specs.Linux{},
- Solaris: &specs.Solaris{},
- Windows: &specs.Windows{},
- }
-}
-
-// State copies the crio container state to ContainerState type for kpod
-func (c *ContainerServer) State(ctr *oci.Container) *ContainerState {
- crioState := ctr.State()
- specState := specs.State{
- Version: crioState.Version,
- ID: crioState.ID,
- Status: crioState.Status,
- Pid: crioState.Pid,
- Bundle: crioState.Bundle,
- Annotations: crioState.Annotations,
- }
- cState := &ContainerState{
- Started: crioState.Started,
- Created: crioState.Created,
- Finished: crioState.Finished,
- }
- cState.State = specState
- return cState
-}
diff --git a/libpod/container.go b/libpod/container.go
index fce64b0dd..d53a863c0 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -22,6 +22,7 @@ import (
"github.com/opencontainers/runtime-tools/generate"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
+ "github.com/projectatomic/libpod/libpod/driver"
crioAnnotations "github.com/projectatomic/libpod/pkg/annotations"
"github.com/sirupsen/logrus"
"github.com/ulule/deepcopier"
@@ -132,6 +133,7 @@ type ContainerConfig struct {
SharedNamespaceMap map[string]string `json:"sharedNamespaces"`
// Time container was created
CreatedTime time.Time `json:"createdTime"`
+
// TODO save log location here and pass into OCI code
// TODO allow overriding of log path
}
@@ -192,7 +194,6 @@ func (c *Container) Labels() map[string]string {
for key, value := range c.config.Labels {
labels[key] = value
}
-
return labels
}
@@ -204,6 +205,68 @@ func (c *Container) Config() *ContainerConfig {
return returnConfig
}
+// RuntimeName returns the name of the runtime
+func (c *Container) RuntimeName() string {
+ return c.runtime.ociRuntime.name
+}
+
+// rootFsSize gets the size of the container's root filesystem
+// A container FS is split into two parts. The first is the top layer, a
+// mutable layer, and the rest is the RootFS: the set of immutable layers
+// that make up the image on which the container is based.
+func (c *Container) rootFsSize() (int64, error) {
+ container, err := c.runtime.store.Container(c.ID())
+ if err != nil {
+ return 0, err
+ }
+
+ // Ignore the size of the top layer. The top layer is a mutable RW layer
+ // and is not considered a part of the rootfs
+ rwLayer, err := c.runtime.store.Layer(container.LayerID)
+ if err != nil {
+ return 0, err
+ }
+ layer, err := c.runtime.store.Layer(rwLayer.Parent)
+ if err != nil {
+ return 0, err
+ }
+
+ size := int64(0)
+ for layer.Parent != "" {
+ layerSize, err := c.runtime.store.DiffSize(layer.Parent, layer.ID)
+ if err != nil {
+ return size, errors.Wrapf(err, "getting diffsize of layer %q and its parent %q", layer.ID, layer.Parent)
+ }
+ size += layerSize
+ layer, err = c.runtime.store.Layer(layer.Parent)
+ if err != nil {
+ return 0, err
+ }
+ }
+ // Get the size of the last layer. Has to be outside of the loop
+ // because the parent of the last layer is "", andlstore.Get("")
+ // will return an error.
+ layerSize, err := c.runtime.store.DiffSize(layer.Parent, layer.ID)
+ return size + layerSize, err
+}
+
+// rwSize Gets the size of the mutable top layer of the container.
+func (c *Container) rwSize() (int64, error) {
+ container, err := c.runtime.store.Container(c.ID())
+ if err != nil {
+ return 0, err
+ }
+
+ // Get the size of the top layer by calculating the size of the diff
+ // between the layer and its parent. The top layer of a container is
+ // the only RW layer, all others are immutable
+ layer, err := c.runtime.store.Layer(container.LayerID)
+ if err != nil {
+ return 0, err
+ }
+ return c.runtime.store.DiffSize(layer.Parent, layer.ID)
+}
+
// LogPath returns the path to the container's log file
// This file will only be present after Init() is called to create the container
// in runc
@@ -829,6 +892,31 @@ func (c *Container) getArtifactPath(name string) string {
return filepath.Join(c.config.StaticDir, artifactsDir, name)
}
+// Inspect a container for low-level information
+func (c *Container) Inspect(size bool) (*ContainerInspectData, error) {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ if err := c.syncContainer(); err != nil {
+ return nil, err
+ }
+
+ storeCtr, err := c.runtime.store.Container(c.ID())
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting container from store %q", c.ID())
+ }
+ layer, err := c.runtime.store.Layer(storeCtr.LayerID)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error reading information about layer %q", storeCtr.LayerID)
+ }
+ driverData, err := driver.GetDriverData(c.runtime.store, layer.ID)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting graph driver info %q", c.ID())
+ }
+
+ return c.getContainerInspectData(size, driverData)
+}
+
// Commit commits the changes between a container and its image, creating a new
// image
func (c *Container) Commit() (*storage.Image, error) {
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
new file mode 100644
index 000000000..5f29a231e
--- /dev/null
+++ b/libpod/container_inspect.go
@@ -0,0 +1,70 @@
+package libpod
+
+import (
+ "github.com/projectatomic/libpod/libpod/driver"
+ "github.com/sirupsen/logrus"
+)
+
+func (c *Container) getContainerInspectData(size bool, driverData *driver.Data) (*ContainerInspectData, error) {
+ config := c.config
+ runtimeInfo := c.state
+ spec := c.config.Spec
+
+ args := config.Spec.Process.Args
+ var path string
+ if len(args) > 0 {
+ path = args[0]
+ }
+ if len(args) > 1 {
+ args = args[1:]
+ }
+
+ data := &ContainerInspectData{
+ ID: config.ID,
+ Created: config.CreatedTime,
+ Path: path,
+ Args: args,
+ State: &ContainerInspectState{
+ OciVersion: spec.Version,
+ Status: runtimeInfo.State.String(),
+ Running: runtimeInfo.State == ContainerStateRunning,
+ Paused: runtimeInfo.State == ContainerStatePaused,
+ OOMKilled: runtimeInfo.OOMKilled,
+ Dead: runtimeInfo.State.String() == "bad state",
+ Pid: runtimeInfo.PID,
+ ExitCode: runtimeInfo.ExitCode,
+ Error: "", // can't get yet
+ StartedAt: runtimeInfo.StartedTime,
+ FinishedAt: runtimeInfo.FinishedTime,
+ },
+ ImageID: config.RootfsImageID,
+ ImageName: config.RootfsImageName,
+ ResolvConfPath: "", // TODO get from networking path
+ HostnamePath: spec.Annotations["io.kubernetes.cri-o.HostnamePath"], // not sure
+ HostsPath: "", // can't get yet
+ StaticDir: config.StaticDir,
+ LogPath: c.LogPath(),
+ Name: config.Name,
+ Driver: driverData.Name,
+ MountLabel: config.MountLabel,
+ ProcessLabel: spec.Process.SelinuxLabel,
+ AppArmorProfile: spec.Process.ApparmorProfile,
+ ExecIDs: []string{}, //TODO
+ GraphDriver: driverData,
+ Mounts: spec.Mounts,
+ NetworkSettings: &NetworkSettings{}, // TODO from networking patch
+ }
+ if size {
+ rootFsSize, err := c.rootFsSize()
+ if err != nil {
+ logrus.Errorf("error getting rootfs size %q: %v", config.ID, err)
+ }
+ rwSize, err := c.rwSize()
+ if err != nil {
+ logrus.Errorf("error getting rw size %q: %v", config.ID, err)
+ }
+ data.SizeRootFs = rootFsSize
+ data.SizeRw = rwSize
+ }
+ return data, nil
+}
diff --git a/libpod/driver/driver.go b/libpod/driver/driver.go
index 4db55852c..8475810a8 100644
--- a/libpod/driver/driver.go
+++ b/libpod/driver/driver.go
@@ -4,8 +4,8 @@ import cstorage "github.com/containers/storage"
// Data handles the data for a storage driver
type Data struct {
- Name string
- Data map[string]string
+ Name string `json:"Name"`
+ Data map[string]string `json:"Data"`
}
// GetDriverName returns the name of the driver for the given store
@@ -25,3 +25,19 @@ func GetDriverMetadata(store cstorage.Store, layerID string) (map[string]string,
}
return driver.Metadata(layerID)
}
+
+// GetDriverData returns the Data struct with information of the driver used by the store
+func GetDriverData(store cstorage.Store, layerID string) (*Data, error) {
+ name, err := GetDriverName(store)
+ if err != nil {
+ return nil, err
+ }
+ metaData, err := GetDriverMetadata(store, layerID)
+ if err != nil {
+ return nil, err
+ }
+ return &Data{
+ Name: name,
+ Data: metaData,
+ }, nil
+}
diff --git a/libpod/image_inspect.go b/libpod/image_inspect.go
new file mode 100644
index 000000000..a08665434
--- /dev/null
+++ b/libpod/image_inspect.go
@@ -0,0 +1,81 @@
+package libpod
+
+import (
+ "encoding/json"
+ "strings"
+
+ "github.com/containers/image/types"
+ "github.com/containers/storage"
+ digest "github.com/opencontainers/go-digest"
+ ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/pkg/errors"
+ "github.com/projectatomic/libpod/libpod/driver"
+)
+
+func getImageData(img storage.Image, imgRef types.Image, size int64, driver *driver.Data) (*ImageData, error) {
+ imgSize, err := imgRef.Size()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error reading size of image %q", img.ID)
+ }
+ manifest, manifestType, err := imgRef.Manifest()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error reading manifest for image %q", img.ID)
+ }
+ imgDigest := digest.Digest("")
+ if len(manifest) > 0 {
+ imgDigest = digest.Canonical.FromBytes(manifest)
+ }
+ annotations := annotations(manifest, manifestType)
+
+ ociv1Img, err := imgRef.OCIConfig()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting oci image info %q", img.ID)
+ }
+ info, err := imgRef.Inspect()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting image info %q", img.ID)
+ }
+
+ var repoDigests []string
+ for _, name := range img.Names {
+ repoDigests = append(repoDigests, strings.SplitN(name, ":", 2)[0]+"@"+imgDigest.String())
+ }
+
+ data := &ImageData{
+ ID: img.ID,
+ RepoTags: img.Names,
+ RepoDigests: repoDigests,
+ Comment: ociv1Img.History[0].Comment,
+ Created: ociv1Img.Created,
+ Author: ociv1Img.History[0].Author,
+ Architecture: ociv1Img.Architecture,
+ Os: ociv1Img.OS,
+ Config: &ociv1Img.Config,
+ Version: info.DockerVersion,
+ Size: size,
+ VirtualSize: size + imgSize,
+ Annotations: annotations,
+ Digest: imgDigest,
+ Labels: info.Labels,
+ RootFS: &RootFS{
+ Type: ociv1Img.RootFS.Type,
+ Layers: ociv1Img.RootFS.DiffIDs,
+ },
+ GraphDriver: driver,
+ }
+ return data, nil
+}
+
+func annotations(manifest []byte, manifestType string) map[string]string {
+ annotations := make(map[string]string)
+ switch manifestType {
+ case ociv1.MediaTypeImageManifest:
+ var m ociv1.Manifest
+ if err := json.Unmarshal(manifest, &m); err == nil {
+ for k, v := range m.Annotations {
+ annotations[k] = v
+ }
+ }
+ }
+ return annotations
+}
diff --git a/libpod/images/image_data.go b/libpod/images/image_data.go
deleted file mode 100644
index 1fa63f6cb..000000000
--- a/libpod/images/image_data.go
+++ /dev/null
@@ -1,202 +0,0 @@
-package images
-
-import (
- "encoding/json"
- "time"
-
- "github.com/containers/image/docker/reference"
- is "github.com/containers/image/storage"
- "github.com/containers/image/transports"
- "github.com/containers/image/types"
- "github.com/containers/storage"
- digest "github.com/opencontainers/go-digest"
- ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
- "github.com/pkg/errors"
- "github.com/projectatomic/libpod/libpod/driver"
-)
-
-// Data handles the data used when inspecting a container
-// nolint
-type Data struct {
- ID string
- Tags []string
- Digests []string
- Digest digest.Digest
- Comment string
- Created *time.Time
- Container string
- Author string
- Config ociv1.ImageConfig
- Architecture string
- OS string
- Annotations map[string]string
- CreatedBy string
- Size uint
- VirtualSize uint
- GraphDriver driver.Data
- RootFS ociv1.RootFS
-}
-
-// ParseImageNames parses the names we've stored with an image into a list of
-// tagged references and a list of references which contain digests.
-func ParseImageNames(names []string) (tags, digests []string, err error) {
- for _, name := range names {
- if named, err := reference.ParseNamed(name); err == nil {
- if digested, ok := named.(reference.Digested); ok {
- canonical, err := reference.WithDigest(named, digested.Digest())
- if err == nil {
- digests = append(digests, canonical.String())
- }
- } else {
- if reference.IsNameOnly(named) {
- named = reference.TagNameOnly(named)
- }
- if tagged, ok := named.(reference.Tagged); ok {
- namedTagged, err := reference.WithTag(named, tagged.Tag())
- if err == nil {
- tags = append(tags, namedTagged.String())
- }
- }
- }
- }
- }
- return tags, digests, nil
-}
-
-func annotations(manifest []byte, manifestType string) map[string]string {
- annotations := make(map[string]string)
- switch manifestType {
- case ociv1.MediaTypeImageManifest:
- var m ociv1.Manifest
- if err := json.Unmarshal(manifest, &m); err == nil {
- for k, v := range m.Annotations {
- annotations[k] = v
- }
- }
- }
- return annotations
-}
-
-// GetData gets the Data for a container with the given name in the given store.
-func GetData(store storage.Store, name string) (*Data, error) {
- img, err := FindImage(store, name)
- if err != nil {
- return nil, errors.Wrapf(err, "error reading image %q", name)
- }
-
- imgRef, err := FindImageRef(store, "@"+img.ID)
- if err != nil {
- return nil, errors.Wrapf(err, "error reading image %q", img.ID)
- }
-
- tags, digests, err := ParseImageNames(img.Names)
- if err != nil {
- return nil, errors.Wrapf(err, "error parsing image names for %q", name)
- }
-
- driverName, err := driver.GetDriverName(store)
- if err != nil {
- return nil, errors.Wrapf(err, "error reading name of storage driver")
- }
-
- topLayerID := img.TopLayer
-
- driverMetadata, err := driver.GetDriverMetadata(store, topLayerID)
- if err != nil {
- return nil, errors.Wrapf(err, "error asking storage driver %q for metadata", driverName)
- }
-
- layer, err := store.Layer(topLayerID)
- if err != nil {
- return nil, errors.Wrapf(err, "error reading information about layer %q", topLayerID)
- }
- size, err := store.DiffSize(layer.Parent, layer.ID)
- if err != nil {
- return nil, errors.Wrapf(err, "error determining size of layer %q", layer.ID)
- }
-
- imgSize, err := imgRef.Size()
- if err != nil {
- return nil, errors.Wrapf(err, "error determining size of image %q", transports.ImageName(imgRef.Reference()))
- }
-
- manifest, manifestType, err := imgRef.Manifest()
- if err != nil {
- return nil, errors.Wrapf(err, "error reading manifest for image %q", img.ID)
- }
- manifestDigest := digest.Digest("")
- if len(manifest) > 0 {
- manifestDigest = digest.Canonical.FromBytes(manifest)
- }
- annotations := annotations(manifest, manifestType)
-
- config, err := imgRef.OCIConfig()
- if err != nil {
- return nil, errors.Wrapf(err, "error reading image configuration for %q", img.ID)
- }
- historyComment := ""
- historyCreatedBy := ""
- if len(config.History) > 0 {
- historyComment = config.History[len(config.History)-1].Comment
- historyCreatedBy = config.History[len(config.History)-1].CreatedBy
- }
-
- return &Data{
- ID: img.ID,
- Tags: tags,
- Digests: digests,
- Digest: manifestDigest,
- Comment: historyComment,
- Created: config.Created,
- Author: config.Author,
- Config: config.Config,
- Architecture: config.Architecture,
- OS: config.OS,
- Annotations: annotations,
- CreatedBy: historyCreatedBy,
- Size: uint(size),
- VirtualSize: uint(size + imgSize),
- GraphDriver: driver.Data{
- Name: driverName,
- Data: driverMetadata,
- },
- RootFS: config.RootFS,
- }, nil
-}
-
-// FindImage searches for a *storage.Image with a matching the given name or ID in the given store.
-func FindImage(store storage.Store, image string) (*storage.Image, error) {
- var img *storage.Image
- ref, err := is.Transport.ParseStoreReference(store, image)
- if err == nil {
- img, err = is.Transport.GetStoreImage(store, ref)
- }
- if err != nil {
- img2, err2 := store.Image(image)
- if err2 != nil {
- if ref == nil {
- return nil, errors.Wrapf(err, "error parsing reference to image %q", image)
- }
- return nil, errors.Wrapf(err, "unable to locate image %q", image)
- }
- img = img2
- }
- return img, nil
-}
-
-// FindImageRef searches for and returns a new types.Image matching the given name or ID in the given store.
-func FindImageRef(store storage.Store, image string) (types.Image, error) {
- img, err := FindImage(store, image)
- if err != nil {
- return nil, errors.Wrapf(err, "unable to locate image %q", image)
- }
- ref, err := is.Transport.ParseStoreReference(store, "@"+img.ID)
- if err != nil {
- return nil, errors.Wrapf(err, "error parsing reference to image %q", img.ID)
- }
- imgRef, err := ref.NewImage(nil)
- if err != nil {
- return nil, errors.Wrapf(err, "error reading image %q", img.ID)
- }
- return imgRef, nil
-}
diff --git a/libpod/inspect_data.go b/libpod/inspect_data.go
new file mode 100644
index 000000000..072b94ab2
--- /dev/null
+++ b/libpod/inspect_data.go
@@ -0,0 +1,103 @@
+package libpod
+
+import (
+ "time"
+
+ digest "github.com/opencontainers/go-digest"
+ "github.com/opencontainers/image-spec/specs-go/v1"
+ specs "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/projectatomic/libpod/libpod/driver"
+)
+
+// ContainerInspectData handles the data used when inspecting a container
+type ContainerInspectData struct {
+ ID string `json:"ID"`
+ Created time.Time `json:"Created"`
+ Path string `json:"Path"`
+ Args []string `json:"Args"`
+ State *ContainerInspectState `json:"State"`
+ ImageID string `json:"Image"`
+ ImageName string `json:"ImageName"`
+ ResolvConfPath string `json:"ResolvConfPath"`
+ HostnamePath string `json:"HostnamePath"` //TODO
+ HostsPath string `json:"HostsPath"` //TODO
+ StaticDir string `json:"StaticDir"`
+ LogPath string `json:"LogPath"`
+ Name string `json:"Name"`
+ RestartCount int32 `json:"RestartCount"` //TODO
+ Driver string `json:"Driver"`
+ MountLabel string `json:"MountLabel"`
+ ProcessLabel string `json:"ProcessLabel"`
+ AppArmorProfile string `json:"AppArmorProfile"`
+ ExecIDs []string `json:"ExecIDs"` //TODO
+ GraphDriver *driver.Data `json:"GraphDriver"`
+ SizeRw int64 `json:"SizeRw,omitempty"`
+ SizeRootFs int64 `json:"SizeRootFs,omitempty"`
+ Mounts []specs.Mount `json:"Mounts"`
+ NetworkSettings *NetworkSettings `json:"NetworkSettings"` //TODO
+}
+
+// ContainerInspectState represents the state of a container.
+type ContainerInspectState struct {
+ OciVersion string `json:"OciVersion"`
+ Status string `json:"Status"`
+ Running bool `json:"Running"`
+ Paused bool `json:"Paused"`
+ Restarting bool `json:"Restarting"` // TODO
+ OOMKilled bool `json:"OOMKilled"`
+ Dead bool `json:"Dead"`
+ Pid int `json:"Pid"`
+ ExitCode int32 `json:"ExitCode"`
+ Error string `json:"Error"` // TODO
+ StartedAt time.Time `json:"StartedAt"`
+ FinishedAt time.Time `json:"FinishedAt"`
+}
+
+// NetworkSettings holds information about the newtwork settings of the container
+type NetworkSettings struct {
+ Bridge string `json:"Bridge"`
+ SandboxID string `json:"SandboxID"`
+ HairpinMode bool `json:"HairpinMode"`
+ LinkLocalIPv6Address string `json:"LinkLocalIPv6Address"`
+ LinkLocalIPv6PrefixLen int `json:"LinkLocalIPv6PrefixLen"`
+ Ports map[string]struct{} `json:"Ports"`
+ SandboxKey string `json:"SandboxKey"`
+ SecondaryIPAddresses string `json:"SecondaryIPAddresses"` //idk type
+ SecondaryIPv6Addresses string `json:"SecondaryIPv6Addresses"` //idk type
+ EndpointID string `json:"EndpointID"`
+ Gateway string `json:"Gateway"`
+ GlobalIPv6Addresses string `json:"GlobalIPv6Addresses"`
+ GlobalIPv6PrefixLen int `json:"GlobalIPv6PrefixLen"`
+ IPAddress string `json:"IPAddress"`
+ IPPrefixLen int `json:"IPPrefixLen"`
+ IPv6Gateway string `json:"IPv6Gateway"`
+ MacAddress string `json:"MacAddress"`
+}
+
+// ImageData holds the inspect information of an image
+type ImageData struct {
+ ID string `json:"ID"`
+ Digest digest.Digest `json:"Digest"`
+ RepoTags []string `json:"RepoTags"`
+ RepoDigests []string `json:"RepoDigests"`
+ Parent string `json:"Parent"`
+ Comment string `json:"Comment"`
+ Created *time.Time `json:"Created"`
+ Config *v1.ImageConfig `json:"Config"`
+ Version string `json:"Version"`
+ Author string `json:"Author"`
+ Architecture string `json:"Architecture"`
+ Os string `json:"Os"`
+ Size int64 `json:"Size"`
+ VirtualSize int64 `json:"VirtualSize"`
+ GraphDriver *driver.Data `json:"GraphDriver"`
+ RootFS *RootFS `json:"RootFS"`
+ Labels map[string]string `json:"Labels"`
+ Annotations map[string]string `json:"Annotations"`
+}
+
+// RootFS holds the root fs information of an image
+type RootFS struct {
+ Type string `json:"Type"`
+ Layers []digest.Digest `json:"Layers"`
+}
diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go
index 26f85b037..d5da35c42 100644
--- a/libpod/runtime_img.go
+++ b/libpod/runtime_img.go
@@ -20,15 +20,14 @@ import (
"github.com/containers/image/signature"
is "github.com/containers/image/storage"
"github.com/containers/image/tarball"
- "github.com/containers/image/transports"
"github.com/containers/image/transports/alltransports"
"github.com/containers/image/types"
"github.com/containers/storage"
"github.com/containers/storage/pkg/archive"
- digest "github.com/opencontainers/go-digest"
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/projectatomic/libpod/libpod/common"
+ "github.com/projectatomic/libpod/libpod/driver"
)
// Runtime API
@@ -452,7 +451,7 @@ func getRegistries() ([]string, error) {
// ImageFilter is a function to determine whether an image is included in
// command output. Images to be outputted are tested using the function. A true
// return will include the image, a false return will exclude it.
-type ImageFilter func(*storage.Image, *types.ImageInspectInfo) bool
+type ImageFilter func(*storage.Image, *ImageData) bool
func (ips imageDecomposeStruct) returnFQName() string {
return fmt.Sprintf("%s%s/%s:%s", ips.transport, ips.registry, ips.imageName, ips.tag)
@@ -1032,7 +1031,7 @@ func (r *Runtime) ImportImage(path string, options CopyOptions) error {
}
// GetImageInspectInfo returns the inspect information of an image
-func (r *Runtime) GetImageInspectInfo(image storage.Image) (*types.ImageInspectInfo, error) {
+func (r *Runtime) GetImageInspectInfo(image storage.Image) (*ImageData, error) {
r.lock.RLock()
defer r.lock.RUnlock()
@@ -1042,12 +1041,25 @@ func (r *Runtime) GetImageInspectInfo(image storage.Image) (*types.ImageInspectI
return r.getImageInspectInfo(image)
}
-func (r *Runtime) getImageInspectInfo(image storage.Image) (*types.ImageInspectInfo, error) {
- img, err := r.getImageRef(image.ID)
+func (r *Runtime) getImageInspectInfo(image storage.Image) (*ImageData, error) {
+ imgRef, err := r.getImageRef("@" + image.ID)
if err != nil {
- return nil, err
+ return nil, errors.Wrapf(err, "error reading image %q", image.ID)
+ }
+
+ layer, err := r.store.Layer(image.TopLayer)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error reading information about layer %q", image.TopLayer)
+ }
+ size, err := r.store.DiffSize(layer.Parent, layer.ID)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error determining size of layer %q", layer.ID)
+ }
+ driverData, err := driver.GetDriverData(r.store, layer.ID)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting graph driver info %q", image.ID)
}
- return img.Inspect()
+ return getImageData(image, imgRef, size, driverData)
}
// ParseImageFilter takes a set of images and a filter string as input, and returns the libpod.ImageFilterParams struct
@@ -1093,7 +1105,7 @@ func (r *Runtime) ParseImageFilter(imageInput, filter string) (*ImageFilterParam
if err != nil {
return nil, err
}
- params.BeforeImage = info.Created
+ params.BeforeImage = *info.Created
} else {
return nil, fmt.Errorf("no such id: %s", pair[0])
}
@@ -1103,7 +1115,7 @@ func (r *Runtime) ParseImageFilter(imageInput, filter string) (*ImageFilterParam
if err != nil {
return nil, err
}
- params.SinceImage = info.Created
+ params.SinceImage = *info.Created
} else {
return nil, fmt.Errorf("no such id: %s``", pair[0])
}
@@ -1116,43 +1128,6 @@ func (r *Runtime) ParseImageFilter(imageInput, filter string) (*ImageFilterParam
return &params, nil
}
-// InfoAndDigestAndSize returns the inspection info and size of the image in the given
-// store and the digest of its manifest, if it has one, or "" if it doesn't.
-func (r *Runtime) InfoAndDigestAndSize(img storage.Image) (*types.ImageInspectInfo, digest.Digest, int64, error) {
- r.lock.RLock()
- defer r.lock.RUnlock()
-
- if !r.valid {
- return nil, "", -1, ErrRuntimeStopped
- }
-
- imgRef, err := r.getImageRef("@" + img.ID)
- if err != nil {
- return nil, "", -1, errors.Wrapf(err, "error reading image %q", img.ID)
- }
- return infoAndDigestAndSize(imgRef)
-}
-
-func infoAndDigestAndSize(imgRef types.Image) (*types.ImageInspectInfo, digest.Digest, int64, error) {
- imgSize, err := imgRef.Size()
- if err != nil {
- return nil, "", -1, errors.Wrapf(err, "error reading size of image %q", transports.ImageName(imgRef.Reference()))
- }
- manifest, _, err := imgRef.Manifest()
- if err != nil {
- return nil, "", -1, errors.Wrapf(err, "error reading manifest for image %q", transports.ImageName(imgRef.Reference()))
- }
- manifestDigest := digest.Digest("")
- if len(manifest) > 0 {
- manifestDigest = digest.Canonical.FromBytes(manifest)
- }
- info, err := imgRef.Inspect()
- if err != nil {
- return nil, "", -1, errors.Wrapf(err, "error inspecting image %q", transports.ImageName(imgRef.Reference()))
- }
- return info, manifestDigest, imgSize, nil
-}
-
// MatchesID returns true if argID is a full or partial match for id
func MatchesID(id, argID string) bool {
return strings.HasPrefix(argID, id)
diff --git a/test/kpod_inspect.bats b/test/kpod_inspect.bats
index ca7e16aad..86a4e7698 100644
--- a/test/kpod_inspect.bats
+++ b/test/kpod_inspect.bats
@@ -40,3 +40,13 @@ function setup() {
echo "$output"
[ "$status" -eq 0 ]
}
+
+@test "kpod inspect container with size" {
+ run bash -c "${KPOD_BINARY} ${KPOD_OPTIONS} create ${BB} ls"
+ echo "$output"
+ [ "$status" -eq 0 ]
+ ctr_id="$output"
+ run bash -c "${KPOD_BINARY} $KPOD_OPTIONS inspect --size $ctr_id | python -m json.tool | grep SizeRootFs"
+ echo "$output"
+ [ "$status" -eq 0 ]
+}