mirror of
https://github.com/kubernetes-sigs/node-feature-discovery.git
synced 2024-12-14 11:57:51 +00:00
Create a Validate pkg
Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
This commit is contained in:
parent
bdfef6df18
commit
affb93ea50
7 changed files with 505 additions and 96 deletions
|
@ -59,8 +59,6 @@ namespaces, excluding `kubernetes.io` namespace and its sub-namespaces
|
|||
`kubernetes.io` and its sub-namespaces are always denied.
|
||||
This option can be used to exclude some vendors or application specific
|
||||
namespaces.
|
||||
Note that the namespaces `feature.node.kubernetes.io` and `profile.node.kubernetes.io`
|
||||
and their sub-namespaces are always allowed and cannot be denied.
|
||||
|
||||
Default: *empty*
|
||||
|
||||
|
|
|
@ -475,9 +475,6 @@ The namespace part (i.e. prefix) of the labels is controlled by nfd:
|
|||
[`-extra-label-ns`](../reference/master-commandline-reference.md#-extra-label-ns)
|
||||
command line flag of nfd-master.
|
||||
e.g: `nfd-master -deny-label-ns="*" -extra-label-ns=example.com`
|
||||
- Built-in default namespaces `feature.node.kubernetes.io` and
|
||||
`profile.node.kubernetes.io` (and their sub-namespaces) are always allowed
|
||||
and cannot be denied.
|
||||
|
||||
## Feature rule format
|
||||
|
||||
|
|
166
pkg/apis/nfd/validate/validate.go
Normal file
166
pkg/apis/nfd/validate/validate.go
Normal file
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
Copyright 2023 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package validate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
k8sQuantity "k8s.io/apimachinery/pkg/api/resource"
|
||||
k8svalidation "k8s.io/apimachinery/pkg/util/validation"
|
||||
|
||||
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
|
||||
)
|
||||
|
||||
var (
|
||||
// Default error message for invalid label/annotation keys
|
||||
ErrNSNotAllowed = fmt.Errorf("namespace is not allowed")
|
||||
// Default error message for invalid label/annotation keys
|
||||
ErrUnprefixedKeysNotAllowed = fmt.Errorf("unprefixed keys are not allowed")
|
||||
// Default error for invalid taint effect
|
||||
ErrInvalidTaintEffect = fmt.Errorf("invalid taint effect")
|
||||
// Default error for empty taint effect
|
||||
ErrEmptyTaintEffect = fmt.Errorf("empty taint effect")
|
||||
)
|
||||
|
||||
// Label validates a label key and value and returns an error if the key or
|
||||
// value is invalid.
|
||||
func Label(key, value string) error {
|
||||
//Validate label key and value
|
||||
if err := k8svalidation.IsQualifiedName(key); len(err) > 0 {
|
||||
return fmt.Errorf("invalid label key %q: %s", key, strings.Join(err, "; "))
|
||||
}
|
||||
// Check label namespace, filter out if ns is not whitelisted
|
||||
ns, _ := splitNs(key)
|
||||
// And is not empty
|
||||
if ns == "" {
|
||||
return ErrUnprefixedKeysNotAllowed
|
||||
}
|
||||
// And is not a denied namespace
|
||||
if ns == "kubernetes.io" || strings.HasSuffix(ns, ".kubernetes.io") {
|
||||
// And is not a default namespace
|
||||
if ns != nfdv1alpha1.FeatureLabelNs && ns != nfdv1alpha1.ProfileLabelNs &&
|
||||
!strings.HasSuffix(ns, nfdv1alpha1.FeatureLabelSubNsSuffix) && !strings.HasSuffix(ns, nfdv1alpha1.ProfileLabelSubNsSuffix) {
|
||||
return ErrNSNotAllowed
|
||||
}
|
||||
}
|
||||
|
||||
// Validate label value
|
||||
if err := k8svalidation.IsValidLabelValue(value); len(err) > 0 {
|
||||
return fmt.Errorf("invalid labelvalue %q: %s", value, strings.Join(err, "; "))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Annotation validates an annotation key and value and returns an error if the
|
||||
// key or value is invalid.
|
||||
func Annotation(key, value string) error {
|
||||
// Validate the annotation key
|
||||
if err := k8svalidation.IsQualifiedName(key); len(err) > 0 {
|
||||
return fmt.Errorf("invalid annotation key %q: %s", key, strings.Join(err, "; "))
|
||||
}
|
||||
|
||||
ns, _ := splitNs(key)
|
||||
// And is not empty
|
||||
if ns == "" {
|
||||
return ErrUnprefixedKeysNotAllowed
|
||||
}
|
||||
// And is not a denied namespace
|
||||
if ns == "kubernetes.io" || strings.HasSuffix(ns, ".kubernetes.io") {
|
||||
// And is not a default namespace
|
||||
if ns != nfdv1alpha1.FeatureAnnotationNs && !strings.HasSuffix(ns, nfdv1alpha1.FeatureAnnotationSubNsSuffix) {
|
||||
return ErrNSNotAllowed
|
||||
}
|
||||
}
|
||||
|
||||
// Validate annotation value
|
||||
if errs := k8svalidation.IsValidLabelValue(value); len(errs) > 0 {
|
||||
return fmt.Errorf("invalid annotation value %q: %s", value, strings.Join(errs, "; "))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Taint validates a taint key and value and returns an error if the key or
|
||||
// value is invalid.
|
||||
func Taint(taint *corev1.Taint) error {
|
||||
ns, _ := splitNs(taint.Key)
|
||||
// And is not empty
|
||||
if ns == "" {
|
||||
return ErrUnprefixedKeysNotAllowed
|
||||
}
|
||||
// And is not a denied namespace
|
||||
if ns == "kubernetes.io" || strings.HasSuffix(ns, ".kubernetes.io") {
|
||||
// And is not a default namespace
|
||||
if ns != nfdv1alpha1.TaintNs && !strings.HasSuffix(ns, nfdv1alpha1.TaintSubNsSuffix) {
|
||||
return ErrNSNotAllowed
|
||||
}
|
||||
}
|
||||
|
||||
// Validate taint effect is not empty
|
||||
if taint.Effect == "" {
|
||||
return ErrEmptyTaintEffect
|
||||
}
|
||||
// Validate effect to be only one of NoSchedule, PreferNoSchedule or NoExecute
|
||||
if taint.Effect != corev1.TaintEffectNoSchedule &&
|
||||
taint.Effect != corev1.TaintEffectPreferNoSchedule &&
|
||||
taint.Effect != corev1.TaintEffectNoExecute {
|
||||
return ErrInvalidTaintEffect
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExtendedResource validates an extended resource key and value and returns an
|
||||
// error if the key or value is invalid.
|
||||
func ExtendedResource(key, value string) error {
|
||||
//Validate extendedResource name
|
||||
if errs := k8svalidation.IsQualifiedName(key); len(errs) > 0 {
|
||||
return fmt.Errorf("invalid name %q: %s", key, strings.Join(errs, "; "))
|
||||
}
|
||||
ns, _ := splitNs(key)
|
||||
// And is not empty
|
||||
if ns == "" {
|
||||
return ErrUnprefixedKeysNotAllowed
|
||||
}
|
||||
// And is not a denied namespace
|
||||
if ns == "kubernetes.io" || strings.HasSuffix(ns, ".kubernetes.io") {
|
||||
// And is not a default namespace
|
||||
if ns != nfdv1alpha1.ExtendedResourceNs && !strings.HasSuffix(ns, nfdv1alpha1.ExtendedResourceSubNsSuffix) {
|
||||
return ErrNSNotAllowed
|
||||
}
|
||||
}
|
||||
|
||||
// Static Value (Pre-Defined at the NodeFeatureRule)
|
||||
_, err := k8sQuantity.ParseQuantity(value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid value %s (from %s): %w", value, value, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// splitNs splits a name into its namespace and name parts
|
||||
func splitNs(fullname string) (string, string) {
|
||||
split := strings.SplitN(fullname, "/", 2)
|
||||
if len(split) == 2 {
|
||||
return split[0], split[1]
|
||||
}
|
||||
return "", fullname
|
||||
}
|
236
pkg/apis/nfd/validate/validate_test.go
Normal file
236
pkg/apis/nfd/validate/validate_test.go
Normal file
|
@ -0,0 +1,236 @@
|
|||
package validate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func TestAnnotation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
key string
|
||||
value string
|
||||
want error
|
||||
fail bool
|
||||
}{
|
||||
{
|
||||
name: "Valid annotation",
|
||||
key: "feature.node.kubernetes.io/feature",
|
||||
value: "true",
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "Invalid annotation key",
|
||||
key: "invalid-key",
|
||||
value: "true",
|
||||
want: ErrUnprefixedKeysNotAllowed,
|
||||
},
|
||||
{
|
||||
name: "Invalid annotation value",
|
||||
key: "feature.node.kubernetes.io/feature",
|
||||
value: "invalid value",
|
||||
want: fmt.Errorf("invalid value \"invalid value\": value must be a valid label value"),
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "Denied annotation key",
|
||||
key: "kubernetes.io/denied",
|
||||
value: "true",
|
||||
want: ErrNSNotAllowed,
|
||||
fail: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := Annotation(tt.key, tt.value)
|
||||
if got != tt.want {
|
||||
if tt.fail {
|
||||
return
|
||||
}
|
||||
t.Errorf("Annotation() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTaint(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
taint *corev1.Taint
|
||||
want error
|
||||
}{
|
||||
{
|
||||
name: "Valid taint",
|
||||
taint: &corev1.Taint{
|
||||
Key: "feature.node.kubernetes.io/taint",
|
||||
Value: "true",
|
||||
Effect: corev1.TaintEffectNoSchedule,
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "UNPREFIXED taint key",
|
||||
taint: &corev1.Taint{
|
||||
Key: "invalid-key",
|
||||
Value: "true",
|
||||
Effect: corev1.TaintEffectNoSchedule,
|
||||
},
|
||||
want: ErrUnprefixedKeysNotAllowed,
|
||||
},
|
||||
{
|
||||
name: "Invalid taint key",
|
||||
taint: &corev1.Taint{
|
||||
Key: "invalid.kubernetes.io/invalid-key",
|
||||
Value: "true",
|
||||
Effect: corev1.TaintEffectNoSchedule,
|
||||
},
|
||||
want: ErrNSNotAllowed,
|
||||
},
|
||||
{
|
||||
name: "Empty taint effect",
|
||||
taint: &corev1.Taint{
|
||||
Key: "feature.node.kubernetes.io/taint",
|
||||
Value: "true",
|
||||
Effect: "",
|
||||
},
|
||||
want: ErrEmptyTaintEffect,
|
||||
},
|
||||
{
|
||||
name: "Invalid taint effect",
|
||||
taint: &corev1.Taint{
|
||||
Key: "feature.node.kubernetes.io/taint",
|
||||
Value: "true",
|
||||
Effect: "invalid-effect",
|
||||
},
|
||||
want: ErrInvalidTaintEffect,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := Taint(tt.taint)
|
||||
if got != tt.want {
|
||||
t.Errorf("Taint() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLabel(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
key string
|
||||
value string
|
||||
want error
|
||||
fail bool
|
||||
}{
|
||||
{
|
||||
name: "Valid label",
|
||||
key: "feature.node.kubernetes.io/label",
|
||||
value: "true",
|
||||
want: nil,
|
||||
fail: false,
|
||||
},
|
||||
{
|
||||
name: "Valid vendor label",
|
||||
key: "vendor.io/label",
|
||||
value: "true",
|
||||
want: nil,
|
||||
fail: false,
|
||||
},
|
||||
{
|
||||
name: "Denied label with prefix",
|
||||
key: "kubernetes.io/label",
|
||||
value: "true",
|
||||
want: ErrNSNotAllowed,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid label key",
|
||||
key: "invalid-key",
|
||||
value: "true",
|
||||
want: ErrNSNotAllowed,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid label value",
|
||||
key: "feature.node.kubernetes.io/label",
|
||||
value: "invalid value",
|
||||
want: fmt.Errorf("invalid value \"invalid value\": value must be a valid label value"),
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "Valid value label",
|
||||
key: "feature.node.kubernetes.io/label",
|
||||
value: "true",
|
||||
want: nil,
|
||||
fail: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := Label(tt.key, tt.value)
|
||||
if err != tt.want {
|
||||
if tt.fail {
|
||||
return
|
||||
}
|
||||
t.Errorf("Label() = %v, want %v", err, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtendedResource(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
key string
|
||||
value string
|
||||
want error
|
||||
fail bool
|
||||
}{
|
||||
{
|
||||
name: "Valid extended resource",
|
||||
key: "feature.node.kubernetes.io/extended-resource",
|
||||
value: "123",
|
||||
want: nil,
|
||||
fail: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid extended resource key",
|
||||
key: "invalid-key",
|
||||
value: "123",
|
||||
want: ErrNSNotAllowed,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid extended resource value",
|
||||
key: "feature.node.kubernetes.io/extended-resource",
|
||||
value: "invalid value",
|
||||
want: fmt.Errorf("invalid value \"invalid value\": value must be a valid label value"),
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "Denied extended resource key",
|
||||
key: "kubernetes.io/extended-resource",
|
||||
value: "123",
|
||||
want: ErrNSNotAllowed,
|
||||
fail: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := ExtendedResource(tt.key, tt.value)
|
||||
if err != tt.want {
|
||||
if tt.fail {
|
||||
return
|
||||
}
|
||||
t.Errorf("ExtendedResource() = %v, want %v", err, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -382,6 +382,7 @@ func TestSetLabels(t *testing.T) {
|
|||
expectedStatusPatches := []apihelper.JsonPatch{}
|
||||
|
||||
Convey("When node update succeeds", func() {
|
||||
mockMaster.config.ExtraLabelNs = map[string]struct{}{"example.io": {}}
|
||||
expectedPatches := []apihelper.JsonPatch{
|
||||
apihelper.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.FeatureLabelsAnnotation, strings.Join(mockLabelNames, ",")),
|
||||
}
|
||||
|
@ -400,6 +401,7 @@ func TestSetLabels(t *testing.T) {
|
|||
})
|
||||
|
||||
Convey("When -label-whitelist is specified", func() {
|
||||
mockMaster.config.ExtraLabelNs = map[string]struct{}{"example.io": {}}
|
||||
expectedPatches := []apihelper.JsonPatch{
|
||||
apihelper.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.FeatureLabelsAnnotation, "example.io/feature-2"),
|
||||
apihelper.NewJsonPatch("add", "/metadata/labels", "example.io/feature-2", mockLabels["example.io/feature-2"]),
|
||||
|
@ -443,7 +445,6 @@ func TestSetLabels(t *testing.T) {
|
|||
}
|
||||
|
||||
mockMaster.deniedNs.normal = map[string]struct{}{"random.denied.ns": {}}
|
||||
mockMaster.deniedNs.wildcard = map[string]struct{}{"kubernetes.io": {}}
|
||||
mockMaster.config.ExtraLabelNs = map[string]struct{}{"valid.ns": {}}
|
||||
mockMaster.args.Instance = instance
|
||||
mockHelper.On("GetClient").Return(mockClient, nil)
|
||||
|
@ -459,10 +460,11 @@ func TestSetLabels(t *testing.T) {
|
|||
})
|
||||
|
||||
Convey("When -resource-labels is specified", func() {
|
||||
mockMaster.config.ExtraLabelNs = map[string]struct{}{"example.io": {}}
|
||||
expectedPatches := []apihelper.JsonPatch{
|
||||
apihelper.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.FeatureLabelsAnnotation, "example.io/feature-2"),
|
||||
apihelper.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.ExtendedResourceAnnotation, "feature-1,feature-3"),
|
||||
apihelper.NewJsonPatch("add", "/metadata/labels", "example.io/feature-2", mockLabels["example.io/feature-2"]),
|
||||
apihelper.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.ExtendedResourceAnnotation, "feature-1,feature-3"),
|
||||
}
|
||||
expectedStatusPatches := []apihelper.JsonPatch{
|
||||
apihelper.NewJsonPatch("add", "/status/capacity", "feature.node.kubernetes.io/feature-1", mockLabels["feature.node.kubernetes.io/feature-1"]),
|
||||
|
@ -502,6 +504,7 @@ func TestSetLabels(t *testing.T) {
|
|||
func TestFilterLabels(t *testing.T) {
|
||||
mockHelper := &apihelper.MockAPIHelpers{}
|
||||
mockMaster := newMockMaster(mockHelper)
|
||||
mockMaster.config.ExtraLabelNs = map[string]struct{}{"example.io": {}}
|
||||
mockMaster.deniedNs = deniedNs{
|
||||
normal: map[string]struct{}{"": struct{}{}, "kubernetes.io": struct{}{}, "denied.ns": struct{}{}},
|
||||
wildcard: map[string]struct{}{".kubernetes.io": struct{}{}, ".denied.subns": struct{}{}},
|
||||
|
@ -940,3 +943,62 @@ func removeLabelsWithPrefix(n *corev1.Node, search string) []apihelper.JsonPatch
|
|||
|
||||
return p
|
||||
}
|
||||
|
||||
func TestGetDynamicValue(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
value string
|
||||
features *nfdv1alpha1.Features
|
||||
want string
|
||||
fail bool
|
||||
}{
|
||||
{
|
||||
name: "Valid dynamic value",
|
||||
value: "@test.feature.LSM",
|
||||
features: &nfdv1alpha1.Features{
|
||||
Attributes: map[string]nfdv1alpha1.AttributeFeatureSet{
|
||||
"test.feature": nfdv1alpha1.AttributeFeatureSet{
|
||||
Elements: map[string]string{
|
||||
"LSM": "123",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: "123",
|
||||
fail: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid feature name",
|
||||
value: "@invalid",
|
||||
features: &nfdv1alpha1.Features{},
|
||||
want: "",
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "Element not found",
|
||||
value: "@test.feature.LSM",
|
||||
features: &nfdv1alpha1.Features{},
|
||||
want: "",
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid dynamic value",
|
||||
value: "@test.feature.LSM",
|
||||
features: &nfdv1alpha1.Features{},
|
||||
want: "",
|
||||
fail: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := getDynamicValue(tt.value, tt.features)
|
||||
if err != nil && !tt.fail {
|
||||
t.Errorf("getDynamicValue() = %v, want %v", err, tt.want)
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("getDynamicValue() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,10 +38,8 @@ import (
|
|||
"google.golang.org/grpc/health/grpc_health_v1"
|
||||
"google.golang.org/grpc/peer"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
k8sQuantity "k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
k8sLabels "k8s.io/apimachinery/pkg/labels"
|
||||
k8svalidation "k8s.io/apimachinery/pkg/util/validation"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/leaderelection"
|
||||
|
@ -55,6 +53,7 @@ import (
|
|||
|
||||
"sigs.k8s.io/node-feature-discovery/pkg/apihelper"
|
||||
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
|
||||
"sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/validate"
|
||||
pb "sigs.k8s.io/node-feature-discovery/pkg/labeler"
|
||||
"sigs.k8s.io/node-feature-discovery/pkg/utils"
|
||||
"sigs.k8s.io/node-feature-discovery/pkg/version"
|
||||
|
@ -553,24 +552,27 @@ func (m *nfdMaster) filterFeatureLabels(labels Labels, features *nfdv1alpha1.Fea
|
|||
}
|
||||
|
||||
func (m *nfdMaster) filterFeatureLabel(name, value string, features *nfdv1alpha1.Features) (string, error) {
|
||||
//Validate label name
|
||||
if errs := k8svalidation.IsQualifiedName(name); len(errs) > 0 {
|
||||
return "", fmt.Errorf("invalid name %q: %s", name, strings.Join(errs, "; "))
|
||||
// Check if Value is dynamic
|
||||
var filteredValue string
|
||||
if strings.HasPrefix(value, "@") {
|
||||
dynamicValue, err := getDynamicValue(value, features)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
filteredValue = dynamicValue
|
||||
} else {
|
||||
filteredValue = value
|
||||
}
|
||||
|
||||
// Check label namespace, filter out if ns is not whitelisted
|
||||
// Validate
|
||||
ns, base := splitNs(name)
|
||||
if ns == "" {
|
||||
return "", fmt.Errorf("labels without namespace (prefix/) not allowed")
|
||||
}
|
||||
if ns != nfdv1alpha1.FeatureLabelNs && ns != nfdv1alpha1.ProfileLabelNs &&
|
||||
!strings.HasSuffix(ns, nfdv1alpha1.FeatureLabelSubNsSuffix) && !strings.HasSuffix(ns, nfdv1alpha1.ProfileLabelSubNsSuffix) {
|
||||
// If the namespace is denied, and not present in the extraLabelNs, label will be ignored
|
||||
if isNamespaceDenied(ns, m.deniedNs.wildcard, m.deniedNs.normal) {
|
||||
if _, ok := m.config.ExtraLabelNs[ns]; !ok {
|
||||
return "", fmt.Errorf("namespace %q is not allowed", ns)
|
||||
}
|
||||
err := validate.Label(name, filteredValue)
|
||||
if err == validate.ErrNSNotAllowed || isNamespaceDenied(ns, m.deniedNs.wildcard, m.deniedNs.normal) {
|
||||
if _, ok := m.config.ExtraLabelNs[ns]; !ok {
|
||||
return "", fmt.Errorf("namespace %q is not allowed", ns)
|
||||
}
|
||||
} else if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Skip if label doesn't match labelWhiteList
|
||||
|
@ -578,24 +580,7 @@ func (m *nfdMaster) filterFeatureLabel(name, value string, features *nfdv1alpha1
|
|||
return "", fmt.Errorf("%s (%s) does not match the whitelist (%s)", base, name, m.config.LabelWhiteList.Regexp.String())
|
||||
}
|
||||
|
||||
var filteredLabel string
|
||||
|
||||
// Dynamic Value
|
||||
if strings.HasPrefix(value, "@") {
|
||||
dynamicValue, err := getDynamicValue(value, features)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
filteredLabel = dynamicValue
|
||||
} else {
|
||||
filteredLabel = value
|
||||
}
|
||||
|
||||
// Validate the label value
|
||||
if errs := k8svalidation.IsValidLabelValue(filteredLabel); len(errs) > 0 {
|
||||
return "", fmt.Errorf("invalid value %q: %s", filteredLabel, strings.Join(errs, "; "))
|
||||
}
|
||||
return filteredLabel, nil
|
||||
return filteredValue, nil
|
||||
}
|
||||
|
||||
func getDynamicValue(value string, features *nfdv1alpha1.Features) (string, error) {
|
||||
|
@ -621,7 +606,7 @@ func filterTaints(taints []corev1.Taint) []corev1.Taint {
|
|||
outTaints := []corev1.Taint{}
|
||||
|
||||
for _, taint := range taints {
|
||||
if err := filterTaint(&taint); err != nil {
|
||||
if err := validate.Taint(&taint); err != nil {
|
||||
klog.ErrorS(err, "ignoring taint", "taint", taint)
|
||||
nodeTaintsRejected.Inc()
|
||||
} else {
|
||||
|
@ -631,19 +616,6 @@ func filterTaints(taints []corev1.Taint) []corev1.Taint {
|
|||
return outTaints
|
||||
}
|
||||
|
||||
func filterTaint(taint *corev1.Taint) error {
|
||||
// Check prefix of the key, filter out disallowed ones
|
||||
ns, _ := splitNs(taint.Key)
|
||||
if ns == "" {
|
||||
return fmt.Errorf("taint keys without namespace (prefix/) are not allowed")
|
||||
}
|
||||
if ns != nfdv1alpha1.TaintNs && !strings.HasSuffix(ns, nfdv1alpha1.TaintSubNsSuffix) &&
|
||||
(ns == "kubernetes.io" || strings.HasSuffix(ns, ".kubernetes.io")) {
|
||||
return fmt.Errorf("prefix %q is not allowed for taint key", ns)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyNodeName(cert *x509.Certificate, nodeName string) error {
|
||||
if cert.Subject.CommonName == nodeName {
|
||||
return nil
|
||||
|
@ -809,35 +781,25 @@ func (m *nfdMaster) filterExtendedResources(features *nfdv1alpha1.Features, exte
|
|||
}
|
||||
|
||||
func filterExtendedResource(name, value string, features *nfdv1alpha1.Features) (string, error) {
|
||||
// Check if given NS is allowed
|
||||
ns, _ := splitNs(name)
|
||||
if ns == "" {
|
||||
return "", fmt.Errorf("extended resource without namespace (prefix/) not allowed")
|
||||
}
|
||||
if ns != nfdv1alpha1.ExtendedResourceNs && !strings.HasSuffix(ns, nfdv1alpha1.ExtendedResourceSubNsSuffix) {
|
||||
if ns == "kubernetes.io" || strings.HasSuffix(ns, ".kubernetes.io") {
|
||||
return "", fmt.Errorf("namespace %q is not allowed", ns)
|
||||
// Dynamic Value
|
||||
var filteredValue string
|
||||
if strings.HasPrefix(value, "@") {
|
||||
dynamicValue, err := getDynamicValue(value, features)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
filteredValue = dynamicValue
|
||||
} else {
|
||||
filteredValue = value
|
||||
}
|
||||
|
||||
// Dynamic Value
|
||||
if strings.HasPrefix(value, "@") {
|
||||
if element, err := getDynamicValue(value, features); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
q, err := k8sQuantity.ParseQuantity(element)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid value %s (from %s): %w", element, value, err)
|
||||
}
|
||||
return q.String(), nil
|
||||
}
|
||||
}
|
||||
// Static Value (Pre-Defined at the NodeFeatureRule)
|
||||
q, err := k8sQuantity.ParseQuantity(value)
|
||||
// Validate
|
||||
err := validate.ExtendedResource(name, filteredValue)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid value %s: %w", value, err)
|
||||
return "", err
|
||||
}
|
||||
return q.String(), nil
|
||||
|
||||
return filteredValue, nil
|
||||
}
|
||||
|
||||
func (m *nfdMaster) refreshNodeFeatures(cli *kubernetes.Clientset, nodeName string, labels map[string]string, features *nfdv1alpha1.Features) error {
|
||||
|
@ -1331,6 +1293,7 @@ func addNs(src string, nsToAdd string) string {
|
|||
}
|
||||
|
||||
// splitNs splits a name into its namespace and name parts
|
||||
// Ported to Validate
|
||||
func splitNs(fullname string) (string, string) {
|
||||
split := strings.SplitN(fullname, "/", 2)
|
||||
if len(split) == 2 {
|
||||
|
@ -1441,22 +1404,11 @@ func (m *nfdMaster) filterFeatureAnnotations(annotations map[string]string) map[
|
|||
outAnnotations := make(map[string]string)
|
||||
|
||||
for annotation, value := range annotations {
|
||||
ns, _ := splitNs(annotation)
|
||||
if ns == "" {
|
||||
klog.ErrorS(fmt.Errorf("annotations without namespace (prefix/) not allowed"), fmt.Sprintf("Ignoring annotation %s", annotation))
|
||||
continue
|
||||
}
|
||||
// Check annotation namespace, filter out if ns is not whitelisted
|
||||
if ns != nfdv1alpha1.FeatureAnnotationNs && !strings.HasSuffix(ns, nfdv1alpha1.FeatureAnnotationSubNsSuffix) {
|
||||
// If the namespace is denied the annotation will be ignored
|
||||
if ns == "" {
|
||||
klog.ErrorS(fmt.Errorf("labels without namespace (prefix/) not allowed"), fmt.Sprintf("Ignoring annotation %s", annotation))
|
||||
continue
|
||||
}
|
||||
if ns == "kubernetes.io" || strings.HasSuffix(ns, ".kubernetes.io") || ns == nfdv1alpha1.AnnotationNs {
|
||||
klog.ErrorS(fmt.Errorf("namespace %q is not allowed", ns), fmt.Sprintf("Ignoring annotation %s", annotation))
|
||||
continue
|
||||
}
|
||||
err := validate.Annotation(annotation, value)
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "ignoring annotation", "annotationKey", annotation, "annotationValue", value)
|
||||
continue
|
||||
}
|
||||
|
||||
outAnnotations[annotation] = value
|
||||
|
|
|
@ -30,7 +30,5 @@ while true; do
|
|||
i=$(( $i + 1 ))
|
||||
done
|
||||
|
||||
|
||||
# Configure environment and run tests
|
||||
make e2e-test
|
||||
|
||||
|
|
Loading…
Reference in a new issue