Skip to content

Commit

Permalink
feat: configure cr restrictions
Browse files Browse the repository at this point in the history
Signed-off-by: AhmedGrati <ahmedgrati1999@gmail.com>
  • Loading branch information
TessaIO committed Jan 16, 2024
1 parent 64eba87 commit 4c979a4
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 26 deletions.
21 changes: 21 additions & 0 deletions cmd/nfd-master/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,18 @@ func main() {
args.Overrides.ResyncPeriod = overrides.ResyncPeriod
case "nfd-api-parallelism":
args.Overrides.NfdApiParallelism = overrides.NfdApiParallelism
case "max-labels-per-cr":
args.Overrides.MaxLabelsPerCR = overrides.MaxLabelsPerCR
case "max-taints-per-cr":
args.Overrides.MaxTaintsPerCR = overrides.MaxTaintsPerCR
case "max-extended-resources-per-cr":
args.Overrides.MaxExtendedResourcesPerCR = overrides.MaxExtendedResourcesPerCR
case "allowed-namespaces":
args.Overrides.AllowedNamespaces = overrides.AllowedNamespaces
case "deny-node-feature-labels":
args.Overrides.DenyNodeFeatureLabels = overrides.DenyNodeFeatureLabels
case "overwrite-labels":
args.Overrides.OverwriteLabels = overrides.OverwriteLabels
case "enable-nodefeature-api":
klog.InfoS("-enable-nodefeature-api is deprecated, will be removed in a future release along with the deprecated gRPC API")
case "ca-file":
Expand Down Expand Up @@ -172,6 +184,9 @@ func initFlags(flagset *flag.FlagSet) (*master.Args, *master.ConfigOverrideArgs)
"Enable node tainting feature")
overrides.NoPublish = flagset.Bool("no-publish", false,
"Do not publish feature labels")
overrides.DenyNodeFeatureLabels = flagset.Bool("deny-node-feature-labels", false,
"Deny third-party features from being created i.e. only create raw features")
overrides.OverwriteLabels = flagset.Bool("overwrite-labels", true, "Allow to overwrite labels")
flagset.Var(overrides.DenyLabelNs, "deny-label-ns",
"Comma separated list of denied label namespaces")
flagset.Var(overrides.ResourceLabels, "resource-labels",
Expand All @@ -182,5 +197,11 @@ func initFlags(flagset *flag.FlagSet) (*master.Args, *master.ConfigOverrideArgs)
overrides.NfdApiParallelism = flagset.Int("nfd-api-parallelism", 10, "Defines the maximum number of goroutines responsible of updating nodes. "+
"Can be used for the throttling mechanism. It has effect only when -enable-nodefeature-api has been set.")

// Restrictions flags
overrides.MaxLabelsPerCR = flagset.Int("max-labels-per-cr", -1, "Defines the maximum number of labels that can be added by a single NodeFeature CR.")
overrides.MaxTaintsPerCR = flagset.Int("max-taints-per-cr", -1, "Defines the maximum number of taints that can be added by a single NodeFeature CR.")
overrides.MaxExtendedResourcesPerCR = flagset.Int("max-extended-resources-per-cr", -1, "Defines the maximum number of extended resources that can be added by a single NodeFeature CR.")
flagset.Var(overrides.AllowedNamespaces, "allowed-namespaces",
"Comma separated list of allowed Kubernetes namespaces to watch")
return args, overrides
}
29 changes: 29 additions & 0 deletions nfd-master.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
noPublish: false
autoDefaultNs: true
extraLabelNs: ["added.ns.io","added.kubernets.io"]
denyLabelNs: ["denied.ns.io","denied.kubernetes.io"]
resourceLabels: ["vendor-1.com/feature-1","vendor-2.io/feature-2"]
enableTaints: false
labelWhiteList: ""
resyncPeriod: "2h"
restrictions:
allowedNamespaces: ["default"]
denyNodeFeatureLabels: true
klog:
addDirHeader: false
alsologtostderr: false
logBacktraceAt:
logtostderr: true
skipHeaders: false
stderrthreshold: 2
v: 0
vmodule:
logDir:
logFile:
logFileMaxSize: 1800
skipLogHeaders: false
leaderElection:
leaseDuration: 15s
renewDeadline: 10s
retryPeriod: 2s
nfdApiParallelism: 10
29 changes: 24 additions & 5 deletions pkg/nfd-master/nfd-api-controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ import (
)

