mirror of
https://github.com/containers/podman.git
synced 2025-06-12 11:02:00 +08:00
Merge pull request #106 from umohnani8/kpod_inspect
Update kpod inspect to use the new container state
This commit is contained in:
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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"`
|
||||
}
|
||||
|
@ -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()
|
||||
|
162
cmd/kpod/spec.go
162
cmd/kpod/spec.go
@ -20,7 +20,7 @@ import (
|
||||
)
|
||||
|
||||
func blockAccessToKernelFilesystems(config *createConfig, g *generate.Generator) {
|
||||
if !config.privileged {
|
||||
if !config.Privileged {
|
||||
for _, mp := range []string{
|
||||
"/proc/kcore",
|
||||
"/proc/latency_stats",
|
||||
@ -47,12 +47,12 @@ func blockAccessToKernelFilesystems(config *createConfig, g *generate.Generator)
|
||||
}
|
||||
|
||||
func addPidNS(config *createConfig, g *generate.Generator) error {
|
||||
pidMode := config.pidMode
|
||||
pidMode := config.PidMode
|
||||
if pidMode.IsHost() {
|
||||
return g.RemoveLinuxNamespace(libpod.PIDNamespace)
|
||||
}
|
||||
if pidMode.IsContainer() {
|
||||
ctr, err := config.runtime.LookupContainer(pidMode.Container())
|
||||
ctr, err := config.Runtime.LookupContainer(pidMode.Container())
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "container %q not found", pidMode.Container())
|
||||
}
|
||||
@ -69,7 +69,7 @@ func addPidNS(config *createConfig, g *generate.Generator) error {
|
||||
}
|
||||
|
||||
func addNetNS(config *createConfig, g *generate.Generator) error {
|
||||
netMode := config.netMode
|
||||
netMode := config.NetMode
|
||||
if netMode.IsHost() {
|
||||
return g.RemoveLinuxNamespace(libpod.NetNamespace)
|
||||
}
|
||||
@ -80,7 +80,7 @@ func addNetNS(config *createConfig, g *generate.Generator) error {
|
||||
return libpod.ErrNotImplemented
|
||||
}
|
||||
if netMode.IsContainer() {
|
||||
ctr, err := config.runtime.LookupContainer(netMode.ConnectedContainer())
|
||||
ctr, err := config.Runtime.LookupContainer(netMode.ConnectedContainer())
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "container %q not found", netMode.ConnectedContainer())
|
||||
}
|
||||
@ -97,7 +97,7 @@ func addNetNS(config *createConfig, g *generate.Generator) error {
|
||||
}
|
||||
|
||||
func addUTSNS(config *createConfig, g *generate.Generator) error {
|
||||
utsMode := config.utsMode
|
||||
utsMode := config.UtsMode
|
||||
if utsMode.IsHost() {
|
||||
return g.RemoveLinuxNamespace(libpod.UTSNamespace)
|
||||
}
|
||||
@ -105,12 +105,12 @@ func addUTSNS(config *createConfig, g *generate.Generator) error {
|
||||
}
|
||||
|
||||
func addIpcNS(config *createConfig, g *generate.Generator) error {
|
||||
ipcMode := config.ipcMode
|
||||
ipcMode := config.IpcMode
|
||||
if ipcMode.IsHost() {
|
||||
return g.RemoveLinuxNamespace(libpod.IPCNamespace)
|
||||
}
|
||||
if ipcMode.IsContainer() {
|
||||
ctr, err := config.runtime.LookupContainer(ipcMode.Container())
|
||||
ctr, err := config.Runtime.LookupContainer(ipcMode.Container())
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "container %q not found", ipcMode.Container())
|
||||
}
|
||||
@ -133,7 +133,7 @@ func addRlimits(config *createConfig, g *generate.Generator) error {
|
||||
err error
|
||||
)
|
||||
|
||||
for _, u := range config.resources.ulimit {
|
||||
for _, u := range config.Resources.Ulimit {
|
||||
if ul, err = units.ParseUlimit(u); err != nil {
|
||||
return errors.Wrapf(err, "ulimit option %q requires name=SOFT:HARD, failed to be parsed", u)
|
||||
}
|
||||
@ -146,10 +146,10 @@ func addRlimits(config *createConfig, g *generate.Generator) error {
|
||||
func setupCapabilities(config *createConfig, configSpec *spec.Spec) error {
|
||||
var err error
|
||||
var caplist []string
|
||||
if config.privileged {
|
||||
if config.Privileged {
|
||||
caplist = caps.GetAllCapabilities()
|
||||
} else {
|
||||
caplist, err = caps.TweakCapabilities(configSpec.Process.Capabilities.Bounding, config.capAdd, config.capDrop)
|
||||
caplist, err = caps.TweakCapabilities(configSpec.Process.Capabilities.Bounding, config.CapAdd, config.CapDrop)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -166,85 +166,85 @@ func setupCapabilities(config *createConfig, configSpec *spec.Spec) error {
|
||||
func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) {
|
||||
g := generate.New()
|
||||
g.AddCgroupsMount("ro")
|
||||
g.SetProcessCwd(config.workDir)
|
||||
g.SetProcessArgs(config.command)
|
||||
g.SetProcessTerminal(config.tty)
|
||||
g.SetProcessCwd(config.WorkDir)
|
||||
g.SetProcessArgs(config.Command)
|
||||
g.SetProcessTerminal(config.Tty)
|
||||
// User and Group must go together
|
||||
g.SetProcessUID(config.user)
|
||||
g.SetProcessGID(config.group)
|
||||
for _, gid := range config.groupAdd {
|
||||
g.SetProcessUID(config.User)
|
||||
g.SetProcessGID(config.Group)
|
||||
for _, gid := range config.GroupAdd {
|
||||
g.AddProcessAdditionalGid(gid)
|
||||
}
|
||||
for key, val := range config.GetAnnotations() {
|
||||
g.AddAnnotation(key, val)
|
||||
}
|
||||
g.SetRootReadonly(config.readOnlyRootfs)
|
||||
g.SetHostname(config.hostname)
|
||||
if config.hostname != "" {
|
||||
g.AddProcessEnv("HOSTNAME", config.hostname)
|
||||
g.SetRootReadonly(config.ReadOnlyRootfs)
|
||||
g.SetHostname(config.Hostname)
|
||||
if config.Hostname != "" {
|
||||
g.AddProcessEnv("HOSTNAME", config.Hostname)
|
||||
}
|
||||
|
||||
for _, sysctl := range config.sysctl {
|
||||
for _, sysctl := range config.Sysctl {
|
||||
s := strings.SplitN(sysctl, "=", 2)
|
||||
g.AddLinuxSysctl(s[0], s[1])
|
||||
}
|
||||
|
||||
// RESOURCES - MEMORY
|
||||
if config.resources.memory != 0 {
|
||||
g.SetLinuxResourcesMemoryLimit(config.resources.memory)
|
||||
if config.Resources.Memory != 0 {
|
||||
g.SetLinuxResourcesMemoryLimit(config.Resources.Memory)
|
||||
}
|
||||
if config.resources.memoryReservation != 0 {
|
||||
g.SetLinuxResourcesMemoryReservation(config.resources.memoryReservation)
|
||||
if config.Resources.MemoryReservation != 0 {
|
||||
g.SetLinuxResourcesMemoryReservation(config.Resources.MemoryReservation)
|
||||
}
|
||||
if config.resources.memorySwap != 0 {
|
||||
g.SetLinuxResourcesMemorySwap(config.resources.memorySwap)
|
||||
if config.Resources.MemorySwap != 0 {
|
||||
g.SetLinuxResourcesMemorySwap(config.Resources.MemorySwap)
|
||||
}
|
||||
if config.resources.kernelMemory != 0 {
|
||||
g.SetLinuxResourcesMemoryKernel(config.resources.kernelMemory)
|
||||
if config.Resources.KernelMemory != 0 {
|
||||
g.SetLinuxResourcesMemoryKernel(config.Resources.KernelMemory)
|
||||
}
|
||||
if config.resources.memorySwappiness != -1 {
|
||||
g.SetLinuxResourcesMemorySwappiness(uint64(config.resources.memorySwappiness))
|
||||
if config.Resources.MemorySwappiness != -1 {
|
||||
g.SetLinuxResourcesMemorySwappiness(uint64(config.Resources.MemorySwappiness))
|
||||
}
|
||||
g.SetLinuxResourcesMemoryDisableOOMKiller(config.resources.disableOomKiller)
|
||||
g.SetProcessOOMScoreAdj(config.resources.oomScoreAdj)
|
||||
g.SetLinuxResourcesMemoryDisableOOMKiller(config.Resources.DisableOomKiller)
|
||||
g.SetProcessOOMScoreAdj(config.Resources.OomScoreAdj)
|
||||
|
||||
// RESOURCES - CPU
|
||||
|
||||
if config.resources.cpuShares != 0 {
|
||||
g.SetLinuxResourcesCPUShares(config.resources.cpuShares)
|
||||
if config.Resources.CpuShares != 0 {
|
||||
g.SetLinuxResourcesCPUShares(config.Resources.CpuShares)
|
||||
}
|
||||
if config.resources.cpuQuota != 0 {
|
||||
g.SetLinuxResourcesCPUQuota(config.resources.cpuQuota)
|
||||
if config.Resources.CpuQuota != 0 {
|
||||
g.SetLinuxResourcesCPUQuota(config.Resources.CpuQuota)
|
||||
}
|
||||
if config.resources.cpuPeriod != 0 {
|
||||
g.SetLinuxResourcesCPUPeriod(config.resources.cpuPeriod)
|
||||
if config.Resources.CpuPeriod != 0 {
|
||||
g.SetLinuxResourcesCPUPeriod(config.Resources.CpuPeriod)
|
||||
}
|
||||
if config.resources.cpuRtRuntime != 0 {
|
||||
g.SetLinuxResourcesCPURealtimeRuntime(config.resources.cpuRtRuntime)
|
||||
if config.Resources.CpuRtRuntime != 0 {
|
||||
g.SetLinuxResourcesCPURealtimeRuntime(config.Resources.CpuRtRuntime)
|
||||
}
|
||||
if config.resources.cpuRtPeriod != 0 {
|
||||
g.SetLinuxResourcesCPURealtimePeriod(config.resources.cpuRtPeriod)
|
||||
if config.Resources.CpuRtPeriod != 0 {
|
||||
g.SetLinuxResourcesCPURealtimePeriod(config.Resources.CpuRtPeriod)
|
||||
}
|
||||
if config.resources.cpus != "" {
|
||||
g.SetLinuxResourcesCPUCpus(config.resources.cpus)
|
||||
if config.Resources.Cpus != "" {
|
||||
g.SetLinuxResourcesCPUCpus(config.Resources.Cpus)
|
||||
}
|
||||
if config.resources.cpusetMems != "" {
|
||||
g.SetLinuxResourcesCPUMems(config.resources.cpusetMems)
|
||||
if config.Resources.CpusetMems != "" {
|
||||
g.SetLinuxResourcesCPUMems(config.Resources.CpusetMems)
|
||||
}
|
||||
|
||||
// SECURITY OPTS
|
||||
g.SetProcessNoNewPrivileges(config.noNewPrivileges)
|
||||
g.SetProcessApparmorProfile(config.apparmorProfile)
|
||||
g.SetProcessSelinuxLabel(config.processLabel)
|
||||
g.SetLinuxMountLabel(config.mountLabel)
|
||||
g.SetProcessNoNewPrivileges(config.NoNewPrivileges)
|
||||
g.SetProcessApparmorProfile(config.ApparmorProfile)
|
||||
g.SetProcessSelinuxLabel(config.ProcessLabel)
|
||||
g.SetLinuxMountLabel(config.MountLabel)
|
||||
blockAccessToKernelFilesystems(config, &g)
|
||||
|
||||
// RESOURCES - PIDS
|
||||
if config.resources.pidsLimit != 0 {
|
||||
g.SetLinuxResourcesPidsLimit(config.resources.pidsLimit)
|
||||
if config.Resources.PidsLimit != 0 {
|
||||
g.SetLinuxResourcesPidsLimit(config.Resources.PidsLimit)
|
||||
}
|
||||
|
||||
for _, i := range config.tmpfs {
|
||||
for _, i := range config.Tmpfs {
|
||||
options := []string{"rw", "noexec", "nosuid", "nodev", "size=65536k"}
|
||||
spliti := strings.SplitN(i, ":", 2)
|
||||
if len(spliti) > 1 {
|
||||
@ -257,7 +257,7 @@ func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) {
|
||||
g.AddTmpfsMount(spliti[0], options)
|
||||
}
|
||||
|
||||
for name, val := range config.env {
|
||||
for name, val := range config.Env {
|
||||
g.AddProcessEnv(name, val)
|
||||
}
|
||||
|
||||
@ -282,14 +282,14 @@ func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) {
|
||||
}
|
||||
configSpec := g.Spec()
|
||||
|
||||
if config.seccompProfilePath != "" && config.seccompProfilePath != "unconfined" {
|
||||
seccompProfile, err := ioutil.ReadFile(config.seccompProfilePath)
|
||||
if config.SeccompProfilePath != "" && config.SeccompProfilePath != "unconfined" {
|
||||
seccompProfile, err := ioutil.ReadFile(config.SeccompProfilePath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "opening seccomp profile (%s) failed", config.seccompProfilePath)
|
||||
return nil, errors.Wrapf(err, "opening seccomp profile (%s) failed", config.SeccompProfilePath)
|
||||
}
|
||||
var seccompConfig spec.LinuxSeccomp
|
||||
if err := json.Unmarshal(seccompProfile, &seccompConfig); err != nil {
|
||||
return nil, errors.Wrapf(err, "decoding seccomp profile (%s) failed", config.seccompProfilePath)
|
||||
return nil, errors.Wrapf(err, "decoding seccomp profile (%s) failed", config.SeccompProfilePath)
|
||||
}
|
||||
configSpec.Linux.Seccomp = &seccompConfig
|
||||
}
|
||||
@ -347,10 +347,10 @@ func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) {
|
||||
|
||||
func (c *createConfig) CreateBlockIO() (spec.LinuxBlockIO, error) {
|
||||
bio := spec.LinuxBlockIO{}
|
||||
bio.Weight = &c.resources.blkioWeight
|
||||
if len(c.resources.blkioWeightDevice) > 0 {
|
||||
bio.Weight = &c.Resources.BlkioWeight
|
||||
if len(c.Resources.BlkioWeightDevice) > 0 {
|
||||
var lwds []spec.LinuxWeightDevice
|
||||
for _, i := range c.resources.blkioWeightDevice {
|
||||
for _, i := range c.Resources.BlkioWeightDevice {
|
||||
wd, err := validateweightDevice(i)
|
||||
if err != nil {
|
||||
return bio, errors.Wrapf(err, "invalid values for blkio-weight-device")
|
||||
@ -364,29 +364,29 @@ func (c *createConfig) CreateBlockIO() (spec.LinuxBlockIO, error) {
|
||||
lwds = append(lwds, lwd)
|
||||
}
|
||||
}
|
||||
if len(c.resources.deviceReadBps) > 0 {
|
||||
readBps, err := makeThrottleArray(c.resources.deviceReadBps)
|
||||
if len(c.Resources.DeviceReadBps) > 0 {
|
||||
readBps, err := makeThrottleArray(c.Resources.DeviceReadBps)
|
||||
if err != nil {
|
||||
return bio, err
|
||||
}
|
||||
bio.ThrottleReadBpsDevice = readBps
|
||||
}
|
||||
if len(c.resources.deviceWriteBps) > 0 {
|
||||
writeBpds, err := makeThrottleArray(c.resources.deviceWriteBps)
|
||||
if len(c.Resources.DeviceWriteBps) > 0 {
|
||||
writeBpds, err := makeThrottleArray(c.Resources.DeviceWriteBps)
|
||||
if err != nil {
|
||||
return bio, err
|
||||
}
|
||||
bio.ThrottleWriteBpsDevice = writeBpds
|
||||
}
|
||||
if len(c.resources.deviceReadIOps) > 0 {
|
||||
readIOps, err := makeThrottleArray(c.resources.deviceReadIOps)
|
||||
if len(c.Resources.DeviceReadIOps) > 0 {
|
||||
readIOps, err := makeThrottleArray(c.Resources.DeviceReadIOps)
|
||||
if err != nil {
|
||||
return bio, err
|
||||
}
|
||||
bio.ThrottleReadIOPSDevice = readIOps
|
||||
}
|
||||
if len(c.resources.deviceWriteIOps) > 0 {
|
||||
writeIOps, err := makeThrottleArray(c.resources.deviceWriteIOps)
|
||||
if len(c.Resources.DeviceWriteIOps) > 0 {
|
||||
writeIOps, err := makeThrottleArray(c.Resources.DeviceWriteIOps)
|
||||
if err != nil {
|
||||
return bio, err
|
||||
}
|
||||
@ -401,7 +401,7 @@ func (c *createConfig) GetAnnotations() map[string]string {
|
||||
a := getDefaultAnnotations()
|
||||
// TODO - Which annotations do we want added by default
|
||||
// TODO - This should be added to the DB long term
|
||||
if c.tty {
|
||||
if c.Tty {
|
||||
a["io.kubernetes.cri-o.TTY"] = "true"
|
||||
}
|
||||
return a
|
||||
@ -445,7 +445,7 @@ func getDefaultAnnotations() map[string]string {
|
||||
func (c *createConfig) GetVolumeMounts() ([]spec.Mount, error) {
|
||||
var m []spec.Mount
|
||||
var options []string
|
||||
for _, i := range c.volumes {
|
||||
for _, i := range c.Volumes {
|
||||
// We need to handle SELinux options better here, specifically :Z
|
||||
spliti := strings.Split(i, ":")
|
||||
if len(spliti) > 2 {
|
||||
@ -472,12 +472,12 @@ func (c *createConfig) GetVolumeMounts() ([]spec.Mount, error) {
|
||||
options = append(options, "rw")
|
||||
}
|
||||
if foundz {
|
||||
if err := label.Relabel(spliti[0], c.mountLabel, true); err != nil {
|
||||
if err := label.Relabel(spliti[0], c.MountLabel, true); err != nil {
|
||||
return nil, errors.Wrapf(err, "relabel failed %q", spliti[0])
|
||||
}
|
||||
}
|
||||
if foundZ {
|
||||
if err := label.Relabel(spliti[0], c.mountLabel, false); err != nil {
|
||||
if err := label.Relabel(spliti[0], c.MountLabel, false); err != nil {
|
||||
return nil, errors.Wrapf(err, "relabel failed %q", spliti[0])
|
||||
}
|
||||
}
|
||||
@ -495,10 +495,10 @@ func (c *createConfig) GetVolumeMounts() ([]spec.Mount, error) {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
//GetTmpfsMounts takes user provided input for tmpfs mounts and creates Mount structs
|
||||
//GetTmpfsMounts takes user provided input for Tmpfs mounts and creates Mount structs
|
||||
func (c *createConfig) GetTmpfsMounts() []spec.Mount {
|
||||
var m []spec.Mount
|
||||
for _, i := range c.tmpfs {
|
||||
for _, i := range c.Tmpfs {
|
||||
// Default options if nothing passed
|
||||
options := []string{"rw", "noexec", "nosuid", "nodev", "size=65536k"}
|
||||
spliti := strings.Split(i, ":")
|
||||
@ -522,12 +522,12 @@ func (c *createConfig) GetContainerCreateOptions() ([]libpod.CtrCreateOption, er
|
||||
// Uncomment after talking to mheon about unimplemented funcs
|
||||
// options = append(options, libpod.WithLabels(c.labels))
|
||||
|
||||
if c.interactive {
|
||||
if c.Interactive {
|
||||
options = append(options, libpod.WithStdin())
|
||||
}
|
||||
if c.name != "" {
|
||||
logrus.Debugf("appending name %s", c.name)
|
||||
options = append(options, libpod.WithName(c.name))
|
||||
if c.Name != "" {
|
||||
logrus.Debugf("appending name %s", c.Name)
|
||||
options = append(options, libpod.WithName(c.Name))
|
||||
}
|
||||
|
||||
return options, nil
|
||||
|
@ -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]))
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
}
|
@ -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) {
|
||||
|
70
libpod/container_inspect.go
Normal file
70
libpod/container_inspect.go
Normal 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
|
||||
}
|
@ -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
81
libpod/image_inspect.go
Normal 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
|
||||
}
|
@ -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
103
libpod/inspect_data.go
Normal 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"`
|
||||
}
|
@ -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 ¶ms, 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)
|
||||
|
@ -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 ]
|
||||
}
|
||||
|
Reference in New Issue
Block a user