diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/apparmor/apparmor.go | 7 | ||||
-rw-r--r-- | pkg/apparmor/apparmor_linux.go | 66 | ||||
-rw-r--r-- | pkg/apparmor/apparmor_unsupported.go | 16 | ||||
-rw-r--r-- | pkg/hooks/exec/exec.go | 7 | ||||
-rw-r--r-- | pkg/hooks/exec/exec_test.go | 6 | ||||
-rw-r--r-- | pkg/hooks/exec/runtimeconfigfilter.go | 68 | ||||
-rw-r--r-- | pkg/hooks/exec/runtimeconfigfilter_test.go | 266 | ||||
-rw-r--r-- | pkg/spec/createconfig.go | 4 | ||||
-rw-r--r-- | pkg/spec/spec.go | 1 | ||||
-rw-r--r-- | pkg/sysinfo/sysinfo_linux.go~ | 254 | ||||
-rw-r--r-- | pkg/util/utils.go | 11 | ||||
-rw-r--r-- | pkg/varlinkapi/images.go | 13 | ||||
-rw-r--r-- | pkg/varlinkapi/system.go | 1 |
13 files changed, 451 insertions, 269 deletions
diff --git a/pkg/apparmor/apparmor.go b/pkg/apparmor/apparmor.go index 8b9f99477..45c029c07 100644 --- a/pkg/apparmor/apparmor.go +++ b/pkg/apparmor/apparmor.go @@ -2,11 +2,16 @@ package apparmor import ( "errors" + libpodVersion "github.com/containers/libpod/version" ) var ( + // DefaultLipodProfilePrefix is used for version-independent presence checks. + DefaultLipodProfilePrefix = "libpod-default" + "-" // DefaultLibpodProfile is the name of default libpod AppArmor profile. - DefaultLibpodProfile = "libpod-default" + DefaultLibpodProfile = DefaultLipodProfilePrefix + libpodVersion.Version // ErrApparmorUnsupported indicates that AppArmor support is not supported. ErrApparmorUnsupported = errors.New("AppArmor is not supported") + // ErrApparmorRootless indicates that AppArmor support is not supported in rootless mode. + ErrApparmorRootless = errors.New("AppArmor is not supported in rootless mode") ) diff --git a/pkg/apparmor/apparmor_linux.go b/pkg/apparmor/apparmor_linux.go index b0e3ca0fd..0787b3fa5 100644 --- a/pkg/apparmor/apparmor_linux.go +++ b/pkg/apparmor/apparmor_linux.go @@ -13,7 +13,10 @@ import ( "strings" "text/template" + "github.com/containers/libpod/pkg/rootless" runcaa "github.com/opencontainers/runc/libcontainer/apparmor" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) // profileDirectory is the file store for apparmor profiles and macros. @@ -21,6 +24,9 @@ var profileDirectory = "/etc/apparmor.d" // IsEnabled returns true if AppArmor is enabled on the host. func IsEnabled() bool { + if rootless.IsRootless() { + return false + } return runcaa.IsEnabled() } @@ -71,6 +77,10 @@ func macroExists(m string) bool { // InstallDefault generates a default profile and loads it into the kernel // using 'apparmor_parser'. func InstallDefault(name string) error { + if rootless.IsRootless() { + return ErrApparmorRootless + } + p := profileData{ Name: name, } @@ -97,6 +107,10 @@ func InstallDefault(name string) error { // IsLoaded checks if a profile with the given name has been loaded into the // kernel. func IsLoaded(name string) (bool, error) { + if name != "" && rootless.IsRootless() { + return false, errors.Wrapf(ErrApparmorRootless, "cannot load AppArmor profile %q", name) + } + file, err := os.Open("/sys/kernel/security/apparmor/profiles") if err != nil { if os.IsNotExist(err) { @@ -188,3 +202,55 @@ func parseAAParserVersion(output string) (int, error) { return numericVersion, nil } + +// CheckProfileAndLoadDefault checks if the specified profile is loaded and +// loads the DefaultLibpodProfile if the specified on is prefixed by +// DefaultLipodProfilePrefix. This allows to always load and apply the latest +// default AppArmor profile. Note that AppArmor requires root. If it's a +// default profile, return DefaultLipodProfilePrefix, otherwise the specified +// one. +func CheckProfileAndLoadDefault(name string) (string, error) { + if name == "unconfined" { + return name, nil + } + + if name != "" && rootless.IsRootless() { + return "", errors.Wrapf(ErrApparmorRootless, "cannot load AppArmor profile %q", name) + } + + if name != "" && !runcaa.IsEnabled() { + return "", fmt.Errorf("profile %q specified but AppArmor is disabled on the host", name) + } + + // If the specified name is not empty or is not a default libpod one, + // ignore it and return the name. + if name != "" && !strings.HasPrefix(name, DefaultLipodProfilePrefix) { + isLoaded, err := IsLoaded(name) + if err != nil { + return "", err + } + if !isLoaded { + return "", fmt.Errorf("AppArmor profile %q specified but not loaded") + } + return name, nil + } + + name = DefaultLibpodProfile + // To avoid expensive redundant loads on each invocation, check + // if it's loaded before installing it. + isLoaded, err := IsLoaded(name) + if err != nil { + return "", err + } + if !isLoaded { + err = InstallDefault(name) + if err != nil { + return "", err + } + logrus.Infof("successfully loaded AppAmor profile %q", name) + } else { + logrus.Infof("AppAmor profile %q is already loaded", name) + } + + return name, nil +} diff --git a/pkg/apparmor/apparmor_unsupported.go b/pkg/apparmor/apparmor_unsupported.go index df1336b07..b2b4de5f5 100644 --- a/pkg/apparmor/apparmor_unsupported.go +++ b/pkg/apparmor/apparmor_unsupported.go @@ -2,19 +2,25 @@ package apparmor -// IsEnabled returns true if AppArmor is enabled on the host. +// IsEnabled dummy. func IsEnabled() bool { return false } -// InstallDefault generates a default profile in a temp directory determined by -// os.TempDir(), then loads the profile into the kernel using 'apparmor_parser'. +// InstallDefault dummy. func InstallDefault(name string) error { return ErrApparmorUnsupported } -// IsLoaded checks if a profile with the given name has been loaded into the -// kernel. +// IsLoaded dummy. func IsLoaded(name string) (bool, error) { return false, ErrApparmorUnsupported } + +// CheckProfileAndLoadDefault dummy. +func CheckProfileAndLoadDefault(name string) (string, error) { + if name == "" { + return "", nil + } + return "", ErrApparmorUnsupported +} diff --git a/pkg/hooks/exec/exec.go b/pkg/hooks/exec/exec.go index 94469b1d2..0dd091561 100644 --- a/pkg/hooks/exec/exec.go +++ b/pkg/hooks/exec/exec.go @@ -10,6 +10,7 @@ import ( "time" rspec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" ) // DefaultPostKillTimeout is the recommended default post-kill timeout. @@ -42,7 +43,11 @@ func Run(ctx context.Context, hook *rspec.Hook, state []byte, stdout io.Writer, } exit := make(chan error, 1) go func() { - exit <- cmd.Wait() + err := cmd.Wait() + if err != nil { + err = errors.Wrapf(err, "executing %v", cmd.Args) + } + exit <- err }() select { diff --git a/pkg/hooks/exec/exec_test.go b/pkg/hooks/exec/exec_test.go index 62e45ff3a..7aac315cb 100644 --- a/pkg/hooks/exec/exec_test.go +++ b/pkg/hooks/exec/exec_test.go @@ -163,14 +163,14 @@ func TestRunCancel(t *testing.T) { name: "context timeout", contextTimeout: time.Duration(1) * time.Second, expectedStdout: "waiting\n", - expectedHookError: "^signal: killed$", + expectedHookError: "^executing \\[sh -c echo waiting; sleep 2; echo done]: signal: killed$", expectedRunError: context.DeadlineExceeded, }, { name: "hook timeout", hookTimeout: &one, expectedStdout: "waiting\n", - expectedHookError: "^signal: killed$", + expectedHookError: "^executing \\[sh -c echo waiting; sleep 2; echo done]: signal: killed$", expectedRunError: context.DeadlineExceeded, }, } { @@ -207,7 +207,7 @@ func TestRunKillTimeout(t *testing.T) { } hookErr, err := Run(ctx, hook, []byte("{}"), nil, nil, time.Duration(0)) assert.Equal(t, context.DeadlineExceeded, err) - assert.Regexp(t, "^(failed to reap process within 0s of the kill signal|signal: killed)$", hookErr) + assert.Regexp(t, "^(failed to reap process within 0s of the kill signal|executing \\[sh -c sleep 1]: signal: killed)$", hookErr) } func init() { diff --git a/pkg/hooks/exec/runtimeconfigfilter.go b/pkg/hooks/exec/runtimeconfigfilter.go new file mode 100644 index 000000000..c6971f680 --- /dev/null +++ b/pkg/hooks/exec/runtimeconfigfilter.go @@ -0,0 +1,68 @@ +package exec + +import ( + "bytes" + "context" + "encoding/json" + "reflect" + "time" + + "github.com/davecgh/go-spew/spew" + spec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" + "github.com/pmezard/go-difflib/difflib" + "github.com/sirupsen/logrus" +) + +var spewConfig = spew.ConfigState{ + Indent: " ", + DisablePointerAddresses: true, + DisableCapacities: true, + SortKeys: true, +} + +// RuntimeConfigFilter calls a series of hooks. But instead of +// passing container state on their standard input, +// RuntimeConfigFilter passes the proposed runtime configuration (and +// reads back a possibly-altered form from their standard output). +func RuntimeConfigFilter(ctx context.Context, hooks []spec.Hook, config *spec.Spec, postKillTimeout time.Duration) (hookErr, err error) { + data, err := json.Marshal(config) + for i, hook := range hooks { + var stdout bytes.Buffer + hookErr, err = Run(ctx, &hook, data, &stdout, nil, postKillTimeout) + if err != nil { + return hookErr, err + } + + data = stdout.Bytes() + var newConfig spec.Spec + err = json.Unmarshal(data, &newConfig) + if err != nil { + logrus.Debugf("invalid JSON from config-filter hook %d:\n%s", i, string(data)) + return nil, errors.Wrapf(err, "unmarshal output from config-filter hook %d", i) + } + + if !reflect.DeepEqual(config, &newConfig) { + old := spewConfig.Sdump(config) + new := spewConfig.Sdump(&newConfig) + diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ + A: difflib.SplitLines(old), + B: difflib.SplitLines(new), + FromFile: "Old", + FromDate: "", + ToFile: "New", + ToDate: "", + Context: 1, + }) + if err == nil { + logrus.Debugf("precreate hook %d made configuration changes:\n%s", i, diff) + } else { + logrus.Warnf("precreate hook %d made configuration changes, but we could not compute a diff: %v", i, err) + } + } + + *config = newConfig + } + + return nil, nil +} diff --git a/pkg/hooks/exec/runtimeconfigfilter_test.go b/pkg/hooks/exec/runtimeconfigfilter_test.go new file mode 100644 index 000000000..52d590d14 --- /dev/null +++ b/pkg/hooks/exec/runtimeconfigfilter_test.go @@ -0,0 +1,266 @@ +package exec + +import ( + "context" + "encoding/json" + "os" + "testing" + "time" + + spec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" +) + +func pointerInt(value int) *int { + return &value +} + +func pointerUInt32(value uint32) *uint32 { + return &value +} + +func pointerFileMode(value os.FileMode) *os.FileMode { + return &value +} + +func TestRuntimeConfigFilter(t *testing.T) { + unexpectedEndOfJSONInput := json.Unmarshal([]byte("{\n"), nil) + + for _, test := range []struct { + name string + contextTimeout time.Duration + hooks []spec.Hook + input *spec.Spec + expected *spec.Spec + expectedHookError string + expectedRunError error + }{ + { + name: "no-op", + hooks: []spec.Hook{ + { + Path: path, + Args: []string{"sh", "-c", "cat"}, + }, + }, + input: &spec.Spec{ + Version: "1.0.0", + Root: &spec.Root{ + Path: "rootfs", + }, + }, + expected: &spec.Spec{ + Version: "1.0.0", + Root: &spec.Root{ + Path: "rootfs", + }, + }, + }, + { + name: "device injection", + hooks: []spec.Hook{ + { + Path: path, + Args: []string{"sh", "-c", `sed 's|\("gid":0}\)|\1,{"path": "/dev/sda","type":"b","major":8,"minor":0,"fileMode":384,"uid":0,"gid":0}|'`}, + }, + }, + input: &spec.Spec{ + Version: "1.0.0", + Root: &spec.Root{ + Path: "rootfs", + }, + Linux: &spec.Linux{ + Devices: []spec.LinuxDevice{ + { + Path: "/dev/fuse", + Type: "c", + Major: 10, + Minor: 229, + FileMode: pointerFileMode(0600), + UID: pointerUInt32(0), + GID: pointerUInt32(0), + }, + }, + }, + }, + expected: &spec.Spec{ + Version: "1.0.0", + Root: &spec.Root{ + Path: "rootfs", + }, + Linux: &spec.Linux{ + Devices: []spec.LinuxDevice{ + { + Path: "/dev/fuse", + Type: "c", + Major: 10, + Minor: 229, + FileMode: pointerFileMode(0600), + UID: pointerUInt32(0), + GID: pointerUInt32(0), + }, + { + Path: "/dev/sda", + Type: "b", + Major: 8, + Minor: 0, + FileMode: pointerFileMode(0600), + UID: pointerUInt32(0), + GID: pointerUInt32(0), + }, + }, + }, + }, + }, + { + name: "chaining", + hooks: []spec.Hook{ + { + Path: path, + Args: []string{"sh", "-c", `sed 's|\("gid":0}\)|\1,{"path": "/dev/sda","type":"b","major":8,"minor":0,"fileMode":384,"uid":0,"gid":0}|'`}, + }, + { + Path: path, + Args: []string{"sh", "-c", `sed 's|/dev/sda|/dev/sdb|'`}, + }, + }, + input: &spec.Spec{ + Version: "1.0.0", + Root: &spec.Root{ + Path: "rootfs", + }, + Linux: &spec.Linux{ + Devices: []spec.LinuxDevice{ + { + Path: "/dev/fuse", + Type: "c", + Major: 10, + Minor: 229, + FileMode: pointerFileMode(0600), + UID: pointerUInt32(0), + GID: pointerUInt32(0), + }, + }, + }, + }, + expected: &spec.Spec{ + Version: "1.0.0", + Root: &spec.Root{ + Path: "rootfs", + }, + Linux: &spec.Linux{ + Devices: []spec.LinuxDevice{ + { + Path: "/dev/fuse", + Type: "c", + Major: 10, + Minor: 229, + FileMode: pointerFileMode(0600), + UID: pointerUInt32(0), + GID: pointerUInt32(0), + }, + { + Path: "/dev/sdb", + Type: "b", + Major: 8, + Minor: 0, + FileMode: pointerFileMode(0600), + UID: pointerUInt32(0), + GID: pointerUInt32(0), + }, + }, + }, + }, + }, + { + name: "context timeout", + contextTimeout: time.Duration(1) * time.Second, + hooks: []spec.Hook{ + { + Path: path, + Args: []string{"sh", "-c", "sleep 2"}, + }, + }, + input: &spec.Spec{ + Version: "1.0.0", + Root: &spec.Root{ + Path: "rootfs", + }, + }, + expected: &spec.Spec{ + Version: "1.0.0", + Root: &spec.Root{ + Path: "rootfs", + }, + }, + expectedHookError: "^executing \\[sh -c sleep 2]: signal: killed$", + expectedRunError: context.DeadlineExceeded, + }, + { + name: "hook timeout", + hooks: []spec.Hook{ + { + Path: path, + Args: []string{"sh", "-c", "sleep 2"}, + Timeout: pointerInt(1), + }, + }, + input: &spec.Spec{ + Version: "1.0.0", + Root: &spec.Root{ + Path: "rootfs", + }, + }, + expected: &spec.Spec{ + Version: "1.0.0", + Root: &spec.Root{ + Path: "rootfs", + }, + }, + expectedHookError: "^executing \\[sh -c sleep 2]: signal: killed$", + expectedRunError: context.DeadlineExceeded, + }, + { + name: "invalid JSON", + hooks: []spec.Hook{ + { + Path: path, + Args: []string{"sh", "-c", "echo '{'"}, + }, + }, + input: &spec.Spec{ + Version: "1.0.0", + Root: &spec.Root{ + Path: "rootfs", + }, + }, + expected: &spec.Spec{ + Version: "1.0.0", + Root: &spec.Root{ + Path: "rootfs", + }, + }, + expectedRunError: unexpectedEndOfJSONInput, + }, + } { + t.Run(test.name, func(t *testing.T) { + ctx := context.Background() + if test.contextTimeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, test.contextTimeout) + defer cancel() + } + hookErr, err := RuntimeConfigFilter(ctx, test.hooks, test.input, DefaultPostKillTimeout) + assert.Equal(t, test.expectedRunError, errors.Cause(err)) + if test.expectedHookError == "" { + if hookErr != nil { + t.Fatal(hookErr) + } + } else { + assert.Regexp(t, test.expectedHookError, hookErr.Error()) + } + assert.Equal(t, test.expected, test.input) + }) + } +} diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go index ffc98e307..87fce7e2e 100644 --- a/pkg/spec/createconfig.go +++ b/pkg/spec/createconfig.go @@ -518,7 +518,9 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime) ([]lib if c.CgroupParent != "" { options = append(options, libpod.WithCgroupParent(c.CgroupParent)) } - if c.Detach { + // For a rootless container always cleanup the storage/network as they + // run in a different namespace thus not reusable when we restart. + if c.Detach || rootless.IsRootless() { options = append(options, libpod.WithExitCommand(c.createExitCommand())) } diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go index ffa999730..9ef0223f2 100644 --- a/pkg/spec/spec.go +++ b/pkg/spec/spec.go @@ -252,6 +252,7 @@ func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint } // SECURITY OPTS g.SetProcessNoNewPrivileges(config.NoNewPrivs) + g.SetProcessApparmorProfile(config.ApparmorProfile) blockAccessToKernelFilesystems(config, &g) diff --git a/pkg/sysinfo/sysinfo_linux.go~ b/pkg/sysinfo/sysinfo_linux.go~ deleted file mode 100644 index 471f786a7..000000000 --- a/pkg/sysinfo/sysinfo_linux.go~ +++ /dev/null @@ -1,254 +0,0 @@ -package sysinfo - -import ( - "fmt" - "io/ioutil" - "os" - "path" - "strings" - - "github.com/opencontainers/runc/libcontainer/cgroups" - "github.com/sirupsen/logrus" - "golang.org/x/sys/unix" -) - -func findCgroupMountpoints() (map[string]string, error) { - cgMounts, err := cgroups.GetCgroupMounts(false) - if err != nil { - return nil, fmt.Errorf("Failed to parse cgroup information: %v", err) - } - mps := make(map[string]string) - for _, m := range cgMounts { - for _, ss := range m.Subsystems { - mps[ss] = m.Mountpoint - } - } - return mps, nil -} - -// New returns a new SysInfo, using the filesystem to detect which features -// the kernel supports. If `quiet` is `false` warnings are printed in logs -// whenever an error occurs or misconfigurations are present. -func New(quiet bool) *SysInfo { - sysInfo := &SysInfo{} - cgMounts, err := findCgroupMountpoints() - if err != nil { - logrus.Warnf("Failed to parse cgroup information: %v", err) - } else { - sysInfo.cgroupMemInfo = checkCgroupMem(cgMounts, quiet) - sysInfo.cgroupCPUInfo = checkCgroupCPU(cgMounts, quiet) - sysInfo.cgroupBlkioInfo = checkCgroupBlkioInfo(cgMounts, quiet) - sysInfo.cgroupCpusetInfo = checkCgroupCpusetInfo(cgMounts, quiet) - sysInfo.cgroupPids = checkCgroupPids(quiet) - } - - _, ok := cgMounts["devices"] - sysInfo.CgroupDevicesEnabled = ok - - sysInfo.IPv4ForwardingDisabled = !readProcBool("/proc/sys/net/ipv4/ip_forward") - sysInfo.BridgeNFCallIPTablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-iptables") - sysInfo.BridgeNFCallIP6TablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-ip6tables") - - // Check if AppArmor is supported. - if _, err := os.Stat("/sys/kernel/security/apparmor"); !os.IsNotExist(err) { - sysInfo.AppArmor = true - } - - // Check if Seccomp is supported, via CONFIG_SECCOMP. - if err := unix.Prctl(unix.PR_GET_SECCOMP, 0, 0, 0, 0); err != unix.EINVAL { - // Make sure the kernel has CONFIG_SECCOMP_FILTER. - if err := unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0); err != unix.EINVAL { - sysInfo.Seccomp = true - } - } - - return sysInfo -} - -// checkCgroupMem reads the memory information from the memory cgroup mount point. -func checkCgroupMem(cgMounts map[string]string, quiet bool) cgroupMemInfo { - mountPoint, ok := cgMounts["memory"] - if !ok { - if !quiet { - logrus.Warn("Your kernel does not support cgroup memory limit") - } - return cgroupMemInfo{} - } - - swapLimit := cgroupEnabled(mountPoint, "memory.memsw.limit_in_bytes") - if !quiet && !swapLimit { - logrus.Warn("Your kernel does not support swap memory limit") - } - memoryReservation := cgroupEnabled(mountPoint, "memory.soft_limit_in_bytes") - if !quiet && !memoryReservation { - logrus.Warn("Your kernel does not support memory reservation") - } - oomKillDisable := cgroupEnabled(mountPoint, "memory.oom_control") - if !quiet && !oomKillDisable { - logrus.Warn("Your kernel does not support oom control") - } - memorySwappiness := cgroupEnabled(mountPoint, "memory.swappiness") - if !quiet && !memorySwappiness { - logrus.Warn("Your kernel does not support memory swappiness") - } - kernelMemory := cgroupEnabled(mountPoint, "memory.kmem.limit_in_bytes") - if !quiet && !kernelMemory { - logrus.Warn("Your kernel does not support kernel memory limit") - } - - return cgroupMemInfo{ - MemoryLimit: true, - SwapLimit: swapLimit, - MemoryReservation: memoryReservation, - OomKillDisable: oomKillDisable, - MemorySwappiness: memorySwappiness, - KernelMemory: kernelMemory, - } -} - -// checkCgroupCPU reads the cpu information from the cpu cgroup mount point. -func checkCgroupCPU(cgMounts map[string]string, quiet bool) cgroupCPUInfo { - mountPoint, ok := cgMounts["cpu"] - if !ok { - if !quiet { - logrus.Warn("Unable to find cpu cgroup in mounts") - } - return cgroupCPUInfo{} - } - - cpuShares := cgroupEnabled(mountPoint, "cpu.shares") - if !quiet && !cpuShares { - logrus.Warn("Your kernel does not support cgroup cpu shares") - } - - cpuCfsPeriod := cgroupEnabled(mountPoint, "cpu.cfs_period_us") - if !quiet && !cpuCfsPeriod { - logrus.Warn("Your kernel does not support cgroup cfs period") - } - - cpuCfsQuota := cgroupEnabled(mountPoint, "cpu.cfs_quota_us") - if !quiet && !cpuCfsQuota { - logrus.Warn("Your kernel does not support cgroup cfs quotas") - } - - cpuRealtimePeriod := cgroupEnabled(mountPoint, "cpu.rt_period_us") - if !quiet && !cpuRealtimePeriod { - logrus.Warn("Your kernel does not support cgroup rt period") - } - - cpuRealtimeRuntime := cgroupEnabled(mountPoint, "cpu.rt_runtime_us") - if !quiet && !cpuRealtimeRuntime { - logrus.Warn("Your kernel does not support cgroup rt runtime") - } - - return cgroupCPUInfo{ - CPUShares: cpuShares, - CPUCfsPeriod: cpuCfsPeriod, - CPUCfsQuota: cpuCfsQuota, - CPURealtimePeriod: cpuRealtimePeriod, - CPURealtimeRuntime: cpuRealtimeRuntime, - } -} - -// checkCgroupBlkioInfo reads the blkio information from the blkio cgroup mount point. -func checkCgroupBlkioInfo(cgMounts map[string]string, quiet bool) cgroupBlkioInfo { - mountPoint, ok := cgMounts["blkio"] - if !ok { - if !quiet { - logrus.Warn("Unable to find blkio cgroup in mounts") - } - return cgroupBlkioInfo{} - } - - weight := cgroupEnabled(mountPoint, "blkio.weight") - if !quiet && !weight { - logrus.Warn("Your kernel does not support cgroup blkio weight") - } - - weightDevice := cgroupEnabled(mountPoint, "blkio.weight_device") - if !quiet && !weightDevice { - logrus.Warn("Your kernel does not support cgroup blkio weight_device") - } - - readBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_bps_device") - if !quiet && !readBpsDevice { - logrus.Warn("Your kernel does not support cgroup blkio throttle.read_bps_device") - } - - writeBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_bps_device") - if !quiet && !writeBpsDevice { - logrus.Warn("Your kernel does not support cgroup blkio throttle.write_bps_device") - } - readIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_iops_device") - if !quiet && !readIOpsDevice { - logrus.Warn("Your kernel does not support cgroup blkio throttle.read_iops_device") - } - - writeIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_iops_device") - if !quiet && !writeIOpsDevice { - logrus.Warn("Your kernel does not support cgroup blkio throttle.write_iops_device") - } - return cgroupBlkioInfo{ - BlkioWeight: weight, - BlkioWeightDevice: weightDevice, - BlkioReadBpsDevice: readBpsDevice, - BlkioWriteBpsDevice: writeBpsDevice, - BlkioReadIOpsDevice: readIOpsDevice, - BlkioWriteIOpsDevice: writeIOpsDevice, - } -} - -// checkCgroupCpusetInfo reads the cpuset information from the cpuset cgroup mount point. -func checkCgroupCpusetInfo(cgMounts map[string]string, quiet bool) cgroupCpusetInfo { - mountPoint, ok := cgMounts["cpuset"] - if !ok { - if !quiet { - logrus.Warn("Unable to find cpuset cgroup in mounts") - } - return cgroupCpusetInfo{} - } - - cpus, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.cpus")) - if err != nil { - return cgroupCpusetInfo{} - } - - mems, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.mems")) - if err != nil { - return cgroupCpusetInfo{} - } - - return cgroupCpusetInfo{ - Cpuset: true, - Cpus: strings.TrimSpace(string(cpus)), - Mems: strings.TrimSpace(string(mems)), - } -} - -// checkCgroupPids reads the pids information from the pids cgroup mount point. -func checkCgroupPids(quiet bool) cgroupPids { - _, err := cgroups.FindCgroupMountpoint("pids") - if err != nil { - if !quiet { - logrus.Warn(err) - } - return cgroupPids{} - } - - return cgroupPids{ - PidsLimit: true, - } -} - -func cgroupEnabled(mountPoint, name string) bool { - _, err := os.Stat(path.Join(mountPoint, name)) - return err == nil -} - -func readProcBool(path string) bool { - val, err := ioutil.ReadFile(path) - if err != nil { - return false - } - return strings.TrimSpace(string(val)) == "1" -} diff --git a/pkg/util/utils.go b/pkg/util/utils.go index a6f52cb3e..2b752afe1 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -314,8 +314,9 @@ func GetDefaultStoreOptions() (storage.StoreOptions, string, error) { return storageOpts, volumePath, err } - storageConf := filepath.Join(os.Getenv("HOME"), ".config/containers/storage.conf") + storageConf := StorageConfigFile() if _, err := os.Stat(storageConf); err == nil { + storageOpts = storage.StoreOptions{} storage.ReloadConfigurationFile(storageConf, &storageOpts) } else if os.IsNotExist(err) { os.MkdirAll(filepath.Dir(storageConf), 0755) @@ -334,3 +335,11 @@ func GetDefaultStoreOptions() (storage.StoreOptions, string, error) { } return storageOpts, volumePath, nil } + +// StorageConfigFile returns the path to the storage config file used +func StorageConfigFile() string { + if rootless.IsRootless() { + return filepath.Join(os.Getenv("HOME"), ".config/containers/storage.conf") + } + return storage.DefaultConfigFile +} diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go index 5e4cb4ccb..8f8934025 100644 --- a/pkg/varlinkapi/images.go +++ b/pkg/varlinkapi/images.go @@ -41,13 +41,18 @@ func (i *LibpodAPI) ListImages(call iopodman.VarlinkCall) error { for _, image := range images { labels, _ := image.Labels(getContext()) containers, _ := image.Containers() + repoDigests, err := image.RepoDigests() + if err != nil { + return err + } + size, _ := image.Size(getContext()) i := iopodman.ImageInList{ Id: image.ID(), ParentId: image.Parent, RepoTags: image.Names(), - RepoDigests: image.RepoDigests(), + RepoDigests: repoDigests, Created: image.Created().String(), Size: int64(*size), VirtualSize: image.VirtualSize, @@ -73,6 +78,10 @@ func (i *LibpodAPI) GetImage(call iopodman.VarlinkCall, name string) error { if err != nil { return err } + repoDigests, err := newImage.RepoDigests() + if err != nil { + return err + } size, err := newImage.Size(getContext()) if err != nil { return err @@ -82,7 +91,7 @@ func (i *LibpodAPI) GetImage(call iopodman.VarlinkCall, name string) error { Id: newImage.ID(), ParentId: newImage.Parent, RepoTags: newImage.Names(), - RepoDigests: newImage.RepoDigests(), + RepoDigests: repoDigests, Created: newImage.Created().String(), Size: int64(*size), VirtualSize: newImage.VirtualSize, diff --git a/pkg/varlinkapi/system.go b/pkg/varlinkapi/system.go index a29d22e7d..e50643dd0 100644 --- a/pkg/varlinkapi/system.go +++ b/pkg/varlinkapi/system.go @@ -102,6 +102,5 @@ func (i *LibpodAPI) GetInfo(call iopodman.VarlinkCall) error { podmanInfo.Podman = pmaninfo podmanInfo.Registries = registries podmanInfo.Insecure_registries = insecureRegistries - return call.ReplyGetInfo(podmanInfo) } |