diff --git a/libcontainer/configs/validate/doc.go b/libcontainer/configs/validate/doc.go new file mode 100644 index 00000000000..8819c8806cd --- /dev/null +++ b/libcontainer/configs/validate/doc.go @@ -0,0 +1,2 @@ +// Package validate provides helpers for validating configuration. +package validate diff --git a/libcontainer/configs/validate/intelrdt.go b/libcontainer/configs/validate/intelrdt.go new file mode 100644 index 00000000000..7b6015b826f --- /dev/null +++ b/libcontainer/configs/validate/intelrdt.go @@ -0,0 +1,41 @@ +package validate + +import ( + "sync" + + "github.com/opencontainers/runc/libcontainer/intelrdt" +) + +// Cache the result of intelrdt IsEnabled functions to avoid repeated sysfs +// access and enable mocking for unit tests. +type intelRdtStatus struct { + sync.Once + rdtEnabled bool + catEnabled bool + mbaEnabled bool +} + +var intelRdt = &intelRdtStatus{} + +func (i *intelRdtStatus) init() { + i.Do(func() { + i.rdtEnabled = intelrdt.IsEnabled() + i.catEnabled = intelrdt.IsCATEnabled() + i.mbaEnabled = intelrdt.IsMBAEnabled() + }) +} + +func (i *intelRdtStatus) isEnabled() bool { + i.init() + return i.rdtEnabled +} + +func (i *intelRdtStatus) isCATEnabled() bool { + i.init() + return i.catEnabled +} + +func (i *intelRdtStatus) isMBAEnabled() bool { + i.init() + return i.mbaEnabled +} diff --git a/libcontainer/configs/validate/intelrdt_test.go b/libcontainer/configs/validate/intelrdt_test.go new file mode 100644 index 00000000000..d8672225fda --- /dev/null +++ b/libcontainer/configs/validate/intelrdt_test.go @@ -0,0 +1,122 @@ +package validate + +import ( + "testing" + + "github.com/opencontainers/runc/libcontainer/configs" +) + +func TestValidateIntelRdt(t *testing.T) { + // Call init to trigger the sync.Once and enable overriding the rdt status + intelRdt.init() + + testCases := []struct { + name string + rdtEnabled bool + catEnabled bool + mbaEnabled bool + config *configs.IntelRdt + isErr bool + }{ + { + name: "rdt not supported, no config", + rdtEnabled: false, + config: nil, + isErr: false, + }, + { + name: "rdt not supported, with config", + rdtEnabled: false, + config: &configs.IntelRdt{}, + isErr: true, + }, + { + name: "empty config", + rdtEnabled: true, + config: &configs.IntelRdt{}, + isErr: false, + }, + { + name: "root clos", + rdtEnabled: true, + config: &configs.IntelRdt{ + ClosID: "/", + }, + isErr: false, + }, + { + name: "invalid ClosID (.)", + rdtEnabled: true, + config: &configs.IntelRdt{ + ClosID: ".", + }, + isErr: true, + }, + { + name: "invalid ClosID (..)", + rdtEnabled: true, + config: &configs.IntelRdt{ + ClosID: "..", + }, + isErr: true, + }, + { + name: "invalid ClosID (contains /)", + rdtEnabled: true, + config: &configs.IntelRdt{ + ClosID: "foo/bar", + }, + isErr: true, + }, + { + name: "cat not supported", + rdtEnabled: true, + catEnabled: false, + config: &configs.IntelRdt{ + L3CacheSchema: "0=ff", + }, + isErr: true, + }, + { + name: "mba not supported", + rdtEnabled: true, + mbaEnabled: false, + config: &configs.IntelRdt{ + MemBwSchema: "0=100", + }, + isErr: true, + }, + { + name: "valid config", + rdtEnabled: true, + catEnabled: true, + mbaEnabled: true, + config: &configs.IntelRdt{ + ClosID: "clos-1", + L3CacheSchema: "0=ff", + MemBwSchema: "0=100", + }, + isErr: false, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + intelRdt.rdtEnabled = tc.rdtEnabled + intelRdt.catEnabled = tc.catEnabled + intelRdt.mbaEnabled = tc.mbaEnabled + + config := &configs.Config{ + Rootfs: "/var", + IntelRdt: tc.config, + } + + err := Validate(config) + if tc.isErr && err == nil { + t.Error("expected error, got nil") + } + if !tc.isErr && err != nil { + t.Error(err) + } + }) + } +} diff --git a/libcontainer/configs/validate/validator.go b/libcontainer/configs/validate/validator.go index 9be17ba05a0..1fe66e148b4 100644 --- a/libcontainer/configs/validate/validator.go +++ b/libcontainer/configs/validate/validator.go @@ -10,7 +10,6 @@ import ( "github.com/opencontainers/cgroups" "github.com/opencontainers/runc/libcontainer/configs" - "github.com/opencontainers/runc/libcontainer/intelrdt" "github.com/opencontainers/runtime-spec/specs-go" selinux "github.com/opencontainers/selinux/go-selinux" "github.com/sirupsen/logrus" @@ -283,18 +282,19 @@ func sysctl(config *configs.Config) error { func intelrdtCheck(config *configs.Config) error { if config.IntelRdt != nil { - if !intelrdt.IsEnabled() { + if !intelRdt.isEnabled() { return fmt.Errorf("intelRdt is specified in config, but Intel RDT is not enabled") } - if config.IntelRdt.ClosID == "." || config.IntelRdt.ClosID == ".." || strings.Contains(config.IntelRdt.ClosID, "/") { - return fmt.Errorf("invalid intelRdt.ClosID %q", config.IntelRdt.ClosID) + switch clos := config.IntelRdt.ClosID; { + case clos == ".", clos == "..", len(clos) > 1 && strings.Contains(clos, "/"): + return fmt.Errorf("invalid intelRdt.ClosID %q", clos) } - if !intelrdt.IsCATEnabled() && config.IntelRdt.L3CacheSchema != "" { + if !intelRdt.isCATEnabled() && config.IntelRdt.L3CacheSchema != "" { return errors.New("intelRdt.l3CacheSchema is specified in config, but Intel RDT/CAT is not enabled") } - if !intelrdt.IsMBAEnabled() && config.IntelRdt.MemBwSchema != "" { + if !intelRdt.isMBAEnabled() && config.IntelRdt.MemBwSchema != "" { return errors.New("intelRdt.memBwSchema is specified in config, but Intel RDT/MBA is not enabled") } }