mirror of
https://github.com/kubernetes-sigs/node-feature-discovery.git
synced 2025-03-05 08:17:04 +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 mod vendor
|
||||||
go generate ./cmd/... ./pkg/... ./source/...
|
go generate ./cmd/... ./pkg/... ./source/...
|
||||||
rm -rf vendor/
|
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:
|
gofmt:
|
||||||
@$(GO_FMT) -w -l $$(find . -name '*.go')
|
@$(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: ""
|
nameOverride: ""
|
||||||
fullnameOverride: ""
|
fullnameOverride: ""
|
||||||
|
|
||||||
|
nodeFeatureRule:
|
||||||
|
createCRD: true
|
||||||
|
|
||||||
master:
|
master:
|
||||||
instance:
|
instance:
|
||||||
extraLabelNs: []
|
extraLabelNs: []
|
||||||
|
|
|
@ -5,6 +5,7 @@ namespace: node-feature-discovery
|
||||||
|
|
||||||
bases:
|
bases:
|
||||||
- ../../base/rbac
|
- ../../base/rbac
|
||||||
|
- ../../base/nfd-crds
|
||||||
- ../../base/master-worker-combined
|
- ../../base/master-worker-combined
|
||||||
|
|
||||||
resources:
|
resources:
|
||||||
|
|
|
@ -5,6 +5,7 @@ namespace: node-feature-discovery
|
||||||
|
|
||||||
bases:
|
bases:
|
||||||
- ../../base/rbac
|
- ../../base/rbac
|
||||||
|
- ../../base/nfd-crds
|
||||||
- ../../base/master
|
- ../../base/master
|
||||||
- ../../base/worker-job
|
- ../../base/worker-job
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ namespace: node-feature-discovery
|
||||||
|
|
||||||
bases:
|
bases:
|
||||||
- ../../base/rbac
|
- ../../base/rbac
|
||||||
|
- ../../base/nfd-crds
|
||||||
- ../../base/master
|
- ../../base/master
|
||||||
- ../../base/worker-daemonset
|
- ../../base/worker-daemonset
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ namespace: node-feature-discovery
|
||||||
bases:
|
bases:
|
||||||
- ../../base/rbac
|
- ../../base/rbac
|
||||||
- ../../base/rbac-topologyupdater
|
- ../../base/rbac-topologyupdater
|
||||||
|
- ../../base/nfd-crds
|
||||||
- ../../base/master
|
- ../../base/master
|
||||||
- ../../base/worker-daemonset
|
- ../../base/worker-daemonset
|
||||||
- ../../base/noderesourcetopologies-crd
|
- ../../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/) |
|
| `rbac` | dict | | RBAC [parameteres](https://kubernetes.io/docs/reference/access-authn-authz/rbac/) |
|
||||||
| `nameOverride` | string | | Override the name of the chart |
|
| `nameOverride` | string | | Override the name of the chart |
|
||||||
| `fullnameOverride` | string | | Override a default fully qualified app name |
|
| `fullnameOverride` | string | | Override a default fully qualified app name |
|
||||||
|
| `nodeFeatureRule.createCRD` | bool | true | Specifies whether to create the NodeFeatureRule CRD |
|
||||||
|
|
||||||
##### Master pod parameters
|
##### 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/go-ozzo/ozzo-validation v3.5.0+incompatible // indirect
|
||||||
github.com/godbus/dbus/v5 v5.0.4 // indirect
|
github.com/godbus/dbus/v5 v5.0.4 // indirect
|
||||||
github.com/gofrs/uuid v4.0.0+incompatible // 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/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
github.com/golang/mock v1.4.4 // indirect
|
github.com/golang/mock v1.4.4 // indirect
|
||||||
github.com/google/cadvisor v0.39.2 // 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.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package expression
|
package v1alpha1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -29,80 +29,6 @@ import (
|
||||||
"sigs.k8s.io/node-feature-discovery/pkg/api/feature"
|
"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{}{
|
var matchOps = map[MatchOp]struct{}{
|
||||||
MatchAny: struct{}{},
|
MatchAny: struct{}{},
|
||||||
MatchIn: struct{}{},
|
MatchIn: struct{}{},
|
||||||
|
@ -117,6 +43,18 @@ var matchOps = map[MatchOp]struct{}{
|
||||||
MatchIsFalse: 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
|
// CreateMatchExpression creates a new MatchExpression instance. Returns an
|
||||||
// error if validation fails.
|
// error if validation fails.
|
||||||
func CreateMatchExpression(op MatchOp, values ...string) (*MatchExpression, error) {
|
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.
|
// MatchKeys evaluates the MatchExpressionSet against a set of keys.
|
||||||
func (m *MatchExpressionSet) MatchKeys(keys map[string]feature.Nil) (bool, error) {
|
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)
|
match, err := e.MatchKeys(n, keys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
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.
|
// MatchValues evaluates the MatchExpressionSet against a set of key-value pairs.
|
||||||
func (m *MatchExpressionSet) MatchValues(values map[string]string) (bool, error) {
|
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)
|
match, err := e.MatchValues(n, values)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
@ -418,7 +356,7 @@ func (m *MatchExpressionSet) MatchInstances(instances []feature.InstanceFeature)
|
||||||
|
|
||||||
// UnmarshalJSON implements the Unmarshaler interface of "encoding/json".
|
// UnmarshalJSON implements the Unmarshaler interface of "encoding/json".
|
||||||
func (m *MatchExpressionSet) UnmarshalJSON(data []byte) error {
|
func (m *MatchExpressionSet) UnmarshalJSON(data []byte) error {
|
||||||
*m = make(MatchExpressionSet)
|
*m = *NewMatchExpressionSet()
|
||||||
|
|
||||||
names := make([]string, 0)
|
names := make([]string, 0)
|
||||||
if err := json.Unmarshal(data, &names); err == nil {
|
if err := json.Unmarshal(data, &names); err == nil {
|
||||||
|
@ -426,9 +364,9 @@ func (m *MatchExpressionSet) UnmarshalJSON(data []byte) error {
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
split := strings.SplitN(name, "=", 2)
|
split := strings.SplitN(name, "=", 2)
|
||||||
if len(split) == 1 {
|
if len(split) == 1 {
|
||||||
(*m)[split[0]] = newMatchExpression(MatchExists)
|
(*m).Expressions[split[0]] = newMatchExpression(MatchExists)
|
||||||
} else {
|
} else {
|
||||||
(*m)[split[0]] = newMatchExpression(MatchIn, split[1])
|
(*m).Expressions[split[0]] = newMatchExpression(MatchIn, split[1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -439,9 +377,9 @@ func (m *MatchExpressionSet) UnmarshalJSON(data []byte) error {
|
||||||
} else {
|
} else {
|
||||||
for k, v := range expressions {
|
for k, v := range expressions {
|
||||||
if v != nil {
|
if v != nil {
|
||||||
(*m)[k] = v
|
(*m).Expressions[k] = v
|
||||||
} else {
|
} else {
|
||||||
(*m)[k] = newMatchExpression(MatchExists)
|
(*m).Expressions[k] = newMatchExpression(MatchExists)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -496,3 +434,22 @@ func (m *MatchValue) UnmarshalJSON(data []byte) error {
|
||||||
|
|
||||||
return nil
|
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/yaml"
|
||||||
|
|
||||||
"sigs.k8s.io/node-feature-discovery/pkg/api/feature"
|
"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/pkg/utils"
|
||||||
"sigs.k8s.io/node-feature-discovery/source"
|
"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"
|
"sigs.k8s.io/node-feature-discovery/source/custom/rules"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ type FeatureMatcher []FeatureMatcherTerm
|
||||||
|
|
||||||
type FeatureMatcherTerm struct {
|
type FeatureMatcherTerm struct {
|
||||||
Feature string
|
Feature string
|
||||||
MatchExpressions expression.MatchExpressionSet
|
MatchExpressions nfdv1alpha1.MatchExpressionSet
|
||||||
}
|
}
|
||||||
|
|
||||||
type config []CustomRule
|
type config []CustomRule
|
||||||
|
|
|
@ -21,7 +21,7 @@ import (
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"sigs.k8s.io/node-feature-discovery/pkg/api/feature"
|
"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) {
|
func TestRule(t *testing.T) {
|
||||||
|
@ -32,7 +32,11 @@ func TestRule(t *testing.T) {
|
||||||
MatchFeatures: FeatureMatcher{
|
MatchFeatures: FeatureMatcher{
|
||||||
FeatureMatcherTerm{
|
FeatureMatcherTerm{
|
||||||
Feature: "domain-1.kf-1",
|
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{
|
MatchFeatures: FeatureMatcher{
|
||||||
FeatureMatcherTerm{
|
FeatureMatcherTerm{
|
||||||
Feature: "domain-1.vf-1",
|
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{
|
MatchFeatures: FeatureMatcher{
|
||||||
FeatureMatcherTerm{
|
FeatureMatcherTerm{
|
||||||
Feature: "domain-1.if-1",
|
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{
|
MatchFeatures: FeatureMatcher{
|
||||||
FeatureMatcherTerm{
|
FeatureMatcherTerm{
|
||||||
Feature: "domain-1.vf-1",
|
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{
|
FeatureMatcherTerm{
|
||||||
Feature: "domain-1.if-1",
|
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.Nilf(t, err, "unexpected error: %v", err)
|
||||||
assert.Nil(t, m, "instances should not have matched")
|
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)
|
m, err = r5.execute(f)
|
||||||
assert.Nilf(t, err, "unexpected error: %v", err)
|
assert.Nilf(t, err, "unexpected error: %v", err)
|
||||||
assert.Equal(t, r5.Labels, m, "instances should have matched")
|
assert.Equal(t, r5.Labels, m, "instances should have matched")
|
||||||
|
@ -156,7 +176,11 @@ func TestRule(t *testing.T) {
|
||||||
MatchFeatures: FeatureMatcher{
|
MatchFeatures: FeatureMatcher{
|
||||||
FeatureMatcherTerm{
|
FeatureMatcherTerm{
|
||||||
Feature: "domain-1.kf-1",
|
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{
|
MatchFeatures: FeatureMatcher{
|
||||||
FeatureMatcherTerm{
|
FeatureMatcherTerm{
|
||||||
Feature: "domain-1.kf-1",
|
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)
|
m, err = r5.execute(f)
|
||||||
assert.Nilf(t, err, "unexpected error: %v", err)
|
assert.Nilf(t, err, "unexpected error: %v", err)
|
||||||
assert.Equal(t, r5.Labels, m, "instances should have matched")
|
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 (
|
import (
|
||||||
"fmt"
|
"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"
|
||||||
"sigs.k8s.io/node-feature-discovery/source/cpu"
|
"sigs.k8s.io/node-feature-discovery/source/cpu"
|
||||||
"sigs.k8s.io/node-feature-discovery/source/custom/expression"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CpuIDRule implements Rule for the custom source
|
// CpuIDRule implements Rule for the custom source
|
||||||
type CpuIDRule struct {
|
type CpuIDRule struct {
|
||||||
expression.MatchExpressionSet
|
nfdv1alpha1.MatchExpressionSet
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *CpuIDRule) Match() (bool, error) {
|
func (r *CpuIDRule) Match() (bool, error) {
|
||||||
|
|
|
@ -19,14 +19,14 @@ package rules
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"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"
|
||||||
"sigs.k8s.io/node-feature-discovery/source/custom/expression"
|
|
||||||
"sigs.k8s.io/node-feature-discovery/source/kernel"
|
"sigs.k8s.io/node-feature-discovery/source/kernel"
|
||||||
)
|
)
|
||||||
|
|
||||||
// KconfigRule implements Rule for the custom source
|
// KconfigRule implements Rule for the custom source
|
||||||
type KconfigRule struct {
|
type KconfigRule struct {
|
||||||
expression.MatchExpressionSet
|
nfdv1alpha1.MatchExpressionSet
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *KconfigRule) Match() (bool, error) {
|
func (r *KconfigRule) Match() (bool, error) {
|
||||||
|
|
|
@ -19,14 +19,14 @@ package rules
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"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"
|
||||||
"sigs.k8s.io/node-feature-discovery/source/custom/expression"
|
|
||||||
"sigs.k8s.io/node-feature-discovery/source/kernel"
|
"sigs.k8s.io/node-feature-discovery/source/kernel"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LoadedKModRule matches loaded kernel modules in the system
|
// LoadedKModRule matches loaded kernel modules in the system
|
||||||
type LoadedKModRule struct {
|
type LoadedKModRule struct {
|
||||||
expression.MatchExpressionSet
|
nfdv1alpha1.MatchExpressionSet
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match loaded kernel modules on provided list of kernel modules
|
// Match loaded kernel modules on provided list of kernel modules
|
||||||
|
|
|
@ -20,14 +20,14 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"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"
|
||||||
"sigs.k8s.io/node-feature-discovery/source/custom/expression"
|
|
||||||
"sigs.k8s.io/node-feature-discovery/source/system"
|
"sigs.k8s.io/node-feature-discovery/source/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NodenameRule matches on nodenames configured in a ConfigMap
|
// NodenameRule matches on nodenames configured in a ConfigMap
|
||||||
type NodenameRule struct {
|
type NodenameRule struct {
|
||||||
expression.MatchExpression
|
nfdv1alpha1.MatchExpression
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *NodenameRule) Match() (bool, error) {
|
func (r *NodenameRule) Match() (bool, error) {
|
||||||
|
@ -43,8 +43,8 @@ func (r *NodenameRule) UnmarshalJSON(data []byte) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Force regexp matching
|
// Force regexp matching
|
||||||
if r.Op == expression.MatchIn {
|
if r.Op == nfdv1alpha1.MatchIn {
|
||||||
r.Op = expression.MatchInRegexp
|
r.Op = nfdv1alpha1.MatchInRegexp
|
||||||
}
|
}
|
||||||
// We need to run Validate() because operator forcing above
|
// We need to run Validate() because operator forcing above
|
||||||
return r.Validate()
|
return r.Validate()
|
||||||
|
|
|
@ -19,13 +19,13 @@ package rules
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"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"
|
||||||
"sigs.k8s.io/node-feature-discovery/source/custom/expression"
|
|
||||||
"sigs.k8s.io/node-feature-discovery/source/pci"
|
"sigs.k8s.io/node-feature-discovery/source/pci"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PciIDRule struct {
|
type PciIDRule struct {
|
||||||
expression.MatchExpressionSet
|
nfdv1alpha1.MatchExpressionSet
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match PCI devices on provided PCI device attributes
|
// Match PCI devices on provided PCI device attributes
|
||||||
|
|
|
@ -19,13 +19,13 @@ package rules
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"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"
|
||||||
"sigs.k8s.io/node-feature-discovery/source/custom/expression"
|
|
||||||
"sigs.k8s.io/node-feature-discovery/source/usb"
|
"sigs.k8s.io/node-feature-discovery/source/usb"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UsbIDRule struct {
|
type UsbIDRule struct {
|
||||||
expression.MatchExpressionSet
|
nfdv1alpha1.MatchExpressionSet
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match USB devices on provided USB device attributes
|
// Match USB devices on provided USB device attributes
|
||||||
|
|
|
@ -17,7 +17,7 @@ limitations under the License.
|
||||||
package custom
|
package custom
|
||||||
|
|
||||||
import (
|
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"
|
"sigs.k8s.io/node-feature-discovery/source/custom/rules"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -31,8 +31,10 @@ func getStaticFeatureConfig() []CustomRule {
|
||||||
MatchOn: []LegacyMatcher{
|
MatchOn: []LegacyMatcher{
|
||||||
{
|
{
|
||||||
PciID: &rules.PciIDRule{
|
PciID: &rules.PciIDRule{
|
||||||
MatchExpressionSet: expression.MatchExpressionSet{
|
MatchExpressionSet: nfdv1alpha1.MatchExpressionSet{
|
||||||
"vendor": expression.MustCreateMatchExpression(expression.MatchIn, "15b3"),
|
Expressions: nfdv1alpha1.Expressions{
|
||||||
|
"vendor": nfdv1alpha1.MustCreateMatchExpression(nfdv1alpha1.MatchIn, "15b3"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -45,9 +47,11 @@ func getStaticFeatureConfig() []CustomRule {
|
||||||
MatchOn: []LegacyMatcher{
|
MatchOn: []LegacyMatcher{
|
||||||
{
|
{
|
||||||
LoadedKMod: &rules.LoadedKModRule{
|
LoadedKMod: &rules.LoadedKModRule{
|
||||||
MatchExpressionSet: expression.MatchExpressionSet{
|
MatchExpressionSet: nfdv1alpha1.MatchExpressionSet{
|
||||||
"ib_uverbs": expression.MustCreateMatchExpression(expression.MatchExists),
|
Expressions: nfdv1alpha1.Expressions{
|
||||||
"rdma_ucm": expression.MustCreateMatchExpression(expression.MatchExists),
|
"ib_uverbs": nfdv1alpha1.MustCreateMatchExpression(nfdv1alpha1.MatchExists),
|
||||||
|
"rdma_ucm": nfdv1alpha1.MustCreateMatchExpression(nfdv1alpha1.MatchExists),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Reference in a new issue