Skip to content

Commit 7269e0d

Browse files
Merge pull request cri-o#8397 from littlejawa/cleanup/refactor_createSandboxContainer
Cleanup/refactor create sandbox container
2 parents bc34150 + 66c0109 commit 7269e0d

File tree

4 files changed

+442
-170
lines changed

4 files changed

+442
-170
lines changed

internal/config/node/cgroups_unsupported.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33

44
package node
55

6+
func CgroupIsV2() bool {
7+
return false
8+
}
9+
610
// CgroupHasMemorySwap returns whether the memory swap controller is present
711
func CgroupHasMemorySwap() bool {
812
return false

internal/factory/container/container.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ import (
2424
kubeletTypes "k8s.io/kubelet/pkg/types"
2525

2626
"github.com/cri-o/cri-o/internal/config/capabilities"
27+
"github.com/cri-o/cri-o/internal/config/cgmgr"
2728
"github.com/cri-o/cri-o/internal/config/device"
29+
"github.com/cri-o/cri-o/internal/config/node"
2830
"github.com/cri-o/cri-o/internal/config/nsmgr"
2931
"github.com/cri-o/cri-o/internal/lib/constants"
3032
"github.com/cri-o/cri-o/internal/log"
@@ -123,6 +125,12 @@ type Container interface {
123125
// SpecSetupCapabilities sets up the container's capabilities
124126
SpecSetupCapabilities(*types.Capability, capabilities.Capabilities, bool) error
125127

128+
// SpecSetPrivileges sets the container's privileges
129+
SpecSetPrivileges(ctx context.Context, securityContext *types.LinuxContainerSecurityContext, cfg *config.Config) error
130+
131+
// SpecSetLinuxContainerResources sets the container resources
132+
SpecSetLinuxContainerResources(resources *types.LinuxContainerResources, containerMinMemory int64) error
133+
126134
// PidNamespace returns the pid namespace created by SpecAddNamespaces.
127135
PidNamespace() nsmgr.Namespace
128136

@@ -758,3 +766,98 @@ func getOCICapabilitiesList() []string {
758766
}
759767
return caps
760768
}
769+
770+
func (c *container) SpecSetPrivileges(ctx context.Context, securityContext *types.LinuxContainerSecurityContext, cfg *config.Config) error {
771+
specgen := c.Spec()
772+
if c.Privileged() {
773+
specgen.SetupPrivileged(true)
774+
} else {
775+
caps := securityContext.Capabilities
776+
if err := c.SpecSetupCapabilities(caps, cfg.DefaultCapabilities, cfg.AddInheritableCapabilities); err != nil {
777+
return err
778+
}
779+
}
780+
781+
if securityContext.NoNewPrivs {
782+
const sysAdminCap = "CAP_SYS_ADMIN"
783+
for _, cap := range specgen.Config.Process.Capabilities.Bounding {
784+
if cap == sysAdminCap {
785+
log.Warnf(ctx, "Setting `noNewPrivileges` flag has no effect because container has %s capability", sysAdminCap)
786+
}
787+
}
788+
789+
if c.Privileged() {
790+
log.Warnf(ctx, "Setting `noNewPrivileges` flag has no effect because container is privileged")
791+
}
792+
}
793+
794+
specgen.SetProcessNoNewPrivileges(securityContext.NoNewPrivs)
795+
796+
if !c.Privileged() {
797+
if securityContext.MaskedPaths != nil {
798+
for _, path := range securityContext.MaskedPaths {
799+
specgen.AddLinuxMaskedPaths(path)
800+
}
801+
}
802+
803+
if securityContext.ReadonlyPaths != nil {
804+
for _, path := range securityContext.ReadonlyPaths {
805+
specgen.AddLinuxReadonlyPaths(path)
806+
}
807+
}
808+
}
809+
return nil
810+
}
811+
812+
func (c *container) SpecSetLinuxContainerResources(resources *types.LinuxContainerResources, containerMinMemory int64) error {
813+
specgen := c.Spec()
814+
specgen.SetLinuxResourcesCPUPeriod(uint64(resources.CpuPeriod))
815+
specgen.SetLinuxResourcesCPUQuota(resources.CpuQuota)
816+
specgen.SetLinuxResourcesCPUShares(uint64(resources.CpuShares))
817+
818+
memoryLimit := resources.MemoryLimitInBytes
819+
if memoryLimit != 0 {
820+
if err := cgmgr.VerifyMemoryIsEnough(memoryLimit, containerMinMemory); err != nil {
821+
return err
822+
}
823+
specgen.SetLinuxResourcesMemoryLimit(memoryLimit)
824+
if resources.MemorySwapLimitInBytes != 0 {
825+
if resources.MemorySwapLimitInBytes > 0 && resources.MemorySwapLimitInBytes < resources.MemoryLimitInBytes {
826+
return fmt.Errorf(
827+
"container %s create failed because memory swap limit (%d) cannot be lower than memory limit (%d)",
828+
c.ID(),
829+
resources.MemorySwapLimitInBytes,
830+
resources.MemoryLimitInBytes,
831+
)
832+
}
833+
memoryLimit = resources.MemorySwapLimitInBytes
834+
}
835+
// If node doesn't have memory swap, then skip setting
836+
// otherwise the container creation fails.
837+
if node.CgroupHasMemorySwap() {
838+
specgen.SetLinuxResourcesMemorySwap(memoryLimit)
839+
}
840+
}
841+
842+
specgen.SetProcessOOMScoreAdj(int(resources.OomScoreAdj))
843+
specgen.SetLinuxResourcesCPUCpus(resources.CpusetCpus)
844+
specgen.SetLinuxResourcesCPUMems(resources.CpusetMems)
845+
846+
// If the kernel has no support for hugetlb, silently ignore the limits
847+
if node.CgroupHasHugetlb() {
848+
hugepageLimits := resources.HugepageLimits
849+
for _, limit := range hugepageLimits {
850+
specgen.AddLinuxResourcesHugepageLimit(limit.PageSize, limit.Limit)
851+
}
852+
}
853+
854+
if node.CgroupIsV2() && len(resources.Unified) != 0 {
855+
if specgen.Config.Linux.Resources.Unified == nil {
856+
specgen.Config.Linux.Resources.Unified = make(map[string]string, len(resources.Unified))
857+
}
858+
for key, value := range resources.Unified {
859+
specgen.Config.Linux.Resources.Unified[key] = value
860+
}
861+
}
862+
return nil
863+
}

internal/factory/container/container_test.go

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
v1 "github.com/opencontainers/image-spec/specs-go/v1"
1313
rspec "github.com/opencontainers/runtime-spec/specs-go"
1414
validate "github.com/opencontainers/runtime-tools/validate/capabilities"
15+
"github.com/syndtr/gocapability/capability"
1516
types "k8s.io/cri-api/pkg/apis/runtime/v1"
1617
kubeletTypes "k8s.io/kubelet/pkg/types"
1718

@@ -23,6 +24,7 @@ import (
2324
"github.com/cri-o/cri-o/internal/storage"
2425
"github.com/cri-o/cri-o/internal/storage/references"
2526
"github.com/cri-o/cri-o/pkg/annotations"
27+
pkgConfig "github.com/cri-o/cri-o/pkg/config"
2628
)
2729

2830
var _ = t.Describe("Container", func() {
@@ -633,4 +635,213 @@ var _ = t.Describe("Container", func() {
633635
Expect(sut.Spec().Config.Process.Capabilities.Inheritable).To(HaveLen(1))
634636
})
635637
})
638+
t.Describe("SpecSetPrivileges", func() {
639+
It("Non privileged container should get selected capabilities", func() {
640+
// Given
641+
sc := &types.LinuxContainerSecurityContext{
642+
Capabilities: &types.Capability{
643+
AddCapabilities: []string{"CHOWN"},
644+
DropCapabilities: nil,
645+
},
646+
}
647+
cfg := &pkgConfig.Config{}
648+
649+
// When
650+
Expect(sut.SpecSetPrivileges(context.Background(), sc, cfg)).To(Succeed())
651+
652+
// Then
653+
Expect(sut.Spec().Config.Process.Capabilities.Bounding).To(HaveLen(len(cfg.DefaultCapabilities) + 1))
654+
Expect(sut.Spec().Config.Process.Capabilities.Effective).To(HaveLen(len(cfg.DefaultCapabilities) + 1))
655+
Expect(sut.Spec().Config.Process.Capabilities.Permitted).To(HaveLen(len(cfg.DefaultCapabilities) + 1))
656+
Expect(sut.Spec().Config.Process.Capabilities.Inheritable).To(BeEmpty())
657+
Expect(sut.Spec().Config.Process.Capabilities.Ambient).To(BeEmpty())
658+
})
659+
It("Privileged container gets all capabilities", func() {
660+
// Given
661+
sc := &types.LinuxContainerSecurityContext{}
662+
cfg := &pkgConfig.Config{}
663+
config := &types.ContainerConfig{
664+
Metadata: &types.ContainerMetadata{Name: "name"},
665+
Linux: &types.LinuxContainerConfig{
666+
SecurityContext: &types.LinuxContainerSecurityContext{
667+
Privileged: true,
668+
},
669+
},
670+
}
671+
sboxConfig := &types.PodSandboxConfig{
672+
Linux: &types.LinuxPodSandboxConfig{
673+
SecurityContext: &types.LinuxSandboxSecurityContext{
674+
Privileged: true,
675+
},
676+
},
677+
}
678+
expectedSize := len(capability.List())
679+
680+
// When
681+
Expect(sut.SetConfig(config, sboxConfig)).To(Succeed())
682+
Expect(sut.SetPrivileged()).To(Succeed())
683+
Expect(sut.SpecSetPrivileges(context.Background(), sc, cfg)).To(Succeed())
684+
685+
// Then
686+
Expect(sut.Spec().Config.Process.Capabilities.Bounding).To(HaveLen(expectedSize))
687+
Expect(sut.Spec().Config.Process.Capabilities.Effective).To(HaveLen(expectedSize))
688+
Expect(sut.Spec().Config.Process.Capabilities.Permitted).To(HaveLen(expectedSize))
689+
Expect(sut.Spec().Config.Process.Capabilities.Inheritable).To(HaveLen(expectedSize))
690+
Expect(sut.Spec().Config.Process.Capabilities.Ambient).To(HaveLen(expectedSize))
691+
})
692+
It("Should set NoNewPrivs flag if set", func() {
693+
// Given
694+
sc := &types.LinuxContainerSecurityContext{
695+
NoNewPrivs: true,
696+
}
697+
cfg := &pkgConfig.Config{}
698+
699+
// When
700+
Expect(sut.SpecSetPrivileges(context.Background(), sc, cfg)).To(Succeed())
701+
702+
// Then
703+
Expect(sut.Spec().Config.Process.NoNewPrivileges).To(BeTrue())
704+
})
705+
It("Should add masked paths if set", func() {
706+
// Given
707+
sc := &types.LinuxContainerSecurityContext{
708+
MaskedPaths: []string{"path1", "path2"},
709+
}
710+
cfg := &pkgConfig.Config{}
711+
712+
// When
713+
Expect(sut.SpecSetPrivileges(context.Background(), sc, cfg)).To(Succeed())
714+
715+
// Then
716+
Expect(sut.Spec().Config.Linux.MaskedPaths).To(HaveLen(2))
717+
})
718+
It("Should add readonly paths if set", func() {
719+
// Given
720+
sc := &types.LinuxContainerSecurityContext{
721+
ReadonlyPaths: []string{"path1", "path2"},
722+
}
723+
cfg := &pkgConfig.Config{}
724+
725+
// When
726+
Expect(sut.SpecSetPrivileges(context.Background(), sc, cfg)).To(Succeed())
727+
728+
// Then
729+
Expect(sut.Spec().Config.Linux.ReadonlyPaths).To(HaveLen(2))
730+
})
731+
})
732+
t.Describe("SpecSetLinuxContainerResources", func() {
733+
It("Sets all fields to their expected values", func() {
734+
// Given
735+
resources := &types.LinuxContainerResources{
736+
CpuPeriod: 1,
737+
CpuQuota: 2,
738+
CpuShares: 3,
739+
OomScoreAdj: 4,
740+
CpusetCpus: "5",
741+
CpusetMems: "6",
742+
}
743+
744+
// When
745+
Expect(sut.SpecSetLinuxContainerResources(resources, 0)).To(Succeed())
746+
747+
// Then
748+
Expect(*sut.Spec().Config.Linux.Resources.CPU.Period).To(Equal(uint64(resources.CpuPeriod)))
749+
Expect(*sut.Spec().Config.Linux.Resources.CPU.Quota).To(Equal(resources.CpuQuota))
750+
Expect(*sut.Spec().Config.Linux.Resources.CPU.Shares).To(Equal(uint64(resources.CpuShares)))
751+
Expect(*sut.Spec().Config.Process.OOMScoreAdj).To(Equal(int(resources.OomScoreAdj)))
752+
Expect(sut.Spec().Config.Linux.Resources.CPU.Cpus).To(Equal(resources.CpusetCpus))
753+
Expect(sut.Spec().Config.Linux.Resources.CPU.Mems).To(Equal(resources.CpusetMems))
754+
})
755+
It("Fails to set memory limit if invalid", func() {
756+
// Given
757+
minMemory := int64(2048)
758+
759+
// When
760+
resources := &types.LinuxContainerResources{
761+
MemoryLimitInBytes: 1024, // must be >= minMemory
762+
}
763+
764+
// Then
765+
Expect(sut.SpecSetLinuxContainerResources(resources, minMemory)).NotTo(Succeed())
766+
})
767+
It("Fails to set memory swap limit if invalid", func() {
768+
// Given
769+
minMemory := int64(2048)
770+
771+
// When
772+
resources := &types.LinuxContainerResources{
773+
MemoryLimitInBytes: 2048,
774+
MemorySwapLimitInBytes: 1024, // must be >= MemoryLimitInBytes
775+
}
776+
777+
// Then
778+
Expect(sut.SpecSetLinuxContainerResources(resources, minMemory)).NotTo(Succeed())
779+
})
780+
It("Set memory limit to both swap and RAM when only MemoryLimit is set", func() {
781+
// Given
782+
resources := &types.LinuxContainerResources{
783+
MemoryLimitInBytes: 4096,
784+
}
785+
786+
// When
787+
Expect(sut.SpecSetLinuxContainerResources(resources, 2048)).To(Succeed())
788+
789+
// Then
790+
Expect(*sut.Spec().Config.Linux.Resources.Memory.Limit).To(Equal(resources.MemoryLimitInBytes))
791+
Expect(*sut.Spec().Config.Linux.Resources.Memory.Swap).To(Equal(resources.MemoryLimitInBytes))
792+
})
793+
It("Set memory limits appropriately when Limit and SwapLimit are set", func() {
794+
// Given
795+
resources := &types.LinuxContainerResources{
796+
MemoryLimitInBytes: 4096,
797+
MemorySwapLimitInBytes: 4096,
798+
}
799+
800+
// When
801+
Expect(sut.SpecSetLinuxContainerResources(resources, 0)).To(Succeed())
802+
803+
// Then
804+
Expect(*sut.Spec().Config.Linux.Resources.Memory.Limit).To(Equal(resources.MemoryLimitInBytes))
805+
Expect(*sut.Spec().Config.Linux.Resources.Memory.Swap).To(Equal(resources.MemorySwapLimitInBytes))
806+
})
807+
It("Set hugepage limits", func() {
808+
// Given
809+
hugepageLimits := []*types.HugepageLimit{
810+
{
811+
PageSize: "1KB",
812+
Limit: 1024,
813+
},
814+
{
815+
PageSize: "2KB",
816+
Limit: 2048,
817+
},
818+
}
819+
resources := &types.LinuxContainerResources{
820+
HugepageLimits: hugepageLimits,
821+
}
822+
823+
// When
824+
Expect(sut.SpecSetLinuxContainerResources(resources, 0)).To(Succeed())
825+
826+
// Then
827+
for i, pageLimit := range sut.Spec().Config.Linux.Resources.HugepageLimits {
828+
Expect(pageLimit.Pagesize).To(Equal(hugepageLimits[i].PageSize))
829+
Expect(pageLimit.Limit).To(Equal(hugepageLimits[i].Limit))
830+
}
831+
})
832+
It("Set Cgroupv2 resources", func() {
833+
// Given
834+
resources := &types.LinuxContainerResources{
835+
Unified: make(map[string]string, 2),
836+
}
837+
resources.Unified["memory.high"] = "8000000"
838+
resources.Unified["memory.low"] = "100000"
839+
840+
// When
841+
Expect(sut.SpecSetLinuxContainerResources(resources, 2048)).To(Succeed())
842+
843+
// Then
844+
Expect(sut.Spec().Config.Linux.Resources.Unified).To(HaveLen(len(resources.Unified)))
845+
})
846+
})
636847
})

0 commit comments

Comments
 (0)