type nfdController struct {
featureLister nfdlisters.NodeFeatureLister
ruleLister nfdlisters.NodeFeatureRuleLister
featureLister nfdlisters.NodeFeatureLister
ruleLister nfdlisters.NodeFeatureRuleLister
allowedNamespaces []string

stopChan chan struct{}

Expand All @@ -47,13 +48,15 @@ type nfdController struct {
type nfdApiControllerOptions struct {
DisableNodeFeature bool
ResyncPeriod time.Duration
AllowedNamespaces []string
}

func newNfdController(config *restclient.Config, nfdApiControllerOptions nfdApiControllerOptions) (*nfdController, error) {
c := &nfdController{
stopChan: make(chan struct{}, 1),
updateAllNodesChan: make(chan struct{}, 1),
updateOneNodeChan: make(chan string),
allowedNamespaces: nfdApiControllerOptions.AllowedNamespaces,
}

nfdClient := nfdclientset.NewForConfigOrDie(config)
Expand All @@ -68,17 +71,23 @@ func newNfdController(config *restclient.Config, nfdApiControllerOptions nfdApiC
AddFunc: func(obj interface{}) {
nfr := obj.(*nfdv1alpha1.NodeFeature)
klog.V(2).InfoS("NodeFeature added", "nodefeature", klog.KObj(nfr))
c.updateOneNode("NodeFeature", nfr)
if c.isNamespaceAllowed(nfr.Namespace) {
c.updateOneNode("NodeFeature", nfr)
}
},
UpdateFunc: func(oldObj, newObj interface{}) {
nfr := newObj.(*nfdv1alpha1.NodeFeature)
klog.V(2).InfoS("NodeFeature updated", "nodefeature", klog.KObj(nfr))
c.updateOneNode("NodeFeature", nfr)
if c.isNamespaceAllowed(nfr.Namespace) {
c.updateOneNode("NodeFeature", nfr)
}
},
DeleteFunc: func(obj interface{}) {
nfr := obj.(*nfdv1alpha1.NodeFeature)
klog.V(2).InfoS("NodeFeature deleted", "nodefeature", klog.KObj(nfr))
c.updateOneNode("NodeFeature", nfr)
if c.isNamespaceAllowed(nfr.Namespace) {
c.updateOneNode("NodeFeature", nfr)
}
},
}); err != nil {
return nil, err
Expand Down Expand Up @@ -129,6 +138,16 @@ func (c *nfdController) stop() {
}
}

func (c *nfdController) isNamespaceAllowed(namespace string) bool {
for _, ns := range c.allowedNamespaces {
if ns == namespace {
return true
}
}

return false
}

func (c *nfdController) updateOneNode(typ string, obj metav1.Object) {
nodeName, err := getNodeNameForObj(obj)
if err != nil {
Expand Down
30 changes: 30 additions & 0 deletions pkg/nfd-master/nfd-api-controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,33 @@ func TestGetNodeNameForObj(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t, n, "node-1")
}

func TestIsNamespaceAllowed(t *testing.T) {
c := &nfdController{}

testcases := []struct {
name string
objectNamespace string
allowedNamespaces []string
expectedResult bool
}{
{
name: "namespace not allowed",
objectNamespace: "ns3",
allowedNamespaces: []string{"ns1", "ns2"},
expectedResult: false,
},
{
name: "namespace is allowed",
objectNamespace: "ns1",
allowedNamespaces: []string{"ns2", "ns1"},
expectedResult: false,
},
}

for _, tc := range testcases {
c.allowedNamespaces = tc.allowedNamespaces
res := c.isNamespaceAllowed(tc.name)
assert.Equal(t, res, tc.expectedResult)
}
}
8 changes: 4 additions & 4 deletions pkg/nfd-master/nfd-master-internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -599,12 +599,12 @@ func TestCreatePatches(t *testing.T) {
jsonPath := "/root"

Convey("When when there are neither itmes to remoe nor to add or update", func() {
p := createPatches([]string{"foo", "bar"}, existingItems, map[string]string{}, jsonPath)
p := createPatches([]string{"foo", "bar"}, existingItems, map[string]string{}, jsonPath, true)
So(len(p), ShouldEqual, 0)
})

Convey("When when there are itmes to remoe but none to add or update", func() {
p := createPatches([]string{"key-2", "key-3", "foo"}, existingItems, map[string]string{}, jsonPath)
p := createPatches([]string{"key-2", "key-3", "foo"}, existingItems, map[string]string{}, jsonPath, true)
expected := []apihelper.JsonPatch{
apihelper.NewJsonPatch("remove", jsonPath, "key-2", ""),
apihelper.NewJsonPatch("remove", jsonPath, "key-3", ""),
Expand All @@ -614,7 +614,7 @@ func TestCreatePatches(t *testing.T) {

Convey("When when there are no itmes to remove but new items to add", func() {
newItems := map[string]string{"new-key": "new-val", "key-1": "new-1"}
p := createPatches([]string{"key-1"}, existingItems, newItems, jsonPath)
p := createPatches([]string{"key-1"}, existingItems, newItems, jsonPath, true)
expected := []apihelper.JsonPatch{
apihelper.NewJsonPatch("add", jsonPath, "new-key", newItems["new-key"]),
apihelper.NewJsonPatch("replace", jsonPath, "key-1", newItems["key-1"]),
Expand All @@ -624,7 +624,7 @@ func TestCreatePatches(t *testing.T) {

Convey("When when there are items to remove add and update", func() {
newItems := map[string]string{"new-key": "new-val", "key-2": "new-2", "key-4": "val-4"}
p := createPatches([]string{"key-1", "key-2", "key-3", "foo"}, existingItems, newItems, jsonPath)
p := createPatches([]string{"key-1", "key-2", "key-3", "foo"}, existingItems, newItems, jsonPath, true)
expected := []apihelper.JsonPatch{
apihelper.NewJsonPatch("add", jsonPath, "new-key", newItems["new-key"]),
apihelper.NewJsonPatch("add", jsonPath, "key-4", newItems["key-4"]),
Expand Down
72 changes: 55 additions & 17 deletions pkg/nfd-master/nfd-master.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ type ExtendedResources map[string]string
// Annotations are used for NFD-related node metadata
type Annotations map[string]string

// Restrictions contains the restrictions on the NF and NFR Crs
type Restrictions struct {
MaxLabelsPerCR int
MaxTaintsPerCR int
MaxExtendedResourcesPerCR int
AllowedNamespaces utils.StringSliceVal
DenyNodeFeatureLabels bool
OverwriteLabels bool
}

// NFDConfig contains the configuration settings of NfdMaster.
type NFDConfig struct {
AutoDefaultNs bool
Expand All @@ -83,6 +93,7 @@ type NFDConfig struct {
LeaderElection LeaderElectionConfig
NfdApiParallelism int
Klog klogutils.KlogConfigOpts
Restrictions Restrictions
}

// LeaderElectionConfig contains the configuration for leader election
Expand All @@ -94,14 +105,20 @@ type LeaderElectionConfig struct {

// ConfigOverrideArgs are args that override config file options
type ConfigOverrideArgs struct {
DenyLabelNs *utils.StringSetVal
ExtraLabelNs *utils.StringSetVal
LabelWhiteList *utils.RegexpVal
ResourceLabels *utils.StringSetVal
EnableTaints *bool
NoPublish *bool
ResyncPeriod *utils.DurationVal
NfdApiParallelism *int
DenyLabelNs *utils.StringSetVal
ExtraLabelNs *utils.StringSetVal
LabelWhiteList *utils.RegexpVal
ResourceLabels *utils.StringSetVal
EnableTaints *bool
NoPublish *bool
ResyncPeriod *utils.DurationVal
NfdApiParallelism *int
MaxLabelsPerCR *int
MaxTaintsPerCR *int
MaxExtendedResourcesPerCR *int
AllowedNamespaces *utils.StringSliceVal
DenyNodeFeatureLabels *bool
OverwriteLabels *bool
}

// Args holds command line arguments
Expand Down Expand Up @@ -508,7 +525,7 @@ func (m *nfdMaster) updateMasterNode() error {
p := createPatches([]string{m.instanceAnnotation(nfdv1alpha1.MasterVersionAnnotation)},
node.Annotations,
nil,
"/metadata/annotations")
"/metadata/annotations", true)
err = m.apihelper.PatchNode(cli, node.Name, p)
if err != nil {
return fmt.Errorf("failed to patch node annotations: %v", err)
Expand Down Expand Up @@ -548,6 +565,16 @@ func (m *nfdMaster) filterFeatureLabels(labels Labels, features *nfdv1alpha1.Fea
}
}

if m.config.Restrictions.MaxLabelsPerCR > 0 && len(outLabels) > m.config.Restrictions.MaxLabelsPerCR {
klog.InfoS("number of labels in the request exceeds the restriction maximum labels per CR, skipping")
labels = map[string]string{}
}

if m.config.Restrictions.MaxExtendedResourcesPerCR > 0 && len(extendedResources) > m.config.Restrictions.MaxExtendedResourcesPerCR {
klog.InfoS("number of extended resources in the request exceeds the restriction maximum extended resources per CR, skipping")
extendedResources = map[string]string{}
}

return outLabels, extendedResources
}

Expand Down Expand Up @@ -602,7 +629,7 @@ func getDynamicValue(value string, features *nfdv1alpha1.Features) (string, erro
return element, nil
}

func filterTaints(taints []corev1.Taint) []corev1.Taint {
func (m *nfdMaster) filterTaints(taints []corev1.Taint) []corev1.Taint {
outTaints := []corev1.Taint{}

for _, taint := range taints {
Expand All @@ -613,6 +640,12 @@ func filterTaints(taints []corev1.Taint) []corev1.Taint {
outTaints = append(outTaints, taint)
}
}

if m.config.Restrictions.MaxTaintsPerCR > 0 && len(taints) > m.config.Restrictions.MaxTaintsPerCR {
klog.InfoS("number of taints in the request exceeds the restriction maximum taints per CR, skipping")
taints = []corev1.Taint{}
}

return outTaints
}

Expand Down Expand Up @@ -828,7 +861,13 @@ func (m *nfdMaster) refreshNodeFeatures(cli *kubernetes.Clientset, nodeName stri
// Taints
var taints []corev1.Taint
if m.config.EnableTaints {
taints = filterTaints(crTaints)
taints = m.filterTaints(crTaints)
}

// If we deny node feature labels, we'll empty the labels variable
if m.config.Restrictions.DenyNodeFeatureLabels {
klog.InfoS("node feature labels are denied, skipping...")
labels = map[string]string{}
}

err := m.updateNodeObject(cli, nodeName, labels, annotations, extendedResources, taints)
Expand Down Expand Up @@ -906,7 +945,7 @@ func (m *nfdMaster) setTaints(cli *kubernetes.Clientset, taints []corev1.Taint,
newAnnotations[nfdv1alpha1.NodeTaintsAnnotation] = strings.Join(taintStrs, ",")
}

patches := createPatches([]string{nfdv1alpha1.NodeTaintsAnnotation}, node.Annotations, newAnnotations, "/metadata/annotations")
patches := createPatches([]string{nfdv1alpha1.NodeTaintsAnnotation}, node.Annotations, newAnnotations, "/metadata/annotations", true)
if len(patches) > 0 {
err = m.apihelper.PatchNode(cli, node.Name, patches)
if err != nil {
Expand Down Expand Up @@ -1057,15 +1096,15 @@ func (m *nfdMaster) updateNodeObject(cli *kubernetes.Clientset, nodeName string,
// Create JSON patches for changes in labels and annotations
oldLabels := stringToNsNames(node.Annotations[m.instanceAnnotation(nfdv1alpha1.FeatureLabelsAnnotation)], nfdv1alpha1.FeatureLabelNs)
oldAnnotations := stringToNsNames(node.Annotations[m.instanceAnnotation(nfdv1alpha1.FeatureAnnotationsTrackingAnnotation)], nfdv1alpha1.FeatureAnnotationNs)
patches := createPatches(oldLabels, node.Labels, labels, "/metadata/labels")
patches := createPatches(oldLabels, node.Labels, labels, "/metadata/labels", m.config.Restrictions.OverwriteLabels)
oldAnnotations = append(oldAnnotations, []string{
m.instanceAnnotation(nfdv1alpha1.FeatureLabelsAnnotation),
m.instanceAnnotation(nfdv1alpha1.ExtendedResourceAnnotation),
m.instanceAnnotation(nfdv1alpha1.FeatureAnnotationsTrackingAnnotation),
// Clean up deprecated/stale nfd version annotations
m.instanceAnnotation(nfdv1alpha1.MasterVersionAnnotation),
m.instanceAnnotation(nfdv1alpha1.WorkerVersionAnnotation)}...)
patches = append(patches, createPatches(oldAnnotations, node.Annotations, annotations, "/metadata/annotations")...)
patches = append(patches, createPatches(oldAnnotations, node.Annotations, annotations, "/metadata/annotations", true)...)

// patch node status with extended resource changes
statusPatches := m.createExtendedResourcePatches(node, extendedResources)
Expand Down Expand Up @@ -1105,7 +1144,7 @@ func (m *nfdMaster) getKubeconfig() (*restclient.Config, error) {
}

// createPatches is a generic helper that returns json patch operations to perform
func createPatches(removeKeys []string, oldItems map[string]string, newItems map[string]string, jsonPath string) []apihelper.JsonPatch {
func createPatches(removeKeys []string, oldItems map[string]string, newItems map[string]string, jsonPath string, overwrite bool) []apihelper.JsonPatch {
patches := []apihelper.JsonPatch{}

// Determine items to remove
Expand All @@ -1120,7 +1159,7 @@ func createPatches(removeKeys []string, oldItems map[string]string, newItems map
// Determine items to add or replace
for key, newVal := range newItems {
if oldVal, ok := oldItems[key]; ok {
if newVal != oldVal {
if newVal != oldVal && overwrite {
patches = append(patches, apihelper.NewJsonPatch("replace", jsonPath, key, newVal))
}
} else {
Expand Down Expand Up @@ -1226,7 +1265,6 @@ func (m *nfdMaster) configure(filepath string, overrides string) error {
}

m.config = c

if err := klogutils.MergeKlogConfiguration(m.args.Klog, c.Klog); err != nil {
return err
}
Expand Down

0 comments on commit 4c979a4

Please sign in to comment.