Update kpod inspect to use the new container state

kpod inspect now uses the new libpod container state
and closely matches the output of docker inspect
some aspects of it are still WIP as the libpod container state
is still being worked on

Signed-off-by: umohnani8 <umohnani@redhat.com>
This commit is contained in:
umohnani8
2017-11-29 16:56:18 -05:00
parent 88121e0747
commit 74ee579375
17 changed files with 1047 additions and 953 deletions

View File

@ -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"),
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"),
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"),
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 !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

View File

@ -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

View File

@ -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
}

View File

@ -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 parsing container data")
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 %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"`
}

View File

@ -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()

View File

@ -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

View File

@ -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]))

View File

@ -28,144 +28,55 @@ Display the total file size if the type is a container
## EXAMPLE
kpod inspect redis:alpine
```
# kpod inspect fedora
{
"ArgsEscaped": true,
"AttachStderr": false,
"AttachStdin": false,
"AttachStdout": false,
"Cmd": [
"/bin/sh",
"-c",
"#(nop) ",
"CMD [\"redis-server\"]"
"Id": "422dc563ca3260ad9ef5c47a1c246f5065d7f177ce51f4dd208efd82967ff182",
"Digest": "sha256:1b9bfb4e634dc1e5c19d0fa1eb2e5a28a5c2b498e3d3e4ac742bd7f5dae08611",
"RepoTags": [
"docker.io/library/fedora:latest"
],
"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"
}
{
"ID": "b3f2436bdb978c1d33b1387afb5d7ba7e3243ed2ce908db431ac0069da86cb45",
"Names": [
"docker.io/library/redis:alpine"
],
"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"
"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"
"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>

View File

@ -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
}

View File

@ -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) {

View File

@ -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
}

View File

@ -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
}

81
libpod/image_inspect.go Normal file
View File

@ -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
}

View File

@ -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
}

103
libpod/inspect_data.go Normal file
View File

@ -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"`
}

View File

@ -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)
}
return img.Inspect()
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 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)

View File

@ -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 ]
}