summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/domain/infra/abi/manifest.go23
-rw-r--r--pkg/domain/infra/abi/play.go9
-rw-r--r--pkg/spec/storage.go2
-rw-r--r--pkg/specgen/generate/config_linux.go5
-rw-r--r--pkg/specgen/generate/security.go10
-rw-r--r--pkg/specgen/generate/validate.go63
-rw-r--r--pkg/util/mountOpts.go2
-rw-r--r--pkg/util/utils.go23
-rw-r--r--pkg/util/utils_test.go20
9 files changed, 129 insertions, 28 deletions
diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go
index 6c518e678..ad7128b42 100644
--- a/pkg/domain/infra/abi/manifest.go
+++ b/pkg/domain/infra/abi/manifest.go
@@ -25,6 +25,7 @@ import (
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/opencontainers/go-digest"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/sirupsen/logrus"
"github.com/pkg/errors"
)
@@ -90,10 +91,6 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte
continue
}
- if !manifest.MIMETypeIsMultiImage(manifestType) {
- appendErr(errors.Errorf("manifest is of type %s (not a list type)", manifestType))
- continue
- }
result = manifestBytes
manType = manifestType
break
@@ -101,7 +98,18 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte
if len(result) == 0 && latestErr != nil {
return nil, latestErr
}
- if manType != manifest.DockerV2ListMediaType {
+
+ switch manType {
+ case manifest.DockerV2Schema2MediaType:
+ logrus.Warnf("Warning! The manifest type %s is not a manifest list but a single image.", manType)
+ schema2Manifest, err := manifest.Schema2FromManifest(result)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error parsing manifest blob %q as a %q", string(result), manType)
+ }
+ if result, err = schema2Manifest.Serialize(); err != nil {
+ return nil, err
+ }
+ default:
listBlob, err := manifest.ListFromBlob(result, manType)
if err != nil {
return nil, errors.Wrapf(err, "error parsing manifest blob %q as a %q", string(result), manType)
@@ -113,10 +121,9 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte
if result, err = list.Serialize(); err != nil {
return nil, err
}
-
}
- err = json.Indent(&b, result, "", " ")
- if err != nil {
+
+ if err = json.Indent(&b, result, "", " "); err != nil {
return nil, errors.Wrapf(err, "error rendering manifest %s for display", name)
}
return b.Bytes(), nil
diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go
index 57de0f3b1..fbba00984 100644
--- a/pkg/domain/infra/abi/play.go
+++ b/pkg/domain/infra/abi/play.go
@@ -36,8 +36,6 @@ const (
kubeDirectoryPermission = 0755
// https://kubernetes.io/docs/concepts/storage/volumes/#hostpath
kubeFilePermission = 0644
- // Kubernetes sets CPUPeriod to 100000us (100ms): https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/
- defaultCPUPeriod = 100000
)
func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) {
@@ -515,10 +513,9 @@ func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container
return nil, errors.Wrap(err, "Failed to set CPU quota")
}
if milliCPU > 0 {
- containerConfig.Resources.CPUPeriod = defaultCPUPeriod
- // CPU quota is a fraction of the period: milliCPU / 1000.0 * period
- // Or, without floating point math:
- containerConfig.Resources.CPUQuota = milliCPU * defaultCPUPeriod / 1000
+ period, quota := util.CoresToPeriodAndQuota(float64(milliCPU) / 1000)
+ containerConfig.Resources.CPUPeriod = period
+ containerConfig.Resources.CPUQuota = quota
}
containerConfig.Resources.Memory, err = quantityToInt64(containerYAML.Resources.Limits.Memory())
diff --git a/pkg/spec/storage.go b/pkg/spec/storage.go
index ebf5ec196..b441daf08 100644
--- a/pkg/spec/storage.go
+++ b/pkg/spec/storage.go
@@ -445,7 +445,7 @@ func getBindMount(args []string) (spec.Mount, error) {
}
setExec = true
newMount.Options = append(newMount.Options, kv[0])
- case "shared", "rshared", "private", "rprivate", "slave", "rslave", "Z", "z":
+ case "shared", "rshared", "private", "rprivate", "slave", "rslave", "unbindable", "runbindable", "Z", "z":
newMount.Options = append(newMount.Options, kv[0])
case "bind-propagation":
if len(kv) == 1 {
diff --git a/pkg/specgen/generate/config_linux.go b/pkg/specgen/generate/config_linux.go
index fcb7641d2..2d40dba8f 100644
--- a/pkg/specgen/generate/config_linux.go
+++ b/pkg/specgen/generate/config_linux.go
@@ -350,3 +350,8 @@ func deviceFromPath(path string) (*spec.LinuxDevice, error) {
Minor: int64(unix.Minor(devNumber)),
}, nil
}
+
+func supportAmbientCapabilities() bool {
+ err := unix.Prctl(unix.PR_CAP_AMBIENT, unix.PR_CAP_AMBIENT_IS_SET, 0, 0, 0)
+ return err == nil
+}
diff --git a/pkg/specgen/generate/security.go b/pkg/specgen/generate/security.go
index d17cd4a9a..dee140282 100644
--- a/pkg/specgen/generate/security.go
+++ b/pkg/specgen/generate/security.go
@@ -135,7 +135,9 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator,
configSpec.Process.Capabilities.Bounding = caplist
configSpec.Process.Capabilities.Inheritable = caplist
- if s.User == "" || s.User == "root" || s.User == "0" {
+ user := strings.Split(s.User, ":")[0]
+
+ if (user == "" && s.UserNS.NSMode != specgen.KeepID) || user == "root" || user == "0" {
configSpec.Process.Capabilities.Effective = caplist
configSpec.Process.Capabilities.Permitted = caplist
} else {
@@ -145,6 +147,12 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator,
}
configSpec.Process.Capabilities.Effective = userCaps
configSpec.Process.Capabilities.Permitted = userCaps
+
+ // Ambient capabilities were added to Linux 4.3. Set ambient
+ // capabilities only when the kernel supports them.
+ if supportAmbientCapabilities() {
+ configSpec.Process.Capabilities.Ambient = userCaps
+ }
}
g.SetProcessNoNewPrivileges(s.NoNewPrivileges)
diff --git a/pkg/specgen/generate/validate.go b/pkg/specgen/generate/validate.go
index f7d80ff75..f0ab4b994 100644
--- a/pkg/specgen/generate/validate.go
+++ b/pkg/specgen/generate/validate.go
@@ -1,22 +1,20 @@
package generate
import (
+ "os"
+ "path/filepath"
+
"github.com/containers/common/pkg/sysinfo"
"github.com/containers/podman/v2/pkg/cgroups"
"github.com/containers/podman/v2/pkg/specgen"
+ "github.com/containers/podman/v2/utils"
"github.com/pkg/errors"
)
-// Verify resource limits are sanely set, removing any limits that are not
-// possible with the current cgroups config.
-func verifyContainerResources(s *specgen.SpecGenerator) ([]string, error) {
+// Verify resource limits are sanely set when running on cgroup v1.
+func verifyContainerResourcesCgroupV1(s *specgen.SpecGenerator) ([]string, error) {
warnings := []string{}
- cgroup2, err := cgroups.IsCgroup2UnifiedMode()
- if err != nil || cgroup2 {
- return warnings, err
- }
-
sysInfo := sysinfo.New(true)
if s.ResourceLimits == nil {
@@ -24,9 +22,7 @@ func verifyContainerResources(s *specgen.SpecGenerator) ([]string, error) {
}
if s.ResourceLimits.Unified != nil {
- if !cgroup2 {
- return nil, errors.New("Cannot use --cgroup-conf without cgroup v2")
- }
+ return nil, errors.New("Cannot use --cgroup-conf without cgroup v2")
}
// Memory checks
@@ -163,3 +159,48 @@ func verifyContainerResources(s *specgen.SpecGenerator) ([]string, error) {
return warnings, nil
}
+
+// Verify resource limits are sanely set when running on cgroup v2.
+func verifyContainerResourcesCgroupV2(s *specgen.SpecGenerator) ([]string, error) {
+ warnings := []string{}
+
+ if s.ResourceLimits == nil {
+ return warnings, nil
+ }
+
+ if s.ResourceLimits.Memory != nil && s.ResourceLimits.Memory.Swap != nil {
+ own, err := utils.GetOwnCgroup()
+ if err != nil {
+ return warnings, err
+ }
+ memoryMax := filepath.Join("/sys/fs/cgroup", own, "memory.max")
+ memorySwapMax := filepath.Join("/sys/fs/cgroup", own, "memory.swap.max")
+ _, errMemoryMax := os.Stat(memoryMax)
+ _, errMemorySwapMax := os.Stat(memorySwapMax)
+ // Differently than cgroup v1, the memory.*max files are not present in the
+ // root directory, so we cannot query directly that, so as best effort use
+ // the current cgroup.
+ // Check whether memory.max exists in the current cgroup and memory.swap.max
+ // does not. In this case we can be sure memory swap is not enabled.
+ // If both files don't exist, the memory controller might not be enabled
+ // for the current cgroup.
+ if errMemoryMax == nil && errMemorySwapMax != nil {
+ warnings = append(warnings, "Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.")
+ s.ResourceLimits.Memory.Swap = nil
+ }
+ }
+ return warnings, nil
+}
+
+// Verify resource limits are sanely set, removing any limits that are not
+// possible with the current cgroups config.
+func verifyContainerResources(s *specgen.SpecGenerator) ([]string, error) {
+ cgroup2, err := cgroups.IsCgroup2UnifiedMode()
+ if err != nil {
+ return []string{}, err
+ }
+ if cgroup2 {
+ return verifyContainerResourcesCgroupV2(s)
+ }
+ return verifyContainerResourcesCgroupV1(s)
+}
diff --git a/pkg/util/mountOpts.go b/pkg/util/mountOpts.go
index eab2657e3..580aaf4f2 100644
--- a/pkg/util/mountOpts.go
+++ b/pkg/util/mountOpts.go
@@ -57,7 +57,7 @@ func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string
return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'rw' and 'ro' can be used")
}
foundWrite = true
- case "private", "rprivate", "slave", "rslave", "shared", "rshared":
+ case "private", "rprivate", "slave", "rslave", "shared", "rshared", "unbindable", "runbindable":
if foundProp {
return nil, errors.Wrapf(ErrDupeMntOption, "only one root propagation mode can be used")
}
diff --git a/pkg/util/utils.go b/pkg/util/utils.go
index a9aad657d..415fd169b 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -653,3 +653,26 @@ func CreateCidFile(cidfile string, id string) error {
cidFile.Close()
return nil
}
+
+// DefaultCPUPeriod is the default CPU period is 100us, which is the same default
+// as Kubernetes.
+const DefaultCPUPeriod uint64 = 100000
+
+// CoresToPeriodAndQuota converts a fraction of cores to the equivalent
+// Completely Fair Scheduler (CFS) parameters period and quota.
+//
+// Cores is a fraction of the CFS period that a container may use. Period and
+// Quota are in microseconds.
+func CoresToPeriodAndQuota(cores float64) (uint64, int64) {
+ return DefaultCPUPeriod, int64(cores * float64(DefaultCPUPeriod))
+}
+
+// PeriodAndQuotaToCores takes the CFS parameters period and quota and returns
+// a fraction that represents the limit to the number of cores that can be
+// utilized over the scheduling period.
+//
+// Cores is a fraction of the CFS period that a container may use. Period and
+// Quota are in microseconds.
+func PeriodAndQuotaToCores(period uint64, quota int64) float64 {
+ return float64(quota) / float64(period)
+}
diff --git a/pkg/util/utils_test.go b/pkg/util/utils_test.go
index a9b37844e..cb737bd76 100644
--- a/pkg/util/utils_test.go
+++ b/pkg/util/utils_test.go
@@ -257,3 +257,23 @@ func TestValidateSysctlBadSysctl(t *testing.T) {
_, err := ValidateSysctls(strSlice)
assert.Error(t, err)
}
+
+func TestCoresToPeriodAndQuota(t *testing.T) {
+ cores := 1.0
+ expectedPeriod := DefaultCPUPeriod
+ expectedQuota := int64(DefaultCPUPeriod)
+
+ actualPeriod, actualQuota := CoresToPeriodAndQuota(cores)
+ assert.Equal(t, actualPeriod, expectedPeriod, "Period does not match")
+ assert.Equal(t, actualQuota, expectedQuota, "Quota does not match")
+}
+
+func TestPeriodAndQuotaToCores(t *testing.T) {
+ var (
+ period uint64 = 100000
+ quota int64 = 50000
+ expectedCores = 0.5
+ )
+
+ assert.Equal(t, PeriodAndQuotaToCores(period, quota), expectedCores)
+}