Merge pull request #106 from umohnani8/kpod_inspect

Update kpod inspect to use the new container state
This commit is contained in:
Matthew Heon
2017-12-13 09:36:14 -05:00
committed by GitHub
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 ]
}