mirror of
https://github.com/kubernetes-sigs/node-feature-discovery.git
synced 2024-12-14 11:57:51 +00:00
Merge pull request #653 from marquiz/devel/nodefeaturerule-crd
specify CRD for custom labeling rules
This commit is contained in:
commit
55ea049ed7
28 changed files with 1531 additions and 561 deletions
2
Makefile
2
Makefile
|
@ -102,6 +102,8 @@ generate:
|
|||
go mod vendor
|
||||
go generate ./cmd/... ./pkg/... ./source/...
|
||||
rm -rf vendor/
|
||||
controller-gen object crd output:crd:stdout paths=./pkg/apis/... > deployment/base/nfd-crds/nodefeaturerule-crd.yaml
|
||||
cp deployment/base/nfd-crds/nodefeaturerule-crd.yaml deployment/helm/node-feature-discovery/manifests/
|
||||
|
||||
gofmt:
|
||||
@$(GO_FMT) -w -l $$(find . -name '*.go')
|
||||
|
|
71
deployment/base/nfd-crds/cr-sample.yaml
Normal file
71
deployment/base/nfd-crds/cr-sample.yaml
Normal file
|
@ -0,0 +1,71 @@
|
|||
apiVersion: nfd.k8s-sigs.io/v1alpha1
|
||||
kind: NodeFeatureRule
|
||||
metadata:
|
||||
name: my-rule
|
||||
spec:
|
||||
rules:
|
||||
# The following feature demonstrates the capabilities of the matchFeatures and
|
||||
# matchAny matchers.
|
||||
- name: "my feature rule"
|
||||
labels:
|
||||
"my-complex-feature": "my-value"
|
||||
# matchFeatures implements a logical AND over feature matchers.
|
||||
matchFeatures:
|
||||
- feature: cpu.cpuid
|
||||
matchExpressions:
|
||||
AVX512F: {op: Exists}
|
||||
- feature: cpu.cstate
|
||||
matchExpressions:
|
||||
enabled: {op: IsTrue}
|
||||
- feature: cpu.pstate
|
||||
matchExpressions:
|
||||
no_turbo: {op: IsFalse}
|
||||
scaling_governor: {op: In, value: ["performance"]}
|
||||
- feature: cpu.rdt
|
||||
matchExpressions:
|
||||
RDTL3CA: {op: Exists}
|
||||
- feature: cpu.sst
|
||||
matchExpressions:
|
||||
bf.enabled: {op: IsTrue}
|
||||
- feature: cpu.topology
|
||||
matchExpressions:
|
||||
hardware_multithreading: {op: IsFalse}
|
||||
|
||||
- feature: kernel.config
|
||||
matchExpressions:
|
||||
X86: {op: Exists}
|
||||
LSM: {op: InRegexp, value: ["apparmor"]}
|
||||
- feature: kernel.loadedmodule
|
||||
matchExpressions:
|
||||
e1000e: {op: Exists}
|
||||
- feature: kernel.selinux
|
||||
matchExpressions:
|
||||
enabled: {op: IsFalse}
|
||||
- feature: kernel.version
|
||||
matchExpressions:
|
||||
major: {op: In, value: ["5"]}
|
||||
minor: {op: Gt, value: ["10"]}
|
||||
|
||||
- feature: system.osrelease
|
||||
matchExpressions:
|
||||
ID: {op: In, value: ["fedora", "centos"]}
|
||||
- feature: system.name
|
||||
matchExpressions:
|
||||
nodename: {op: InRegexp, value: ["^worker-X"]}
|
||||
|
||||
- feature: local.label
|
||||
matchExpressions:
|
||||
custom-feature-knob: {op: Gt, value: ["100"]}
|
||||
|
||||
# matchAny implements a logical IF over all listed matchers (i.e. at
|
||||
# least one must match)
|
||||
matchAny:
|
||||
- matchFeatures:
|
||||
- feature: pci.device
|
||||
matchExpressions:
|
||||
vendor: {op: In, value: ["8086"]}
|
||||
class: {op: In, value: ["0200"]}
|
||||
- feature: usb.device
|
||||
matchExpressions:
|
||||
vendor: {op: In, value: ["8086"]}
|
||||
class: {op: In, value: ["02"]}
|
5
deployment/base/nfd-crds/kustomization.yaml
Normal file
5
deployment/base/nfd-crds/kustomization.yaml
Normal file
|
@ -0,0 +1,5 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- nodefeaturerule-crd.yaml
|
208
deployment/base/nfd-crds/nodefeaturerule-crd.yaml
Normal file
208
deployment/base/nfd-crds/nodefeaturerule-crd.yaml
Normal file
|
@ -0,0 +1,208 @@
|
|||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.7.0
|
||||
creationTimestamp: null
|
||||
name: nodefeaturerules.nfd.k8s-sigs.io
|
||||
spec:
|
||||
group: nfd.k8s-sigs.io
|
||||
names:
|
||||
kind: NodeFeatureRule
|
||||
listKind: NodeFeatureRuleList
|
||||
plural: nodefeaturerules
|
||||
singular: nodefeaturerule
|
||||
scope: Cluster
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: NodeFeatureRule resource specifies a configuration for feature-based
|
||||
customization of node objects, such as node labeling.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: NodeFeatureRuleSpec describes a NodeFeatureRule.
|
||||
properties:
|
||||
rules:
|
||||
description: Rules is a list of node customization rules.
|
||||
items:
|
||||
description: Rule defines a rule for node customization such as
|
||||
labeling.
|
||||
properties:
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: Labels to create if the rule matches.
|
||||
type: object
|
||||
labelsTemplate:
|
||||
description: LabelsTemplate specifies a template to expand for
|
||||
dynamically generating multiple labels. Data (after template
|
||||
expansion) must be keys with an optional value (<key>[=<value>])
|
||||
separated by newlines.
|
||||
type: string
|
||||
matchAny:
|
||||
description: MatchAny specifies a list of matchers one of which
|
||||
must match.
|
||||
items:
|
||||
description: MatchAnyElem specifies one sub-matcher of MatchAny.
|
||||
properties:
|
||||
matchFeatures:
|
||||
description: MatchFeatures specifies a set of matcher
|
||||
terms all of which must match.
|
||||
items:
|
||||
description: FeatureMatcherTerm defines requirements
|
||||
against one feature set. All requirements (specified
|
||||
as MatchExpressions) are evaluated against each element
|
||||
in the feature set.
|
||||
properties:
|
||||
feature:
|
||||
type: string
|
||||
matchExpressions:
|
||||
additionalProperties:
|
||||
description: "MatchExpression specifies an expression
|
||||
to evaluate against a set of input values. It
|
||||
contains an operator that is applied when matching
|
||||
the input and an array of values that the operator
|
||||
evaluates the input against. \n NB: CreateMatchExpression
|
||||
or MustCreateMatchExpression() should be used
|
||||
for creating new instances. NB: Validate()
|
||||
must be called if Op or Value fields are modified
|
||||
or if a new instance is created from scratch
|
||||
without using the helper functions."
|
||||
properties:
|
||||
op:
|
||||
description: Op is the operator to be applied.
|
||||
enum:
|
||||
- In
|
||||
- NotIn
|
||||
- InRegexp
|
||||
- Exists
|
||||
- DoesNotExist
|
||||
- Gt
|
||||
- Lt
|
||||
- GtLt
|
||||
- IsTrue
|
||||
- IsFalse
|
||||
type: string
|
||||
value:
|
||||
description: Value is the list of values that
|
||||
the operand evaluates the input against.
|
||||
Value should be empty if the operator is
|
||||
Exists, DoesNotExist, IsTrue or IsFalse.
|
||||
Value should contain exactly one element
|
||||
if the operator is Gt or Lt and exactly
|
||||
two elements if the operator is GtLt. In
|
||||
other cases Value should contain at least
|
||||
one element.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- op
|
||||
type: object
|
||||
description: MatchExpressionSet contains a set of
|
||||
MatchExpressions, each of which is evaluated against
|
||||
a set of input values.
|
||||
type: object
|
||||
required:
|
||||
- feature
|
||||
- matchExpressions
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- matchFeatures
|
||||
type: object
|
||||
type: array
|
||||
matchFeatures:
|
||||
description: MatchFeatures specifies a set of matcher terms
|
||||
all of which must match.
|
||||
items:
|
||||
description: FeatureMatcherTerm defines requirements against
|
||||
one feature set. All requirements (specified as MatchExpressions)
|
||||
are evaluated against each element in the feature set.
|
||||
properties:
|
||||
feature:
|
||||
type: string
|
||||
matchExpressions:
|
||||
additionalProperties:
|
||||
description: "MatchExpression specifies an expression
|
||||
to evaluate against a set of input values. It contains
|
||||
an operator that is applied when matching the input
|
||||
and an array of values that the operator evaluates
|
||||
the input against. \n NB: CreateMatchExpression or
|
||||
MustCreateMatchExpression() should be used for creating
|
||||
new instances. NB: Validate() must be called if Op
|
||||
or Value fields are modified or if a new instance
|
||||
is created from scratch without using the helper functions."
|
||||
properties:
|
||||
op:
|
||||
description: Op is the operator to be applied.
|
||||
enum:
|
||||
- In
|
||||
- NotIn
|
||||
- InRegexp
|
||||
- Exists
|
||||
- DoesNotExist
|
||||
- Gt
|
||||
- Lt
|
||||
- GtLt
|
||||
- IsTrue
|
||||
- IsFalse
|
||||
type: string
|
||||
value:
|
||||
description: Value is the list of values that the
|
||||
operand evaluates the input against. Value should
|
||||
be empty if the operator is Exists, DoesNotExist,
|
||||
IsTrue or IsFalse. Value should contain exactly
|
||||
one element if the operator is Gt or Lt and exactly
|
||||
two elements if the operator is GtLt. In other
|
||||
cases Value should contain at least one element.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- op
|
||||
type: object
|
||||
description: MatchExpressionSet contains a set of MatchExpressions,
|
||||
each of which is evaluated against a set of input values.
|
||||
type: object
|
||||
required:
|
||||
- feature
|
||||
- matchExpressions
|
||||
type: object
|
||||
type: array
|
||||
name:
|
||||
description: Name of the rule.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- rules
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
|
@ -0,0 +1,208 @@
|
|||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.7.0
|
||||
creationTimestamp: null
|
||||
name: nodefeaturerules.nfd.k8s-sigs.io
|
||||
spec:
|
||||
group: nfd.k8s-sigs.io
|
||||
names:
|
||||
kind: NodeFeatureRule
|
||||
listKind: NodeFeatureRuleList
|
||||
plural: nodefeaturerules
|
||||
singular: nodefeaturerule
|
||||
scope: Cluster
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: NodeFeatureRule resource specifies a configuration for feature-based
|
||||
customization of node objects, such as node labeling.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: NodeFeatureRuleSpec describes a NodeFeatureRule.
|
||||
properties:
|
||||
rules:
|
||||
description: Rules is a list of node customization rules.
|
||||
items:
|
||||
description: Rule defines a rule for node customization such as
|
||||
labeling.
|
||||
properties:
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: Labels to create if the rule matches.
|
||||
type: object
|
||||
labelsTemplate:
|
||||
description: LabelsTemplate specifies a template to expand for
|
||||
dynamically generating multiple labels. Data (after template
|
||||
expansion) must be keys with an optional value (<key>[=<value>])
|
||||
separated by newlines.
|
||||
type: string
|
||||
matchAny:
|
||||
description: MatchAny specifies a list of matchers one of which
|
||||
must match.
|
||||
items:
|
||||
description: MatchAnyElem specifies one sub-matcher of MatchAny.
|
||||
properties:
|
||||
matchFeatures:
|
||||
description: MatchFeatures specifies a set of matcher
|
||||
terms all of which must match.
|
||||
items:
|
||||
description: FeatureMatcherTerm defines requirements
|
||||
against one feature set. All requirements (specified
|
||||
as MatchExpressions) are evaluated against each element
|
||||
in the feature set.
|
||||
properties:
|
||||
feature:
|
||||
type: string
|
||||
matchExpressions:
|
||||
additionalProperties:
|
||||
description: "MatchExpression specifies an expression
|
||||
to evaluate against a set of input values. It
|
||||
contains an operator that is applied when matching
|
||||
the input and an array of values that the operator
|
||||
evaluates the input against. \n NB: CreateMatchExpression
|
||||
or MustCreateMatchExpression() should be used
|
||||
for creating new instances. NB: Validate()
|
||||
must be called if Op or Value fields are modified
|
||||
or if a new instance is created from scratch
|
||||
without using the helper functions."
|
||||
properties:
|
||||
op:
|
||||
description: Op is the operator to be applied.
|
||||
enum:
|
||||
- In
|
||||
- NotIn
|
||||
- InRegexp
|
||||
- Exists
|
||||
- DoesNotExist
|
||||
- Gt
|
||||
- Lt
|
||||
- GtLt
|
||||
- IsTrue
|
||||
- IsFalse
|
||||
type: string
|
||||
value:
|
||||
description: Value is the list of values that
|
||||
the operand evaluates the input against.
|
||||
Value should be empty if the operator is
|
||||
Exists, DoesNotExist, IsTrue or IsFalse.
|
||||
Value should contain exactly one element
|
||||
if the operator is Gt or Lt and exactly
|
||||
two elements if the operator is GtLt. In
|
||||
other cases Value should contain at least
|
||||
one element.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- op
|
||||
type: object
|
||||
description: MatchExpressionSet contains a set of
|
||||
MatchExpressions, each of which is evaluated against
|
||||
a set of input values.
|
||||
type: object
|
||||
required:
|
||||
- feature
|
||||
- matchExpressions
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- matchFeatures
|
||||
type: object
|
||||
type: array
|
||||
matchFeatures:
|
||||
description: MatchFeatures specifies a set of matcher terms
|
||||
all of which must match.
|
||||
items:
|
||||
description: FeatureMatcherTerm defines requirements against
|
||||
one feature set. All requirements (specified as MatchExpressions)
|
||||
are evaluated against each element in the feature set.
|
||||
properties:
|
||||
feature:
|
||||
type: string
|
||||
matchExpressions:
|
||||
additionalProperties:
|
||||
description: "MatchExpression specifies an expression
|
||||
to evaluate against a set of input values. It contains
|
||||
an operator that is applied when matching the input
|
||||
and an array of values that the operator evaluates
|
||||
the input against. \n NB: CreateMatchExpression or
|
||||
MustCreateMatchExpression() should be used for creating
|
||||
new instances. NB: Validate() must be called if Op
|
||||
or Value fields are modified or if a new instance
|
||||
is created from scratch without using the helper functions."
|
||||
properties:
|
||||
op:
|
||||
description: Op is the operator to be applied.
|
||||
enum:
|
||||
- In
|
||||
- NotIn
|
||||
- InRegexp
|
||||
- Exists
|
||||
- DoesNotExist
|
||||
- Gt
|
||||
- Lt
|
||||
- GtLt
|
||||
- IsTrue
|
||||
- IsFalse
|
||||
type: string
|
||||
value:
|
||||
description: Value is the list of values that the
|
||||
operand evaluates the input against. Value should
|
||||
be empty if the operator is Exists, DoesNotExist,
|
||||
IsTrue or IsFalse. Value should contain exactly
|
||||
one element if the operator is Gt or Lt and exactly
|
||||
two elements if the operator is GtLt. In other
|
||||
cases Value should contain at least one element.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- op
|
||||
type: object
|
||||
description: MatchExpressionSet contains a set of MatchExpressions,
|
||||
each of which is evaluated against a set of input values.
|
||||
type: object
|
||||
required:
|
||||
- feature
|
||||
- matchExpressions
|
||||
type: object
|
||||
type: array
|
||||
name:
|
||||
description: Name of the rule.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- rules
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
|
@ -0,0 +1,3 @@
|
|||
{{- if .Values.nodeFeatureRule.createCRD }}
|
||||
{{ .Files.Get "manifests/nodefeaturerule-crd.yaml" }}
|
||||
{{- end}}
|
|
@ -18,6 +18,9 @@ serviceAccount:
|
|||
nameOverride: ""
|
||||
fullnameOverride: ""
|
||||
|
||||
nodeFeatureRule:
|
||||
createCRD: true
|
||||
|
||||
master:
|
||||
instance:
|
||||
extraLabelNs: []
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace: node-feature-discovery
|
|||
|
||||
bases:
|
||||
- ../../base/rbac
|
||||
- ../../base/nfd-crds
|
||||
- ../../base/master-worker-combined
|
||||
|
||||
resources:
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace: node-feature-discovery
|
|||
|
||||
bases:
|
||||
- ../../base/rbac
|
||||
- ../../base/nfd-crds
|
||||
- ../../base/master
|
||||
- ../../base/worker-job
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace: node-feature-discovery
|
|||
|
||||
bases:
|
||||
- ../../base/rbac
|
||||
- ../../base/nfd-crds
|
||||
- ../../base/master
|
||||
- ../../base/worker-daemonset
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ namespace: node-feature-discovery
|
|||
bases:
|
||||
- ../../base/rbac
|
||||
- ../../base/rbac-topologyupdater
|
||||
- ../../base/nfd-crds
|
||||
- ../../base/master
|
||||
- ../../base/worker-daemonset
|
||||
- ../../base/noderesourcetopologies-crd
|
||||
|
|
|
@ -290,6 +290,7 @@ We have introduced the following Chart parameters.
|
|||
| `rbac` | dict | | RBAC [parameteres](https://kubernetes.io/docs/reference/access-authn-authz/rbac/) |
|
||||
| `nameOverride` | string | | Override the name of the chart |
|
||||
| `fullnameOverride` | string | | Override a default fully qualified app name |
|
||||
| `nodeFeatureRule.createCRD` | bool | true | Specifies whether to create the NodeFeatureRule CRD |
|
||||
|
||||
##### Master pod parameters
|
||||
|
||||
|
|
1
go.mod
1
go.mod
|
@ -87,7 +87,6 @@ require (
|
|||
github.com/go-ozzo/ozzo-validation v3.5.0+incompatible // indirect
|
||||
github.com/godbus/dbus/v5 v5.0.4 // indirect
|
||||
github.com/gofrs/uuid v4.0.0+incompatible // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/mock v1.4.4 // indirect
|
||||
github.com/google/cadvisor v0.39.2 // indirect
|
||||
|
|
20
pkg/apis/nfd/v1alpha1/doc.go
Normal file
20
pkg/apis/nfd/v1alpha1/doc.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
Copyright 2021 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.
|
||||
*/
|
||||
|
||||
// +k8s:deepcopy-gen=package
|
||||
// +kubebuilder:object:generate=true
|
||||
// +groupName=nfd.k8s-sigs.io
|
||||
package v1alpha1
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package expression
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -29,80 +29,6 @@ import (
|
|||
"sigs.k8s.io/node-feature-discovery/pkg/api/feature"
|
||||
)
|
||||
|
||||
// MatchExpressionSet contains a set of MatchExpressions, each of which is
|
||||
// evaluated against a set of input values.
|
||||
type MatchExpressionSet map[string]*MatchExpression
|
||||
|
||||
// MatchExpression specifies an expression to evaluate against a set of input
|
||||
// values. It contains an operator that is applied when matching the input and
|
||||
// an array of values that the operator evaluates the input against.
|
||||
// NB: CreateMatchExpression or MustCreateMatchExpression() should be used for
|
||||
// creating new instances.
|
||||
// NB: Validate() must be called if Op or Value fields are modified or if a new
|
||||
// instance is created from scratch without using the helper functions.
|
||||
type MatchExpression struct {
|
||||
// Op is the operator to be applied.
|
||||
Op MatchOp
|
||||
|
||||
// Value is the list of values that the operand evaluates the input
|
||||
// against. Value should be empty if the operator is Exists, DoesNotExist,
|
||||
// IsTrue or IsFalse. Value should contain exactly one element if the
|
||||
// operator is Gt or Lt and exactly two elements if the operator is GtLt.
|
||||
// In other cases Value should contain at least one element.
|
||||
Value MatchValue `json:",omitempty"`
|
||||
|
||||
// valueRe caches compiled regexps for "InRegexp" operator
|
||||
valueRe []*regexp.Regexp
|
||||
}
|
||||
|
||||
// MatchOp is the match operator that is applied on values when evaluating a
|
||||
// MatchExpression.
|
||||
type MatchOp string
|
||||
|
||||
// MatchValue is the list of values associated with a MatchExpression.
|
||||
type MatchValue []string
|
||||
|
||||
const (
|
||||
// MatchAny returns always true.
|
||||
MatchAny MatchOp = ""
|
||||
// MatchIn returns true if any of the values stored in the expression is
|
||||
// equal to the input.
|
||||
MatchIn MatchOp = "In"
|
||||
// MatchIn returns true if none of the values in the expression are equal
|
||||
// to the input.
|
||||
MatchNotIn MatchOp = "NotIn"
|
||||
// MatchInRegexp treats values of the expression as regular expressions and
|
||||
// returns true if any of them matches the input.
|
||||
MatchInRegexp MatchOp = "InRegexp"
|
||||
// MatchExists returns true if the input is valid. The expression must not
|
||||
// have any values.
|
||||
MatchExists MatchOp = "Exists"
|
||||
// MatchDoesNotExist returns true if the input is not valid. The expression
|
||||
// must not have any values.
|
||||
MatchDoesNotExist MatchOp = "DoesNotExist"
|
||||
// MatchGt returns true if the input is greater than the value of the
|
||||
// expression (number of values in the expression must be exactly one).
|
||||
// Both the input and value must be integer numbers, otherwise an error is
|
||||
// returned.
|
||||
MatchGt MatchOp = "Gt"
|
||||
// MatchLt returns true if the input is less than the value of the
|
||||
// expression (number of values in the expression must be exactly one).
|
||||
// Both the input and value must be integer numbers, otherwise an error is
|
||||
// returned.
|
||||
MatchLt MatchOp = "Lt"
|
||||
// MatchGtLt returns true if the input is between two values, i.e. greater
|
||||
// than the first value and less than the second value of the expression
|
||||
// (number of values in the expression must be exactly two). Both the input
|
||||
// and values must be integer numbers, otherwise an error is returned.
|
||||
MatchGtLt MatchOp = "GtLt"
|
||||
// MatchIsTrue returns true if the input holds the value "true". The
|
||||
// expression must not have any values.
|
||||
MatchIsTrue MatchOp = "IsTrue"
|
||||
// MatchIsTrue returns true if the input holds the value "false". The
|
||||
// expression must not have any values.
|
||||
MatchIsFalse MatchOp = "IsFalse"
|
||||
)
|
||||
|
||||
var matchOps = map[MatchOp]struct{}{
|
||||
MatchAny: struct{}{},
|
||||
MatchIn: struct{}{},
|
||||
|
@ -117,6 +43,18 @@ var matchOps = map[MatchOp]struct{}{
|
|||
MatchIsFalse: struct{}{},
|
||||
}
|
||||
|
||||
type valueRegexpCache []*regexp.Regexp
|
||||
|
||||
// NewMatchExpressionSet returns a new MatchExpressionSet instance.
|
||||
func NewMatchExpressionSet() *MatchExpressionSet {
|
||||
return &MatchExpressionSet{Expressions: make(Expressions)}
|
||||
}
|
||||
|
||||
// Len returns the number of expressions.
|
||||
func (e *Expressions) Len() int {
|
||||
return len(*e)
|
||||
}
|
||||
|
||||
// CreateMatchExpression creates a new MatchExpression instance. Returns an
|
||||
// error if validation fails.
|
||||
func CreateMatchExpression(op MatchOp, values ...string) (*MatchExpression, error) {
|
||||
|
@ -376,7 +314,7 @@ func (m *MatchExpression) UnmarshalJSON(data []byte) error {
|
|||
|
||||
// MatchKeys evaluates the MatchExpressionSet against a set of keys.
|
||||
func (m *MatchExpressionSet) MatchKeys(keys map[string]feature.Nil) (bool, error) {
|
||||
for n, e := range *m {
|
||||
for n, e := range (*m).Expressions {
|
||||
match, err := e.MatchKeys(n, keys)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
@ -390,7 +328,7 @@ func (m *MatchExpressionSet) MatchKeys(keys map[string]feature.Nil) (bool, error
|
|||
|
||||
// MatchValues evaluates the MatchExpressionSet against a set of key-value pairs.
|
||||
func (m *MatchExpressionSet) MatchValues(values map[string]string) (bool, error) {
|
||||
for n, e := range *m {
|
||||
for n, e := range (*m).Expressions {
|
||||
match, err := e.MatchValues(n, values)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
@ -418,7 +356,7 @@ func (m *MatchExpressionSet) MatchInstances(instances []feature.InstanceFeature)
|
|||
|
||||
// UnmarshalJSON implements the Unmarshaler interface of "encoding/json".
|
||||
func (m *MatchExpressionSet) UnmarshalJSON(data []byte) error {
|
||||
*m = make(MatchExpressionSet)
|
||||
*m = *NewMatchExpressionSet()
|
||||
|
||||
names := make([]string, 0)
|
||||
if err := json.Unmarshal(data, &names); err == nil {
|
||||
|
@ -426,9 +364,9 @@ func (m *MatchExpressionSet) UnmarshalJSON(data []byte) error {
|
|||
for _, name := range names {
|
||||
split := strings.SplitN(name, "=", 2)
|
||||
if len(split) == 1 {
|
||||
(*m)[split[0]] = newMatchExpression(MatchExists)
|
||||
(*m).Expressions[split[0]] = newMatchExpression(MatchExists)
|
||||
} else {
|
||||
(*m)[split[0]] = newMatchExpression(MatchIn, split[1])
|
||||
(*m).Expressions[split[0]] = newMatchExpression(MatchIn, split[1])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -439,9 +377,9 @@ func (m *MatchExpressionSet) UnmarshalJSON(data []byte) error {
|
|||
} else {
|
||||
for k, v := range expressions {
|
||||
if v != nil {
|
||||
(*m)[k] = v
|
||||
(*m).Expressions[k] = v
|
||||
} else {
|
||||
(*m)[k] = newMatchExpression(MatchExists)
|
||||
(*m).Expressions[k] = newMatchExpression(MatchExists)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -496,3 +434,22 @@ func (m *MatchValue) UnmarshalJSON(data []byte) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopy supplements the auto-generated code
|
||||
func (in *valueRegexpCache) DeepCopy() *valueRegexpCache {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(valueRegexpCache)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is a stub to augment the auto-generated code
|
||||
//nolint:staticcheck // re.Copy is deprecated but we want to use it here
|
||||
func (in *valueRegexpCache) DeepCopyInto(out *valueRegexpCache) {
|
||||
*out = make(valueRegexpCache, len(*in))
|
||||
for i, re := range *in {
|
||||
(*out)[i] = re.Copy()
|
||||
}
|
||||
}
|
439
pkg/apis/nfd/v1alpha1/expression_test.go
Normal file
439
pkg/apis/nfd/v1alpha1/expression_test.go
Normal file
|
@ -0,0 +1,439 @@
|
|||
/*
|
||||
Copyright 2021 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 v1alpha1_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"sigs.k8s.io/node-feature-discovery/pkg/api/feature"
|
||||
api "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
|
||||
)
|
||||
|
||||
type BoolAssertionFuncf func(assert.TestingT, bool, string, ...interface{}) bool
|
||||
|
||||
type ValueAssertionFuncf func(assert.TestingT, interface{}, string, ...interface{}) bool
|
||||
|
||||
func TestCreateMatchExpression(t *testing.T) {
|
||||
type V = api.MatchValue
|
||||
type TC struct {
|
||||
op api.MatchOp
|
||||
values V
|
||||
err ValueAssertionFuncf
|
||||
}
|
||||
|
||||
tcs := []TC{
|
||||
{op: api.MatchAny, err: assert.Nilf}, // #0
|
||||
{op: api.MatchAny, values: V{"1"}, err: assert.NotNilf},
|
||||
|
||||
{op: api.MatchIn, err: assert.NotNilf},
|
||||
{op: api.MatchIn, values: V{"1"}, err: assert.Nilf},
|
||||
{op: api.MatchIn, values: V{"1", "2", "3", "4"}, err: assert.Nilf},
|
||||
|
||||
{op: api.MatchNotIn, err: assert.NotNilf},
|
||||
{op: api.MatchNotIn, values: V{"1"}, err: assert.Nilf},
|
||||
{op: api.MatchNotIn, values: V{"1", "2"}, err: assert.Nilf},
|
||||
|
||||
{op: api.MatchInRegexp, err: assert.NotNilf},
|
||||
{op: api.MatchInRegexp, values: V{"1"}, err: assert.Nilf},
|
||||
{op: api.MatchInRegexp, values: V{"()", "2", "3"}, err: assert.Nilf},
|
||||
{op: api.MatchInRegexp, values: V{"("}, err: assert.NotNilf},
|
||||
|
||||
{op: api.MatchExists, err: assert.Nilf},
|
||||
{op: api.MatchExists, values: V{"1"}, err: assert.NotNilf},
|
||||
|
||||
{op: api.MatchDoesNotExist, err: assert.Nilf},
|
||||
{op: api.MatchDoesNotExist, values: V{"1"}, err: assert.NotNilf},
|
||||
|
||||
{op: api.MatchGt, err: assert.NotNilf},
|
||||
{op: api.MatchGt, values: V{"1"}, err: assert.Nilf},
|
||||
{op: api.MatchGt, values: V{"-10"}, err: assert.Nilf},
|
||||
{op: api.MatchGt, values: V{"1", "2"}, err: assert.NotNilf},
|
||||
{op: api.MatchGt, values: V{""}, err: assert.NotNilf},
|
||||
|
||||
{op: api.MatchLt, err: assert.NotNilf},
|
||||
{op: api.MatchLt, values: V{"1"}, err: assert.Nilf},
|
||||
{op: api.MatchLt, values: V{"-1"}, err: assert.Nilf},
|
||||
{op: api.MatchLt, values: V{"1", "2", "3"}, err: assert.NotNilf},
|
||||
{op: api.MatchLt, values: V{"a"}, err: assert.NotNilf},
|
||||
|
||||
{op: api.MatchGtLt, err: assert.NotNilf},
|
||||
{op: api.MatchGtLt, values: V{"1"}, err: assert.NotNilf},
|
||||
{op: api.MatchGtLt, values: V{"1", "2"}, err: assert.Nilf},
|
||||
{op: api.MatchGtLt, values: V{"2", "1"}, err: assert.NotNilf},
|
||||
{op: api.MatchGtLt, values: V{"1", "2", "3"}, err: assert.NotNilf},
|
||||
{op: api.MatchGtLt, values: V{"a", "2"}, err: assert.NotNilf},
|
||||
|
||||
{op: api.MatchIsTrue, err: assert.Nilf},
|
||||
{op: api.MatchIsTrue, values: V{"1"}, err: assert.NotNilf},
|
||||
|
||||
{op: api.MatchIsFalse, err: assert.Nilf},
|
||||
{op: api.MatchIsFalse, values: V{"1", "2"}, err: assert.NotNilf},
|
||||
}
|
||||
|
||||
for i, tc := range tcs {
|
||||
_, err := api.CreateMatchExpression(tc.op, tc.values...)
|
||||
tc.err(t, err, "test case #%d (%v) failed", i, tc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatch(t *testing.T) {
|
||||
type V = api.MatchValue
|
||||
type TC struct {
|
||||
op api.MatchOp
|
||||
values V
|
||||
input interface{}
|
||||
valid bool
|
||||
result BoolAssertionFuncf
|
||||
err ValueAssertionFuncf
|
||||
}
|
||||
|
||||
tcs := []TC{
|
||||
{op: api.MatchAny, result: assert.Truef, err: assert.Nilf},
|
||||
{op: api.MatchAny, input: "2", valid: false, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{op: api.MatchIn, values: V{"1"}, input: "2", valid: false, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchIn, values: V{"1"}, input: "2", valid: true, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchIn, values: V{"1", "2", "3"}, input: "2", valid: false, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchIn, values: V{"1", "2", "3"}, input: "2", valid: true, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{op: api.MatchNotIn, values: V{"2"}, input: 2, valid: false, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchNotIn, values: V{"1"}, input: 2, valid: true, result: assert.Truef, err: assert.Nilf},
|
||||
{op: api.MatchNotIn, values: V{"1", "2", "3"}, input: "2", valid: false, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchNotIn, values: V{"1", "2", "3"}, input: "2", valid: true, result: assert.Falsef, err: assert.Nilf},
|
||||
|
||||
{op: api.MatchInRegexp, values: V{"val-[0-9]$"}, input: "val-1", valid: false, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchInRegexp, values: V{"val-[0-9]$"}, input: "val-1", valid: true, result: assert.Truef, err: assert.Nilf},
|
||||
{op: api.MatchInRegexp, values: V{"val-[0-9]$"}, input: "val-12", valid: true, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchInRegexp, values: V{"val-[0-9]$", "al-[1-9]"}, input: "val-12", valid: true, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{op: api.MatchExists, input: nil, valid: false, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchExists, input: nil, valid: true, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{op: api.MatchDoesNotExist, input: false, valid: false, result: assert.Truef, err: assert.Nilf},
|
||||
{op: api.MatchDoesNotExist, input: false, valid: true, result: assert.Falsef, err: assert.Nilf},
|
||||
|
||||
{op: api.MatchGt, values: V{"2"}, input: 3, valid: false, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchGt, values: V{"2"}, input: 2, valid: true, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchGt, values: V{"2"}, input: 3, valid: true, result: assert.Truef, err: assert.Nilf},
|
||||
{op: api.MatchGt, values: V{"-10"}, input: -3, valid: true, result: assert.Truef, err: assert.Nilf},
|
||||
{op: api.MatchGt, values: V{"2"}, input: "3a", valid: true, result: assert.Falsef, err: assert.NotNilf},
|
||||
|
||||
{op: api.MatchLt, values: V{"2"}, input: "1", valid: false, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchLt, values: V{"2"}, input: "2", valid: true, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchLt, values: V{"-10"}, input: -3, valid: true, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchLt, values: V{"2"}, input: "1", valid: true, result: assert.Truef, err: assert.Nilf},
|
||||
{op: api.MatchLt, values: V{"2"}, input: "1.0", valid: true, result: assert.Falsef, err: assert.NotNilf},
|
||||
|
||||
{op: api.MatchGtLt, values: V{"1", "10"}, input: "1", valid: false, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchGtLt, values: V{"1", "10"}, input: "1", valid: true, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchGtLt, values: V{"1", "10"}, input: "10", valid: true, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchGtLt, values: V{"1", "10"}, input: "2", valid: true, result: assert.Truef, err: assert.Nilf},
|
||||
{op: api.MatchGtLt, values: V{"1", "10"}, input: "1.0", valid: true, result: assert.Falsef, err: assert.NotNilf},
|
||||
|
||||
{op: api.MatchIsTrue, input: true, valid: false, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchIsTrue, input: true, valid: true, result: assert.Truef, err: assert.Nilf},
|
||||
{op: api.MatchIsTrue, input: false, valid: true, result: assert.Falsef, err: assert.Nilf},
|
||||
|
||||
{op: api.MatchIsFalse, input: "false", valid: false, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchIsFalse, input: "false", valid: true, result: assert.Truef, err: assert.Nilf},
|
||||
{op: api.MatchIsFalse, input: "true", valid: true, result: assert.Falsef, err: assert.Nilf},
|
||||
}
|
||||
|
||||
for i, tc := range tcs {
|
||||
me := api.MustCreateMatchExpression(tc.op, tc.values...)
|
||||
res, err := me.Match(tc.valid, tc.input)
|
||||
tc.result(t, res, "test case #%d (%v) failed", i, tc)
|
||||
tc.err(t, err, "test case #%d (%v) failed", i, tc)
|
||||
}
|
||||
|
||||
// Check some special error cases separately because MustCreateMatch panics
|
||||
tcs = []TC{
|
||||
|
||||
{op: api.MatchGt, values: V{"3.0"}, input: 1, valid: true},
|
||||
{op: api.MatchLt, values: V{"0x2"}, input: 1, valid: true},
|
||||
{op: api.MatchGtLt, values: V{"1", "str"}, input: 1, valid: true},
|
||||
{op: "non-existent-op", values: V{"1"}, input: 1, valid: true},
|
||||
}
|
||||
|
||||
for i, tc := range tcs {
|
||||
me := api.MatchExpression{Op: tc.op, Value: tc.values}
|
||||
res, err := me.Match(tc.valid, tc.input)
|
||||
assert.Falsef(t, res, "err test case #%d (%v) failed", i, tc)
|
||||
assert.NotNilf(t, err, "err test case #%d (%v) failed", i, tc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchKeys(t *testing.T) {
|
||||
type V = api.MatchValue
|
||||
type I = map[string]feature.Nil
|
||||
type TC struct {
|
||||
op api.MatchOp
|
||||
values V
|
||||
name string
|
||||
input I
|
||||
result BoolAssertionFuncf
|
||||
err ValueAssertionFuncf
|
||||
}
|
||||
|
||||
tcs := []TC{
|
||||
{op: api.MatchAny, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{op: api.MatchExists, name: "foo", input: nil, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchExists, name: "foo", input: I{"bar": {}}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchExists, name: "foo", input: I{"bar": {}, "foo": {}}, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{op: api.MatchDoesNotExist, name: "foo", input: nil, result: assert.Truef, err: assert.Nilf},
|
||||
{op: api.MatchDoesNotExist, name: "foo", input: I{}, result: assert.Truef, err: assert.Nilf},
|
||||
{op: api.MatchDoesNotExist, name: "foo", input: I{"bar": {}}, result: assert.Truef, err: assert.Nilf},
|
||||
{op: api.MatchDoesNotExist, name: "foo", input: I{"bar": {}, "foo": {}}, result: assert.Falsef, err: assert.Nilf},
|
||||
|
||||
// All other ops should return an error
|
||||
{op: api.MatchIn, values: V{"foo"}, name: "foo", result: assert.Falsef, err: assert.NotNilf},
|
||||
{op: api.MatchNotIn, values: V{"foo"}, name: "foo", result: assert.Falsef, err: assert.NotNilf},
|
||||
{op: api.MatchInRegexp, values: V{"foo"}, name: "foo", result: assert.Falsef, err: assert.NotNilf},
|
||||
{op: api.MatchGt, values: V{"1"}, name: "foo", result: assert.Falsef, err: assert.NotNilf},
|
||||
{op: api.MatchLt, values: V{"1"}, name: "foo", result: assert.Falsef, err: assert.NotNilf},
|
||||
{op: api.MatchGtLt, values: V{"1", "10"}, name: "foo", result: assert.Falsef, err: assert.NotNilf},
|
||||
{op: api.MatchIsTrue, name: "foo", result: assert.Falsef, err: assert.NotNilf},
|
||||
{op: api.MatchIsFalse, name: "foo", result: assert.Falsef, err: assert.NotNilf},
|
||||
}
|
||||
|
||||
for i, tc := range tcs {
|
||||
me := api.MustCreateMatchExpression(tc.op, tc.values...)
|
||||
res, err := me.MatchKeys(tc.name, tc.input)
|
||||
tc.result(t, res, "test case #%d (%v) failed", i, tc)
|
||||
tc.err(t, err, "test case #%d (%v) failed", i, tc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchValues(t *testing.T) {
|
||||
type V = []string
|
||||
type I = map[string]string
|
||||
|
||||
type TC struct {
|
||||
op api.MatchOp
|
||||
values V
|
||||
name string
|
||||
input I
|
||||
result BoolAssertionFuncf
|
||||
err ValueAssertionFuncf
|
||||
}
|
||||
|
||||
tcs := []TC{
|
||||
{op: api.MatchAny, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{op: api.MatchIn, values: V{"1", "2"}, name: "foo", input: I{"bar": "2"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchIn, values: V{"1", "2"}, name: "foo", input: I{"foo": "3"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchIn, values: V{"1", "2"}, name: "foo", input: I{"foo": "2"}, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{op: api.MatchNotIn, values: V{"1", "2"}, name: "foo", input: I{"bar": "2"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchNotIn, values: V{"1", "2"}, name: "foo", input: I{"foo": "3"}, result: assert.Truef, err: assert.Nilf},
|
||||
{op: api.MatchNotIn, values: V{"1", "2"}, name: "foo", input: I{"foo": "2"}, result: assert.Falsef, err: assert.Nilf},
|
||||
|
||||
{op: api.MatchInRegexp, values: V{"1", "2"}, name: "foo", input: I{"bar": "2"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchInRegexp, values: V{"1", "[0-8]"}, name: "foo", input: I{"foo": "9"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchInRegexp, values: V{"1", "[0-8]"}, name: "foo", input: I{"foo": "2"}, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{op: api.MatchExists, name: "foo", input: I{"bar": "1"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchExists, name: "foo", input: I{"foo": "1"}, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{op: api.MatchDoesNotExist, name: "foo", input: nil, result: assert.Truef, err: assert.Nilf},
|
||||
{op: api.MatchDoesNotExist, name: "foo", input: I{"foo": "1"}, result: assert.Falsef, err: assert.Nilf},
|
||||
|
||||
{op: api.MatchGt, values: V{"2"}, name: "foo", input: I{"bar": "3"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchGt, values: V{"2"}, name: "foo", input: I{"bar": "3", "foo": "2"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchGt, values: V{"2"}, name: "foo", input: I{"bar": "3", "foo": "3"}, result: assert.Truef, err: assert.Nilf},
|
||||
{op: api.MatchGt, values: V{"2"}, name: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.Falsef, err: assert.NotNilf},
|
||||
|
||||
{op: api.MatchLt, values: V{"2"}, name: "foo", input: I{"bar": "1"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchLt, values: V{"2"}, name: "foo", input: I{"bar": "1", "foo": "2"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchLt, values: V{"2"}, name: "foo", input: I{"bar": "1", "foo": "1"}, result: assert.Truef, err: assert.Nilf},
|
||||
{op: api.MatchLt, values: V{"2"}, name: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.Falsef, err: assert.NotNilf},
|
||||
|
||||
{op: api.MatchGtLt, values: V{"-10", "10"}, name: "foo", input: I{"bar": "1"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchGtLt, values: V{"-10", "10"}, name: "foo", input: I{"bar": "1", "foo": "11"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchGtLt, values: V{"-10", "10"}, name: "foo", input: I{"bar": "1", "foo": "-11"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchGtLt, values: V{"-10", "10"}, name: "foo", input: I{"bar": "1", "foo": "1"}, result: assert.Truef, err: assert.Nilf},
|
||||
{op: api.MatchGtLt, values: V{"-10", "10"}, name: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.Falsef, err: assert.NotNilf},
|
||||
|
||||
{op: api.MatchIsTrue, name: "foo", result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchIsTrue, name: "foo", input: I{"foo": "1"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchIsTrue, name: "foo", input: I{"foo": "true"}, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{op: api.MatchIsFalse, name: "foo", input: I{"foo": "true"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: api.MatchIsFalse, name: "foo", input: I{"foo": "false"}, result: assert.Truef, err: assert.Nilf},
|
||||
}
|
||||
|
||||
for i, tc := range tcs {
|
||||
me := api.MustCreateMatchExpression(tc.op, tc.values...)
|
||||
res, err := me.MatchValues(tc.name, tc.input)
|
||||
tc.result(t, res, "test case #%d (%v) failed", i, tc)
|
||||
tc.err(t, err, "test case #%d (%v) failed", i, tc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMESMatchKeys(t *testing.T) {
|
||||
type I = map[string]feature.Nil
|
||||
type TC struct {
|
||||
mes string
|
||||
input I
|
||||
result BoolAssertionFuncf
|
||||
err ValueAssertionFuncf
|
||||
}
|
||||
|
||||
tcs := []TC{
|
||||
{result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{input: I{"foo": {}}, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{mes: `
|
||||
foo: { op: DoesNotExist }
|
||||
bar: { op: Exists }
|
||||
`,
|
||||
input: I{"bar": {}, "baz": {}},
|
||||
result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{mes: `
|
||||
foo: { op: DoesNotExist }
|
||||
bar: { op: Exists }
|
||||
`,
|
||||
input: I{"foo": {}, "bar": {}, "baz": {}},
|
||||
result: assert.Falsef, err: assert.Nilf},
|
||||
|
||||
{mes: `
|
||||
foo: { op: In, value: ["bar"] }
|
||||
bar: { op: Exists }
|
||||
`,
|
||||
input: I{"bar": {}, "baz": {}},
|
||||
result: assert.Falsef, err: assert.NotNilf},
|
||||
}
|
||||
|
||||
for i, tc := range tcs {
|
||||
mes := &api.MatchExpressionSet{}
|
||||
if err := yaml.Unmarshal([]byte(tc.mes), mes); err != nil {
|
||||
t.Fatalf("failed to parse data of test case #%d (%v): %v", i, tc, err)
|
||||
}
|
||||
|
||||
res, err := mes.MatchKeys(tc.input)
|
||||
tc.result(t, res, "test case #%d (%v) failed", i, tc)
|
||||
tc.err(t, err, "test case #%d (%v) failed", i, tc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMESMatchValues(t *testing.T) {
|
||||
type I = map[string]string
|
||||
type TC struct {
|
||||
mes string
|
||||
input I
|
||||
result BoolAssertionFuncf
|
||||
err ValueAssertionFuncf
|
||||
}
|
||||
|
||||
tcs := []TC{
|
||||
{result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{input: I{"foo": "bar"}, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{mes: `
|
||||
foo: { op: Exists }
|
||||
bar: { op: In, value: ["val", "wal"] }
|
||||
baz: { op: Gt, value: ["10"] }
|
||||
`,
|
||||
input: I{"bar": "val"},
|
||||
result: assert.Falsef, err: assert.Nilf},
|
||||
|
||||
{mes: `
|
||||
foo: { op: Exists }
|
||||
bar: { op: In, value: ["val", "wal"] }
|
||||
baz: { op: Gt, value: ["10"] }
|
||||
`,
|
||||
input: I{"foo": "1", "bar": "val", "baz": "123"},
|
||||
result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{mes: `
|
||||
foo: { op: Exists }
|
||||
bar: { op: In, value: ["val"] }
|
||||
baz: { op: Gt, value: ["10"] }
|
||||
`,
|
||||
input: I{"foo": "1", "bar": "val", "baz": "123.0"},
|
||||
result: assert.Falsef, err: assert.NotNilf},
|
||||
}
|
||||
|
||||
for i, tc := range tcs {
|
||||
mes := &api.MatchExpressionSet{}
|
||||
if err := yaml.Unmarshal([]byte(tc.mes), mes); err != nil {
|
||||
t.Fatalf("failed to parse data of test case #%d (%v): %v", i, tc, err)
|
||||
}
|
||||
|
||||
res, err := mes.MatchValues(tc.input)
|
||||
tc.result(t, res, "test case #%d (%v) failed", i, tc)
|
||||
tc.err(t, err, "test case #%d (%v) failed", i, tc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMESMatchInstances(t *testing.T) {
|
||||
type I = feature.InstanceFeature
|
||||
type A = map[string]string
|
||||
type TC struct {
|
||||
mes string
|
||||
input []I
|
||||
result BoolAssertionFuncf
|
||||
err ValueAssertionFuncf
|
||||
}
|
||||
|
||||
tcs := []TC{
|
||||
{result: assert.Falsef, err: assert.Nilf}, // nil instances -> false
|
||||
|
||||
{input: []I{}, result: assert.Falsef, err: assert.Nilf}, // zero instances -> false
|
||||
|
||||
{input: []I{I{Attributes: A{}}}, result: assert.Truef, err: assert.Nilf}, // one "empty" instance
|
||||
|
||||
{mes: `
|
||||
foo: { op: Exists }
|
||||
bar: { op: Lt, value: ["10"] }
|
||||
`,
|
||||
input: []I{I{Attributes: A{"foo": "1"}}, I{Attributes: A{"bar": "1"}}},
|
||||
result: assert.Falsef, err: assert.Nilf},
|
||||
|
||||
{mes: `
|
||||
foo: { op: Exists }
|
||||
bar: { op: Lt, value: ["10"] }
|
||||
`,
|
||||
input: []I{I{Attributes: A{"foo": "1"}}, I{Attributes: A{"foo": "2", "bar": "1"}}},
|
||||
result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{mes: `
|
||||
bar: { op: Lt, value: ["10"] }
|
||||
`,
|
||||
input: []I{I{Attributes: A{"foo": "1"}}, I{Attributes: A{"bar": "0x1"}}},
|
||||
result: assert.Falsef, err: assert.NotNilf},
|
||||
}
|
||||
|
||||
for i, tc := range tcs {
|
||||
mes := &api.MatchExpressionSet{}
|
||||
if err := yaml.Unmarshal([]byte(tc.mes), mes); err != nil {
|
||||
t.Fatalf("failed to parse data of test case #%d (%v): %v", i, tc, err)
|
||||
}
|
||||
|
||||
res, err := mes.MatchInstances(tc.input)
|
||||
tc.result(t, res, "test case #%d (%v) failed", i, tc)
|
||||
tc.err(t, err, "test case #%d (%v) failed", i, tc)
|
||||
}
|
||||
}
|
173
pkg/apis/nfd/v1alpha1/types.go
Normal file
173
pkg/apis/nfd/v1alpha1/types.go
Normal file
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
Copyright 2021 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 v1alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// NodeFeatureRuleList contains a list of NodeFeatureRule objects.
|
||||
// +kubebuilder:object:root=true
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type NodeFeatureRuleList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata"`
|
||||
|
||||
Items []NodeFeatureRule `json:"items"`
|
||||
}
|
||||
|
||||
// NodeFeatureRule resource specifies a configuration for feature-based
|
||||
// customization of node objects, such as node labeling.
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:resource:scope=Cluster
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type NodeFeatureRule struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec NodeFeatureRuleSpec `json:"spec"`
|
||||
}
|
||||
|
||||
// NodeFeatureRuleSpec describes a NodeFeatureRule.
|
||||
type NodeFeatureRuleSpec struct {
|
||||
// Rules is a list of node customization rules.
|
||||
Rules []Rule `json:"rules"`
|
||||
}
|
||||
|
||||
// Rule defines a rule for node customization such as labeling.
|
||||
type Rule struct {
|
||||
// Name of the rule.
|
||||
Name string `json:"name"`
|
||||
|
||||
// Labels to create if the rule matches.
|
||||
// +optional
|
||||
Labels map[string]string `json:"labels"`
|
||||
|
||||
// LabelsTemplate specifies a template to expand for dynamically generating
|
||||
// multiple labels. Data (after template expansion) must be keys with an
|
||||
// optional value (<key>[=<value>]) separated by newlines.
|
||||
// +optional
|
||||
LabelsTemplate string `json:"labelsTemplate"`
|
||||
|
||||
// MatchFeatures specifies a set of matcher terms all of which must match.
|
||||
// +optional
|
||||
MatchFeatures FeatureMatcher `json:"matchFeatures"`
|
||||
|
||||
// MatchAny specifies a list of matchers one of which must match.
|
||||
// +optional
|
||||
MatchAny []MatchAnyElem `json:"matchAny"`
|
||||
}
|
||||
|
||||
// MatchAnyElem specifies one sub-matcher of MatchAny.
|
||||
type MatchAnyElem struct {
|
||||
// MatchFeatures specifies a set of matcher terms all of which must match.
|
||||
MatchFeatures FeatureMatcher `json:"matchFeatures"`
|
||||
}
|
||||
|
||||
// FeatureMatcher specifies a set of feature matcher terms (i.e. per-feature
|
||||
// matchers), all of which must match.
|
||||
type FeatureMatcher []FeatureMatcherTerm
|
||||
|
||||
// FeatureMatcherTerm defines requirements against one feature set. All
|
||||
// requirements (specified as MatchExpressions) are evaluated against each
|
||||
// element in the feature set.
|
||||
type FeatureMatcherTerm struct {
|
||||
Feature string `json:"feature"`
|
||||
MatchExpressions MatchExpressionSet `json:"matchExpressions"`
|
||||
}
|
||||
|
||||
// MatchExpressionSet contains a set of MatchExpressions, each of which is
|
||||
// evaluated against a set of input values.
|
||||
type MatchExpressionSet struct {
|
||||
Expressions `json:",inline"`
|
||||
}
|
||||
|
||||
// Expressions is a helper type to work around issues with k8s deepcopy-gen
|
||||
type Expressions map[string]*MatchExpression
|
||||
|
||||
// MatchExpression specifies an expression to evaluate against a set of input
|
||||
// values. It contains an operator that is applied when matching the input and
|
||||
// an array of values that the operator evaluates the input against.
|
||||
//
|
||||
// NB: CreateMatchExpression or MustCreateMatchExpression() should be used for
|
||||
// creating new instances.
|
||||
// NB: Validate() must be called if Op or Value fields are modified or if a new
|
||||
// instance is created from scratch without using the helper functions.
|
||||
type MatchExpression struct {
|
||||
// Op is the operator to be applied.
|
||||
Op MatchOp `json:"op"`
|
||||
|
||||
// Value is the list of values that the operand evaluates the input
|
||||
// against. Value should be empty if the operator is Exists, DoesNotExist,
|
||||
// IsTrue or IsFalse. Value should contain exactly one element if the
|
||||
// operator is Gt or Lt and exactly two elements if the operator is GtLt.
|
||||
// In other cases Value should contain at least one element.
|
||||
// +optional
|
||||
Value MatchValue `json:"value,omitempty"`
|
||||
|
||||
// valueRe caches compiled regexps for "InRegexp" operator
|
||||
valueRe valueRegexpCache `json:"-"`
|
||||
}
|
||||
|
||||
// MatchOp is the match operator that is applied on values when evaluating a
|
||||
// MatchExpression.
|
||||
// +kubebuilder:validation:Enum="In";"NotIn";"InRegexp";"Exists";"DoesNotExist";"Gt";"Lt";"GtLt";"IsTrue";"IsFalse"
|
||||
type MatchOp string
|
||||
|
||||
// MatchValue is the list of values associated with a MatchExpression.
|
||||
type MatchValue []string
|
||||
|
||||
const (
|
||||
// MatchAny returns always true.
|
||||
MatchAny MatchOp = ""
|
||||
// MatchIn returns true if any of the values stored in the expression is
|
||||
// equal to the input.
|
||||
MatchIn MatchOp = "In"
|
||||
// MatchIn returns true if none of the values in the expression are equal
|
||||
// to the input.
|
||||
MatchNotIn MatchOp = "NotIn"
|
||||
// MatchInRegexp treats values of the expression as regular expressions and
|
||||
// returns true if any of them matches the input.
|
||||
MatchInRegexp MatchOp = "InRegexp"
|
||||
// MatchExists returns true if the input is valid. The expression must not
|
||||
// have any values.
|
||||
MatchExists MatchOp = "Exists"
|
||||
// MatchDoesNotExist returns true if the input is not valid. The expression
|
||||
// must not have any values.
|
||||
MatchDoesNotExist MatchOp = "DoesNotExist"
|
||||
// MatchGt returns true if the input is greater than the value of the
|
||||
// expression (number of values in the expression must be exactly one).
|
||||
// Both the input and value must be integer numbers, otherwise an error is
|
||||
// returned.
|
||||
MatchGt MatchOp = "Gt"
|
||||
// MatchLt returns true if the input is less than the value of the
|
||||
// expression (number of values in the expression must be exactly one).
|
||||
// Both the input and value must be integer numbers, otherwise an error is
|
||||
// returned.
|
||||
MatchLt MatchOp = "Lt"
|
||||
// MatchGtLt returns true if the input is between two values, i.e. greater
|
||||
// than the first value and less than the second value of the expression
|
||||
// (number of values in the expression must be exactly two). Both the input
|
||||
// and values must be integer numbers, otherwise an error is returned.
|
||||
MatchGtLt MatchOp = "GtLt"
|
||||
// MatchIsTrue returns true if the input holds the value "true". The
|
||||
// expression must not have any values.
|
||||
MatchIsTrue MatchOp = "IsTrue"
|
||||
// MatchIsTrue returns true if the input holds the value "false". The
|
||||
// expression must not have any values.
|
||||
MatchIsFalse MatchOp = "IsFalse"
|
||||
)
|
284
pkg/apis/nfd/v1alpha1/zz_generated.deepcopy.go
Normal file
284
pkg/apis/nfd/v1alpha1/zz_generated.deepcopy.go
Normal file
|
@ -0,0 +1,284 @@
|
|||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
// Code generated by controller-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in Expressions) DeepCopyInto(out *Expressions) {
|
||||
{
|
||||
in := &in
|
||||
*out = make(Expressions, len(*in))
|
||||
for key, val := range *in {
|
||||
var outVal *MatchExpression
|
||||
if val == nil {
|
||||
(*out)[key] = nil
|
||||
} else {
|
||||
in, out := &val, &outVal
|
||||
*out = new(MatchExpression)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
(*out)[key] = outVal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Expressions.
|
||||
func (in Expressions) DeepCopy() Expressions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Expressions)
|
||||
in.DeepCopyInto(out)
|
||||
return *out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in FeatureMatcher) DeepCopyInto(out *FeatureMatcher) {
|
||||
{
|
||||
in := &in
|
||||
*out = make(FeatureMatcher, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FeatureMatcher.
|
||||
func (in FeatureMatcher) DeepCopy() FeatureMatcher {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(FeatureMatcher)
|
||||
in.DeepCopyInto(out)
|
||||
return *out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *FeatureMatcherTerm) DeepCopyInto(out *FeatureMatcherTerm) {
|
||||
*out = *in
|
||||
in.MatchExpressions.DeepCopyInto(&out.MatchExpressions)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FeatureMatcherTerm.
|
||||
func (in *FeatureMatcherTerm) DeepCopy() *FeatureMatcherTerm {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(FeatureMatcherTerm)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *MatchAnyElem) DeepCopyInto(out *MatchAnyElem) {
|
||||
*out = *in
|
||||
if in.MatchFeatures != nil {
|
||||
in, out := &in.MatchFeatures, &out.MatchFeatures
|
||||
*out = make(FeatureMatcher, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MatchAnyElem.
|
||||
func (in *MatchAnyElem) DeepCopy() *MatchAnyElem {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(MatchAnyElem)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *MatchExpression) DeepCopyInto(out *MatchExpression) {
|
||||
*out = *in
|
||||
if in.Value != nil {
|
||||
in, out := &in.Value, &out.Value
|
||||
*out = make(MatchValue, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
in.valueRe.DeepCopyInto(&out.valueRe)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MatchExpression.
|
||||
func (in *MatchExpression) DeepCopy() *MatchExpression {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(MatchExpression)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *MatchExpressionSet) DeepCopyInto(out *MatchExpressionSet) {
|
||||
*out = *in
|
||||
if in.Expressions != nil {
|
||||
in, out := &in.Expressions, &out.Expressions
|
||||
*out = make(Expressions, len(*in))
|
||||
for key, val := range *in {
|
||||
var outVal *MatchExpression
|
||||
if val == nil {
|
||||
(*out)[key] = nil
|
||||
} else {
|
||||
in, out := &val, &outVal
|
||||
*out = new(MatchExpression)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
(*out)[key] = outVal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MatchExpressionSet.
|
||||
func (in *MatchExpressionSet) DeepCopy() *MatchExpressionSet {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(MatchExpressionSet)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in MatchValue) DeepCopyInto(out *MatchValue) {
|
||||
{
|
||||
in := &in
|
||||
*out = make(MatchValue, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MatchValue.
|
||||
func (in MatchValue) DeepCopy() MatchValue {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(MatchValue)
|
||||
in.DeepCopyInto(out)
|
||||
return *out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NodeFeatureRule) DeepCopyInto(out *NodeFeatureRule) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeFeatureRule.
|
||||
func (in *NodeFeatureRule) DeepCopy() *NodeFeatureRule {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NodeFeatureRule)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *NodeFeatureRule) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NodeFeatureRuleList) DeepCopyInto(out *NodeFeatureRuleList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]NodeFeatureRule, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeFeatureRuleList.
|
||||
func (in *NodeFeatureRuleList) DeepCopy() *NodeFeatureRuleList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NodeFeatureRuleList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *NodeFeatureRuleList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NodeFeatureRuleSpec) DeepCopyInto(out *NodeFeatureRuleSpec) {
|
||||
*out = *in
|
||||
if in.Rules != nil {
|
||||
in, out := &in.Rules, &out.Rules
|
||||
*out = make([]Rule, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeFeatureRuleSpec.
|
||||
func (in *NodeFeatureRuleSpec) DeepCopy() *NodeFeatureRuleSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NodeFeatureRuleSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Rule) DeepCopyInto(out *Rule) {
|
||||
*out = *in
|
||||
if in.Labels != nil {
|
||||
in, out := &in.Labels, &out.Labels
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.MatchFeatures != nil {
|
||||
in, out := &in.MatchFeatures, &out.MatchFeatures
|
||||
*out = make(FeatureMatcher, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.MatchAny != nil {
|
||||
in, out := &in.MatchAny, &out.MatchAny
|
||||
*out = make([]MatchAnyElem, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rule.
|
||||
func (in *Rule) DeepCopy() *Rule {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Rule)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
|
@ -26,9 +26,9 @@ import (
|
|||
"sigs.k8s.io/yaml"
|
||||
|
||||
"sigs.k8s.io/node-feature-discovery/pkg/api/feature"
|
||||
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
|
||||
"sigs.k8s.io/node-feature-discovery/pkg/utils"
|
||||
"sigs.k8s.io/node-feature-discovery/source"
|
||||
"sigs.k8s.io/node-feature-discovery/source/custom/expression"
|
||||
"sigs.k8s.io/node-feature-discovery/source/custom/rules"
|
||||
)
|
||||
|
||||
|
@ -65,7 +65,7 @@ type FeatureMatcher []FeatureMatcherTerm
|
|||
|
||||
type FeatureMatcherTerm struct {
|
||||
Feature string
|
||||
MatchExpressions expression.MatchExpressionSet
|
||||
MatchExpressions nfdv1alpha1.MatchExpressionSet
|
||||
}
|
||||
|
||||
type config []CustomRule
|
||||
|
|
|
@ -21,7 +21,7 @@ import (
|
|||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/node-feature-discovery/pkg/api/feature"
|
||||
"sigs.k8s.io/node-feature-discovery/source/custom/expression"
|
||||
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
|
||||
)
|
||||
|
||||
func TestRule(t *testing.T) {
|
||||
|
@ -32,7 +32,11 @@ func TestRule(t *testing.T) {
|
|||
MatchFeatures: FeatureMatcher{
|
||||
FeatureMatcherTerm{
|
||||
Feature: "domain-1.kf-1",
|
||||
MatchExpressions: expression.MatchExpressionSet{"key-1": expression.MustCreateMatchExpression(expression.MatchExists)},
|
||||
MatchExpressions: nfdv1alpha1.MatchExpressionSet{
|
||||
Expressions: nfdv1alpha1.Expressions{
|
||||
"key-1": nfdv1alpha1.MustCreateMatchExpression(nfdv1alpha1.MatchExists),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -95,7 +99,11 @@ func TestRule(t *testing.T) {
|
|||
MatchFeatures: FeatureMatcher{
|
||||
FeatureMatcherTerm{
|
||||
Feature: "domain-1.vf-1",
|
||||
MatchExpressions: expression.MatchExpressionSet{"key-1": expression.MustCreateMatchExpression(expression.MatchIn, "val-1")},
|
||||
MatchExpressions: nfdv1alpha1.MatchExpressionSet{
|
||||
Expressions: nfdv1alpha1.Expressions{
|
||||
"key-1": nfdv1alpha1.MustCreateMatchExpression(nfdv1alpha1.MatchIn, "val-1"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -114,7 +122,11 @@ func TestRule(t *testing.T) {
|
|||
MatchFeatures: FeatureMatcher{
|
||||
FeatureMatcherTerm{
|
||||
Feature: "domain-1.if-1",
|
||||
MatchExpressions: expression.MatchExpressionSet{"attr-1": expression.MustCreateMatchExpression(expression.MatchIn, "val-1")},
|
||||
MatchExpressions: nfdv1alpha1.MatchExpressionSet{
|
||||
Expressions: nfdv1alpha1.Expressions{
|
||||
"attr-1": nfdv1alpha1.MustCreateMatchExpression(nfdv1alpha1.MatchIn, "val-1"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -133,11 +145,19 @@ func TestRule(t *testing.T) {
|
|||
MatchFeatures: FeatureMatcher{
|
||||
FeatureMatcherTerm{
|
||||
Feature: "domain-1.vf-1",
|
||||
MatchExpressions: expression.MatchExpressionSet{"key-1": expression.MustCreateMatchExpression(expression.MatchIn, "val-x")},
|
||||
MatchExpressions: nfdv1alpha1.MatchExpressionSet{
|
||||
Expressions: nfdv1alpha1.Expressions{
|
||||
"key-1": nfdv1alpha1.MustCreateMatchExpression(nfdv1alpha1.MatchIn, "val-x"),
|
||||
},
|
||||
},
|
||||
},
|
||||
FeatureMatcherTerm{
|
||||
Feature: "domain-1.if-1",
|
||||
MatchExpressions: expression.MatchExpressionSet{"attr-1": expression.MustCreateMatchExpression(expression.MatchIn, "val-1")},
|
||||
MatchExpressions: nfdv1alpha1.MatchExpressionSet{
|
||||
Expressions: nfdv1alpha1.Expressions{
|
||||
"attr-1": nfdv1alpha1.MustCreateMatchExpression(nfdv1alpha1.MatchIn, "val-1"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -145,7 +165,7 @@ func TestRule(t *testing.T) {
|
|||
assert.Nilf(t, err, "unexpected error: %v", err)
|
||||
assert.Nil(t, m, "instances should not have matched")
|
||||
|
||||
r5.MatchFeatures[0].MatchExpressions["key-1"] = expression.MustCreateMatchExpression(expression.MatchIn, "val-1")
|
||||
r5.MatchFeatures[0].MatchExpressions.Expressions["key-1"] = nfdv1alpha1.MustCreateMatchExpression(nfdv1alpha1.MatchIn, "val-1")
|
||||
m, err = r5.execute(f)
|
||||
assert.Nilf(t, err, "unexpected error: %v", err)
|
||||
assert.Equal(t, r5.Labels, m, "instances should have matched")
|
||||
|
@ -156,7 +176,11 @@ func TestRule(t *testing.T) {
|
|||
MatchFeatures: FeatureMatcher{
|
||||
FeatureMatcherTerm{
|
||||
Feature: "domain-1.kf-1",
|
||||
MatchExpressions: expression.MatchExpressionSet{"key-na": expression.MustCreateMatchExpression(expression.MatchExists)},
|
||||
MatchExpressions: nfdv1alpha1.MatchExpressionSet{
|
||||
Expressions: nfdv1alpha1.Expressions{
|
||||
"key-na": nfdv1alpha1.MustCreateMatchExpression(nfdv1alpha1.MatchExists),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -170,11 +194,15 @@ func TestRule(t *testing.T) {
|
|||
MatchFeatures: FeatureMatcher{
|
||||
FeatureMatcherTerm{
|
||||
Feature: "domain-1.kf-1",
|
||||
MatchExpressions: expression.MatchExpressionSet{"key-1": expression.MustCreateMatchExpression(expression.MatchExists)},
|
||||
MatchExpressions: nfdv1alpha1.MatchExpressionSet{
|
||||
Expressions: nfdv1alpha1.Expressions{
|
||||
"key-1": nfdv1alpha1.MustCreateMatchExpression(nfdv1alpha1.MatchExists),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
r5.MatchFeatures[0].MatchExpressions["key-1"] = expression.MustCreateMatchExpression(expression.MatchIn, "val-1")
|
||||
r5.MatchFeatures[0].MatchExpressions.Expressions["key-1"] = nfdv1alpha1.MustCreateMatchExpression(nfdv1alpha1.MatchIn, "val-1")
|
||||
m, err = r5.execute(f)
|
||||
assert.Nilf(t, err, "unexpected error: %v", err)
|
||||
assert.Equal(t, r5.Labels, m, "instances should have matched")
|
||||
|
|
|
@ -1,439 +0,0 @@
|
|||
/*
|
||||
Copyright 2021 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 expression_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"sigs.k8s.io/node-feature-discovery/pkg/api/feature"
|
||||
e "sigs.k8s.io/node-feature-discovery/source/custom/expression"
|
||||
)
|
||||
|
||||
type BoolAssertionFuncf func(assert.TestingT, bool, string, ...interface{}) bool
|
||||
|
||||
type ValueAssertionFuncf func(assert.TestingT, interface{}, string, ...interface{}) bool
|
||||
|
||||
func TestCreateMatchExpression(t *testing.T) {
|
||||
type V = e.MatchValue
|
||||
type TC struct {
|
||||
op e.MatchOp
|
||||
values V
|
||||
err ValueAssertionFuncf
|
||||
}
|
||||
|
||||
tcs := []TC{
|
||||
{op: e.MatchAny, err: assert.Nilf}, // #0
|
||||
{op: e.MatchAny, values: V{"1"}, err: assert.NotNilf},
|
||||
|
||||
{op: e.MatchIn, err: assert.NotNilf},
|
||||
{op: e.MatchIn, values: V{"1"}, err: assert.Nilf},
|
||||
{op: e.MatchIn, values: V{"1", "2", "3", "4"}, err: assert.Nilf},
|
||||
|
||||
{op: e.MatchNotIn, err: assert.NotNilf},
|
||||
{op: e.MatchNotIn, values: V{"1"}, err: assert.Nilf},
|
||||
{op: e.MatchNotIn, values: V{"1", "2"}, err: assert.Nilf},
|
||||
|
||||
{op: e.MatchInRegexp, err: assert.NotNilf},
|
||||
{op: e.MatchInRegexp, values: V{"1"}, err: assert.Nilf},
|
||||
{op: e.MatchInRegexp, values: V{"()", "2", "3"}, err: assert.Nilf},
|
||||
{op: e.MatchInRegexp, values: V{"("}, err: assert.NotNilf},
|
||||
|
||||
{op: e.MatchExists, err: assert.Nilf},
|
||||
{op: e.MatchExists, values: V{"1"}, err: assert.NotNilf},
|
||||
|
||||
{op: e.MatchDoesNotExist, err: assert.Nilf},
|
||||
{op: e.MatchDoesNotExist, values: V{"1"}, err: assert.NotNilf},
|
||||
|
||||
{op: e.MatchGt, err: assert.NotNilf},
|
||||
{op: e.MatchGt, values: V{"1"}, err: assert.Nilf},
|
||||
{op: e.MatchGt, values: V{"-10"}, err: assert.Nilf},
|
||||
{op: e.MatchGt, values: V{"1", "2"}, err: assert.NotNilf},
|
||||
{op: e.MatchGt, values: V{""}, err: assert.NotNilf},
|
||||
|
||||
{op: e.MatchLt, err: assert.NotNilf},
|
||||
{op: e.MatchLt, values: V{"1"}, err: assert.Nilf},
|
||||
{op: e.MatchLt, values: V{"-1"}, err: assert.Nilf},
|
||||
{op: e.MatchLt, values: V{"1", "2", "3"}, err: assert.NotNilf},
|
||||
{op: e.MatchLt, values: V{"a"}, err: assert.NotNilf},
|
||||
|
||||
{op: e.MatchGtLt, err: assert.NotNilf},
|
||||
{op: e.MatchGtLt, values: V{"1"}, err: assert.NotNilf},
|
||||
{op: e.MatchGtLt, values: V{"1", "2"}, err: assert.Nilf},
|
||||
{op: e.MatchGtLt, values: V{"2", "1"}, err: assert.NotNilf},
|
||||
{op: e.MatchGtLt, values: V{"1", "2", "3"}, err: assert.NotNilf},
|
||||
{op: e.MatchGtLt, values: V{"a", "2"}, err: assert.NotNilf},
|
||||
|
||||
{op: e.MatchIsTrue, err: assert.Nilf},
|
||||
{op: e.MatchIsTrue, values: V{"1"}, err: assert.NotNilf},
|
||||
|
||||
{op: e.MatchIsFalse, err: assert.Nilf},
|
||||
{op: e.MatchIsFalse, values: V{"1", "2"}, err: assert.NotNilf},
|
||||
}
|
||||
|
||||
for i, tc := range tcs {
|
||||
_, err := e.CreateMatchExpression(tc.op, tc.values...)
|
||||
tc.err(t, err, "test case #%d (%v) failed", i, tc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatch(t *testing.T) {
|
||||
type V = e.MatchValue
|
||||
type TC struct {
|
||||
op e.MatchOp
|
||||
values V
|
||||
input interface{}
|
||||
valid bool
|
||||
result BoolAssertionFuncf
|
||||
err ValueAssertionFuncf
|
||||
}
|
||||
|
||||
tcs := []TC{
|
||||
{op: e.MatchAny, result: assert.Truef, err: assert.Nilf},
|
||||
{op: e.MatchAny, input: "2", valid: false, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{op: e.MatchIn, values: V{"1"}, input: "2", valid: false, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchIn, values: V{"1"}, input: "2", valid: true, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchIn, values: V{"1", "2", "3"}, input: "2", valid: false, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchIn, values: V{"1", "2", "3"}, input: "2", valid: true, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{op: e.MatchNotIn, values: V{"2"}, input: 2, valid: false, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchNotIn, values: V{"1"}, input: 2, valid: true, result: assert.Truef, err: assert.Nilf},
|
||||
{op: e.MatchNotIn, values: V{"1", "2", "3"}, input: "2", valid: false, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchNotIn, values: V{"1", "2", "3"}, input: "2", valid: true, result: assert.Falsef, err: assert.Nilf},
|
||||
|
||||
{op: e.MatchInRegexp, values: V{"val-[0-9]$"}, input: "val-1", valid: false, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchInRegexp, values: V{"val-[0-9]$"}, input: "val-1", valid: true, result: assert.Truef, err: assert.Nilf},
|
||||
{op: e.MatchInRegexp, values: V{"val-[0-9]$"}, input: "val-12", valid: true, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchInRegexp, values: V{"val-[0-9]$", "al-[1-9]"}, input: "val-12", valid: true, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{op: e.MatchExists, input: nil, valid: false, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchExists, input: nil, valid: true, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{op: e.MatchDoesNotExist, input: false, valid: false, result: assert.Truef, err: assert.Nilf},
|
||||
{op: e.MatchDoesNotExist, input: false, valid: true, result: assert.Falsef, err: assert.Nilf},
|
||||
|
||||
{op: e.MatchGt, values: V{"2"}, input: 3, valid: false, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchGt, values: V{"2"}, input: 2, valid: true, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchGt, values: V{"2"}, input: 3, valid: true, result: assert.Truef, err: assert.Nilf},
|
||||
{op: e.MatchGt, values: V{"-10"}, input: -3, valid: true, result: assert.Truef, err: assert.Nilf},
|
||||
{op: e.MatchGt, values: V{"2"}, input: "3a", valid: true, result: assert.Falsef, err: assert.NotNilf},
|
||||
|
||||
{op: e.MatchLt, values: V{"2"}, input: "1", valid: false, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchLt, values: V{"2"}, input: "2", valid: true, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchLt, values: V{"-10"}, input: -3, valid: true, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchLt, values: V{"2"}, input: "1", valid: true, result: assert.Truef, err: assert.Nilf},
|
||||
{op: e.MatchLt, values: V{"2"}, input: "1.0", valid: true, result: assert.Falsef, err: assert.NotNilf},
|
||||
|
||||
{op: e.MatchGtLt, values: V{"1", "10"}, input: "1", valid: false, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchGtLt, values: V{"1", "10"}, input: "1", valid: true, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchGtLt, values: V{"1", "10"}, input: "10", valid: true, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchGtLt, values: V{"1", "10"}, input: "2", valid: true, result: assert.Truef, err: assert.Nilf},
|
||||
{op: e.MatchGtLt, values: V{"1", "10"}, input: "1.0", valid: true, result: assert.Falsef, err: assert.NotNilf},
|
||||
|
||||
{op: e.MatchIsTrue, input: true, valid: false, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchIsTrue, input: true, valid: true, result: assert.Truef, err: assert.Nilf},
|
||||
{op: e.MatchIsTrue, input: false, valid: true, result: assert.Falsef, err: assert.Nilf},
|
||||
|
||||
{op: e.MatchIsFalse, input: "false", valid: false, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchIsFalse, input: "false", valid: true, result: assert.Truef, err: assert.Nilf},
|
||||
{op: e.MatchIsFalse, input: "true", valid: true, result: assert.Falsef, err: assert.Nilf},
|
||||
}
|
||||
|
||||
for i, tc := range tcs {
|
||||
me := e.MustCreateMatchExpression(tc.op, tc.values...)
|
||||
res, err := me.Match(tc.valid, tc.input)
|
||||
tc.result(t, res, "test case #%d (%v) failed", i, tc)
|
||||
tc.err(t, err, "test case #%d (%v) failed", i, tc)
|
||||
}
|
||||
|
||||
// Check some special error cases separately because MustCreateMatch panics
|
||||
tcs = []TC{
|
||||
|
||||
{op: e.MatchGt, values: V{"3.0"}, input: 1, valid: true},
|
||||
{op: e.MatchLt, values: V{"0x2"}, input: 1, valid: true},
|
||||
{op: e.MatchGtLt, values: V{"1", "str"}, input: 1, valid: true},
|
||||
{op: "non-existent-op", values: V{"1"}, input: 1, valid: true},
|
||||
}
|
||||
|
||||
for i, tc := range tcs {
|
||||
me := e.MatchExpression{Op: tc.op, Value: tc.values}
|
||||
res, err := me.Match(tc.valid, tc.input)
|
||||
assert.Falsef(t, res, "err test case #%d (%v) failed", i, tc)
|
||||
assert.NotNilf(t, err, "err test case #%d (%v) failed", i, tc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchKeys(t *testing.T) {
|
||||
type V = e.MatchValue
|
||||
type I = map[string]feature.Nil
|
||||
type TC struct {
|
||||
op e.MatchOp
|
||||
values V
|
||||
name string
|
||||
input I
|
||||
result BoolAssertionFuncf
|
||||
err ValueAssertionFuncf
|
||||
}
|
||||
|
||||
tcs := []TC{
|
||||
{op: e.MatchAny, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{op: e.MatchExists, name: "foo", input: nil, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchExists, name: "foo", input: I{"bar": {}}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchExists, name: "foo", input: I{"bar": {}, "foo": {}}, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{op: e.MatchDoesNotExist, name: "foo", input: nil, result: assert.Truef, err: assert.Nilf},
|
||||
{op: e.MatchDoesNotExist, name: "foo", input: I{}, result: assert.Truef, err: assert.Nilf},
|
||||
{op: e.MatchDoesNotExist, name: "foo", input: I{"bar": {}}, result: assert.Truef, err: assert.Nilf},
|
||||
{op: e.MatchDoesNotExist, name: "foo", input: I{"bar": {}, "foo": {}}, result: assert.Falsef, err: assert.Nilf},
|
||||
|
||||
// All other ops should return an error
|
||||
{op: e.MatchIn, values: V{"foo"}, name: "foo", result: assert.Falsef, err: assert.NotNilf},
|
||||
{op: e.MatchNotIn, values: V{"foo"}, name: "foo", result: assert.Falsef, err: assert.NotNilf},
|
||||
{op: e.MatchInRegexp, values: V{"foo"}, name: "foo", result: assert.Falsef, err: assert.NotNilf},
|
||||
{op: e.MatchGt, values: V{"1"}, name: "foo", result: assert.Falsef, err: assert.NotNilf},
|
||||
{op: e.MatchLt, values: V{"1"}, name: "foo", result: assert.Falsef, err: assert.NotNilf},
|
||||
{op: e.MatchGtLt, values: V{"1", "10"}, name: "foo", result: assert.Falsef, err: assert.NotNilf},
|
||||
{op: e.MatchIsTrue, name: "foo", result: assert.Falsef, err: assert.NotNilf},
|
||||
{op: e.MatchIsFalse, name: "foo", result: assert.Falsef, err: assert.NotNilf},
|
||||
}
|
||||
|
||||
for i, tc := range tcs {
|
||||
me := e.MustCreateMatchExpression(tc.op, tc.values...)
|
||||
res, err := me.MatchKeys(tc.name, tc.input)
|
||||
tc.result(t, res, "test case #%d (%v) failed", i, tc)
|
||||
tc.err(t, err, "test case #%d (%v) failed", i, tc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchValues(t *testing.T) {
|
||||
type V = []string
|
||||
type I = map[string]string
|
||||
|
||||
type TC struct {
|
||||
op e.MatchOp
|
||||
values V
|
||||
name string
|
||||
input I
|
||||
result BoolAssertionFuncf
|
||||
err ValueAssertionFuncf
|
||||
}
|
||||
|
||||
tcs := []TC{
|
||||
{op: e.MatchAny, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{op: e.MatchIn, values: V{"1", "2"}, name: "foo", input: I{"bar": "2"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchIn, values: V{"1", "2"}, name: "foo", input: I{"foo": "3"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchIn, values: V{"1", "2"}, name: "foo", input: I{"foo": "2"}, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{op: e.MatchNotIn, values: V{"1", "2"}, name: "foo", input: I{"bar": "2"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchNotIn, values: V{"1", "2"}, name: "foo", input: I{"foo": "3"}, result: assert.Truef, err: assert.Nilf},
|
||||
{op: e.MatchNotIn, values: V{"1", "2"}, name: "foo", input: I{"foo": "2"}, result: assert.Falsef, err: assert.Nilf},
|
||||
|
||||
{op: e.MatchInRegexp, values: V{"1", "2"}, name: "foo", input: I{"bar": "2"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchInRegexp, values: V{"1", "[0-8]"}, name: "foo", input: I{"foo": "9"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchInRegexp, values: V{"1", "[0-8]"}, name: "foo", input: I{"foo": "2"}, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{op: e.MatchExists, name: "foo", input: I{"bar": "1"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchExists, name: "foo", input: I{"foo": "1"}, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{op: e.MatchDoesNotExist, name: "foo", input: nil, result: assert.Truef, err: assert.Nilf},
|
||||
{op: e.MatchDoesNotExist, name: "foo", input: I{"foo": "1"}, result: assert.Falsef, err: assert.Nilf},
|
||||
|
||||
{op: e.MatchGt, values: V{"2"}, name: "foo", input: I{"bar": "3"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchGt, values: V{"2"}, name: "foo", input: I{"bar": "3", "foo": "2"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchGt, values: V{"2"}, name: "foo", input: I{"bar": "3", "foo": "3"}, result: assert.Truef, err: assert.Nilf},
|
||||
{op: e.MatchGt, values: V{"2"}, name: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.Falsef, err: assert.NotNilf},
|
||||
|
||||
{op: e.MatchLt, values: V{"2"}, name: "foo", input: I{"bar": "1"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchLt, values: V{"2"}, name: "foo", input: I{"bar": "1", "foo": "2"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchLt, values: V{"2"}, name: "foo", input: I{"bar": "1", "foo": "1"}, result: assert.Truef, err: assert.Nilf},
|
||||
{op: e.MatchLt, values: V{"2"}, name: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.Falsef, err: assert.NotNilf},
|
||||
|
||||
{op: e.MatchGtLt, values: V{"-10", "10"}, name: "foo", input: I{"bar": "1"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchGtLt, values: V{"-10", "10"}, name: "foo", input: I{"bar": "1", "foo": "11"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchGtLt, values: V{"-10", "10"}, name: "foo", input: I{"bar": "1", "foo": "-11"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchGtLt, values: V{"-10", "10"}, name: "foo", input: I{"bar": "1", "foo": "1"}, result: assert.Truef, err: assert.Nilf},
|
||||
{op: e.MatchGtLt, values: V{"-10", "10"}, name: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.Falsef, err: assert.NotNilf},
|
||||
|
||||
{op: e.MatchIsTrue, name: "foo", result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchIsTrue, name: "foo", input: I{"foo": "1"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchIsTrue, name: "foo", input: I{"foo": "true"}, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{op: e.MatchIsFalse, name: "foo", input: I{"foo": "true"}, result: assert.Falsef, err: assert.Nilf},
|
||||
{op: e.MatchIsFalse, name: "foo", input: I{"foo": "false"}, result: assert.Truef, err: assert.Nilf},
|
||||
}
|
||||
|
||||
for i, tc := range tcs {
|
||||
me := e.MustCreateMatchExpression(tc.op, tc.values...)
|
||||
res, err := me.MatchValues(tc.name, tc.input)
|
||||
tc.result(t, res, "test case #%d (%v) failed", i, tc)
|
||||
tc.err(t, err, "test case #%d (%v) failed", i, tc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMESMatchKeys(t *testing.T) {
|
||||
type I = map[string]feature.Nil
|
||||
type TC struct {
|
||||
mes string
|
||||
input I
|
||||
result BoolAssertionFuncf
|
||||
err ValueAssertionFuncf
|
||||
}
|
||||
|
||||
tcs := []TC{
|
||||
{result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{input: I{"foo": {}}, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{mes: `
|
||||
foo: { op: DoesNotExist }
|
||||
bar: { op: Exists }
|
||||
`,
|
||||
input: I{"bar": {}, "baz": {}},
|
||||
result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{mes: `
|
||||
foo: { op: DoesNotExist }
|
||||
bar: { op: Exists }
|
||||
`,
|
||||
input: I{"foo": {}, "bar": {}, "baz": {}},
|
||||
result: assert.Falsef, err: assert.Nilf},
|
||||
|
||||
{mes: `
|
||||
foo: { op: In, value: ["bar"] }
|
||||
bar: { op: Exists }
|
||||
`,
|
||||
input: I{"bar": {}, "baz": {}},
|
||||
result: assert.Falsef, err: assert.NotNilf},
|
||||
}
|
||||
|
||||
for i, tc := range tcs {
|
||||
mes := &e.MatchExpressionSet{}
|
||||
if err := yaml.Unmarshal([]byte(tc.mes), mes); err != nil {
|
||||
t.Fatalf("failed to parse data of test case #%d (%v): %v", i, tc, err)
|
||||
}
|
||||
|
||||
res, err := mes.MatchKeys(tc.input)
|
||||
tc.result(t, res, "test case #%d (%v) failed", i, tc)
|
||||
tc.err(t, err, "test case #%d (%v) failed", i, tc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMESMatchValues(t *testing.T) {
|
||||
type I = map[string]string
|
||||
type TC struct {
|
||||
mes string
|
||||
input I
|
||||
result BoolAssertionFuncf
|
||||
err ValueAssertionFuncf
|
||||
}
|
||||
|
||||
tcs := []TC{
|
||||
{result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{input: I{"foo": "bar"}, result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{mes: `
|
||||
foo: { op: Exists }
|
||||
bar: { op: In, value: ["val", "wal"] }
|
||||
baz: { op: Gt, value: ["10"] }
|
||||
`,
|
||||
input: I{"bar": "val"},
|
||||
result: assert.Falsef, err: assert.Nilf},
|
||||
|
||||
{mes: `
|
||||
foo: { op: Exists }
|
||||
bar: { op: In, value: ["val", "wal"] }
|
||||
baz: { op: Gt, value: ["10"] }
|
||||
`,
|
||||
input: I{"foo": "1", "bar": "val", "baz": "123"},
|
||||
result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{mes: `
|
||||
foo: { op: Exists }
|
||||
bar: { op: In, value: ["val"] }
|
||||
baz: { op: Gt, value: ["10"] }
|
||||
`,
|
||||
input: I{"foo": "1", "bar": "val", "baz": "123.0"},
|
||||
result: assert.Falsef, err: assert.NotNilf},
|
||||
}
|
||||
|
||||
for i, tc := range tcs {
|
||||
mes := &e.MatchExpressionSet{}
|
||||
if err := yaml.Unmarshal([]byte(tc.mes), mes); err != nil {
|
||||
t.Fatalf("failed to parse data of test case #%d (%v): %v", i, tc, err)
|
||||
}
|
||||
|
||||
res, err := mes.MatchValues(tc.input)
|
||||
tc.result(t, res, "test case #%d (%v) failed", i, tc)
|
||||
tc.err(t, err, "test case #%d (%v) failed", i, tc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMESMatchInstances(t *testing.T) {
|
||||
type I = feature.InstanceFeature
|
||||
type A = map[string]string
|
||||
type TC struct {
|
||||
mes string
|
||||
input []I
|
||||
result BoolAssertionFuncf
|
||||
err ValueAssertionFuncf
|
||||
}
|
||||
|
||||
tcs := []TC{
|
||||
{result: assert.Falsef, err: assert.Nilf}, // nil instances -> false
|
||||
|
||||
{input: []I{}, result: assert.Falsef, err: assert.Nilf}, // zero instances -> false
|
||||
|
||||
{input: []I{I{Attributes: A{}}}, result: assert.Truef, err: assert.Nilf}, // one "empty" instance
|
||||
|
||||
{mes: `
|
||||
foo: { op: Exists }
|
||||
bar: { op: Lt, value: ["10"] }
|
||||
`,
|
||||
input: []I{I{Attributes: A{"foo": "1"}}, I{Attributes: A{"bar": "1"}}},
|
||||
result: assert.Falsef, err: assert.Nilf},
|
||||
|
||||
{mes: `
|
||||
foo: { op: Exists }
|
||||
bar: { op: Lt, value: ["10"] }
|
||||
`,
|
||||
input: []I{I{Attributes: A{"foo": "1"}}, I{Attributes: A{"foo": "2", "bar": "1"}}},
|
||||
result: assert.Truef, err: assert.Nilf},
|
||||
|
||||
{mes: `
|
||||
bar: { op: Lt, value: ["10"] }
|
||||
`,
|
||||
input: []I{I{Attributes: A{"foo": "1"}}, I{Attributes: A{"bar": "0x1"}}},
|
||||
result: assert.Falsef, err: assert.NotNilf},
|
||||
}
|
||||
|
||||
for i, tc := range tcs {
|
||||
mes := &e.MatchExpressionSet{}
|
||||
if err := yaml.Unmarshal([]byte(tc.mes), mes); err != nil {
|
||||
t.Fatalf("failed to parse data of test case #%d (%v): %v", i, tc, err)
|
||||
}
|
||||
|
||||
res, err := mes.MatchInstances(tc.input)
|
||||
tc.result(t, res, "test case #%d (%v) failed", i, tc)
|
||||
tc.err(t, err, "test case #%d (%v) failed", i, tc)
|
||||
}
|
||||
}
|
|
@ -19,14 +19,14 @@ package rules
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
|
||||
"sigs.k8s.io/node-feature-discovery/source"
|
||||
"sigs.k8s.io/node-feature-discovery/source/cpu"
|
||||
"sigs.k8s.io/node-feature-discovery/source/custom/expression"
|
||||
)
|
||||
|
||||
// CpuIDRule implements Rule for the custom source
|
||||
type CpuIDRule struct {
|
||||
expression.MatchExpressionSet
|
||||
nfdv1alpha1.MatchExpressionSet
|
||||
}
|
||||
|
||||
func (r *CpuIDRule) Match() (bool, error) {
|
||||
|
|
|
@ -19,14 +19,14 @@ package rules
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
|
||||
"sigs.k8s.io/node-feature-discovery/source"
|
||||
"sigs.k8s.io/node-feature-discovery/source/custom/expression"
|
||||
"sigs.k8s.io/node-feature-discovery/source/kernel"
|
||||
)
|
||||
|
||||
// KconfigRule implements Rule for the custom source
|
||||
type KconfigRule struct {
|
||||
expression.MatchExpressionSet
|
||||
nfdv1alpha1.MatchExpressionSet
|
||||
}
|
||||
|
||||
func (r *KconfigRule) Match() (bool, error) {
|
||||
|
|
|
@ -19,14 +19,14 @@ package rules
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
|
||||
"sigs.k8s.io/node-feature-discovery/source"
|
||||
"sigs.k8s.io/node-feature-discovery/source/custom/expression"
|
||||
"sigs.k8s.io/node-feature-discovery/source/kernel"
|
||||
)
|
||||
|
||||
// LoadedKModRule matches loaded kernel modules in the system
|
||||
type LoadedKModRule struct {
|
||||
expression.MatchExpressionSet
|
||||
nfdv1alpha1.MatchExpressionSet
|
||||
}
|
||||
|
||||
// Match loaded kernel modules on provided list of kernel modules
|
||||
|
|
|
@ -20,14 +20,14 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
|
||||
"sigs.k8s.io/node-feature-discovery/source"
|
||||
"sigs.k8s.io/node-feature-discovery/source/custom/expression"
|
||||
"sigs.k8s.io/node-feature-discovery/source/system"
|
||||
)
|
||||
|
||||
// NodenameRule matches on nodenames configured in a ConfigMap
|
||||
type NodenameRule struct {
|
||||
expression.MatchExpression
|
||||
nfdv1alpha1.MatchExpression
|
||||
}
|
||||
|
||||
func (r *NodenameRule) Match() (bool, error) {
|
||||
|
@ -43,8 +43,8 @@ func (r *NodenameRule) UnmarshalJSON(data []byte) error {
|
|||
return err
|
||||
}
|
||||
// Force regexp matching
|
||||
if r.Op == expression.MatchIn {
|
||||
r.Op = expression.MatchInRegexp
|
||||
if r.Op == nfdv1alpha1.MatchIn {
|
||||
r.Op = nfdv1alpha1.MatchInRegexp
|
||||
}
|
||||
// We need to run Validate() because operator forcing above
|
||||
return r.Validate()
|
||||
|
|
|
@ -19,13 +19,13 @@ package rules
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
|
||||
"sigs.k8s.io/node-feature-discovery/source"
|
||||
"sigs.k8s.io/node-feature-discovery/source/custom/expression"
|
||||
"sigs.k8s.io/node-feature-discovery/source/pci"
|
||||
)
|
||||
|
||||
type PciIDRule struct {
|
||||
expression.MatchExpressionSet
|
||||
nfdv1alpha1.MatchExpressionSet
|
||||
}
|
||||
|
||||
// Match PCI devices on provided PCI device attributes
|
||||
|
|
|
@ -19,13 +19,13 @@ package rules
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
|
||||
"sigs.k8s.io/node-feature-discovery/source"
|
||||
"sigs.k8s.io/node-feature-discovery/source/custom/expression"
|
||||
"sigs.k8s.io/node-feature-discovery/source/usb"
|
||||
)
|
||||
|
||||
type UsbIDRule struct {
|
||||
expression.MatchExpressionSet
|
||||
nfdv1alpha1.MatchExpressionSet
|
||||
}
|
||||
|
||||
// Match USB devices on provided USB device attributes
|
||||
|
|
|
@ -17,7 +17,7 @@ limitations under the License.
|
|||
package custom
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/node-feature-discovery/source/custom/expression"
|
||||
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
|
||||
"sigs.k8s.io/node-feature-discovery/source/custom/rules"
|
||||
)
|
||||
|
||||
|
@ -31,8 +31,10 @@ func getStaticFeatureConfig() []CustomRule {
|
|||
MatchOn: []LegacyMatcher{
|
||||
{
|
||||
PciID: &rules.PciIDRule{
|
||||
MatchExpressionSet: expression.MatchExpressionSet{
|
||||
"vendor": expression.MustCreateMatchExpression(expression.MatchIn, "15b3"),
|
||||
MatchExpressionSet: nfdv1alpha1.MatchExpressionSet{
|
||||
Expressions: nfdv1alpha1.Expressions{
|
||||
"vendor": nfdv1alpha1.MustCreateMatchExpression(nfdv1alpha1.MatchIn, "15b3"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -45,9 +47,11 @@ func getStaticFeatureConfig() []CustomRule {
|
|||
MatchOn: []LegacyMatcher{
|
||||
{
|
||||
LoadedKMod: &rules.LoadedKModRule{
|
||||
MatchExpressionSet: expression.MatchExpressionSet{
|
||||
"ib_uverbs": expression.MustCreateMatchExpression(expression.MatchExists),
|
||||
"rdma_ucm": expression.MustCreateMatchExpression(expression.MatchExists),
|
||||
MatchExpressionSet: nfdv1alpha1.MatchExpressionSet{
|
||||
Expressions: nfdv1alpha1.Expressions{
|
||||
"ib_uverbs": nfdv1alpha1.MustCreateMatchExpression(nfdv1alpha1.MatchExists),
|
||||
"rdma_ucm": nfdv1alpha1.MustCreateMatchExpression(nfdv1alpha1.MatchExists),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue