1
0
Fork 0
mirror of https://github.com/kubernetes-sigs/node-feature-discovery.git synced 2025-03-06 16:57:10 +00:00

Merge branch 'master' into feat/skip-nodes

This commit is contained in:
Markus Lehtonen 2024-12-20 10:38:44 +02:00 committed by GitHub
commit 97345a4a96
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
53 changed files with 2386 additions and 2913 deletions

View file

@ -5,18 +5,7 @@ FROM ${BUILDER_IMAGE} as builder
RUN --mount=type=cache,target=/go/pkg/mod/ \ RUN --mount=type=cache,target=/go/pkg/mod/ \
go install github.com/vektra/mockery/v2@v2.42.0 && \ go install github.com/vektra/mockery/v2@v2.42.0 && \
go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.16.3 && \ go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.16.3 && \
go install golang.org/x/tools/cmd/goimports@v0.25.0 && \ go install golang.org/x/tools/cmd/goimports@v0.25.0
go install github.com/golang/protobuf/protoc-gen-go@v1.4.3
RUN apt-get update && apt-get install unzip
RUN curl -LO https://github.com/protocolbuffers/protobuf/releases/download/v25.3/protoc-25.3-linux-x86_64.zip && \
unzip protoc-25.3-linux-x86_64.zip -d /usr/local && \
rm protoc-25.3-linux-x86_64.zip && \
chmod a+x /usr/local/bin/protoc && \
find /usr/local -type d | xargs chmod 755 && \
find /usr/local -type f | xargs chmod a+r
# Expect to be working with nfd # Expect to be working with nfd
WORKDIR /go/node-feature-discovery WORKDIR /go/node-feature-discovery

View file

@ -90,7 +90,7 @@ IMAGE_BUILD_ARGS_MINIMAL = --target minimal \
all: image all: image
BUILD_BINARIES := nfd-master nfd-worker nfd-topology-updater nfd-gc kubectl-nfd BUILD_BINARIES := nfd-master nfd-worker nfd-topology-updater nfd-gc kubectl-nfd nfd
build-%: build-%:
$(GO_CMD) build -v -o bin/ $(BUILD_FLAGS) ./cmd/$* $(GO_CMD) build -v -o bin/ $(BUILD_FLAGS) ./cmd/$*

View file

@ -19,8 +19,8 @@ limitations under the License.
package versioned package versioned
import ( import (
"fmt" fmt "fmt"
"net/http" http "net/http"
discovery "k8s.io/client-go/discovery" discovery "k8s.io/client-go/discovery"
rest "k8s.io/client-go/rest" rest "k8s.io/client-go/rest"

View file

@ -29,15 +29,15 @@ type FakeNfdV1alpha1 struct {
} }
func (c *FakeNfdV1alpha1) NodeFeatures(namespace string) v1alpha1.NodeFeatureInterface { func (c *FakeNfdV1alpha1) NodeFeatures(namespace string) v1alpha1.NodeFeatureInterface {
return &FakeNodeFeatures{c, namespace} return newFakeNodeFeatures(c, namespace)
} }
func (c *FakeNfdV1alpha1) NodeFeatureGroups(namespace string) v1alpha1.NodeFeatureGroupInterface { func (c *FakeNfdV1alpha1) NodeFeatureGroups(namespace string) v1alpha1.NodeFeatureGroupInterface {
return &FakeNodeFeatureGroups{c, namespace} return newFakeNodeFeatureGroups(c, namespace)
} }
func (c *FakeNfdV1alpha1) NodeFeatureRules() v1alpha1.NodeFeatureRuleInterface { func (c *FakeNfdV1alpha1) NodeFeatureRules() v1alpha1.NodeFeatureRuleInterface {
return &FakeNodeFeatureRules{c} return newFakeNodeFeatureRules(c)
} }
// RESTClient returns a RESTClient that is used to communicate // RESTClient returns a RESTClient that is used to communicate

View file

@ -19,116 +19,34 @@ limitations under the License.
package fake package fake
import ( import (
"context" gentype "k8s.io/client-go/gentype"
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned/typed/nfd/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
v1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1" v1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
) )
// FakeNodeFeatures implements NodeFeatureInterface // fakeNodeFeatures implements NodeFeatureInterface
type FakeNodeFeatures struct { type fakeNodeFeatures struct {
*gentype.FakeClientWithList[*v1alpha1.NodeFeature, *v1alpha1.NodeFeatureList]
Fake *FakeNfdV1alpha1 Fake *FakeNfdV1alpha1
ns string
} }
var nodefeaturesResource = v1alpha1.SchemeGroupVersion.WithResource("nodefeatures") func newFakeNodeFeatures(fake *FakeNfdV1alpha1, namespace string) nfdv1alpha1.NodeFeatureInterface {
return &fakeNodeFeatures{
var nodefeaturesKind = v1alpha1.SchemeGroupVersion.WithKind("NodeFeature") gentype.NewFakeClientWithList[*v1alpha1.NodeFeature, *v1alpha1.NodeFeatureList](
fake.Fake,
// Get takes name of the nodeFeature, and returns the corresponding nodeFeature object, and an error if there is any. namespace,
func (c *FakeNodeFeatures) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.NodeFeature, err error) { v1alpha1.SchemeGroupVersion.WithResource("nodefeatures"),
emptyResult := &v1alpha1.NodeFeature{} v1alpha1.SchemeGroupVersion.WithKind("NodeFeature"),
obj, err := c.Fake. func() *v1alpha1.NodeFeature { return &v1alpha1.NodeFeature{} },
Invokes(testing.NewGetActionWithOptions(nodefeaturesResource, c.ns, name, options), emptyResult) func() *v1alpha1.NodeFeatureList { return &v1alpha1.NodeFeatureList{} },
func(dst, src *v1alpha1.NodeFeatureList) { dst.ListMeta = src.ListMeta },
if obj == nil { func(list *v1alpha1.NodeFeatureList) []*v1alpha1.NodeFeature {
return emptyResult, err return gentype.ToPointerSlice(list.Items)
},
func(list *v1alpha1.NodeFeatureList, items []*v1alpha1.NodeFeature) {
list.Items = gentype.FromPointerSlice(items)
},
),
fake,
} }
return obj.(*v1alpha1.NodeFeature), err
}
// List takes label and field selectors, and returns the list of NodeFeatures that match those selectors.
func (c *FakeNodeFeatures) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.NodeFeatureList, err error) {
emptyResult := &v1alpha1.NodeFeatureList{}
obj, err := c.Fake.
Invokes(testing.NewListActionWithOptions(nodefeaturesResource, nodefeaturesKind, c.ns, opts), emptyResult)
if obj == nil {
return emptyResult, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &v1alpha1.NodeFeatureList{ListMeta: obj.(*v1alpha1.NodeFeatureList).ListMeta}
for _, item := range obj.(*v1alpha1.NodeFeatureList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested nodeFeatures.
func (c *FakeNodeFeatures) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchActionWithOptions(nodefeaturesResource, c.ns, opts))
}
// Create takes the representation of a nodeFeature and creates it. Returns the server's representation of the nodeFeature, and an error, if there is any.
func (c *FakeNodeFeatures) Create(ctx context.Context, nodeFeature *v1alpha1.NodeFeature, opts v1.CreateOptions) (result *v1alpha1.NodeFeature, err error) {
emptyResult := &v1alpha1.NodeFeature{}
obj, err := c.Fake.
Invokes(testing.NewCreateActionWithOptions(nodefeaturesResource, c.ns, nodeFeature, opts), emptyResult)
if obj == nil {
return emptyResult, err
}
return obj.(*v1alpha1.NodeFeature), err
}
// Update takes the representation of a nodeFeature and updates it. Returns the server's representation of the nodeFeature, and an error, if there is any.
func (c *FakeNodeFeatures) Update(ctx context.Context, nodeFeature *v1alpha1.NodeFeature, opts v1.UpdateOptions) (result *v1alpha1.NodeFeature, err error) {
emptyResult := &v1alpha1.NodeFeature{}
obj, err := c.Fake.
Invokes(testing.NewUpdateActionWithOptions(nodefeaturesResource, c.ns, nodeFeature, opts), emptyResult)
if obj == nil {
return emptyResult, err
}
return obj.(*v1alpha1.NodeFeature), err
}
// Delete takes name of the nodeFeature and deletes it. Returns an error if one occurs.
func (c *FakeNodeFeatures) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteActionWithOptions(nodefeaturesResource, c.ns, name, opts), &v1alpha1.NodeFeature{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeNodeFeatures) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
action := testing.NewDeleteCollectionActionWithOptions(nodefeaturesResource, c.ns, opts, listOpts)
_, err := c.Fake.Invokes(action, &v1alpha1.NodeFeatureList{})
return err
}
// Patch applies the patch and returns the patched nodeFeature.
func (c *FakeNodeFeatures) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.NodeFeature, err error) {
emptyResult := &v1alpha1.NodeFeature{}
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceActionWithOptions(nodefeaturesResource, c.ns, name, pt, data, opts, subresources...), emptyResult)
if obj == nil {
return emptyResult, err
}
return obj.(*v1alpha1.NodeFeature), err
} }

View file

@ -19,129 +19,34 @@ limitations under the License.
package fake package fake
import ( import (
"context" gentype "k8s.io/client-go/gentype"
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned/typed/nfd/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
v1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1" v1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
) )
// FakeNodeFeatureGroups implements NodeFeatureGroupInterface // fakeNodeFeatureGroups implements NodeFeatureGroupInterface
type FakeNodeFeatureGroups struct { type fakeNodeFeatureGroups struct {
*gentype.FakeClientWithList[*v1alpha1.NodeFeatureGroup, *v1alpha1.NodeFeatureGroupList]
Fake *FakeNfdV1alpha1 Fake *FakeNfdV1alpha1
ns string
} }
var nodefeaturegroupsResource = v1alpha1.SchemeGroupVersion.WithResource("nodefeaturegroups") func newFakeNodeFeatureGroups(fake *FakeNfdV1alpha1, namespace string) nfdv1alpha1.NodeFeatureGroupInterface {
return &fakeNodeFeatureGroups{
var nodefeaturegroupsKind = v1alpha1.SchemeGroupVersion.WithKind("NodeFeatureGroup") gentype.NewFakeClientWithList[*v1alpha1.NodeFeatureGroup, *v1alpha1.NodeFeatureGroupList](
fake.Fake,
// Get takes name of the nodeFeatureGroup, and returns the corresponding nodeFeatureGroup object, and an error if there is any. namespace,
func (c *FakeNodeFeatureGroups) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.NodeFeatureGroup, err error) { v1alpha1.SchemeGroupVersion.WithResource("nodefeaturegroups"),
emptyResult := &v1alpha1.NodeFeatureGroup{} v1alpha1.SchemeGroupVersion.WithKind("NodeFeatureGroup"),
obj, err := c.Fake. func() *v1alpha1.NodeFeatureGroup { return &v1alpha1.NodeFeatureGroup{} },
Invokes(testing.NewGetActionWithOptions(nodefeaturegroupsResource, c.ns, name, options), emptyResult) func() *v1alpha1.NodeFeatureGroupList { return &v1alpha1.NodeFeatureGroupList{} },
func(dst, src *v1alpha1.NodeFeatureGroupList) { dst.ListMeta = src.ListMeta },
if obj == nil { func(list *v1alpha1.NodeFeatureGroupList) []*v1alpha1.NodeFeatureGroup {
return emptyResult, err return gentype.ToPointerSlice(list.Items)
},
func(list *v1alpha1.NodeFeatureGroupList, items []*v1alpha1.NodeFeatureGroup) {
list.Items = gentype.FromPointerSlice(items)
},
),
fake,
} }
return obj.(*v1alpha1.NodeFeatureGroup), err
}
// List takes label and field selectors, and returns the list of NodeFeatureGroups that match those selectors.
func (c *FakeNodeFeatureGroups) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.NodeFeatureGroupList, err error) {
emptyResult := &v1alpha1.NodeFeatureGroupList{}
obj, err := c.Fake.
Invokes(testing.NewListActionWithOptions(nodefeaturegroupsResource, nodefeaturegroupsKind, c.ns, opts), emptyResult)
if obj == nil {
return emptyResult, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &v1alpha1.NodeFeatureGroupList{ListMeta: obj.(*v1alpha1.NodeFeatureGroupList).ListMeta}
for _, item := range obj.(*v1alpha1.NodeFeatureGroupList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested nodeFeatureGroups.
func (c *FakeNodeFeatureGroups) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchActionWithOptions(nodefeaturegroupsResource, c.ns, opts))
}
// Create takes the representation of a nodeFeatureGroup and creates it. Returns the server's representation of the nodeFeatureGroup, and an error, if there is any.
func (c *FakeNodeFeatureGroups) Create(ctx context.Context, nodeFeatureGroup *v1alpha1.NodeFeatureGroup, opts v1.CreateOptions) (result *v1alpha1.NodeFeatureGroup, err error) {
emptyResult := &v1alpha1.NodeFeatureGroup{}
obj, err := c.Fake.
Invokes(testing.NewCreateActionWithOptions(nodefeaturegroupsResource, c.ns, nodeFeatureGroup, opts), emptyResult)
if obj == nil {
return emptyResult, err
}
return obj.(*v1alpha1.NodeFeatureGroup), err
}
// Update takes the representation of a nodeFeatureGroup and updates it. Returns the server's representation of the nodeFeatureGroup, and an error, if there is any.
func (c *FakeNodeFeatureGroups) Update(ctx context.Context, nodeFeatureGroup *v1alpha1.NodeFeatureGroup, opts v1.UpdateOptions) (result *v1alpha1.NodeFeatureGroup, err error) {
emptyResult := &v1alpha1.NodeFeatureGroup{}
obj, err := c.Fake.
Invokes(testing.NewUpdateActionWithOptions(nodefeaturegroupsResource, c.ns, nodeFeatureGroup, opts), emptyResult)
if obj == nil {
return emptyResult, err
}
return obj.(*v1alpha1.NodeFeatureGroup), err
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *FakeNodeFeatureGroups) UpdateStatus(ctx context.Context, nodeFeatureGroup *v1alpha1.NodeFeatureGroup, opts v1.UpdateOptions) (result *v1alpha1.NodeFeatureGroup, err error) {
emptyResult := &v1alpha1.NodeFeatureGroup{}
obj, err := c.Fake.
Invokes(testing.NewUpdateSubresourceActionWithOptions(nodefeaturegroupsResource, "status", c.ns, nodeFeatureGroup, opts), emptyResult)
if obj == nil {
return emptyResult, err
}
return obj.(*v1alpha1.NodeFeatureGroup), err
}
// Delete takes name of the nodeFeatureGroup and deletes it. Returns an error if one occurs.
func (c *FakeNodeFeatureGroups) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteActionWithOptions(nodefeaturegroupsResource, c.ns, name, opts), &v1alpha1.NodeFeatureGroup{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeNodeFeatureGroups) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
action := testing.NewDeleteCollectionActionWithOptions(nodefeaturegroupsResource, c.ns, opts, listOpts)
_, err := c.Fake.Invokes(action, &v1alpha1.NodeFeatureGroupList{})
return err
}
// Patch applies the patch and returns the patched nodeFeatureGroup.
func (c *FakeNodeFeatureGroups) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.NodeFeatureGroup, err error) {
emptyResult := &v1alpha1.NodeFeatureGroup{}
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceActionWithOptions(nodefeaturegroupsResource, c.ns, name, pt, data, opts, subresources...), emptyResult)
if obj == nil {
return emptyResult, err
}
return obj.(*v1alpha1.NodeFeatureGroup), err
} }

View file

@ -19,108 +19,34 @@ limitations under the License.
package fake package fake
import ( import (
"context" gentype "k8s.io/client-go/gentype"
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned/typed/nfd/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
v1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1" v1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
) )
// FakeNodeFeatureRules implements NodeFeatureRuleInterface // fakeNodeFeatureRules implements NodeFeatureRuleInterface
type FakeNodeFeatureRules struct { type fakeNodeFeatureRules struct {
*gentype.FakeClientWithList[*v1alpha1.NodeFeatureRule, *v1alpha1.NodeFeatureRuleList]
Fake *FakeNfdV1alpha1 Fake *FakeNfdV1alpha1
} }
var nodefeaturerulesResource = v1alpha1.SchemeGroupVersion.WithResource("nodefeaturerules") func newFakeNodeFeatureRules(fake *FakeNfdV1alpha1) nfdv1alpha1.NodeFeatureRuleInterface {
return &fakeNodeFeatureRules{
var nodefeaturerulesKind = v1alpha1.SchemeGroupVersion.WithKind("NodeFeatureRule") gentype.NewFakeClientWithList[*v1alpha1.NodeFeatureRule, *v1alpha1.NodeFeatureRuleList](
fake.Fake,
// Get takes name of the nodeFeatureRule, and returns the corresponding nodeFeatureRule object, and an error if there is any. "",
func (c *FakeNodeFeatureRules) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.NodeFeatureRule, err error) { v1alpha1.SchemeGroupVersion.WithResource("nodefeaturerules"),
emptyResult := &v1alpha1.NodeFeatureRule{} v1alpha1.SchemeGroupVersion.WithKind("NodeFeatureRule"),
obj, err := c.Fake. func() *v1alpha1.NodeFeatureRule { return &v1alpha1.NodeFeatureRule{} },
Invokes(testing.NewRootGetActionWithOptions(nodefeaturerulesResource, name, options), emptyResult) func() *v1alpha1.NodeFeatureRuleList { return &v1alpha1.NodeFeatureRuleList{} },
if obj == nil { func(dst, src *v1alpha1.NodeFeatureRuleList) { dst.ListMeta = src.ListMeta },
return emptyResult, err func(list *v1alpha1.NodeFeatureRuleList) []*v1alpha1.NodeFeatureRule {
return gentype.ToPointerSlice(list.Items)
},
func(list *v1alpha1.NodeFeatureRuleList, items []*v1alpha1.NodeFeatureRule) {
list.Items = gentype.FromPointerSlice(items)
},
),
fake,
} }
return obj.(*v1alpha1.NodeFeatureRule), err
}
// List takes label and field selectors, and returns the list of NodeFeatureRules that match those selectors.
func (c *FakeNodeFeatureRules) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.NodeFeatureRuleList, err error) {
emptyResult := &v1alpha1.NodeFeatureRuleList{}
obj, err := c.Fake.
Invokes(testing.NewRootListActionWithOptions(nodefeaturerulesResource, nodefeaturerulesKind, opts), emptyResult)
if obj == nil {
return emptyResult, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &v1alpha1.NodeFeatureRuleList{ListMeta: obj.(*v1alpha1.NodeFeatureRuleList).ListMeta}
for _, item := range obj.(*v1alpha1.NodeFeatureRuleList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested nodeFeatureRules.
func (c *FakeNodeFeatureRules) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewRootWatchActionWithOptions(nodefeaturerulesResource, opts))
}
// Create takes the representation of a nodeFeatureRule and creates it. Returns the server's representation of the nodeFeatureRule, and an error, if there is any.
func (c *FakeNodeFeatureRules) Create(ctx context.Context, nodeFeatureRule *v1alpha1.NodeFeatureRule, opts v1.CreateOptions) (result *v1alpha1.NodeFeatureRule, err error) {
emptyResult := &v1alpha1.NodeFeatureRule{}
obj, err := c.Fake.
Invokes(testing.NewRootCreateActionWithOptions(nodefeaturerulesResource, nodeFeatureRule, opts), emptyResult)
if obj == nil {
return emptyResult, err
}
return obj.(*v1alpha1.NodeFeatureRule), err
}
// Update takes the representation of a nodeFeatureRule and updates it. Returns the server's representation of the nodeFeatureRule, and an error, if there is any.
func (c *FakeNodeFeatureRules) Update(ctx context.Context, nodeFeatureRule *v1alpha1.NodeFeatureRule, opts v1.UpdateOptions) (result *v1alpha1.NodeFeatureRule, err error) {
emptyResult := &v1alpha1.NodeFeatureRule{}
obj, err := c.Fake.
Invokes(testing.NewRootUpdateActionWithOptions(nodefeaturerulesResource, nodeFeatureRule, opts), emptyResult)
if obj == nil {
return emptyResult, err
}
return obj.(*v1alpha1.NodeFeatureRule), err
}
// Delete takes name of the nodeFeatureRule and deletes it. Returns an error if one occurs.
func (c *FakeNodeFeatureRules) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewRootDeleteActionWithOptions(nodefeaturerulesResource, name, opts), &v1alpha1.NodeFeatureRule{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeNodeFeatureRules) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
action := testing.NewRootDeleteCollectionActionWithOptions(nodefeaturerulesResource, opts, listOpts)
_, err := c.Fake.Invokes(action, &v1alpha1.NodeFeatureRuleList{})
return err
}
// Patch applies the patch and returns the patched nodeFeatureRule.
func (c *FakeNodeFeatureRules) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.NodeFeatureRule, err error) {
emptyResult := &v1alpha1.NodeFeatureRule{}
obj, err := c.Fake.
Invokes(testing.NewRootPatchSubresourceActionWithOptions(nodefeaturerulesResource, name, pt, data, opts, subresources...), emptyResult)
if obj == nil {
return emptyResult, err
}
return obj.(*v1alpha1.NodeFeatureRule), err
} }

View file

@ -19,11 +19,11 @@ limitations under the License.
package v1alpha1 package v1alpha1
import ( import (
"net/http" http "net/http"
rest "k8s.io/client-go/rest" rest "k8s.io/client-go/rest"
"sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned/scheme" scheme "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned/scheme"
v1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1" nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
) )
type NfdV1alpha1Interface interface { type NfdV1alpha1Interface interface {
@ -95,10 +95,10 @@ func New(c rest.Interface) *NfdV1alpha1Client {
} }
func setConfigDefaults(config *rest.Config) error { func setConfigDefaults(config *rest.Config) error {
gv := v1alpha1.SchemeGroupVersion gv := nfdv1alpha1.SchemeGroupVersion
config.GroupVersion = &gv config.GroupVersion = &gv
config.APIPath = "/apis" config.APIPath = "/apis"
config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() config.NegotiatedSerializer = rest.CodecFactoryForGeneratedClient(scheme.Scheme, scheme.Codecs).WithoutConversion()
if config.UserAgent == "" { if config.UserAgent == "" {
config.UserAgent = rest.DefaultKubernetesUserAgent() config.UserAgent = rest.DefaultKubernetesUserAgent()

View file

@ -19,14 +19,14 @@ limitations under the License.
package v1alpha1 package v1alpha1
import ( import (
"context" context "context"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types" types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch" watch "k8s.io/apimachinery/pkg/watch"
gentype "k8s.io/client-go/gentype" gentype "k8s.io/client-go/gentype"
scheme "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned/scheme" scheme "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned/scheme"
v1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1" nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
) )
// NodeFeaturesGetter has a method to return a NodeFeatureInterface. // NodeFeaturesGetter has a method to return a NodeFeatureInterface.
@ -37,31 +37,32 @@ type NodeFeaturesGetter interface {
// NodeFeatureInterface has methods to work with NodeFeature resources. // NodeFeatureInterface has methods to work with NodeFeature resources.
type NodeFeatureInterface interface { type NodeFeatureInterface interface {
Create(ctx context.Context, nodeFeature *v1alpha1.NodeFeature, opts v1.CreateOptions) (*v1alpha1.NodeFeature, error) Create(ctx context.Context, nodeFeature *nfdv1alpha1.NodeFeature, opts v1.CreateOptions) (*nfdv1alpha1.NodeFeature, error)
Update(ctx context.Context, nodeFeature *v1alpha1.NodeFeature, opts v1.UpdateOptions) (*v1alpha1.NodeFeature, error) Update(ctx context.Context, nodeFeature *nfdv1alpha1.NodeFeature, opts v1.UpdateOptions) (*nfdv1alpha1.NodeFeature, error)
Delete(ctx context.Context, name string, opts v1.DeleteOptions) error Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.NodeFeature, error) Get(ctx context.Context, name string, opts v1.GetOptions) (*nfdv1alpha1.NodeFeature, error)
List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.NodeFeatureList, error) List(ctx context.Context, opts v1.ListOptions) (*nfdv1alpha1.NodeFeatureList, error)
Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.NodeFeature, err error) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *nfdv1alpha1.NodeFeature, err error)
NodeFeatureExpansion NodeFeatureExpansion
} }
// nodeFeatures implements NodeFeatureInterface // nodeFeatures implements NodeFeatureInterface
type nodeFeatures struct { type nodeFeatures struct {
*gentype.ClientWithList[*v1alpha1.NodeFeature, *v1alpha1.NodeFeatureList] *gentype.ClientWithList[*nfdv1alpha1.NodeFeature, *nfdv1alpha1.NodeFeatureList]
} }
// newNodeFeatures returns a NodeFeatures // newNodeFeatures returns a NodeFeatures
func newNodeFeatures(c *NfdV1alpha1Client, namespace string) *nodeFeatures { func newNodeFeatures(c *NfdV1alpha1Client, namespace string) *nodeFeatures {
return &nodeFeatures{ return &nodeFeatures{
gentype.NewClientWithList[*v1alpha1.NodeFeature, *v1alpha1.NodeFeatureList]( gentype.NewClientWithList[*nfdv1alpha1.NodeFeature, *nfdv1alpha1.NodeFeatureList](
"nodefeatures", "nodefeatures",
c.RESTClient(), c.RESTClient(),
scheme.ParameterCodec, scheme.ParameterCodec,
namespace, namespace,
func() *v1alpha1.NodeFeature { return &v1alpha1.NodeFeature{} }, func() *nfdv1alpha1.NodeFeature { return &nfdv1alpha1.NodeFeature{} },
func() *v1alpha1.NodeFeatureList { return &v1alpha1.NodeFeatureList{} }), func() *nfdv1alpha1.NodeFeatureList { return &nfdv1alpha1.NodeFeatureList{} },
),
} }
} }

View file

@ -19,14 +19,14 @@ limitations under the License.
package v1alpha1 package v1alpha1
import ( import (
"context" context "context"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types" types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch" watch "k8s.io/apimachinery/pkg/watch"
gentype "k8s.io/client-go/gentype" gentype "k8s.io/client-go/gentype"
scheme "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned/scheme" scheme "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned/scheme"
v1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1" nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
) )
// NodeFeatureGroupsGetter has a method to return a NodeFeatureGroupInterface. // NodeFeatureGroupsGetter has a method to return a NodeFeatureGroupInterface.
@ -37,33 +37,34 @@ type NodeFeatureGroupsGetter interface {
// NodeFeatureGroupInterface has methods to work with NodeFeatureGroup resources. // NodeFeatureGroupInterface has methods to work with NodeFeatureGroup resources.
type NodeFeatureGroupInterface interface { type NodeFeatureGroupInterface interface {
Create(ctx context.Context, nodeFeatureGroup *v1alpha1.NodeFeatureGroup, opts v1.CreateOptions) (*v1alpha1.NodeFeatureGroup, error) Create(ctx context.Context, nodeFeatureGroup *nfdv1alpha1.NodeFeatureGroup, opts v1.CreateOptions) (*nfdv1alpha1.NodeFeatureGroup, error)
Update(ctx context.Context, nodeFeatureGroup *v1alpha1.NodeFeatureGroup, opts v1.UpdateOptions) (*v1alpha1.NodeFeatureGroup, error) Update(ctx context.Context, nodeFeatureGroup *nfdv1alpha1.NodeFeatureGroup, opts v1.UpdateOptions) (*nfdv1alpha1.NodeFeatureGroup, error)
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
UpdateStatus(ctx context.Context, nodeFeatureGroup *v1alpha1.NodeFeatureGroup, opts v1.UpdateOptions) (*v1alpha1.NodeFeatureGroup, error) UpdateStatus(ctx context.Context, nodeFeatureGroup *nfdv1alpha1.NodeFeatureGroup, opts v1.UpdateOptions) (*nfdv1alpha1.NodeFeatureGroup, error)
Delete(ctx context.Context, name string, opts v1.DeleteOptions) error Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.NodeFeatureGroup, error) Get(ctx context.Context, name string, opts v1.GetOptions) (*nfdv1alpha1.NodeFeatureGroup, error)
List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.NodeFeatureGroupList, error) List(ctx context.Context, opts v1.ListOptions) (*nfdv1alpha1.NodeFeatureGroupList, error)
Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.NodeFeatureGroup, err error) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *nfdv1alpha1.NodeFeatureGroup, err error)
NodeFeatureGroupExpansion NodeFeatureGroupExpansion
} }
// nodeFeatureGroups implements NodeFeatureGroupInterface // nodeFeatureGroups implements NodeFeatureGroupInterface
type nodeFeatureGroups struct { type nodeFeatureGroups struct {
*gentype.ClientWithList[*v1alpha1.NodeFeatureGroup, *v1alpha1.NodeFeatureGroupList] *gentype.ClientWithList[*nfdv1alpha1.NodeFeatureGroup, *nfdv1alpha1.NodeFeatureGroupList]
} }
// newNodeFeatureGroups returns a NodeFeatureGroups // newNodeFeatureGroups returns a NodeFeatureGroups
func newNodeFeatureGroups(c *NfdV1alpha1Client, namespace string) *nodeFeatureGroups { func newNodeFeatureGroups(c *NfdV1alpha1Client, namespace string) *nodeFeatureGroups {
return &nodeFeatureGroups{ return &nodeFeatureGroups{
gentype.NewClientWithList[*v1alpha1.NodeFeatureGroup, *v1alpha1.NodeFeatureGroupList]( gentype.NewClientWithList[*nfdv1alpha1.NodeFeatureGroup, *nfdv1alpha1.NodeFeatureGroupList](
"nodefeaturegroups", "nodefeaturegroups",
c.RESTClient(), c.RESTClient(),
scheme.ParameterCodec, scheme.ParameterCodec,
namespace, namespace,
func() *v1alpha1.NodeFeatureGroup { return &v1alpha1.NodeFeatureGroup{} }, func() *nfdv1alpha1.NodeFeatureGroup { return &nfdv1alpha1.NodeFeatureGroup{} },
func() *v1alpha1.NodeFeatureGroupList { return &v1alpha1.NodeFeatureGroupList{} }), func() *nfdv1alpha1.NodeFeatureGroupList { return &nfdv1alpha1.NodeFeatureGroupList{} },
),
} }
} }

View file

@ -19,14 +19,14 @@ limitations under the License.
package v1alpha1 package v1alpha1
import ( import (
"context" context "context"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types" types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch" watch "k8s.io/apimachinery/pkg/watch"
gentype "k8s.io/client-go/gentype" gentype "k8s.io/client-go/gentype"
scheme "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned/scheme" scheme "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned/scheme"
v1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1" nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
) )
// NodeFeatureRulesGetter has a method to return a NodeFeatureRuleInterface. // NodeFeatureRulesGetter has a method to return a NodeFeatureRuleInterface.
@ -37,31 +37,32 @@ type NodeFeatureRulesGetter interface {
// NodeFeatureRuleInterface has methods to work with NodeFeatureRule resources. // NodeFeatureRuleInterface has methods to work with NodeFeatureRule resources.
type NodeFeatureRuleInterface interface { type NodeFeatureRuleInterface interface {
Create(ctx context.Context, nodeFeatureRule *v1alpha1.NodeFeatureRule, opts v1.CreateOptions) (*v1alpha1.NodeFeatureRule, error) Create(ctx context.Context, nodeFeatureRule *nfdv1alpha1.NodeFeatureRule, opts v1.CreateOptions) (*nfdv1alpha1.NodeFeatureRule, error)
Update(ctx context.Context, nodeFeatureRule *v1alpha1.NodeFeatureRule, opts v1.UpdateOptions) (*v1alpha1.NodeFeatureRule, error) Update(ctx context.Context, nodeFeatureRule *nfdv1alpha1.NodeFeatureRule, opts v1.UpdateOptions) (*nfdv1alpha1.NodeFeatureRule, error)
Delete(ctx context.Context, name string, opts v1.DeleteOptions) error Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.NodeFeatureRule, error) Get(ctx context.Context, name string, opts v1.GetOptions) (*nfdv1alpha1.NodeFeatureRule, error)
List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.NodeFeatureRuleList, error) List(ctx context.Context, opts v1.ListOptions) (*nfdv1alpha1.NodeFeatureRuleList, error)
Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.NodeFeatureRule, err error) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *nfdv1alpha1.NodeFeatureRule, err error)
NodeFeatureRuleExpansion NodeFeatureRuleExpansion
} }
// nodeFeatureRules implements NodeFeatureRuleInterface // nodeFeatureRules implements NodeFeatureRuleInterface
type nodeFeatureRules struct { type nodeFeatureRules struct {
*gentype.ClientWithList[*v1alpha1.NodeFeatureRule, *v1alpha1.NodeFeatureRuleList] *gentype.ClientWithList[*nfdv1alpha1.NodeFeatureRule, *nfdv1alpha1.NodeFeatureRuleList]
} }
// newNodeFeatureRules returns a NodeFeatureRules // newNodeFeatureRules returns a NodeFeatureRules
func newNodeFeatureRules(c *NfdV1alpha1Client) *nodeFeatureRules { func newNodeFeatureRules(c *NfdV1alpha1Client) *nodeFeatureRules {
return &nodeFeatureRules{ return &nodeFeatureRules{
gentype.NewClientWithList[*v1alpha1.NodeFeatureRule, *v1alpha1.NodeFeatureRuleList]( gentype.NewClientWithList[*nfdv1alpha1.NodeFeatureRule, *nfdv1alpha1.NodeFeatureRuleList](
"nodefeaturerules", "nodefeaturerules",
c.RESTClient(), c.RESTClient(),
scheme.ParameterCodec, scheme.ParameterCodec,
"", "",
func() *v1alpha1.NodeFeatureRule { return &v1alpha1.NodeFeatureRule{} }, func() *nfdv1alpha1.NodeFeatureRule { return &nfdv1alpha1.NodeFeatureRule{} },
func() *v1alpha1.NodeFeatureRuleList { return &v1alpha1.NodeFeatureRuleList{} }), func() *nfdv1alpha1.NodeFeatureRuleList { return &nfdv1alpha1.NodeFeatureRuleList{} },
),
} }
} }

View file

@ -19,7 +19,7 @@ limitations under the License.
package externalversions package externalversions
import ( import (
"fmt" fmt "fmt"
schema "k8s.io/apimachinery/pkg/runtime/schema" schema "k8s.io/apimachinery/pkg/runtime/schema"
cache "k8s.io/client-go/tools/cache" cache "k8s.io/client-go/tools/cache"

View file

@ -19,7 +19,7 @@ limitations under the License.
package v1alpha1 package v1alpha1
import ( import (
"context" context "context"
time "time" time "time"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -28,15 +28,15 @@ import (
cache "k8s.io/client-go/tools/cache" cache "k8s.io/client-go/tools/cache"
versioned "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned" versioned "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned"
internalinterfaces "sigs.k8s.io/node-feature-discovery/api/generated/informers/externalversions/internalinterfaces" internalinterfaces "sigs.k8s.io/node-feature-discovery/api/generated/informers/externalversions/internalinterfaces"
v1alpha1 "sigs.k8s.io/node-feature-discovery/api/generated/listers/nfd/v1alpha1" nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/generated/listers/nfd/v1alpha1"
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1" apinfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
) )
// NodeFeatureInformer provides access to a shared informer and lister for // NodeFeatureInformer provides access to a shared informer and lister for
// NodeFeatures. // NodeFeatures.
type NodeFeatureInformer interface { type NodeFeatureInformer interface {
Informer() cache.SharedIndexInformer Informer() cache.SharedIndexInformer
Lister() v1alpha1.NodeFeatureLister Lister() nfdv1alpha1.NodeFeatureLister
} }
type nodeFeatureInformer struct { type nodeFeatureInformer struct {
@ -71,7 +71,7 @@ func NewFilteredNodeFeatureInformer(client versioned.Interface, namespace string
return client.NfdV1alpha1().NodeFeatures(namespace).Watch(context.TODO(), options) return client.NfdV1alpha1().NodeFeatures(namespace).Watch(context.TODO(), options)
}, },
}, },
&nfdv1alpha1.NodeFeature{}, &apinfdv1alpha1.NodeFeature{},
resyncPeriod, resyncPeriod,
indexers, indexers,
) )
@ -82,9 +82,9 @@ func (f *nodeFeatureInformer) defaultInformer(client versioned.Interface, resync
} }
func (f *nodeFeatureInformer) Informer() cache.SharedIndexInformer { func (f *nodeFeatureInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&nfdv1alpha1.NodeFeature{}, f.defaultInformer) return f.factory.InformerFor(&apinfdv1alpha1.NodeFeature{}, f.defaultInformer)
} }
func (f *nodeFeatureInformer) Lister() v1alpha1.NodeFeatureLister { func (f *nodeFeatureInformer) Lister() nfdv1alpha1.NodeFeatureLister {
return v1alpha1.NewNodeFeatureLister(f.Informer().GetIndexer()) return nfdv1alpha1.NewNodeFeatureLister(f.Informer().GetIndexer())
} }

View file

@ -19,7 +19,7 @@ limitations under the License.
package v1alpha1 package v1alpha1
import ( import (
"context" context "context"
time "time" time "time"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -28,15 +28,15 @@ import (
cache "k8s.io/client-go/tools/cache" cache "k8s.io/client-go/tools/cache"
versioned "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned" versioned "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned"
internalinterfaces "sigs.k8s.io/node-feature-discovery/api/generated/informers/externalversions/internalinterfaces" internalinterfaces "sigs.k8s.io/node-feature-discovery/api/generated/informers/externalversions/internalinterfaces"
v1alpha1 "sigs.k8s.io/node-feature-discovery/api/generated/listers/nfd/v1alpha1" nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/generated/listers/nfd/v1alpha1"
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1" apinfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
) )
// NodeFeatureGroupInformer provides access to a shared informer and lister for // NodeFeatureGroupInformer provides access to a shared informer and lister for
// NodeFeatureGroups. // NodeFeatureGroups.
type NodeFeatureGroupInformer interface { type NodeFeatureGroupInformer interface {
Informer() cache.SharedIndexInformer Informer() cache.SharedIndexInformer
Lister() v1alpha1.NodeFeatureGroupLister Lister() nfdv1alpha1.NodeFeatureGroupLister
} }
type nodeFeatureGroupInformer struct { type nodeFeatureGroupInformer struct {
@ -71,7 +71,7 @@ func NewFilteredNodeFeatureGroupInformer(client versioned.Interface, namespace s
return client.NfdV1alpha1().NodeFeatureGroups(namespace).Watch(context.TODO(), options) return client.NfdV1alpha1().NodeFeatureGroups(namespace).Watch(context.TODO(), options)
}, },
}, },
&nfdv1alpha1.NodeFeatureGroup{}, &apinfdv1alpha1.NodeFeatureGroup{},
resyncPeriod, resyncPeriod,
indexers, indexers,
) )
@ -82,9 +82,9 @@ func (f *nodeFeatureGroupInformer) defaultInformer(client versioned.Interface, r
} }
func (f *nodeFeatureGroupInformer) Informer() cache.SharedIndexInformer { func (f *nodeFeatureGroupInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&nfdv1alpha1.NodeFeatureGroup{}, f.defaultInformer) return f.factory.InformerFor(&apinfdv1alpha1.NodeFeatureGroup{}, f.defaultInformer)
} }
func (f *nodeFeatureGroupInformer) Lister() v1alpha1.NodeFeatureGroupLister { func (f *nodeFeatureGroupInformer) Lister() nfdv1alpha1.NodeFeatureGroupLister {
return v1alpha1.NewNodeFeatureGroupLister(f.Informer().GetIndexer()) return nfdv1alpha1.NewNodeFeatureGroupLister(f.Informer().GetIndexer())
} }

View file

@ -19,7 +19,7 @@ limitations under the License.
package v1alpha1 package v1alpha1
import ( import (
"context" context "context"
time "time" time "time"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -28,15 +28,15 @@ import (
cache "k8s.io/client-go/tools/cache" cache "k8s.io/client-go/tools/cache"
versioned "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned" versioned "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned"
internalinterfaces "sigs.k8s.io/node-feature-discovery/api/generated/informers/externalversions/internalinterfaces" internalinterfaces "sigs.k8s.io/node-feature-discovery/api/generated/informers/externalversions/internalinterfaces"
v1alpha1 "sigs.k8s.io/node-feature-discovery/api/generated/listers/nfd/v1alpha1" nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/generated/listers/nfd/v1alpha1"
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1" apinfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
) )
// NodeFeatureRuleInformer provides access to a shared informer and lister for // NodeFeatureRuleInformer provides access to a shared informer and lister for
// NodeFeatureRules. // NodeFeatureRules.
type NodeFeatureRuleInformer interface { type NodeFeatureRuleInformer interface {
Informer() cache.SharedIndexInformer Informer() cache.SharedIndexInformer
Lister() v1alpha1.NodeFeatureRuleLister Lister() nfdv1alpha1.NodeFeatureRuleLister
} }
type nodeFeatureRuleInformer struct { type nodeFeatureRuleInformer struct {
@ -70,7 +70,7 @@ func NewFilteredNodeFeatureRuleInformer(client versioned.Interface, resyncPeriod
return client.NfdV1alpha1().NodeFeatureRules().Watch(context.TODO(), options) return client.NfdV1alpha1().NodeFeatureRules().Watch(context.TODO(), options)
}, },
}, },
&nfdv1alpha1.NodeFeatureRule{}, &apinfdv1alpha1.NodeFeatureRule{},
resyncPeriod, resyncPeriod,
indexers, indexers,
) )
@ -81,9 +81,9 @@ func (f *nodeFeatureRuleInformer) defaultInformer(client versioned.Interface, re
} }
func (f *nodeFeatureRuleInformer) Informer() cache.SharedIndexInformer { func (f *nodeFeatureRuleInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&nfdv1alpha1.NodeFeatureRule{}, f.defaultInformer) return f.factory.InformerFor(&apinfdv1alpha1.NodeFeatureRule{}, f.defaultInformer)
} }
func (f *nodeFeatureRuleInformer) Lister() v1alpha1.NodeFeatureRuleLister { func (f *nodeFeatureRuleInformer) Lister() nfdv1alpha1.NodeFeatureRuleLister {
return v1alpha1.NewNodeFeatureRuleLister(f.Informer().GetIndexer()) return nfdv1alpha1.NewNodeFeatureRuleLister(f.Informer().GetIndexer())
} }

View file

@ -19,10 +19,10 @@ limitations under the License.
package v1alpha1 package v1alpha1
import ( import (
"k8s.io/apimachinery/pkg/labels" labels "k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/listers" listers "k8s.io/client-go/listers"
"k8s.io/client-go/tools/cache" cache "k8s.io/client-go/tools/cache"
v1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1" nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
) )
// NodeFeatureLister helps list NodeFeatures. // NodeFeatureLister helps list NodeFeatures.
@ -30,7 +30,7 @@ import (
type NodeFeatureLister interface { type NodeFeatureLister interface {
// List lists all NodeFeatures in the indexer. // List lists all NodeFeatures in the indexer.
// Objects returned here must be treated as read-only. // Objects returned here must be treated as read-only.
List(selector labels.Selector) (ret []*v1alpha1.NodeFeature, err error) List(selector labels.Selector) (ret []*nfdv1alpha1.NodeFeature, err error)
// NodeFeatures returns an object that can list and get NodeFeatures. // NodeFeatures returns an object that can list and get NodeFeatures.
NodeFeatures(namespace string) NodeFeatureNamespaceLister NodeFeatures(namespace string) NodeFeatureNamespaceLister
NodeFeatureListerExpansion NodeFeatureListerExpansion
@ -38,17 +38,17 @@ type NodeFeatureLister interface {
// nodeFeatureLister implements the NodeFeatureLister interface. // nodeFeatureLister implements the NodeFeatureLister interface.
type nodeFeatureLister struct { type nodeFeatureLister struct {
listers.ResourceIndexer[*v1alpha1.NodeFeature] listers.ResourceIndexer[*nfdv1alpha1.NodeFeature]
} }
// NewNodeFeatureLister returns a new NodeFeatureLister. // NewNodeFeatureLister returns a new NodeFeatureLister.
func NewNodeFeatureLister(indexer cache.Indexer) NodeFeatureLister { func NewNodeFeatureLister(indexer cache.Indexer) NodeFeatureLister {
return &nodeFeatureLister{listers.New[*v1alpha1.NodeFeature](indexer, v1alpha1.Resource("nodefeature"))} return &nodeFeatureLister{listers.New[*nfdv1alpha1.NodeFeature](indexer, nfdv1alpha1.Resource("nodefeature"))}
} }
// NodeFeatures returns an object that can list and get NodeFeatures. // NodeFeatures returns an object that can list and get NodeFeatures.
func (s *nodeFeatureLister) NodeFeatures(namespace string) NodeFeatureNamespaceLister { func (s *nodeFeatureLister) NodeFeatures(namespace string) NodeFeatureNamespaceLister {
return nodeFeatureNamespaceLister{listers.NewNamespaced[*v1alpha1.NodeFeature](s.ResourceIndexer, namespace)} return nodeFeatureNamespaceLister{listers.NewNamespaced[*nfdv1alpha1.NodeFeature](s.ResourceIndexer, namespace)}
} }
// NodeFeatureNamespaceLister helps list and get NodeFeatures. // NodeFeatureNamespaceLister helps list and get NodeFeatures.
@ -56,15 +56,15 @@ func (s *nodeFeatureLister) NodeFeatures(namespace string) NodeFeatureNamespaceL
type NodeFeatureNamespaceLister interface { type NodeFeatureNamespaceLister interface {
// List lists all NodeFeatures in the indexer for a given namespace. // List lists all NodeFeatures in the indexer for a given namespace.
// Objects returned here must be treated as read-only. // Objects returned here must be treated as read-only.
List(selector labels.Selector) (ret []*v1alpha1.NodeFeature, err error) List(selector labels.Selector) (ret []*nfdv1alpha1.NodeFeature, err error)
// Get retrieves the NodeFeature from the indexer for a given namespace and name. // Get retrieves the NodeFeature from the indexer for a given namespace and name.
// Objects returned here must be treated as read-only. // Objects returned here must be treated as read-only.
Get(name string) (*v1alpha1.NodeFeature, error) Get(name string) (*nfdv1alpha1.NodeFeature, error)
NodeFeatureNamespaceListerExpansion NodeFeatureNamespaceListerExpansion
} }
// nodeFeatureNamespaceLister implements the NodeFeatureNamespaceLister // nodeFeatureNamespaceLister implements the NodeFeatureNamespaceLister
// interface. // interface.
type nodeFeatureNamespaceLister struct { type nodeFeatureNamespaceLister struct {
listers.ResourceIndexer[*v1alpha1.NodeFeature] listers.ResourceIndexer[*nfdv1alpha1.NodeFeature]
} }

View file

@ -19,10 +19,10 @@ limitations under the License.
package v1alpha1 package v1alpha1
import ( import (
"k8s.io/apimachinery/pkg/labels" labels "k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/listers" listers "k8s.io/client-go/listers"
"k8s.io/client-go/tools/cache" cache "k8s.io/client-go/tools/cache"
v1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1" nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
) )
// NodeFeatureGroupLister helps list NodeFeatureGroups. // NodeFeatureGroupLister helps list NodeFeatureGroups.
@ -30,7 +30,7 @@ import (
type NodeFeatureGroupLister interface { type NodeFeatureGroupLister interface {
// List lists all NodeFeatureGroups in the indexer. // List lists all NodeFeatureGroups in the indexer.
// Objects returned here must be treated as read-only. // Objects returned here must be treated as read-only.
List(selector labels.Selector) (ret []*v1alpha1.NodeFeatureGroup, err error) List(selector labels.Selector) (ret []*nfdv1alpha1.NodeFeatureGroup, err error)
// NodeFeatureGroups returns an object that can list and get NodeFeatureGroups. // NodeFeatureGroups returns an object that can list and get NodeFeatureGroups.
NodeFeatureGroups(namespace string) NodeFeatureGroupNamespaceLister NodeFeatureGroups(namespace string) NodeFeatureGroupNamespaceLister
NodeFeatureGroupListerExpansion NodeFeatureGroupListerExpansion
@ -38,17 +38,17 @@ type NodeFeatureGroupLister interface {
// nodeFeatureGroupLister implements the NodeFeatureGroupLister interface. // nodeFeatureGroupLister implements the NodeFeatureGroupLister interface.
type nodeFeatureGroupLister struct { type nodeFeatureGroupLister struct {
listers.ResourceIndexer[*v1alpha1.NodeFeatureGroup] listers.ResourceIndexer[*nfdv1alpha1.NodeFeatureGroup]
} }
// NewNodeFeatureGroupLister returns a new NodeFeatureGroupLister. // NewNodeFeatureGroupLister returns a new NodeFeatureGroupLister.
func NewNodeFeatureGroupLister(indexer cache.Indexer) NodeFeatureGroupLister { func NewNodeFeatureGroupLister(indexer cache.Indexer) NodeFeatureGroupLister {
return &nodeFeatureGroupLister{listers.New[*v1alpha1.NodeFeatureGroup](indexer, v1alpha1.Resource("nodefeaturegroup"))} return &nodeFeatureGroupLister{listers.New[*nfdv1alpha1.NodeFeatureGroup](indexer, nfdv1alpha1.Resource("nodefeaturegroup"))}
} }
// NodeFeatureGroups returns an object that can list and get NodeFeatureGroups. // NodeFeatureGroups returns an object that can list and get NodeFeatureGroups.
func (s *nodeFeatureGroupLister) NodeFeatureGroups(namespace string) NodeFeatureGroupNamespaceLister { func (s *nodeFeatureGroupLister) NodeFeatureGroups(namespace string) NodeFeatureGroupNamespaceLister {
return nodeFeatureGroupNamespaceLister{listers.NewNamespaced[*v1alpha1.NodeFeatureGroup](s.ResourceIndexer, namespace)} return nodeFeatureGroupNamespaceLister{listers.NewNamespaced[*nfdv1alpha1.NodeFeatureGroup](s.ResourceIndexer, namespace)}
} }
// NodeFeatureGroupNamespaceLister helps list and get NodeFeatureGroups. // NodeFeatureGroupNamespaceLister helps list and get NodeFeatureGroups.
@ -56,15 +56,15 @@ func (s *nodeFeatureGroupLister) NodeFeatureGroups(namespace string) NodeFeature
type NodeFeatureGroupNamespaceLister interface { type NodeFeatureGroupNamespaceLister interface {
// List lists all NodeFeatureGroups in the indexer for a given namespace. // List lists all NodeFeatureGroups in the indexer for a given namespace.
// Objects returned here must be treated as read-only. // Objects returned here must be treated as read-only.
List(selector labels.Selector) (ret []*v1alpha1.NodeFeatureGroup, err error) List(selector labels.Selector) (ret []*nfdv1alpha1.NodeFeatureGroup, err error)
// Get retrieves the NodeFeatureGroup from the indexer for a given namespace and name. // Get retrieves the NodeFeatureGroup from the indexer for a given namespace and name.
// Objects returned here must be treated as read-only. // Objects returned here must be treated as read-only.
Get(name string) (*v1alpha1.NodeFeatureGroup, error) Get(name string) (*nfdv1alpha1.NodeFeatureGroup, error)
NodeFeatureGroupNamespaceListerExpansion NodeFeatureGroupNamespaceListerExpansion
} }
// nodeFeatureGroupNamespaceLister implements the NodeFeatureGroupNamespaceLister // nodeFeatureGroupNamespaceLister implements the NodeFeatureGroupNamespaceLister
// interface. // interface.
type nodeFeatureGroupNamespaceLister struct { type nodeFeatureGroupNamespaceLister struct {
listers.ResourceIndexer[*v1alpha1.NodeFeatureGroup] listers.ResourceIndexer[*nfdv1alpha1.NodeFeatureGroup]
} }

View file

@ -19,10 +19,10 @@ limitations under the License.
package v1alpha1 package v1alpha1
import ( import (
"k8s.io/apimachinery/pkg/labels" labels "k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/listers" listers "k8s.io/client-go/listers"
"k8s.io/client-go/tools/cache" cache "k8s.io/client-go/tools/cache"
v1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1" nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
) )
// NodeFeatureRuleLister helps list NodeFeatureRules. // NodeFeatureRuleLister helps list NodeFeatureRules.
@ -30,19 +30,19 @@ import (
type NodeFeatureRuleLister interface { type NodeFeatureRuleLister interface {
// List lists all NodeFeatureRules in the indexer. // List lists all NodeFeatureRules in the indexer.
// Objects returned here must be treated as read-only. // Objects returned here must be treated as read-only.
List(selector labels.Selector) (ret []*v1alpha1.NodeFeatureRule, err error) List(selector labels.Selector) (ret []*nfdv1alpha1.NodeFeatureRule, err error)
// Get retrieves the NodeFeatureRule from the index for a given name. // Get retrieves the NodeFeatureRule from the index for a given name.
// Objects returned here must be treated as read-only. // Objects returned here must be treated as read-only.
Get(name string) (*v1alpha1.NodeFeatureRule, error) Get(name string) (*nfdv1alpha1.NodeFeatureRule, error)
NodeFeatureRuleListerExpansion NodeFeatureRuleListerExpansion
} }
// nodeFeatureRuleLister implements the NodeFeatureRuleLister interface. // nodeFeatureRuleLister implements the NodeFeatureRuleLister interface.
type nodeFeatureRuleLister struct { type nodeFeatureRuleLister struct {
listers.ResourceIndexer[*v1alpha1.NodeFeatureRule] listers.ResourceIndexer[*nfdv1alpha1.NodeFeatureRule]
} }
// NewNodeFeatureRuleLister returns a new NodeFeatureRuleLister. // NewNodeFeatureRuleLister returns a new NodeFeatureRuleLister.
func NewNodeFeatureRuleLister(indexer cache.Indexer) NodeFeatureRuleLister { func NewNodeFeatureRuleLister(indexer cache.Indexer) NodeFeatureRuleLister {
return &nodeFeatureRuleLister{listers.New[*v1alpha1.NodeFeatureRule](indexer, v1alpha1.Resource("nodefeaturerule"))} return &nodeFeatureRuleLister{listers.New[*nfdv1alpha1.NodeFeatureRule](indexer, nfdv1alpha1.Resource("nodefeaturerule"))}
} }

View file

@ -0,0 +1,48 @@
/*
Copyright 2024 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 (
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
)
// ArtifactType is a type of OCI artifact that contains image compatibility metadata.
const (
ArtifactType = "application/vnd.nfd.image-compatibility.v1alpha1"
Version = "v1alpha1"
)
// Spec represents image compatibility metadata.
type Spec struct {
// Version of the spec.
Version string `json:"version"`
// Compatibilities contains list of compatibility sets.
Compatibilties []Compatibility `json:"compatibilities"`
}
// Compatibility represents image compatibility metadata
// that describe the image requirements for the host and OS.
type Compatibility struct {
// Rules represents a list of Node Feature Rules.
Rules []nfdv1alpha1.Rule `json:"rules"`
// Weight indicates the priority of the compatibility set.
Weight int `json:"weight,omitempty"`
// Tag enables grouping or distinguishing between compatibility sets.
Tag string `json:"tag,omitempty"`
// Description of the compatibility set.
Description string `json:"description,omitempty"`
}

View file

@ -5,8 +5,8 @@ go 1.22.2
require ( require (
github.com/gogo/protobuf v1.3.2 github.com/gogo/protobuf v1.3.2
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.8.4
k8s.io/api v0.30.7 k8s.io/api v0.30.8
k8s.io/apimachinery v0.30.7 k8s.io/apimachinery v0.30.8
) )
require ( require (

View file

@ -75,10 +75,10 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/api v0.30.7 h1:wB2eHI+IptVYsz5WsAQpI6+Dqi3+11wEWBqIh4fh980= k8s.io/api v0.30.8 h1:Y+yZRF3c1WC0MTkLe0qBkiLCquRNa4I21/iDioGMCbo=
k8s.io/api v0.30.7/go.mod h1:bR0EwbmhYmJvUoeza7ZzBUmYCrVXccQ9JOdfv0BxhH0= k8s.io/api v0.30.8/go.mod h1:89IE5MzirZ5HHxU/Hq1/KWGqXkhXClu/FHGesFhQ0A4=
k8s.io/apimachinery v0.30.7 h1:CoQFxvzPFKwU1eJGN/8LgM3ZJBC3hKgvwGqRrL43uIY= k8s.io/apimachinery v0.30.8 h1:9jyTItYzmJc00cBDxZC5ArFNxUeKCwbw0m760iFUMKY=
k8s.io/apimachinery v0.30.7/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= k8s.io/apimachinery v0.30.8/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=

View file

@ -19,5 +19,3 @@ limitations under the License.
// +kubebuilder:object:generate=true // +kubebuilder:object:generate=true
// +groupName=nfd.k8s-sigs.io // +groupName=nfd.k8s-sigs.io
package v1alpha1 package v1alpha1
//go:generate ./generate.sh

View file

@ -1,12 +0,0 @@
#!/bin/sh -ex
go-to-protobuf \
--output-base=. \
--go-header-file ../../../../hack/boilerplate.go.txt \
--proto-import ../../../../vendor/ \
--packages +sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1=v1alpha1 \
--keep-gogoproto=false \
--apimachinery-packages "-k8s.io/apimachinery/pkg/util/intstr"
mv sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1/* .
rm -rf sigs.k8s.io

File diff suppressed because it is too large Load diff

View file

@ -1,77 +0,0 @@
/*
Copyright 2024 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.
*/
// This file was autogenerated by go-to-protobuf. Do not edit it manually!
syntax = "proto2";
package v1alpha1;
// Package-wide variables from generator "generated".
option go_package = "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1";
// AttributeFeatureSet is a set of features having string value.
//
// +protobuf=true
message AttributeFeatureSet {
map<string, string> elements = 1;
}
// Features is the collection of all discovered features.
//
// +protobuf=true
message Features {
// Flags contains all the flag-type features of the node.
// +optional
map<string, FlagFeatureSet> flags = 1;
// Attributes contains all the attribute-type features of the node.
// +optional
map<string, AttributeFeatureSet> vattributes = 2;
// Instances contains all the instance-type features of the node.
// +optional
map<string, InstanceFeatureSet> instances = 3;
}
// FlagFeatureSet is a set of simple features only containing names without values.
//
// +protobuf=true
message FlagFeatureSet {
map<string, Nil> elements = 1;
}
// InstanceFeature represents one instance of a complex features, e.g. a device.
//
// +protobuf=true
message InstanceFeature {
map<string, string> attributes = 1;
}
// InstanceFeatureSet is a set of features each of which is an instance having multiple attributes.
//
// +protobuf=true
message InstanceFeatureSet {
repeated InstanceFeature elements = 1;
}
// Nil is a dummy empty struct for protobuf compatibility
//
// +protobuf=true
message Nil {
}

View file

@ -56,55 +56,44 @@ type NodeFeatureSpec struct {
} }
// Features is the collection of all discovered features. // Features is the collection of all discovered features.
//
// +protobuf=true
type Features struct { type Features struct {
// Flags contains all the flag-type features of the node. // Flags contains all the flag-type features of the node.
// +optional // +optional
Flags map[string]FlagFeatureSet `json:"flags" protobuf:"bytes,1,rep,name=flags"` Flags map[string]FlagFeatureSet `json:"flags"`
// Attributes contains all the attribute-type features of the node. // Attributes contains all the attribute-type features of the node.
// +optional // +optional
Attributes map[string]AttributeFeatureSet `json:"attributes" protobuf:"bytes,2,rep,name=vattributes"` Attributes map[string]AttributeFeatureSet `json:"attributes"`
// Instances contains all the instance-type features of the node. // Instances contains all the instance-type features of the node.
// +optional // +optional
Instances map[string]InstanceFeatureSet `json:"instances" protobuf:"bytes,3,rep,name=instances"` Instances map[string]InstanceFeatureSet `json:"instances"`
} }
// FlagFeatureSet is a set of simple features only containing names without values. // FlagFeatureSet is a set of simple features only containing names without values.
//
// +protobuf=true
type FlagFeatureSet struct { type FlagFeatureSet struct {
// Individual features of the feature set. // Individual features of the feature set.
Elements map[string]Nil `json:"elements" protobuf:"bytes,1,rep,name=elements"` Elements map[string]Nil `json:"elements"`
} }
// AttributeFeatureSet is a set of features having string value. // AttributeFeatureSet is a set of features having string value.
//
// +protobuf=true
type AttributeFeatureSet struct { type AttributeFeatureSet struct {
// Individual features of the feature set. // Individual features of the feature set.
Elements map[string]string `json:"elements" protobuf:"bytes,1,rep,name=elements"` Elements map[string]string `json:"elements"`
} }
// InstanceFeatureSet is a set of features each of which is an instance having multiple attributes. // InstanceFeatureSet is a set of features each of which is an instance having multiple attributes.
//
// +protobuf=true
type InstanceFeatureSet struct { type InstanceFeatureSet struct {
// Individual features of the feature set. // Individual features of the feature set.
Elements []InstanceFeature `json:"elements" protobuf:"bytes,1,rep,name=elements"` Elements []InstanceFeature `json:"elements"`
} }
// InstanceFeature represents one instance of a complex features, e.g. a device. // InstanceFeature represents one instance of a complex features, e.g. a device.
//
// +protobuf=true
type InstanceFeature struct { type InstanceFeature struct {
// Attributes of the instance feature. // Attributes of the instance feature.
Attributes map[string]string `json:"attributes" protobuf:"bytes,1,rep,name=attributes"` Attributes map[string]string `json:"attributes"`
} }
// Nil is a dummy empty struct for protobuf compatibility // Nil is a dummy empty struct for protobuf compatibility.
// // NOTE: protobuf definitions have been removed but this is kept for API compatibility.
// +protobuf=true
type Nil struct{} type Nil struct{}
// NodeFeatureRuleList contains a list of NodeFeatureRule objects. // NodeFeatureRuleList contains a list of NodeFeatureRule objects.

29
api/nfd/v1alpha1/utils.go Normal file
View file

@ -0,0 +1,29 @@
/*
Copyright 2024 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 (
"fmt"
)
// String represents the match expression as a string type.
func (m MatchExpression) String() string {
if len(m.Value) < 1 {
return fmt.Sprintf("{op: %q}", m.Op)
}
return fmt.Sprintf("{op: %q, value: %q}", m.Op, m.Value)
}

27
cmd/nfd/main.go Normal file
View file

@ -0,0 +1,27 @@
/*
Copyright 2024 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 main
import (
"sigs.k8s.io/node-feature-discovery/cmd/nfd/subcmd"
)
const ProgramName = "nfd"
func main() {
subcmd.Execute()
}

View file

@ -0,0 +1,36 @@
/*
Copyright 2024 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 compat
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
var CompatCmd = &cobra.Command{
Use: "compat",
Short: "Image compatibility commands",
}
func Execute() {
if err := CompatCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

View file

@ -0,0 +1,70 @@
/*
Copyright 2024 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 options
import (
"fmt"
"runtime"
"strings"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/spf13/cobra"
)
// PlatformOption represents
type PlatformOption struct {
// PlatformStr contains the raw platform argument provided by the user.
PlatformStr string
// Platform represents the OCI platform specification, built from PlatformStr.
Platform *ocispec.Platform
}
// Parse takes the PlatformStr argument provided by the user
// to build OCI platform specification.
func (opt *PlatformOption) Parse(*cobra.Command) error {
var pStr string
if opt.PlatformStr == "" {
return nil
}
platform := &ocispec.Platform{}
pStr, platform.OSVersion, _ = strings.Cut(opt.PlatformStr, ":")
parts := strings.Split(pStr, "/")
switch len(parts) {
case 3:
platform.Variant = parts[2]
fallthrough
case 2:
platform.Architecture = parts[1]
case 1:
platform.Architecture = runtime.GOARCH
default:
return fmt.Errorf("failed to parse platform %q: expected format os[/arch[/variant]]", opt.PlatformStr)
}
platform.OS = parts[0]
if platform.OS == "" {
return fmt.Errorf("invalid platform: OS cannot be empty")
}
if platform.Architecture == "" {
return fmt.Errorf("invalid platform: Architecture cannot be empty")
}
opt.Platform = platform
return nil
}

View file

@ -0,0 +1,224 @@
/*
Copyright 2024 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 compat
import (
"context"
"encoding/json"
"fmt"
"io"
"os"
"strings"
"time"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"github.com/spf13/cobra"
"oras.land/oras-go/v2/registry"
"sigs.k8s.io/node-feature-discovery/cmd/nfd/subcmd/compat/options"
artifactcli "sigs.k8s.io/node-feature-discovery/pkg/client-nfd/compat/artifact-client"
nodevalidator "sigs.k8s.io/node-feature-discovery/pkg/client-nfd/compat/node-validator"
"sigs.k8s.io/node-feature-discovery/source"
)
var (
image string
tags []string
platform options.PlatformOption
plainHTTP bool
outputJSON bool
// secrets
readPassword bool
readAccessToken bool
username string
password string
accessToken string
)
var validateNodeCmd = &cobra.Command{
Use: "validate-node",
Short: "Perform node validation based on its associated image compatibility artifact",
PreRunE: func(cmd *cobra.Command, args []string) error {
var err error
if err = platform.Parse(cmd); err != nil {
return err
}
if readAccessToken && readPassword {
return fmt.Errorf("cannot use --registry-token-stdin and --registry-password-stdin at the same time")
} else if readAccessToken {
accessToken, err = readStdin()
if err != nil {
return err
}
} else if readPassword {
password, err = readStdin()
if err != nil {
return err
}
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
ref, err := registry.ParseReference(image)
if err != nil {
return err
}
sources := map[string]source.FeatureSource{}
for k, v := range source.GetAllFeatureSources() {
if ts, ok := v.(source.SupplementalSource); ok && ts.DisableByDefault() {
continue
}
sources[k] = v
}
authOpt := artifactcli.WithAuthDefault()
if username != "" && password != "" {
authOpt = artifactcli.WithAuthPassword(username, password)
} else if accessToken != "" {
authOpt = artifactcli.WithAuthToken(accessToken)
}
ac := artifactcli.New(
&ref,
artifactcli.WithArgs(artifactcli.Args{PlainHttp: plainHTTP}),
artifactcli.WithPlatform(platform.Platform),
authOpt,
)
nv := nodevalidator.New(
nodevalidator.WithArgs(&nodevalidator.Args{Tags: tags}),
nodevalidator.WithArtifactClient(ac),
nodevalidator.WithSources(sources),
)
out, err := nv.Execute(ctx)
if err != nil {
return err
}
if outputJSON {
b, err := json.Marshal(out)
if err != nil {
return err
}
fmt.Printf("%s", b)
} else {
pprintResult(out)
}
return nil
},
}
func readStdin() (string, error) {
secretRaw, err := io.ReadAll(os.Stdin)
if err != nil {
return "", err
}
secret := strings.TrimSuffix(string(secretRaw), "\n")
secret = strings.TrimSuffix(secret, "\r")
return secret, nil
}
func pprintResult(css []*nodevalidator.CompatibilityStatus) {
for i, cs := range css {
fmt.Print(text.Colors{text.FgCyan, text.Bold}.Sprintf("COMPATIBILITY SET #%d ", i+1))
fmt.Print(text.FgCyan.Sprintf("Weight: %d", cs.Weight))
if cs.Tag != "" {
fmt.Print(text.FgCyan.Sprintf("; Tag: %s", cs.Tag))
}
fmt.Println()
fmt.Println(text.FgWhite.Sprintf("Description: %s", cs.Description))
for _, r := range cs.Rules {
printTable(r)
}
fmt.Println()
}
}
func printTable(rs nodevalidator.ProcessedRuleStatus) {
t := table.NewWriter()
t.SetStyle(table.StyleLight)
t.SetOutputMirror(os.Stdout)
t.Style().Format.Header = text.FormatDefault
t.SetAutoIndex(true)
validTxt := text.BgRed.Sprint(" FAIL ")
if rs.IsMatch {
validTxt = text.BgGreen.Sprint(" OK ")
}
ruleTxt := strings.ToUpper(fmt.Sprintf("rule: %s", rs.Name))
t.SetTitle(text.Bold.Sprintf("%s - %s", ruleTxt, validTxt))
t.AppendHeader(table.Row{"Feature", "Expression", "Matcher Type", "Status"})
if mf := rs.MatchedExpressions; len(mf) > 0 {
renderMatchFeatures(t, mf)
}
if ma := rs.MatchedAny; len(ma) > 0 {
for _, elem := range ma {
t.AppendSeparator()
renderMatchFeatures(t, elem.MatchedExpressions)
}
}
t.Render()
}
func renderMatchFeatures(t table.Writer, matchedExpressions []nodevalidator.MatchedExpression) {
for _, fm := range matchedExpressions {
fullFeatureDomain := fm.Feature
if fm.Name != "" {
fullFeatureDomain = fmt.Sprintf("%s.%s", fm.Feature, fm.Name)
}
addTableRows(t, fullFeatureDomain, fm.Expression.String(), fm.MatcherType, fm.IsMatch)
}
}
func addTableRows(t table.Writer, fullFeatureDomain, expression string, matcherType nodevalidator.MatcherType, isMatch bool) {
status := text.FgHiRed.Sprint("FAIL")
if isMatch {
status = text.FgHiGreen.Sprint("OK")
}
t.AppendRow(table.Row{fullFeatureDomain, expression, matcherType, status})
}
func init() {
CompatCmd.AddCommand(validateNodeCmd)
validateNodeCmd.Flags().StringVar(&image, "image", "", "the URL of the image containing compatibility metadata")
validateNodeCmd.Flags().StringSliceVar(&tags, "tags", []string{}, "a list of tags that must match the tags set on the compatibility objects")
validateNodeCmd.Flags().StringVar(&platform.PlatformStr, "platform", "", "the artifact platform in the format os[/arch][/variant][:os_version]")
validateNodeCmd.Flags().BoolVar(&plainHTTP, "plain-http", false, "use of HTTP protocol for all registry communications")
validateNodeCmd.Flags().BoolVar(&outputJSON, "output-json", false, "print a JSON object")
validateNodeCmd.Flags().StringVar(&username, "registry-username", "", "registry username")
validateNodeCmd.Flags().BoolVar(&readPassword, "registry-password-stdin", false, "read registry password from stdin")
validateNodeCmd.Flags().BoolVar(&readAccessToken, "registry-token-stdin", false, "read registry access token from stdin")
if err := validateNodeCmd.MarkFlagRequired("image"); err != nil {
panic(err)
}
}

45
cmd/nfd/subcmd/root.go Normal file
View file

@ -0,0 +1,45 @@
/*
Copyright 2024 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 subcmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"sigs.k8s.io/node-feature-discovery/cmd/nfd/subcmd/compat"
)
// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
Use: "nfd",
Short: "Node Feature Discovery client",
}
func init() {
RootCmd.AddCommand(compat.CompatCmd)
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
if err := RootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

View file

@ -69,8 +69,9 @@ spec:
properties: properties:
elements: elements:
additionalProperties: additionalProperties:
description: Nil is a dummy empty struct for protobuf description: |-
compatibility Nil is a dummy empty struct for protobuf compatibility.
NOTE: protobuf definitions have been removed but this is kept for API compatibility.
type: object type: object
description: Individual features of the feature set. description: Individual features of the feature set.
type: object type: object

View file

@ -69,8 +69,9 @@ spec:
properties: properties:
elements: elements:
additionalProperties: additionalProperties:
description: Nil is a dummy empty struct for protobuf description: |-
compatibility Nil is a dummy empty struct for protobuf compatibility.
NOTE: protobuf definitions have been removed but this is kept for API compatibility.
type: object type: object
description: Individual features of the feature set. description: Individual features of the feature set.
type: object type: object

View file

@ -1,7 +1,7 @@
--- ---
title: "Feature Gates" title: "Feature Gates"
layout: default layout: default
sort: 10 sort: 11
--- ---
# Feature Gates # Feature Gates

View file

@ -0,0 +1,65 @@
---
title: "Node Feature client cmdline reference"
layout: default
sort: 9
---
# Commandline flags of nfd client
{: .no_toc}
## Table of contents
{: .no_toc .text-delta}
1. TOC
{:toc}
---
**The client is in the experimental `v1alpha1` version.**
To quickly view available command line flags execute `nfd --help`.
### -h, --help
Print usage and exit.
## compat
Image Compatibility commands.
### validate-node
Perform node validation based on its associated image compatibility artifact.
#### --image
The `--image` flag specifies the URL of the image containing compatibility metadata.
#### --plain-http
The `--plain-http` flag forces the use of HTTP protocol for all registry communications.
Default: `false`
#### --platform
The `--platform` flag specifies the artifact platform in the format `os[/arch][/variant][:os_version]`.
#### --tags
The `--tags` flag specifies a list of tags that must match the tags
set on the compatibility objects.
#### --output-json
The `--output-json` flag prints the output as a JSON object.
#### --registry-username
The `--registry-username` flag specifies the username for the registry.
#### --registry-password-stdin
The `--registry-password-stdin` flag enables reading of registry password from stdin.
#### --registry-token-stdin
The `--registry-token-stdin` flag enables reading of registry token from stdin.

View file

@ -1,7 +1,7 @@
--- ---
title: "Versions" title: "Versions"
layout: default layout: default
sort: 9 sort: 10
--- ---
# Versions and deprecation # Versions and deprecation

View file

@ -0,0 +1,226 @@
---
title: "Image Compatibility Artifact"
layout: default
sort: 11
---
# Image Compatibility Artifact
{: .no_toc}
## Table of contents
{: .no_toc .text-delta}
1. TOC
{:toc}
---
## Image Compatibility
**Image Compatibility is in the experimental `v1alpha1` version.**
Image compatibility metadata enables container image authors to define their
image requirements using [Node Feature Rules](./custom-resources.md#nodefeaturerule).
This complementary solution allows features discovered on nodes to be matched
directly from images. As a result, container requirements become discoverable
and programmable, supporting various consumers and use cases where applications
need a specific compatible environment.
### Compatibility Specification
The compatibility specification is a list of compatibility objects that contain
[Node Feature Rules](./custom-resources.md#nodefeaturerule), along with
additional fields to control the execution of validation between the image and
the host.
### Schema
- **version** - *string*
This REQUIRED property specifies the version of the API in use.
- **compatibilities** - *array of object*
This REQUIRED property is a list of compatibility sets.
- **rules** - *object*
This REQUIRED property is a reference to the spec of the [NodeFeatureRule API](./custom-resources.md#nodefeaturerule).
The spec allows image requirements to be described using the features
discovered from NFD sources. For more details, please refer to [the documentation](./custom-resources.md#nodefeaturerule).
- **weight** - *int*
This OPTIONAL property specifies the [node affinity weight](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity-weight).
- **tag** - *string*
This OPTIONAL property allows for the grouping or separation of
compatibility sets.
- **description** - *string*
This OPTIONAL property provides a brief description of a compatibility set.
#### Example
```yaml
version: v1alpha1
compatibilities:
- description: "My image requirements"
rules:
- name: "kernel and cpu"
matchFeatures:
- feature: kernel.loadedmodule
matchExpressions:
vfio-pci: {op: Exists}
- feature: cpu.model
matchExpressions:
vendor_id: {op: In, value: ["Intel", "AMD"]}
- name: "one of available nics"
matchAny:
- matchFeatures:
- feature: pci.device
matchExpressions:
vendor: {op: In, value: ["0eee"]}
class: {op: In, value: ["0200"]}
- matchFeatures:
- feature: pci.device
matchExpressions:
vendor: {op: In, value: ["0fff"]}
class: {op: In, value: ["0200"]}
```
### OCI Artifact
An [OCI artifact](https://github.com/opencontainers/image-spec/blob/main/manifest.md#guidelines-for-artifact-usage)
is used to store image compatibility metadata.
The artifact can be associated with a specific image through [the subject field](https://github.com/opencontainers/distribution-spec/blob/11b8e3fba7d2d7329513d0cff53058243c334858/spec.md#pushing-manifests-with-subject)
and pushed to the registry along with the image.
Example manifest:
```json
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"artifactType": "application/vnd.nfd.image-compatibility.v1alpha1",
"config": {
"mediaType": "application/vnd.oci.empty.v1+json",
"digest": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
"size": 2
},
"layers": [
{
"mediaType": "application/vnd.nfd.image-compatibility.spec.v1alpha1+yaml",
"digest": "sha256:4a47f8ae4c713906618413cb9795824d09eeadf948729e213a1ba11a1e31d052",
"size": 1710
}
],
"subject": {
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
"size": 7682
},
"annotations": {
"oci.opencontainers.image.created": "2024-03-27T08:08:08Z"
}
}
```
#### Attach the artifact to the image
Create an image compatibility specification for the image, then install the
[ORAS](https://github.com/oras-project/oras/) tool and execute `oras attach`
command.
Example:
```sh
oras attach --artifact-type application/vnd.nfd.image-compatibility.v1alpha1 \
<image-url> <path-to-spec>.yaml:application/vnd.nfd.image-compatibility.spec.v1alpha1+yaml
```
**Note**: The attach command is planned to be integrated into the `nfd` client
tool. This will streamline the process, allowing you to perform the operation
directly within the tool rather than using a separate command.
### Validate the host against the image compatibility specification
1. Build `nfd` client: `make build`
1. Run `./bin/nfd compat validate-node --image <image-url>`
For more information about the available commands and flags, refer to
[the client documentation](../reference/node-feature-client-reference.md).
### Validate the k8s cluster node with the validate-image Job
**Note**: This does not require installation of NFD master and workers.
Additionally, public registry certificates must be included in the job.
In the example below, this is done using hostPath,
but it can be done using any Kubernetes-supported method.
```yaml
apiVersion: batch/v1
kind: Job
metadata:
name: validate-image
spec:
backoffLimit: 1
template:
spec:
restartPolicy: Never
containers:
- name: image-compatibility
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsNonRoot: true
image: <image-with-nfd-client>
command: ["nfd", "compat", "validate-node", "--image", "<image-to-be-validated>"]
volumeMounts:
- mountPath: /host-boot
name: host-boot
readOnly: true
- mountPath: /host-etc/os-release
name: host-os-release
readOnly: true
- mountPath: /host-sys
name: host-sys
readOnly: true
- mountPath: /host-usr/lib
name: host-usr-lib
readOnly: true
- mountPath: /host-lib
name: host-lib
readOnly: true
- mountPath: /host-proc
name: host-proc
readOnly: true
volumes:
- hostPath:
path: /boot
type: ""
name: host-boot
- hostPath:
path: /etc/os-release
type: ""
name: host-os-release
- hostPath:
path: /sys
type: ""
name: host-sys
- hostPath:
path: /usr/lib
type: ""
name: host-usr-lib
- hostPath:
path: /lib
type: ""
name: host-lib
- hostPath:
path: /proc
type: ""
name: host-proc
- hostPath:
path: "<path-to-registry-public-certs>"
type: ""
name: certs
```

View file

@ -0,0 +1,27 @@
version: v1alpha1
compatibilities:
- description: "my image requirements"
rules:
- name: "kernel and cpu"
matchFeatures:
- feature: kernel.loadedmodule
matchExpressions:
vfio-pci: {op: Exists}
ip_tables: {op: Exists}
- feature: cpu.model
matchExpressions:
vendor_id: {op: In, value: ["Intel", "AMD"]}
- feature: cpu.cpuid
matchName: {op: InRegexp, value: ["^AVX"]}
- name: "one of available nics"
matchAny:
- matchFeatures:
- feature: pci.device
matchExpressions:
vendor: {op: In, value: ["0eee"]}
class: {op: In, value: ["0200"]}
- matchFeatures:
- feature: pci.device
matchExpressions:
vendor: {op: In, value: ["0fff"]}
class: {op: In, value: ["0200"]}

152
go.mod
View file

@ -7,12 +7,14 @@ require (
github.com/google/go-cmp v0.6.0 github.com/google/go-cmp v0.6.0
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/jaypipes/ghw v0.13.0 github.com/jaypipes/ghw v0.13.0
github.com/jedib0t/go-pretty/v6 v6.6.1
github.com/k8stopologyawareschedwg/noderesourcetopology-api v0.1.2 github.com/k8stopologyawareschedwg/noderesourcetopology-api v0.1.2
github.com/k8stopologyawareschedwg/podfingerprint v0.2.2 github.com/k8stopologyawareschedwg/podfingerprint v0.2.2
github.com/klauspost/cpuid/v2 v2.2.9 github.com/klauspost/cpuid/v2 v2.2.9
github.com/onsi/ginkgo/v2 v2.22.0 github.com/onsi/ginkgo/v2 v2.22.0
github.com/onsi/gomega v1.36.1 github.com/onsi/gomega v1.36.1
github.com/opencontainers/runc v1.2.2 github.com/opencontainers/image-spec v1.1.0
github.com/opencontainers/runc v1.2.3
github.com/prometheus/client_golang v1.19.1 github.com/prometheus/client_golang v1.19.1
github.com/smartystreets/goconvey v1.8.1 github.com/smartystreets/goconvey v1.8.1
github.com/spf13/cobra v1.8.1 github.com/spf13/cobra v1.8.1
@ -22,24 +24,27 @@ require (
golang.org/x/net v0.32.0 golang.org/x/net v0.32.0
golang.org/x/time v0.8.0 golang.org/x/time v0.8.0
google.golang.org/grpc v1.69.0 google.golang.org/grpc v1.69.0
k8s.io/api v0.31.3 k8s.io/api v0.32.0
k8s.io/apiextensions-apiserver v0.31.3 k8s.io/apiextensions-apiserver v0.32.0
k8s.io/apimachinery v0.31.3 k8s.io/apimachinery v0.32.0
k8s.io/client-go v0.31.3 k8s.io/client-go v0.32.0
k8s.io/code-generator v0.31.3 k8s.io/code-generator v0.32.0
k8s.io/component-base v0.31.3 k8s.io/component-base v0.32.0
k8s.io/klog/v2 v2.130.1 k8s.io/klog/v2 v2.130.1
k8s.io/kubectl v0.31.3 k8s.io/kubectl v0.32.0
k8s.io/kubelet v0.31.3 k8s.io/kubelet v0.32.0
k8s.io/kubernetes v1.31.3 k8s.io/kubernetes v1.32.0
k8s.io/pod-security-admission v0.31.3 k8s.io/pod-security-admission v0.32.0
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738
oras.land/oras-go/v2 v2.5.0
sigs.k8s.io/node-feature-discovery/api/nfd v0.0.0-00010101000000-000000000000 sigs.k8s.io/node-feature-discovery/api/nfd v0.0.0-00010101000000-000000000000
sigs.k8s.io/yaml v1.4.0 sigs.k8s.io/yaml v1.4.0
) )
require ( require (
github.com/Microsoft/go-winio v0.6.0 // indirect cel.dev/expr v0.18.0 // indirect
github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/OneOfOne/xxhash v1.2.8 // indirect github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/StackExchange/wmi v1.2.1 // indirect github.com/StackExchange/wmi v1.2.1 // indirect
@ -49,26 +54,33 @@ require (
github.com/blang/semver/v4 v4.0.0 // indirect github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/containerd/containerd/api v1.7.19 // indirect
github.com/containerd/errdefs v0.1.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/ttrpc v1.2.5 // indirect
github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-semver v0.3.1 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cyphar/filepath-securejoin v0.3.4 // indirect github.com/cyphar/filepath-securejoin v0.3.5 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/distribution/reference v0.5.0 // indirect github.com/distribution/reference v0.6.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/euank/go-kmsg-parser v2.0.0+incompatible // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.4 // indirect github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect github.com/golang/protobuf v1.5.4 // indirect
github.com/google/cel-go v0.20.1 // indirect github.com/google/btree v1.0.1 // indirect
github.com/google/cadvisor v0.51.0 // indirect
github.com/google/cel-go v0.22.0 // indirect
github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/gofuzz v1.2.0 // indirect github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect
@ -76,16 +88,18 @@ require (
github.com/gorilla/websocket v1.5.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
github.com/imdario/mergo v0.3.6 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jaypipes/pcidb v1.0.1 // indirect github.com/jaypipes/pcidb v1.0.1 // indirect
github.com/josharian/intern v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/jtolds/gls v4.20.0+incompatible // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect
github.com/karrick/godirwalk v1.17.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/moby/spdystream v0.4.0 // indirect github.com/moby/spdystream v0.5.0 // indirect
github.com/moby/sys/mountinfo v0.7.1 // indirect github.com/moby/sys/mountinfo v0.7.2 // indirect
github.com/moby/sys/userns v0.1.0 // indirect github.com/moby/sys/userns v0.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
@ -93,20 +107,22 @@ require (
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/runtime-spec v1.2.0 // indirect github.com/opencontainers/runtime-spec v1.2.0 // indirect
github.com/opencontainers/selinux v1.11.1 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect github.com/prometheus/procfs v0.15.1 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect github.com/sirupsen/logrus v1.9.3 // indirect
github.com/smarty/assertions v1.15.1 // indirect github.com/smarty/assertions v1.15.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/stoewer/go-strcase v1.2.0 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/objx v0.5.2 // indirect
github.com/x448/float16 v0.8.4 // indirect github.com/x448/float16 v0.8.4 // indirect
go.etcd.io/etcd/api/v3 v3.5.14 // indirect go.etcd.io/etcd/api/v3 v3.5.16 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.14 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.16 // indirect
go.etcd.io/etcd/client/v3 v3.5.14 // indirect go.etcd.io/etcd/client/v3 v3.5.16 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
go.opentelemetry.io/otel v1.31.0 // indirect go.opentelemetry.io/otel v1.31.0 // indirect
@ -117,7 +133,7 @@ require (
go.opentelemetry.io/otel/trace v1.31.0 // indirect go.opentelemetry.io/otel/trace v1.31.0 // indirect
go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0 // indirect go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.31.0 // indirect golang.org/x/crypto v0.31.0 // indirect
golang.org/x/mod v0.21.0 // indirect golang.org/x/mod v0.21.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect
@ -133,55 +149,55 @@ require (
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
howett.net/plist v1.0.0 // indirect howett.net/plist v1.0.0 // indirect
k8s.io/apiserver v0.31.3 // indirect k8s.io/apiserver v0.32.0 // indirect
k8s.io/cloud-provider v0.31.3 // indirect k8s.io/cloud-provider v0.32.0 // indirect
k8s.io/component-helpers v0.31.3 // indirect k8s.io/component-helpers v0.32.0 // indirect
k8s.io/controller-manager v0.31.3 // indirect k8s.io/controller-manager v0.32.0 // indirect
k8s.io/cri-api v0.31.3 // indirect k8s.io/cri-api v0.32.0 // indirect
k8s.io/cri-client v0.0.0 // indirect k8s.io/cri-client v0.0.0 // indirect
k8s.io/csi-translation-lib v0.31.3 // indirect k8s.io/csi-translation-lib v0.32.0 // indirect
k8s.io/dynamic-resource-allocation v0.31.3 // indirect k8s.io/dynamic-resource-allocation v0.32.0 // indirect
k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 // indirect k8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9 // indirect
k8s.io/kms v0.31.3 // indirect k8s.io/kms v0.32.0 // indirect
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
k8s.io/mount-utils v0.31.3 // indirect k8s.io/kube-scheduler v0.0.0 // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 // indirect k8s.io/mount-utils v0.32.0 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
) )
// The k8s "sub-"packages do not have 'semver' compatible versions. Thus, we // The k8s "sub-"packages do not have 'semver' compatible versions. Thus, we
// need to override with commits (corresponding their kubernetes-* tags) // need to override with commits (corresponding their kubernetes-* tags)
replace ( replace (
github.com/gogo/protobuf => github.com/gogo/protobuf v1.3.2 github.com/gogo/protobuf => github.com/gogo/protobuf v1.3.2
k8s.io/api => k8s.io/api v0.31.3 k8s.io/api => k8s.io/api v0.32.0
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.31.3 k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.32.0
k8s.io/apimachinery => k8s.io/apimachinery v0.31.3 k8s.io/apimachinery => k8s.io/apimachinery v0.32.0
k8s.io/apiserver => k8s.io/apiserver v0.31.3 k8s.io/apiserver => k8s.io/apiserver v0.32.0
k8s.io/cli-runtime => k8s.io/cli-runtime v0.31.3 k8s.io/cli-runtime => k8s.io/cli-runtime v0.32.0
k8s.io/client-go => k8s.io/client-go v0.31.3 k8s.io/client-go => k8s.io/client-go v0.32.0
k8s.io/cloud-provider => k8s.io/cloud-provider v0.31.3 k8s.io/cloud-provider => k8s.io/cloud-provider v0.32.0
k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.31.3 k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.32.0
k8s.io/code-generator => k8s.io/code-generator v0.31.3 k8s.io/code-generator => k8s.io/code-generator v0.32.0
k8s.io/component-base => k8s.io/component-base v0.31.3 k8s.io/component-base => k8s.io/component-base v0.32.0
k8s.io/component-helpers => k8s.io/component-helpers v0.31.3 k8s.io/component-helpers => k8s.io/component-helpers v0.32.0
k8s.io/controller-manager => k8s.io/controller-manager v0.31.3 k8s.io/controller-manager => k8s.io/controller-manager v0.32.0
k8s.io/cri-api => k8s.io/cri-api v0.31.3 k8s.io/cri-api => k8s.io/cri-api v0.32.0
k8s.io/cri-client => k8s.io/cri-client v0.31.3 k8s.io/cri-client => k8s.io/cri-client v0.32.0
k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.31.3 k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.32.0
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.31.3 k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.32.0
k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.31.3 k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.32.0
k8s.io/kube-proxy => k8s.io/kube-proxy v0.31.3 k8s.io/kube-proxy => k8s.io/kube-proxy v0.32.0
k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.31.3 k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.32.0
k8s.io/kubectl => k8s.io/kubectl v0.31.3 k8s.io/kubectl => k8s.io/kubectl v0.32.0
k8s.io/kubelet => k8s.io/kubelet v0.31.3 k8s.io/kubelet => k8s.io/kubelet v0.32.0
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.31.3 k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.32.0
k8s.io/metrics => k8s.io/metrics v0.31.3 k8s.io/metrics => k8s.io/metrics v0.32.0
k8s.io/mount-utils => k8s.io/mount-utils v0.31.3 k8s.io/mount-utils => k8s.io/mount-utils v0.32.0
k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.31.3 k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.32.0
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.31.3 k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.32.0
sigs.k8s.io/node-feature-discovery/api/nfd => ./api/nfd sigs.k8s.io/node-feature-discovery/api/nfd => ./api/nfd
) )

255
go.sum
View file

@ -1,7 +1,9 @@
cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo=
cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab h1:UKkYhof1njT1/xq4SEg5z+VpTgjmNeHwPGRQl7takDI= github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab h1:UKkYhof1njT1/xq4SEg5z+VpTgjmNeHwPGRQl7takDI=
github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab/go.mod h1:3VYc5hodBMJ5+l/7J4xAyMeuM2PNuepvHlGs8yilUCA= github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab/go.mod h1:3VYc5hodBMJ5+l/7J4xAyMeuM2PNuepvHlGs8yilUCA=
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
@ -22,26 +24,34 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/checkpoint-restore/go-criu/v6 v6.3.0 h1:mIdrSO2cPNWQY1truPg6uHLXyKHk3Z5Odx4wjKOASzA= github.com/containerd/containerd/api v1.7.19 h1:VWbJL+8Ap4Ju2mx9c9qS1uFSB1OVYr5JJrW2yT5vFoA=
github.com/checkpoint-restore/go-criu/v6 v6.3.0/go.mod h1:rrRTN/uSwY2X+BPRl/gkulo9gsKOSAeVp9/K2tv7xZI= github.com/containerd/containerd/api v1.7.19/go.mod h1:fwGavl3LNwAV5ilJ0sbrABL44AQxmNjDRcwheXDb6Ig=
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro= github.com/containerd/errdefs v0.1.0 h1:m0wCRBiu1WJT/Fr+iOoQHMQS/eP5myQ8lCv4Dz5ZURM=
github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/containerd/errdefs v0.1.0/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0=
github.com/containerd/ttrpc v1.2.2 h1:9vqZr0pxwOF5koz6N0N3kJ0zDHokrcPxIR/ZR2YFtOs= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/ttrpc v1.2.2/go.mod h1:sIT6l32Ph/H9cvnJsfXM5drIVzTr5A2flTf1G5tYZak= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/ttrpc v1.2.5 h1:IFckT1EFQoFBMG4c3sMdT8EP3/aKfumK1msY+Ze4oLU=
github.com/containerd/ttrpc v1.2.5/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
github.com/containerd/typeurl/v2 v2.2.0 h1:6NBDbQzr7I5LHgp34xAXYF5DOTQDn05X58lsPEmzLso=
github.com/containerd/typeurl/v2 v2.2.0/go.mod h1:8XOOxnyatxSWuG8OfsZXVnAF4iZfedjS/8UHSPJnX4g=
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.3.4 h1:VBWugsJh2ZxJmLFSM06/0qzQyiQX2Qs0ViKrUAcqdZ8= github.com/cyphar/filepath-securejoin v0.3.5 h1:L81NHjquoQmcPgXcttUS9qTSR/+bXry6pbSINQGpjj4=
github.com/cyphar/filepath-securejoin v0.3.4/go.mod h1:8s/MCNJREmFK0H02MF6Ihv1nakJe4L/w3WZLHNkvlYM= github.com/cyphar/filepath-securejoin v0.3.5/go.mod h1:edhVd3c6OXKjUmSrVa/tGJRS9joFTxlslFCAyaxigkE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v26.1.4+incompatible h1:vuTpXDuoga+Z38m1OZHzl7NKisKWaWlhjQk7IDPSLsU=
github.com/docker/docker v26.1.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
@ -66,13 +76,14 @@ github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@ -82,16 +93,14 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/cadvisor v0.49.0 h1:1PYeiORXmcFYi609M4Qvq5IzcvcVaWgYxDt78uH8jYA= github.com/google/cadvisor v0.51.0 h1:BspqSPdZoLKrnvuZNOvM/KiJ/A+RdixwagN20n+2H8k=
github.com/google/cadvisor v0.49.0/go.mod h1:s6Fqwb2KiWG6leCegVhw4KW40tf9f7m+SF1aXiE8Wsk= github.com/google/cadvisor v0.51.0/go.mod h1:czGE/c/P/i0QFpVNKTFrIEzord9Y10YfpwuaSWXELc0=
github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= github.com/google/cel-go v0.22.0 h1:b3FJZxpiv1vTMo2/5RDUqAHPxkT8mmMfJIrq1llbf7g=
github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
@ -116,17 +125,17 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jaypipes/ghw v0.13.0 h1:log8MXuB8hzTNnSktqpXMHc0c/2k/WgjOMSUtnI1RV4= github.com/jaypipes/ghw v0.13.0 h1:log8MXuB8hzTNnSktqpXMHc0c/2k/WgjOMSUtnI1RV4=
github.com/jaypipes/ghw v0.13.0/go.mod h1:In8SsaDqlb1oTyrbmTC14uy+fbBMvp+xdqX51MidlD8= github.com/jaypipes/ghw v0.13.0/go.mod h1:In8SsaDqlb1oTyrbmTC14uy+fbBMvp+xdqX51MidlD8=
github.com/jaypipes/pcidb v1.0.1 h1:WB2zh27T3nwg8AE8ei81sNRb9yWBii3JGNJtT7K9Oic= github.com/jaypipes/pcidb v1.0.1 h1:WB2zh27T3nwg8AE8ei81sNRb9yWBii3JGNJtT7K9Oic=
github.com/jaypipes/pcidb v1.0.1/go.mod h1:6xYUz/yYEyOkIkUt2t2J2folIuZ4Yg6uByCGFXMCeE4= github.com/jaypipes/pcidb v1.0.1/go.mod h1:6xYUz/yYEyOkIkUt2t2J2folIuZ4Yg6uByCGFXMCeE4=
github.com/jedib0t/go-pretty/v6 v6.6.1 h1:iJ65Xjb680rHcikRj6DSIbzCex2huitmc7bDtxYVWyc=
github.com/jedib0t/go-pretty/v6 v6.6.1/go.mod h1:zbn98qrYlh95FIhwwsbIip0LYpwSG8SUOScs+v9/t0E=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@ -152,16 +161,18 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible h1:aKW/4cBs+yK6gpqU3K/oIwk9Q/XICqd3zOX/UFuvqmk= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible h1:aKW/4cBs+yK6gpqU3K/oIwk9Q/XICqd3zOX/UFuvqmk=
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/sys/mountinfo v0.7.1 h1:/tTvQaSJRr2FshkhXiIpux6fQ2Zvc4j7tAhMTStAG2g= github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=
github.com/moby/sys/mountinfo v0.7.1/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo= github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -169,8 +180,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mrunalp/fileutils v0.5.1 h1:F+S7ZlNKnrwHfSwdlgNSkKo67ReVf8o9fel6C3dkm/Q=
github.com/mrunalp/fileutils v0.5.1/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
@ -181,12 +190,14 @@ github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw=
github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/runc v1.2.2 h1:jTg3Vw2A5f0N9PoxFTEwUhvpANGaNPT3689Yfd/zaX0= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/runc v1.2.2/go.mod h1:/PXzF0h531HTMsYQnmxXkBD7YaGShm/2zcRB79dksUc= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/opencontainers/runc v1.2.3 h1:fxE7amCzfZflJO2lHXf4y/y8M1BoAqp+FVmG19oYB80=
github.com/opencontainers/runc v1.2.3/go.mod h1:nSxcWUydXrsBZVYNSkTjoQ/N6rcyTtn+1SD5D4+kRIM=
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk= github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU= github.com/opencontainers/selinux v1.11.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jDMcgULaH8=
github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@ -200,11 +211,11 @@ github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/seccomp/libseccomp-golang v0.10.0 h1:aA4bp+/Zzi0BnWZ2F1wgNBs5gTpm+na2rWM6M9YjLpY=
github.com/seccomp/libseccomp-golang v0.10.0/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smarty/assertions v1.15.1 h1:812oFiXI+G55vxsFf+8bIZ1ux30qtkdqzKbEFwyX3Tk= github.com/smarty/assertions v1.15.1 h1:812oFiXI+G55vxsFf+8bIZ1ux30qtkdqzKbEFwyX3Tk=
@ -217,53 +228,46 @@ github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE=
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk=
github.com/vektra/errors v0.0.0-20140903201135-c64d83aba85a h1:lUVfiMMY/te9icPKBqOKkBIMZNxSpM90dxokDeCcfBg= github.com/vektra/errors v0.0.0-20140903201135-c64d83aba85a h1:lUVfiMMY/te9icPKBqOKkBIMZNxSpM90dxokDeCcfBg=
github.com/vektra/errors v0.0.0-20140903201135-c64d83aba85a/go.mod h1:KUxJS71XlMs+ztT+RzsLRoWUQRUpECo/+Rb0EBk8/Wc= github.com/vektra/errors v0.0.0-20140903201135-c64d83aba85a/go.mod h1:KUxJS71XlMs+ztT+RzsLRoWUQRUpECo/+Rb0EBk8/Wc=
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 h1:S2dVYn90KE98chqDkyE9Z4N61UnQd+KOfgp5Iu53llk=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
go.etcd.io/etcd/api/v3 v3.5.14 h1:vHObSCxyB9zlF60w7qzAdTcGaglbJOpSj1Xj9+WGxq0= go.etcd.io/etcd/api/v3 v3.5.16 h1:WvmyJVbjWqK4R1E+B12RRHz3bRGy9XVfh++MgbN+6n0=
go.etcd.io/etcd/api/v3 v3.5.14/go.mod h1:BmtWcRlQvwa1h3G2jvKYwIQy4PkHlDej5t7uLMUdJUU= go.etcd.io/etcd/api/v3 v3.5.16/go.mod h1:1P4SlIP/VwkDmGo3OlOD7faPeP8KDIFhqvciH5EfN28=
go.etcd.io/etcd/client/pkg/v3 v3.5.14 h1:SaNH6Y+rVEdxfpA2Jr5wkEvN6Zykme5+YnbCkxvuWxQ= go.etcd.io/etcd/client/pkg/v3 v3.5.16 h1:ZgY48uH6UvB+/7R9Yf4x574uCO3jIx0TRDyetSfId3Q=
go.etcd.io/etcd/client/pkg/v3 v3.5.14/go.mod h1:8uMgAokyG1czCtIdsq+AGyYQMvpIKnSvPjFMunkgeZI= go.etcd.io/etcd/client/pkg/v3 v3.5.16/go.mod h1:V8acl8pcEK0Y2g19YlOV9m9ssUe6MgiDSobSoaBAM0E=
go.etcd.io/etcd/client/v2 v2.305.13 h1:RWfV1SX5jTU0lbCvpVQe3iPQeAHETWdOTb6pxhd77C8= go.etcd.io/etcd/client/v2 v2.305.16 h1:kQrn9o5czVNaukf2A2At43cE9ZtWauOtf9vRZuiKXow=
go.etcd.io/etcd/client/v2 v2.305.13/go.mod h1:iQnL7fepbiomdXMb3om1rHq96htNNGv2sJkEcZGDRRg= go.etcd.io/etcd/client/v2 v2.305.16/go.mod h1:h9YxWCzcdvZENbfzBTFCnoNumr2ax3F19sKMqHFmXHE=
go.etcd.io/etcd/client/v3 v3.5.14 h1:CWfRs4FDaDoSz81giL7zPpZH2Z35tbOrAJkkjMqOupg= go.etcd.io/etcd/client/v3 v3.5.16 h1:sSmVYOAHeC9doqi0gv7v86oY/BTld0SEFGaxsU9eRhE=
go.etcd.io/etcd/client/v3 v3.5.14/go.mod h1:k3XfdV/VIHy/97rqWjoUzrj9tk7GgJGH9J8L4dNXmAk= go.etcd.io/etcd/client/v3 v3.5.16/go.mod h1:X+rExSGkyqxvu276cr2OwPLBaeqFu1cIl4vmRjAD/50=
go.etcd.io/etcd/pkg/v3 v3.5.13 h1:st9bDWNsKkBNpP4PR1MvM/9NqUPfvYZx/YXegsYEH8M= go.etcd.io/etcd/pkg/v3 v3.5.16 h1:cnavs5WSPWeK4TYwPYfmcr3Joz9BH+TZ6qoUtz6/+mc=
go.etcd.io/etcd/pkg/v3 v3.5.13/go.mod h1:N+4PLrp7agI/Viy+dUYpX7iRtSPvKq+w8Y14d1vX+m0= go.etcd.io/etcd/pkg/v3 v3.5.16/go.mod h1:+lutCZHG5MBBFI/U4eYT5yL7sJfnexsoM20Y0t2uNuY=
go.etcd.io/etcd/raft/v3 v3.5.13 h1:7r/NKAOups1YnKcfro2RvGGo2PTuizF/xh26Z2CTAzA= go.etcd.io/etcd/raft/v3 v3.5.16 h1:zBXA3ZUpYs1AwiLGPafYAKKl/CORn/uaxYDwlNwndAk=
go.etcd.io/etcd/raft/v3 v3.5.13/go.mod h1:uUFibGLn2Ksm2URMxN1fICGhk8Wu96EfDQyuLhAcAmw= go.etcd.io/etcd/raft/v3 v3.5.16/go.mod h1:P4UP14AxofMJ/54boWilabqqWoW9eLodl6I5GdGzazI=
go.etcd.io/etcd/server/v3 v3.5.13 h1:V6KG+yMfMSqWt+lGnhFpP5z5dRUj1BDRJ5k1fQ9DFok= go.etcd.io/etcd/server/v3 v3.5.16 h1:d0/SAdJ3vVsZvF8IFVb1k8zqMZ+heGcNfft71ul9GWE=
go.etcd.io/etcd/server/v3 v3.5.13/go.mod h1:K/8nbsGupHqmr5MkgaZpLlH1QdX1pcNQLAkODy44XcQ= go.etcd.io/etcd/server/v3 v3.5.16/go.mod h1:ynhyZZpdDp1Gq49jkUg5mfkDWZwXnn3eIqCqtJnrD/s=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
@ -288,8 +292,8 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@ -318,7 +322,6 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
@ -360,70 +363,68 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
k8s.io/api v0.31.3 h1:umzm5o8lFbdN/hIXbrK9oRpOproJO62CV1zqxXrLgk8= k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE=
k8s.io/api v0.31.3/go.mod h1:UJrkIp9pnMOI9K2nlL6vwpxRzzEX5sWgn8kGQe92kCE= k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0=
k8s.io/apiextensions-apiserver v0.31.3 h1:+GFGj2qFiU7rGCsA5o+p/rul1OQIq6oYpQw4+u+nciE= k8s.io/apiextensions-apiserver v0.32.0 h1:S0Xlqt51qzzqjKPxfgX1xh4HBZE+p8KKBq+k2SWNOE0=
k8s.io/apiextensions-apiserver v0.31.3/go.mod h1:2DSpFhUZZJmn/cr/RweH1cEVVbzFw9YBu4T+U3mf1e4= k8s.io/apiextensions-apiserver v0.32.0/go.mod h1:86hblMvN5yxMvZrZFX2OhIHAuFIMJIZ19bTvzkP+Fmw=
k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4= k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg=
k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
k8s.io/apiserver v0.31.3 h1:+1oHTtCB+OheqFEz375D0IlzHZ5VeQKX1KGXnx+TTuY= k8s.io/apiserver v0.32.0 h1:VJ89ZvQZ8p1sLeiWdRJpRD6oLozNZD2+qVSLi+ft5Qs=
k8s.io/apiserver v0.31.3/go.mod h1:PrxVbebxrxQPFhJk4powDISIROkNMKHibTg9lTRQ0Qg= k8s.io/apiserver v0.32.0/go.mod h1:HFh+dM1/BE/Hm4bS4nTXHVfN6Z6tFIZPi649n83b4Ag=
k8s.io/client-go v0.31.3 h1:CAlZuM+PH2cm+86LOBemaJI/lQ5linJ6UFxKX/SoG+4= k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8=
k8s.io/client-go v0.31.3/go.mod h1:2CgjPUTpv3fE5dNygAr2NcM8nhHzXvxB8KL5gYc3kJs= k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8=
k8s.io/cloud-provider v0.31.3 h1:7C3CHQUUwnv/HWWVIaibZH06iPg663RYQ6C6Zy4FnO8= k8s.io/cloud-provider v0.32.0 h1:QXYJGmwME2q2rprymbmw2GroMChQYc/MWN6l/I4Kgp8=
k8s.io/cloud-provider v0.31.3/go.mod h1:c7csKppoVb9Ej6upJ28AvHy4B3BtlRMzXfgezsDdPKw= k8s.io/cloud-provider v0.32.0/go.mod h1:cz3gVodkhgwi2ugj/JUPglIruLSdDaThxawuDyCHfr8=
k8s.io/code-generator v0.31.3 h1:Pj0fYOBms+ZrsulLi4DMsCEx1jG8fWKRLy44onHsLBI= k8s.io/code-generator v0.32.0 h1:s0lNN8VSWny8LBz5t5iy7MCdgwdOhdg7vAGVxvS+VWU=
k8s.io/code-generator v0.31.3/go.mod h1:/umCIlT84g1+Yu5ZXtP1KGSRTnGiIzzX5AzUAxsNlts= k8s.io/code-generator v0.32.0/go.mod h1:b7Q7KMZkvsYFy72A79QYjiv4aTz3GvW0f1T3UfhFq4s=
k8s.io/component-base v0.31.3 h1:DMCXXVx546Rfvhj+3cOm2EUxhS+EyztH423j+8sOwhQ= k8s.io/component-base v0.32.0 h1:d6cWHZkCiiep41ObYQS6IcgzOUQUNpywm39KVYaUqzU=
k8s.io/component-base v0.31.3/go.mod h1:xME6BHfUOafRgT0rGVBGl7TuSg8Z9/deT7qq6w7qjIU= k8s.io/component-base v0.32.0/go.mod h1:JLG2W5TUxUu5uDyKiH2R/7NnxJo1HlPoRIIbVLkK5eM=
k8s.io/component-helpers v0.31.3 h1:0zGPD2PrekhFWgmz85XxlMEl7dfhlKC1tERZDe3onQc= k8s.io/component-helpers v0.32.0 h1:pQEEBmRt3pDJJX98cQvZshDgJFeKRM4YtYkMmfOlczw=
k8s.io/component-helpers v0.31.3/go.mod h1:HZ1HZx2TKXM7xSUV2cR9L5yDoyZPhhHQNaE3BPBLPUQ= k8s.io/component-helpers v0.32.0/go.mod h1:9RuClQatbClcokXOcDWSzFKQm1huIf0FzQlPRpizlMc=
k8s.io/controller-manager v0.31.3 h1:TyUav69iNYwLGwA96JDhusoZoGRdh1sdrLjXmWTcPgs= k8s.io/controller-manager v0.32.0 h1:tpQl1rvH4huFB6Avl1nhowZHtZoCNWqn6OYdZPl7Ybc=
k8s.io/controller-manager v0.31.3/go.mod h1:yuhec+dbXmBz+4c32kxJxmcauB+1pjO2ttfYODWuv18= k8s.io/controller-manager v0.32.0/go.mod h1:JRuYnYCkKj3NgBTy+KNQKIUm/lJRoDAvGbfdEmk9LhY=
k8s.io/cri-api v0.31.3 h1:dsZXzrGrCEwHjsTDlAV7rutEplpMLY8bfNRMIqrtXjo= k8s.io/cri-api v0.32.0 h1:pzXJfyG7Tm4acrEt5HPqAq3r4cN5guLeapAN/NM2b70=
k8s.io/cri-api v0.31.3/go.mod h1:Po3TMAYH/+KrZabi7QiwQI4a692oZcUOUThd/rqwxrI= k8s.io/cri-api v0.32.0/go.mod h1:DCzMuTh2padoinefWME0G678Mc3QFbLMF2vEweGzBAI=
k8s.io/cri-client v0.31.3 h1:9ZwddaNJomqkTBYQqSmB+Ccns3beY4HyYDwmRtWTCJM= k8s.io/cri-client v0.32.0 h1:K6aTYDyS2AS8O4h79eI5r26562xstdybprtaaszjn+E=
k8s.io/cri-client v0.31.3/go.mod h1:klbWiYkOatOQOkXOYZMZMGSTM8q9eC/efsYGuXcgPes= k8s.io/cri-client v0.32.0/go.mod h1:FB8qZNj8KrwIFfVIR2zBGb+l6KUhrp+k8YFsVp3D+kw=
k8s.io/csi-translation-lib v0.31.3 h1:hxcPRNdtEsk766jCXSKjgH1V8jUNx5tVqdooQ1Ars/M= k8s.io/csi-translation-lib v0.32.0 h1:RAn9RGgYXHJQtDSb6qQ7zvq6QObOejzmsXDARI+f4OQ=
k8s.io/csi-translation-lib v0.31.3/go.mod h1:0B1gQwd868XUIDwJYy5gB2jDXWEwlcWvSsfcQEgzbRk= k8s.io/csi-translation-lib v0.32.0/go.mod h1:TjCJzkTNstdOESAXNnEImrYOMIEzP14aqM7H+vkehqw=
k8s.io/dynamic-resource-allocation v0.31.3 h1:hiQMhyxrm7WA6tWJCLINE2P11Ii3rCVw+YzV3B6+eWo= k8s.io/dynamic-resource-allocation v0.32.0 h1:0ZLSCKzlLZLVwKHxg6vafpd2U8b7jPMO3k8bbMFodis=
k8s.io/dynamic-resource-allocation v0.31.3/go.mod h1:l7OvYDokvnXhFAOhInQUY/z0/HezzmRuLlspld4Xfg4= k8s.io/dynamic-resource-allocation v0.32.0/go.mod h1:MfoAUi0vCJtchNirAVk7c3IYfGGB3n+zbZ9GuyX4eeo=
k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 h1:NGrVE502P0s0/1hudf8zjgwki1X/TByhmAoILTarmzo= k8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9 h1:si3PfKm8dDYxgfbeA6orqrtLkvvIeH8UqffFJDl0bz4=
k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70/go.mod h1:VH3AT8AaQOqiGjMF9p0/IM1Dj+82ZwjfxUP1IxaHE+8= k8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kms v0.31.3 h1:XCFmiJn5CCKs8xoOLpCmu42Ubm/KW85wNHybGFcSAYc= k8s.io/kms v0.32.0 h1:jwOfunHIrcdYl5FRcA+uUKKtg6qiqoPCwmS2T3XTYL4=
k8s.io/kms v0.31.3/go.mod h1:OZKwl1fan3n3N5FFxnW5C4V3ygrah/3YXeJWS3O6+94= k8s.io/kms v0.32.0/go.mod h1:Bk2evz/Yvk0oVrvm4MvZbgq8BD34Ksxs2SRHn4/UiOM=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
k8s.io/kube-scheduler v0.31.3 h1:indE2jtvbwyyEYDrQMjPR2EmnoM1pd4L6cK8r2/KY0I= k8s.io/kube-scheduler v0.32.0 h1:FCsF/3TPvR51ptx/gLUrqcoKqAMhQKrydYCJzPz9VGM=
k8s.io/kube-scheduler v0.31.3/go.mod h1:vhDtABcWycLi8139K5ScOg54WKlbEZJRgtQmLhW0Wjo= k8s.io/kube-scheduler v0.32.0/go.mod h1:yof3vmyx70TWoQ6XZruYEGIUT/r0H/ELGdnWiqPF5EE=
k8s.io/kubectl v0.31.3 h1:3r111pCjPsvnR98oLLxDMwAeM6OPGmPty6gSKaLTQes= k8s.io/kubectl v0.32.0 h1:rpxl+ng9qeG79YA4Em9tLSfX0G8W0vfaiPVrc/WR7Xw=
k8s.io/kubectl v0.31.3/go.mod h1:lhMECDCbJN8He12qcKqs2QfmVo9Pue30geovBVpH5fs= k8s.io/kubectl v0.32.0/go.mod h1:qIjSX+QgPQUgdy8ps6eKsYNF+YmFOAO3WygfucIqFiE=
k8s.io/kubelet v0.31.3 h1:DIXRAmvVGp42mV2vpA1GCLU6oO8who0/vp3Oq6kSpbI= k8s.io/kubelet v0.32.0 h1:uLyiKlz195Wo4an/K2tyge8o3QHx0ZkhVN3pevvp59A=
k8s.io/kubelet v0.31.3/go.mod h1:KSdbEfNy5VzqUlAHlytA/fH12s+sE1u8fb/8JY9sL/8= k8s.io/kubelet v0.32.0/go.mod h1:lAwuVZT/Hm7EdLn0jW2D+WdrJoorjJL2rVSdhOFnegw=
k8s.io/kubernetes v1.31.3 h1:oqb7HdfnTelrGlZ6ziNugvQ/L/aJWR704114EAhUn9Q= k8s.io/kubernetes v1.32.0 h1:4BDBWSolqPrv8GC3YfZw0CJvh5kA1TPnoX0FxDVd+qc=
k8s.io/kubernetes v1.31.3/go.mod h1:9xmT2buyTYj8TRKwRae7FcuY8k5+xlxv7VivvO0KKfs= k8s.io/kubernetes v1.32.0/go.mod h1:tiIKO63GcdPRBHW2WiUFm3C0eoLczl3f7qi56Dm1W8I=
k8s.io/mount-utils v0.31.3 h1:CANy3prUYvvDCc2X7ZKgpjpDhAidx4gjGh/WwDrCPq8= k8s.io/mount-utils v0.32.0 h1:KOQAhPzJICATXnc6XCkWoexKbkOexRnMCUW8APFfwg4=
k8s.io/mount-utils v0.31.3/go.mod h1:HV/VYBUGqYUj4vt82YltzpWvgv8FPg0G9ItyInT3NPU= k8s.io/mount-utils v0.32.0/go.mod h1:Kun5c2svjAPx0nnvJKYQWhfeNW+O0EpzHgRhDcYoSY0=
k8s.io/pod-security-admission v0.31.3 h1:8NzEV0HtdStX367AuSKfRMIZHn0hT4xuz8xNEf7/zO8= k8s.io/pod-security-admission v0.32.0 h1:I+Og0uZIiMpIgTgPrTbW4jlwRI5RWazi8y+jrx1v10w=
k8s.io/pod-security-admission v0.31.3/go.mod h1:YMIcTe/7f9R9d+3ErCMMM3Wtbj9ejKo7Z9S0OxZQrRg= k8s.io/pod-security-admission v0.32.0/go.mod h1:RvrcY0+5UAoCIJ7BscgDF3nbmXprgfnjTW+byCyXDvA=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 h1:2770sDpzrjjsAtVhSeUFseziht227YAWYHLGNM8QPwY= oras.land/oras-go/v2 v2.5.0 h1:o8Me9kLY74Vp5uw07QXPiitjsw7qNXi8Twd+19Zf02c=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= oras.land/oras-go/v2 v2.5.0/go.mod h1:z4eisnLP530vwIOUOJeBIj0aGI0L1C3d53atvCBqZHg=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 h1:CPT0ExVicCzcpeN4baWEV2ko2Z/AsiZgEdwgcfwLgMo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=
sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=

View file

@ -116,7 +116,7 @@ bar: { op: Exists }
t.Fatal("failed to parse data of test case") t.Fatal("failed to parse data of test case")
} }
res, out, err := api.MatchGetKeys(mes, tc.input) res, out, _, err := api.MatchGetKeys(mes, tc.input)
tc.result(t, res) tc.result(t, res)
assert.Equal(t, tc.output, out) assert.Equal(t, tc.output, out)
tc.err(t, err) tc.err(t, err)
@ -183,12 +183,12 @@ baz: { op: Gt, value: ["10"] }
t.Fatal("failed to parse data of test case") t.Fatal("failed to parse data of test case")
} }
res, out, err := api.MatchGetValues(mes, tc.input) res, out, _, err := api.MatchGetValues(mes, tc.input, true)
tc.result(t, res) tc.result(t, res)
assert.Equal(t, tc.output, out) assert.Equal(t, tc.output, out)
tc.err(t, err) tc.err(t, err)
res, err = api.MatchValues(mes, tc.input) res, _, err = api.MatchValues(mes, tc.input, true)
tc.result(t, res) tc.result(t, res)
tc.err(t, err) tc.err(t, err)
}) })
@ -248,12 +248,12 @@ bar: { op: Lt, value: ["10"] }
t.Fatal("failed to parse data of test case") t.Fatal("failed to parse data of test case")
} }
res, out, err := api.MatchGetInstances(mes, tc.input) res, out, _, err := api.MatchGetInstances(mes, tc.input, true)
assert.Equal(t, tc.output, out) assert.Equal(t, tc.output, out)
tc.result(t, res) tc.result(t, res)
tc.err(t, err) tc.err(t, err)
res, err = api.MatchInstances(mes, tc.input) res, err = api.MatchInstances(mes, tc.input, true)
tc.result(t, res) tc.result(t, res)
tc.err(t, err) tc.err(t, err)
}) })
@ -745,7 +745,7 @@ func TestMatchMulti(t *testing.T) {
for _, tc := range tcs { for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
res, out, err := api.MatchMulti(tc.mes, tc.inputKeys, tc.inputValues, tc.inputInstances) res, out, _, err := api.MatchMulti(tc.mes, tc.inputKeys, tc.inputValues, tc.inputInstances, true)
if tc.expectErr { if tc.expectErr {
assert.NotNil(t, err) assert.NotNil(t, err)
} else { } else {

View file

@ -29,6 +29,13 @@ import (
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1" nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
) )
const (
// MatchedKeyName is the name of the matched flag/attribute element.
MatchedKeyName = "Name"
// MatchedKeyValue is the value of the matched attribute element.
MatchedKeyValue = "Value"
)
var matchOps = map[nfdv1alpha1.MatchOp]struct{}{ var matchOps = map[nfdv1alpha1.MatchOp]struct{}{
nfdv1alpha1.MatchAny: {}, nfdv1alpha1.MatchAny: {},
nfdv1alpha1.MatchIn: {}, nfdv1alpha1.MatchIn: {},
@ -202,16 +209,16 @@ func MatchKeyNames(m *nfdv1alpha1.MatchExpression, keys map[string]nfdv1alpha1.N
if match, err := evaluateMatchExpression(m, true, k); err != nil { if match, err := evaluateMatchExpression(m, true, k); err != nil {
return false, nil, err return false, nil, err
} else if match { } else if match {
ret = append(ret, MatchedElement{"Name": k}) ret = append(ret, MatchedElement{MatchedKeyName: k})
} }
} }
// Sort for reproducible output // Sort for reproducible output
sort.Slice(ret, func(i, j int) bool { return ret[i]["Name"] < ret[j]["Name"] }) sort.Slice(ret, func(i, j int) bool { return ret[i][MatchedKeyName] < ret[j][MatchedKeyName] })
if klogV3 := klog.V(3); klogV3.Enabled() { if klogV3 := klog.V(3); klogV3.Enabled() {
mk := make([]string, len(ret)) mk := make([]string, len(ret))
for i, v := range ret { for i, v := range ret {
mk[i] = v["Name"] mk[i] = v[MatchedKeyName]
} }
mkMsg := strings.Join(mk, ", ") mkMsg := strings.Join(mk, ", ")
@ -238,16 +245,16 @@ func MatchValueNames(m *nfdv1alpha1.MatchExpression, values map[string]string) (
if match, err := evaluateMatchExpression(m, true, k); err != nil { if match, err := evaluateMatchExpression(m, true, k); err != nil {
return false, nil, err return false, nil, err
} else if match { } else if match {
ret = append(ret, MatchedElement{"Name": k, "Value": v}) ret = append(ret, MatchedElement{MatchedKeyName: k, MatchedKeyValue: v})
} }
} }
// Sort for reproducible output // Sort for reproducible output
sort.Slice(ret, func(i, j int) bool { return ret[i]["Name"] < ret[j]["Name"] }) sort.Slice(ret, func(i, j int) bool { return ret[i][MatchedKeyName] < ret[j][MatchedKeyName] })
if klogV3 := klog.V(3); klogV3.Enabled() { if klogV3 := klog.V(3); klogV3.Enabled() {
mk := make([]string, len(ret)) mk := make([]string, len(ret))
for i, v := range ret { for i, v := range ret {
mk[i] = v["Name"] mk[i] = v[MatchedKeyName]
} }
mkMsg := strings.Join(mk, ", ") mkMsg := strings.Join(mk, ", ")
@ -278,7 +285,7 @@ func MatchInstanceAttributeNames(m *nfdv1alpha1.MatchExpression, instances []nfd
// MatchKeys evaluates the MatchExpressionSet against a set of keys. // MatchKeys evaluates the MatchExpressionSet against a set of keys.
func MatchKeys(m *nfdv1alpha1.MatchExpressionSet, keys map[string]nfdv1alpha1.Nil) (bool, error) { func MatchKeys(m *nfdv1alpha1.MatchExpressionSet, keys map[string]nfdv1alpha1.Nil) (bool, error) {
matched, _, err := MatchGetKeys(m, keys) matched, _, _, err := MatchGetKeys(m, keys)
return matched, err return matched, err
} }
@ -288,56 +295,65 @@ type MatchedElement map[string]string
// MatchGetKeys evaluates the MatchExpressionSet against a set of keys and // MatchGetKeys evaluates the MatchExpressionSet against a set of keys and
// returns all matched keys or nil if no match was found. Note that an empty // returns all matched keys or nil if no match was found. Note that an empty
// MatchExpressionSet returns a match with an empty slice of matched features. // MatchExpressionSet returns a match with an empty slice of matched features.
func MatchGetKeys(m *nfdv1alpha1.MatchExpressionSet, keys map[string]nfdv1alpha1.Nil) (bool, []MatchedElement, error) { func MatchGetKeys(m *nfdv1alpha1.MatchExpressionSet, keys map[string]nfdv1alpha1.Nil) (bool, []MatchedElement, *nfdv1alpha1.MatchExpressionSet, error) {
ret := make([]MatchedElement, 0, len(*m)) matchedElements := make([]MatchedElement, 0, len(*m))
matchedExpressions := make(nfdv1alpha1.MatchExpressionSet)
for n, e := range *m { for n, e := range *m {
match, err := evaluateMatchExpressionKeys(e, n, keys) match, err := evaluateMatchExpressionKeys(e, n, keys)
if err != nil { if err != nil {
return false, nil, err return false, nil, nil, err
} }
if !match { if !match {
return false, nil, nil return false, nil, nil, nil
} }
ret = append(ret, MatchedElement{"Name": n}) matchedElements = append(matchedElements, MatchedElement{MatchedKeyName: n})
matchedExpressions[n] = e
} }
// Sort for reproducible output // Sort for reproducible output
sort.Slice(ret, func(i, j int) bool { return ret[i]["Name"] < ret[j]["Name"] }) sort.Slice(matchedElements, func(i, j int) bool { return matchedElements[i][MatchedKeyName] < matchedElements[j][MatchedKeyName] })
return true, ret, nil return true, matchedElements, &matchedExpressions, nil
} }
// MatchValues evaluates the MatchExpressionSet against a set of key-value pairs. // MatchValues evaluates the MatchExpressionSet against a set of key-value pairs.
func MatchValues(m *nfdv1alpha1.MatchExpressionSet, values map[string]string) (bool, error) { func MatchValues(m *nfdv1alpha1.MatchExpressionSet, values map[string]string, failFast bool) (bool, *nfdv1alpha1.MatchExpressionSet, error) {
matched, _, err := MatchGetValues(m, values) matched, _, matchedExpressions, err := MatchGetValues(m, values, failFast)
return matched, err return matched, matchedExpressions, err
} }
// MatchGetValues evaluates the MatchExpressionSet against a set of key-value // MatchGetValues evaluates the MatchExpressionSet against a set of key-value
// pairs and returns all matched key-value pairs. Note that an empty // pairs and returns all matched key-value pairs. Note that an empty
// MatchExpressionSet returns a match with an empty slice of matched features. // MatchExpressionSet returns a match with an empty slice of matched features.
func MatchGetValues(m *nfdv1alpha1.MatchExpressionSet, values map[string]string) (bool, []MatchedElement, error) { func MatchGetValues(m *nfdv1alpha1.MatchExpressionSet, values map[string]string, failFast bool) (bool, []MatchedElement, *nfdv1alpha1.MatchExpressionSet, error) {
ret := make([]MatchedElement, 0, len(*m)) matchedElements := make([]MatchedElement, 0, len(*m))
matchedExpressions := make(nfdv1alpha1.MatchExpressionSet)
isMatch := true
for n, e := range *m { for n, e := range *m {
match, err := evaluateMatchExpressionValues(e, n, values) match, err := evaluateMatchExpressionValues(e, n, values)
if err != nil { if err != nil {
return false, nil, err return false, nil, nil, err
} }
if !match { if match {
return false, nil, nil matchedElements = append(matchedElements, MatchedElement{MatchedKeyName: n, MatchedKeyValue: values[n]})
matchedExpressions[n] = e
} else {
if failFast {
return false, nil, nil, nil
}
isMatch = false
} }
ret = append(ret, MatchedElement{"Name": n, "Value": values[n]})
} }
// Sort for reproducible output // Sort for reproducible output
sort.Slice(ret, func(i, j int) bool { return ret[i]["Name"] < ret[j]["Name"] }) sort.Slice(matchedElements, func(i, j int) bool { return matchedElements[i][MatchedKeyName] < matchedElements[j][MatchedKeyName] })
return true, ret, nil return isMatch, matchedElements, &matchedExpressions, nil
} }
// MatchInstances evaluates the MatchExpressionSet against a set of instance // MatchInstances evaluates the MatchExpressionSet against a set of instance
// features, each of which is an individual set of key-value pairs // features, each of which is an individual set of key-value pairs
// (attributes). // (attributes).
func MatchInstances(m *nfdv1alpha1.MatchExpressionSet, instances []nfdv1alpha1.InstanceFeature) (bool, error) { func MatchInstances(m *nfdv1alpha1.MatchExpressionSet, instances []nfdv1alpha1.InstanceFeature, failFast bool) (bool, error) {
isMatch, _, err := MatchGetInstances(m, instances) isMatch, _, _, err := MatchGetInstances(m, instances, failFast)
return isMatch, err return isMatch, err
} }
@ -346,17 +362,28 @@ func MatchInstances(m *nfdv1alpha1.MatchExpressionSet, instances []nfdv1alpha1.I
// (attributes). Returns a boolean that reports whether the expression matched. // (attributes). Returns a boolean that reports whether the expression matched.
// Also, returns a slice containing all matching instances. An empty (non-nil) // Also, returns a slice containing all matching instances. An empty (non-nil)
// slice is returned if no matching instances were found. // slice is returned if no matching instances were found.
func MatchGetInstances(m *nfdv1alpha1.MatchExpressionSet, instances []nfdv1alpha1.InstanceFeature) (bool, []MatchedElement, error) { func MatchGetInstances(m *nfdv1alpha1.MatchExpressionSet, instances []nfdv1alpha1.InstanceFeature, failFast bool) (bool, []MatchedElement, *nfdv1alpha1.MatchExpressionSet, error) {
ret := []MatchedElement{} var (
match bool
err error
expressionSet *nfdv1alpha1.MatchExpressionSet
)
matchedElements := []MatchedElement{}
matchedExpressions := &nfdv1alpha1.MatchExpressionSet{}
for _, i := range instances { for _, i := range instances {
if match, err := MatchValues(m, i.Attributes); err != nil { if match, expressionSet, err = MatchValues(m, i.Attributes, failFast); err != nil {
return false, nil, err return false, nil, nil, err
} else if match { } else if match {
ret = append(ret, i.Attributes) matchedElements = append(matchedElements, i.Attributes)
}
if expressionSet != nil {
for name, exp := range *expressionSet {
(*matchedExpressions)[name] = exp
} }
} }
return len(ret) > 0, ret, nil }
return len(matchedElements) > 0, matchedElements, matchedExpressions, nil
} }
// MatchMulti evaluates a MatchExpressionSet against key, value and instance // MatchMulti evaluates a MatchExpressionSet against key, value and instance
@ -365,8 +392,9 @@ func MatchGetInstances(m *nfdv1alpha1.MatchExpressionSet, instances []nfdv1alpha
// handled separately as the way of evaluating match expressions is different. // handled separately as the way of evaluating match expressions is different.
// This function is written to handle "multi-type" features where one feature // This function is written to handle "multi-type" features where one feature
// (say "cpu.cpuid") contains multiple types (flag, attribute and/or instance). // (say "cpu.cpuid") contains multiple types (flag, attribute and/or instance).
func MatchMulti(m *nfdv1alpha1.MatchExpressionSet, keys map[string]nfdv1alpha1.Nil, values map[string]string, instances []nfdv1alpha1.InstanceFeature) (bool, []MatchedElement, error) { func MatchMulti(m *nfdv1alpha1.MatchExpressionSet, keys map[string]nfdv1alpha1.Nil, values map[string]string, instances []nfdv1alpha1.InstanceFeature, failFast bool) (bool, []MatchedElement, *nfdv1alpha1.MatchExpressionSet, error) {
matchedElems := []MatchedElement{} matchedElems := []MatchedElement{}
matchedExpressions := nfdv1alpha1.MatchExpressionSet{}
isMatch := false isMatch := false
// Keys and values are handled as a union, it is enough to find a match in // Keys and values are handled as a union, it is enough to find a match in
@ -384,14 +412,19 @@ func MatchMulti(m *nfdv1alpha1.MatchExpressionSet, keys map[string]nfdv1alpha1.N
if keys != nil { if keys != nil {
matchK, err = evaluateMatchExpressionKeys(e, n, keys) matchK, err = evaluateMatchExpressionKeys(e, n, keys)
if err != nil { if err != nil {
return false, nil, err return false, nil, nil, err
} }
if matchK { if matchK {
matchedElems = append(matchedElems, MatchedElement{"Name": n}) matchedElems = append(matchedElems, MatchedElement{MatchedKeyName: n})
matchedExpressions[n] = e
} else if e.Op == nfdv1alpha1.MatchDoesNotExist { } else if e.Op == nfdv1alpha1.MatchDoesNotExist {
// DoesNotExist is special in that both "keys" and "values" should match (i.e. the name is not found in either of them). // DoesNotExist is special in that both "keys" and "values" should match (i.e. the name is not found in either of them).
isMatch = false isMatch = false
if !failFast {
continue
}
matchedElems = []MatchedElement{} matchedElems = []MatchedElement{}
matchedExpressions = nfdv1alpha1.MatchExpressionSet{}
break break
} }
} }
@ -399,39 +432,53 @@ func MatchMulti(m *nfdv1alpha1.MatchExpressionSet, keys map[string]nfdv1alpha1.N
if values != nil { if values != nil {
matchV, err = evaluateMatchExpressionValues(e, n, values) matchV, err = evaluateMatchExpressionValues(e, n, values)
if err != nil { if err != nil {
return false, nil, err return false, nil, nil, err
} }
if matchV { if matchV {
matchedElems = append(matchedElems, MatchedElement{"Name": n, "Value": values[n]}) matchedElems = append(matchedElems, MatchedElement{MatchedKeyName: n, MatchedKeyValue: values[n]})
matchedExpressions[n] = e
} else if e.Op == nfdv1alpha1.MatchDoesNotExist { } else if e.Op == nfdv1alpha1.MatchDoesNotExist {
// DoesNotExist is special in that both "keys" and "values" should match (i.e. the name is not found in either of them). // DoesNotExist is special in that both "keys" and "values" should match (i.e. the name is not found in either of them).
isMatch = false isMatch = false
if !failFast {
continue
}
matchedElems = []MatchedElement{} matchedElems = []MatchedElement{}
matchedExpressions = nfdv1alpha1.MatchExpressionSet{}
break break
} }
} }
if !matchK && !matchV { if !matchK && !matchV {
isMatch = false isMatch = false
if !failFast {
continue
}
matchedElems = []MatchedElement{} matchedElems = []MatchedElement{}
matchedExpressions = nfdv1alpha1.MatchExpressionSet{}
break break
} }
} }
// Sort for reproducible output // Sort for reproducible output
sort.Slice(matchedElems, func(i, j int) bool { return matchedElems[i]["Name"] < matchedElems[j]["Name"] }) sort.Slice(matchedElems, func(i, j int) bool { return matchedElems[i][MatchedKeyName] < matchedElems[j][MatchedKeyName] })
// Instances are handled separately as the logic is fundamentally different // Instances are handled separately as the logic is fundamentally different
// from keys and values and cannot be combined with them. We want to find // from keys and values and cannot be combined with them. We want to find
// instance(s) that match all match expressions. I.e. the set of all match // instance(s) that match all match expressions. I.e. the set of all match
// expressions are evaluated against every instance separately. // expressions are evaluated against every instance separately.
ma, me, err := MatchGetInstances(m, instances) ma, melems, mexps, err := MatchGetInstances(m, instances, failFast)
if err != nil { if err != nil {
return false, nil, err return false, nil, nil, err
} }
isMatch = isMatch || ma isMatch = isMatch || ma
matchedElems = append(matchedElems, me...) matchedElems = append(matchedElems, melems...)
if mexps != nil {
for k, v := range *mexps {
matchedExpressions[k] = v
}
}
return isMatch, matchedElems, nil return isMatch, matchedElems, &matchedExpressions, nil
} }
// MatchNamesMulti evaluates the MatchExpression against the names of key, // MatchNamesMulti evaluates the MatchExpression against the names of key,
@ -440,12 +487,11 @@ func MatchMulti(m *nfdv1alpha1.MatchExpressionSet, keys map[string]nfdv1alpha1.N
// types (flag, attribute and/or instance). // types (flag, attribute and/or instance).
func MatchNamesMulti(m *nfdv1alpha1.MatchExpression, keys map[string]nfdv1alpha1.Nil, values map[string]string, instances []nfdv1alpha1.InstanceFeature) (bool, []MatchedElement, error) { func MatchNamesMulti(m *nfdv1alpha1.MatchExpression, keys map[string]nfdv1alpha1.Nil, values map[string]string, instances []nfdv1alpha1.InstanceFeature) (bool, []MatchedElement, error) {
ret := []MatchedElement{} ret := []MatchedElement{}
for k := range keys { for k := range keys {
if match, err := evaluateMatchExpression(m, true, k); err != nil { if match, err := evaluateMatchExpression(m, true, k); err != nil {
return false, nil, err return false, nil, err
} else if match { } else if match {
ret = append(ret, MatchedElement{"Name": k}) ret = append(ret, MatchedElement{MatchedKeyName: k})
} }
} }
@ -453,12 +499,12 @@ func MatchNamesMulti(m *nfdv1alpha1.MatchExpression, keys map[string]nfdv1alpha1
if match, err := evaluateMatchExpression(m, true, k); err != nil { if match, err := evaluateMatchExpression(m, true, k); err != nil {
return false, nil, err return false, nil, err
} else if match { } else if match {
ret = append(ret, MatchedElement{"Name": k, "Value": v}) ret = append(ret, MatchedElement{MatchedKeyName: k, MatchedKeyValue: v})
} }
} }
// Sort for reproducible output // Sort for reproducible output
sort.Slice(ret, func(i, j int) bool { return ret[i]["Name"] < ret[j]["Name"] }) sort.Slice(ret, func(i, j int) bool { return ret[i][MatchedKeyName] < ret[j][MatchedKeyName] })
_, me, err := MatchInstanceAttributeNames(m, instances) _, me, err := MatchInstanceAttributeNames(m, instances)
if err != nil { if err != nil {
@ -469,7 +515,7 @@ func MatchNamesMulti(m *nfdv1alpha1.MatchExpression, keys map[string]nfdv1alpha1
if klogV3 := klog.V(3); klogV3.Enabled() { if klogV3 := klog.V(3); klogV3.Enabled() {
mk := make([]string, len(ret)) mk := make([]string, len(ret))
for i, v := range ret { for i, v := range ret {
mk[i] = v["Name"] mk[i] = v[MatchedKeyName]
} }
mkMsg := strings.Join(mk, ", ") mkMsg := strings.Join(mk, ", ")

View file

@ -31,6 +31,31 @@ import (
"sigs.k8s.io/node-feature-discovery/pkg/utils" "sigs.k8s.io/node-feature-discovery/pkg/utils"
) )
// MatchStatus represents the status of a processed rule.
// It includes information about successful expressions and their results, which are the matched host features.
// For example, for the expression: cpu.cpuid: {op: "InRegexp", value: ["^AVX"]},
// the result could include matched host features such as AVX, AVX2, AVX512 etc.
// +k8s:deepcopy-gen=false
type MatchStatus struct {
*MatchFeatureStatus
// IsMatch informes whether a rule succeeded or failed.
IsMatch bool
// MatchAny represents an array of logical OR conditions between MatchFeatureStatus entries.
MatchAny []*MatchFeatureStatus
}
// MatchFeatureStatus represents a matched expression
// with its result, which is matched host features.
// +k8s:deepcopy-gen=false
type MatchFeatureStatus struct {
// MatchedFeatures represents the features matched on the host,
// which is a result of the FeatureMatcher.
MatchedFeatures matchedFeatures
// MatchedFeaturesTerms represents the expressions that successfully matched on the host.
MatchedFeaturesTerms nfdv1alpha1.FeatureMatcher
}
// RuleOutput contains the output out rule execution. // RuleOutput contains the output out rule execution.
// +k8s:deepcopy-gen=false // +k8s:deepcopy-gen=false
type RuleOutput struct { type RuleOutput struct {
@ -39,56 +64,69 @@ type RuleOutput struct {
Annotations map[string]string Annotations map[string]string
Vars map[string]string Vars map[string]string
Taints []corev1.Taint Taints []corev1.Taint
MatchStatus *MatchStatus
} }
// Execute the rule against a set of input features. // Execute the rule against a set of input features.
func Execute(r *nfdv1alpha1.Rule, features *nfdv1alpha1.Features) (RuleOutput, error) { func Execute(r *nfdv1alpha1.Rule, features *nfdv1alpha1.Features, failFast bool) (RuleOutput, error) {
var (
matchStatus MatchStatus
isMatch bool
err error
)
labels := make(map[string]string) labels := make(map[string]string)
vars := make(map[string]string) vars := make(map[string]string)
if len(r.MatchAny) > 0 { if n := len(r.MatchAny); n > 0 {
matchStatus.MatchAny = make([]*MatchFeatureStatus, 0, n)
// Logical OR over the matchAny matchers // Logical OR over the matchAny matchers
matched := false var (
featureStatus *MatchFeatureStatus
matched bool
)
for _, matcher := range r.MatchAny { for _, matcher := range r.MatchAny {
if isMatch, matches, err := evaluateMatchAnyElem(&matcher, features); err != nil { if matched, featureStatus, err = evaluateMatchAnyElem(&matcher, features, failFast); err != nil {
return RuleOutput{}, err return RuleOutput{}, err
} else if isMatch { } else if matched {
matched = true isMatch = true
klog.V(4).InfoS("matchAny matched", "ruleName", r.Name, "matchedFeatures", utils.DelayedDumper(matches)) klog.V(4).InfoS("matchAny matched", "ruleName", r.Name, "matchedFeatures", utils.DelayedDumper(featureStatus.MatchedFeatures))
if r.LabelsTemplate == "" && r.VarsTemplate == "" { if r.LabelsTemplate == "" && r.VarsTemplate == "" && failFast {
// there's no need to evaluate other matchers in MatchAny // there's no need to evaluate other matchers in MatchAny
// if there are no templates to be executed on them - so // if there are no templates to be executed on them - so
// short-circuit and stop on first match here // short-circuit and stop on first match here
break break
} }
if err := executeLabelsTemplate(r, matches, labels); err != nil { if err := executeLabelsTemplate(r, featureStatus.MatchedFeatures, labels); err != nil {
return RuleOutput{}, err return RuleOutput{}, err
} }
if err := executeVarsTemplate(r, matches, vars); err != nil { if err := executeVarsTemplate(r, featureStatus.MatchedFeatures, vars); err != nil {
return RuleOutput{}, err return RuleOutput{}, err
} }
} }
matchStatus.MatchAny = append(matchStatus.MatchAny, featureStatus)
} }
if !matched {
if !isMatch {
klog.V(2).InfoS("rule did not match", "ruleName", r.Name) klog.V(2).InfoS("rule did not match", "ruleName", r.Name)
return RuleOutput{}, nil return RuleOutput{MatchStatus: &matchStatus}, nil
} }
} }
if len(r.MatchFeatures) > 0 { if len(r.MatchFeatures) > 0 {
if isMatch, matches, err := evaluateFeatureMatcher(&r.MatchFeatures, features); err != nil { if isMatch, matchStatus.MatchFeatureStatus, err = evaluateFeatureMatcher(&r.MatchFeatures, features, failFast); err != nil {
return RuleOutput{}, err return RuleOutput{}, err
} else if !isMatch { } else if !isMatch {
klog.V(2).InfoS("rule did not match", "ruleName", r.Name) klog.V(2).InfoS("rule did not match", "ruleName", r.Name)
return RuleOutput{}, nil return RuleOutput{MatchStatus: &matchStatus}, nil
} else { } else {
klog.V(4).InfoS("matchFeatures matched", "ruleName", r.Name, "matchedFeatures", utils.DelayedDumper(matches)) klog.V(4).InfoS("matchFeatures matched", "ruleName", r.Name, "matchedFeatures", utils.DelayedDumper(matchStatus.MatchedFeatures))
if err := executeLabelsTemplate(r, matches, labels); err != nil { if err := executeLabelsTemplate(r, matchStatus.MatchedFeatures, labels); err != nil {
return RuleOutput{}, err return RuleOutput{}, err
} }
if err := executeVarsTemplate(r, matches, vars); err != nil { if err := executeVarsTemplate(r, matchStatus.MatchedFeatures, vars); err != nil {
return RuleOutput{}, err return RuleOutput{}, err
} }
} }
@ -96,6 +134,7 @@ func Execute(r *nfdv1alpha1.Rule, features *nfdv1alpha1.Features) (RuleOutput, e
maps.Copy(labels, r.Labels) maps.Copy(labels, r.Labels)
maps.Copy(vars, r.Vars) maps.Copy(vars, r.Vars)
matchStatus.IsMatch = true
ret := RuleOutput{ ret := RuleOutput{
Labels: labels, Labels: labels,
@ -103,6 +142,7 @@ func Execute(r *nfdv1alpha1.Rule, features *nfdv1alpha1.Features) (RuleOutput, e
Annotations: maps.Clone(r.Annotations), Annotations: maps.Clone(r.Annotations),
ExtendedResources: maps.Clone(r.ExtendedResources), ExtendedResources: maps.Clone(r.ExtendedResources),
Taints: slices.Clone(r.Taints), Taints: slices.Clone(r.Taints),
MatchStatus: &matchStatus,
} }
klog.V(2).InfoS("rule matched", "ruleName", r.Name, "ruleOutput", utils.DelayedDumper(ret)) klog.V(2).InfoS("rule matched", "ruleName", r.Name, "ruleOutput", utils.DelayedDumper(ret))
return ret, nil return ret, nil
@ -110,12 +150,12 @@ func Execute(r *nfdv1alpha1.Rule, features *nfdv1alpha1.Features) (RuleOutput, e
// ExecuteGroupRule executes the GroupRule against a set of input features, and return true if the // ExecuteGroupRule executes the GroupRule against a set of input features, and return true if the
// rule matches. // rule matches.
func ExecuteGroupRule(r *nfdv1alpha1.GroupRule, features *nfdv1alpha1.Features) (bool, error) { func ExecuteGroupRule(r *nfdv1alpha1.GroupRule, features *nfdv1alpha1.Features, failFast bool) (bool, error) {
matched := false matched := false
if len(r.MatchAny) > 0 { if len(r.MatchAny) > 0 {
// Logical OR over the matchAny matchers // Logical OR over the matchAny matchers
for _, matcher := range r.MatchAny { for _, matcher := range r.MatchAny {
if isMatch, matches, err := evaluateMatchAnyElem(&matcher, features); err != nil { if isMatch, matches, err := evaluateMatchAnyElem(&matcher, features, failFast); err != nil {
return false, err return false, err
} else if isMatch { } else if isMatch {
matched = true matched = true
@ -131,7 +171,7 @@ func ExecuteGroupRule(r *nfdv1alpha1.GroupRule, features *nfdv1alpha1.Features)
} }
if len(r.MatchFeatures) > 0 { if len(r.MatchFeatures) > 0 {
if isMatch, _, err := evaluateFeatureMatcher(&r.MatchFeatures, features); err != nil { if isMatch, _, err := evaluateFeatureMatcher(&r.MatchFeatures, features, failFast); err != nil {
return false, err return false, err
} else if !isMatch { } else if !isMatch {
klog.V(2).InfoS("rule did not match", "ruleName", r.Name) klog.V(2).InfoS("rule did not match", "ruleName", r.Name)
@ -187,12 +227,18 @@ type matchedFeatures map[string]domainMatchedFeatures
type domainMatchedFeatures map[string][]MatchedElement type domainMatchedFeatures map[string][]MatchedElement
func evaluateMatchAnyElem(e *nfdv1alpha1.MatchAnyElem, features *nfdv1alpha1.Features) (bool, matchedFeatures, error) { func evaluateMatchAnyElem(e *nfdv1alpha1.MatchAnyElem, features *nfdv1alpha1.Features, failFast bool) (bool, *MatchFeatureStatus, error) {
return evaluateFeatureMatcher(&e.MatchFeatures, features) return evaluateFeatureMatcher(&e.MatchFeatures, features, failFast)
} }
func evaluateFeatureMatcher(m *nfdv1alpha1.FeatureMatcher, features *nfdv1alpha1.Features) (bool, matchedFeatures, error) { func evaluateFeatureMatcher(m *nfdv1alpha1.FeatureMatcher, features *nfdv1alpha1.Features, failFast bool) (bool, *MatchFeatureStatus, error) {
matches := make(matchedFeatures, len(*m)) var (
isMatch = true
isTermMatch = true
)
status := &MatchFeatureStatus{
MatchedFeatures: make(matchedFeatures, len(*m)),
}
// Logical AND over the terms // Logical AND over the terms
for _, term := range *m { for _, term := range *m {
@ -207,14 +253,17 @@ func evaluateFeatureMatcher(m *nfdv1alpha1.FeatureMatcher, features *nfdv1alpha1
dom := nameSplit[0] dom := nameSplit[0]
nam := nameSplit[1] nam := nameSplit[1]
if _, ok := matches[dom]; !ok { if _, ok := status.MatchedFeatures[dom]; !ok {
matches[dom] = make(domainMatchedFeatures) status.MatchedFeatures[dom] = make(domainMatchedFeatures)
} }
var isMatch = true
var matchedElems []MatchedElement var matchedElems []MatchedElement
var matchedExpressions *nfdv1alpha1.MatchExpressionSet
var err error var err error
matchedFeatureTerm := nfdv1alpha1.FeatureMatcherTerm{
Feature: featureName,
}
fF, okF := features.Flags[featureName] fF, okF := features.Flags[featureName]
fA, okA := features.Attributes[featureName] fA, okA := features.Attributes[featureName]
fI, okI := features.Instances[featureName] fI, okI := features.Instances[featureName]
@ -224,24 +273,37 @@ func evaluateFeatureMatcher(m *nfdv1alpha1.FeatureMatcher, features *nfdv1alpha1
} }
if term.MatchExpressions != nil { if term.MatchExpressions != nil {
isMatch, matchedElems, err = MatchMulti(term.MatchExpressions, fF.Elements, fA.Elements, fI.Elements) isTermMatch, matchedElems, matchedExpressions, err = MatchMulti(term.MatchExpressions, fF.Elements, fA.Elements, fI.Elements, failFast)
matchedFeatureTerm.MatchExpressions = matchedExpressions
} }
if err == nil && isMatch && term.MatchName != nil { if err == nil && isTermMatch && term.MatchName != nil {
var meTmp []MatchedElement var meTmp []MatchedElement
isMatch, meTmp, err = MatchNamesMulti(term.MatchName, fF.Elements, fA.Elements, fI.Elements) isTermMatch, meTmp, err = MatchNamesMulti(term.MatchName, fF.Elements, fA.Elements, fI.Elements)
matchedElems = append(matchedElems, meTmp...) matchedElems = append(matchedElems, meTmp...)
// MatchName has only one expression, in this case it's enough to check the isTermMatch flag
// to judge if the expression succeeded on the host.
if isTermMatch {
matchedFeatureTerm.MatchName = term.MatchName
}
} }
matches[dom][nam] = append(matches[dom][nam], matchedElems...) status.MatchedFeatures[dom][nam] = append(status.MatchedFeatures[dom][nam], matchedElems...)
if matchedFeatureTerm.MatchName != nil || (matchedFeatureTerm.MatchExpressions != nil && len(*matchedFeatureTerm.MatchExpressions) > 0) {
status.MatchedFeaturesTerms = append(status.MatchedFeaturesTerms, matchedFeatureTerm)
}
if err != nil { if err != nil {
return false, nil, err return false, nil, err
} else if !isMatch { } else if !isTermMatch {
return false, nil, nil if !failFast {
isMatch = false
} else {
return false, status, nil
} }
} }
return true, matches, nil }
return isMatch, status, nil
} }
type templateHelper struct { type templateHelper struct {

View file

@ -49,11 +49,11 @@ func TestRule(t *testing.T) {
} }
// Test totally empty features // Test totally empty features
m, err := Execute(r1, f) m, err := Execute(r1, f, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, r1.Labels, m.Labels, "empty matcher should have matched empty features") assert.Equal(t, r1.Labels, m.Labels, "empty matcher should have matched empty features")
m, err = Execute(r2, f) m, err = Execute(r2, f, true)
assert.NoError(t, err, "matching against a missing feature should not have returned an error") assert.NoError(t, err, "matching against a missing feature should not have returned an error")
assert.Empty(t, m.Labels) assert.Empty(t, m.Labels)
assert.Empty(t, m.Vars) assert.Empty(t, m.Vars)
@ -61,12 +61,12 @@ func TestRule(t *testing.T) {
// Test properly initialized empty features // Test properly initialized empty features
f = nfdv1alpha1.NewFeatures() f = nfdv1alpha1.NewFeatures()
m, err = Execute(r1, f) m, err = Execute(r1, f, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, r1.Labels, m.Labels, "empty matcher should have matched empty features") assert.Equal(t, r1.Labels, m.Labels, "empty matcher should have matched empty features")
assert.Empty(t, r1.Vars, "vars should be empty") assert.Empty(t, r1.Vars, "vars should be empty")
m, err = Execute(r2, f) m, err = Execute(r2, f, true)
assert.NoError(t, err, "matching against a missing feature should not have returned an error") assert.NoError(t, err, "matching against a missing feature should not have returned an error")
assert.Empty(t, m.Labels) assert.Empty(t, m.Labels)
assert.Empty(t, m.Vars) assert.Empty(t, m.Vars)
@ -76,11 +76,11 @@ func TestRule(t *testing.T) {
f.Attributes["domain-1.vf-1"] = nfdv1alpha1.NewAttributeFeatures(nil) f.Attributes["domain-1.vf-1"] = nfdv1alpha1.NewAttributeFeatures(nil)
f.Instances["domain-1.if-1"] = nfdv1alpha1.NewInstanceFeatures() f.Instances["domain-1.if-1"] = nfdv1alpha1.NewInstanceFeatures()
m, err = Execute(r1, f) m, err = Execute(r1, f, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, r1.Labels, m.Labels, "empty matcher should have matched empty features") assert.Equal(t, r1.Labels, m.Labels, "empty matcher should have matched empty features")
m, err = Execute(r2, f) m, err = Execute(r2, f, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Nil(t, m.Labels, "unexpected match") assert.Nil(t, m.Labels, "unexpected match")
@ -89,7 +89,7 @@ func TestRule(t *testing.T) {
f.Attributes["domain-1.vf-1"].Elements["key-1"] = "val-x" f.Attributes["domain-1.vf-1"].Elements["key-1"] = "val-x"
f.Instances["domain-1.if-1"] = nfdv1alpha1.NewInstanceFeatures(*nfdv1alpha1.NewInstanceFeature(map[string]string{"attr-1": "val-x"})) f.Instances["domain-1.if-1"] = nfdv1alpha1.NewInstanceFeatures(*nfdv1alpha1.NewInstanceFeature(map[string]string{"attr-1": "val-x"}))
m, err = Execute(r1, f) m, err = Execute(r1, f, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, r1.Labels, m.Labels, "empty matcher should have matched empty features") assert.Equal(t, r1.Labels, m.Labels, "empty matcher should have matched empty features")
@ -100,17 +100,17 @@ func TestRule(t *testing.T) {
MatchExpressions: &nfdv1alpha1.MatchExpressionSet{}, MatchExpressions: &nfdv1alpha1.MatchExpressionSet{},
}, },
} }
m, err = Execute(r1, f) m, err = Execute(r1, f, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, r1.Labels, m.Labels, "empty match expression set mathces anything") assert.Equal(t, r1.Labels, m.Labels, "empty match expression set mathces anything")
// Match "key" features // Match "key" features
m, err = Execute(r2, f) m, err = Execute(r2, f, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Nil(t, m.Labels, "keys should not have matched") assert.Nil(t, m.Labels, "keys should not have matched")
f.Flags["domain-1.kf-1"].Elements["key-1"] = nfdv1alpha1.Nil{} f.Flags["domain-1.kf-1"].Elements["key-1"] = nfdv1alpha1.Nil{}
m, err = Execute(r2, f) m, err = Execute(r2, f, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, r2.Labels, m.Labels, "keys should have matched") assert.Equal(t, r2.Labels, m.Labels, "keys should have matched")
assert.Equal(t, r2.Vars, m.Vars, "vars should be present") assert.Equal(t, r2.Vars, m.Vars, "vars should be present")
@ -127,12 +127,12 @@ func TestRule(t *testing.T) {
}, },
}, },
} }
m, err = Execute(r3, f) m, err = Execute(r3, f, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Nil(t, m.Labels, "values should not have matched") assert.Nil(t, m.Labels, "values should not have matched")
f.Attributes["domain-1.vf-1"].Elements["key-1"] = "val-1" f.Attributes["domain-1.vf-1"].Elements["key-1"] = "val-1"
m, err = Execute(r3, f) m, err = Execute(r3, f, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, r3.Labels, m.Labels, "values should have matched") assert.Equal(t, r3.Labels, m.Labels, "values should have matched")
@ -148,12 +148,12 @@ func TestRule(t *testing.T) {
}, },
}, },
} }
m, err = Execute(r3, f) m, err = Execute(r3, f, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Nil(t, m.Labels, "instances should not have matched") assert.Nil(t, m.Labels, "instances should not have matched")
f.Instances["domain-1.if-1"].Elements[0].Attributes["attr-1"] = "val-1" f.Instances["domain-1.if-1"].Elements[0].Attributes["attr-1"] = "val-1"
m, err = Execute(r3, f) m, err = Execute(r3, f, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, r3.Labels, m.Labels, "instances should have matched") assert.Equal(t, r3.Labels, m.Labels, "instances should have matched")
@ -177,7 +177,7 @@ func TestRule(t *testing.T) {
}, },
}, },
} }
m, err = Execute(r3, f2) m, err = Execute(r3, f2, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, r3.Labels, m.Labels, "key in multi-type feature should have matched") assert.Equal(t, r3.Labels, m.Labels, "key in multi-type feature should have matched")
@ -189,7 +189,7 @@ func TestRule(t *testing.T) {
}, },
}, },
} }
m, err = Execute(r3, f2) m, err = Execute(r3, f2, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, r3.Labels, m.Labels, "attribute in multi-type feature should have matched") assert.Equal(t, r3.Labels, m.Labels, "attribute in multi-type feature should have matched")
@ -201,7 +201,7 @@ func TestRule(t *testing.T) {
}, },
}, },
} }
m, err = Execute(r3, f2) m, err = Execute(r3, f2, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, r3.Labels, m.Labels, "attribute in multi-type feature should have matched") assert.Equal(t, r3.Labels, m.Labels, "attribute in multi-type feature should have matched")
@ -214,7 +214,7 @@ func TestRule(t *testing.T) {
}, },
}, },
} }
m, err = Execute(r3, f2) m, err = Execute(r3, f2, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, r3.Labels, m.Labels, "features in multi-type feature should have matched flags and attributes") assert.Equal(t, r3.Labels, m.Labels, "features in multi-type feature should have matched flags and attributes")
@ -226,7 +226,7 @@ func TestRule(t *testing.T) {
}, },
}, },
} }
m, err = Execute(r3, f2) m, err = Execute(r3, f2, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, r3.Labels, m.Labels, "features in multi-type feature should have matched instance") assert.Equal(t, r3.Labels, m.Labels, "features in multi-type feature should have matched instance")
@ -248,12 +248,12 @@ func TestRule(t *testing.T) {
}, },
}, },
} }
m, err = Execute(r3, f) m, err = Execute(r3, f, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Nil(t, m.Labels, "instances should not have matched") assert.Nil(t, m.Labels, "instances should not have matched")
(*r3.MatchFeatures[0].MatchExpressions)["key-1"] = newMatchExpression(nfdv1alpha1.MatchIn, "val-1") (*r3.MatchFeatures[0].MatchExpressions)["key-1"] = newMatchExpression(nfdv1alpha1.MatchIn, "val-1")
m, err = Execute(r3, f) m, err = Execute(r3, f, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, r3.Labels, m.Labels, "instances should have matched") assert.Equal(t, r3.Labels, m.Labels, "instances should have matched")
@ -270,7 +270,7 @@ func TestRule(t *testing.T) {
}, },
}, },
} }
m, err = Execute(r3, f) m, err = Execute(r3, f, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Nil(t, m.Labels, "instances should not have matched") assert.Nil(t, m.Labels, "instances should not have matched")
@ -286,7 +286,7 @@ func TestRule(t *testing.T) {
}, },
}) })
(*r3.MatchFeatures[0].MatchExpressions)["key-1"] = newMatchExpression(nfdv1alpha1.MatchIn, "val-1") (*r3.MatchFeatures[0].MatchExpressions)["key-1"] = newMatchExpression(nfdv1alpha1.MatchIn, "val-1")
m, err = Execute(r3, f) m, err = Execute(r3, f, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, r3.Labels, m.Labels, "instances should have matched") assert.Equal(t, r3.Labels, m.Labels, "instances should have matched")
} }
@ -437,12 +437,12 @@ var-2=
"kf-foo": "true", "kf-foo": "true",
} }
m, err := Execute(r1, f) m, err := Execute(r1, f, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, expectedLabels, m.Labels, "instances should have matched") assert.Equal(t, expectedLabels, m.Labels, "instances should have matched")
assert.Equal(t, expectedVars, m.Vars, "instances should have matched") assert.Equal(t, expectedVars, m.Vars, "instances should have matched")
m, err = Execute(r3, f) m, err = Execute(r3, f, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, expectedLabels, m.Labels, "instances should have matched") assert.Equal(t, expectedLabels, m.Labels, "instances should have matched")
assert.Equal(t, expectedVars, m.Vars, "instances should have matched") assert.Equal(t, expectedVars, m.Vars, "instances should have matched")
@ -468,7 +468,7 @@ var-2=
"mf-key-d": "found", "mf-key-d": "found",
} }
expectedVars = map[string]string{} expectedVars = map[string]string{}
m, err = Execute(r3, f) m, err = Execute(r3, f, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, expectedLabels, m.Labels, "instances should have matched") assert.Equal(t, expectedLabels, m.Labels, "instances should have matched")
assert.Equal(t, expectedVars, m.Vars, "instances should have matched") assert.Equal(t, expectedVars, m.Vars, "instances should have matched")
@ -490,32 +490,32 @@ var-2=
} }
r2.LabelsTemplate = "foo=bar" r2.LabelsTemplate = "foo=bar"
m, err = Execute(r2, f) m, err = Execute(r2, f, true)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, map[string]string{"foo": "bar"}, m.Labels, "instances should have matched") assert.Equal(t, map[string]string{"foo": "bar"}, m.Labels, "instances should have matched")
assert.Empty(t, m.Vars) assert.Empty(t, m.Vars)
r2.LabelsTemplate = "foo" r2.LabelsTemplate = "foo"
_, err = Execute(r2, f) _, err = Execute(r2, f, true)
assert.Error(t, err) assert.Error(t, err)
r2.LabelsTemplate = "{{" r2.LabelsTemplate = "{{"
_, err = Execute(r2, f) _, err = Execute(r2, f, true)
assert.Error(t, err) assert.Error(t, err)
r2.LabelsTemplate = "" r2.LabelsTemplate = ""
r2.VarsTemplate = "bar=baz" r2.VarsTemplate = "bar=baz"
m, err = Execute(r2, f) m, err = Execute(r2, f, true)
assert.Nil(t, err) assert.Nil(t, err)
assert.Empty(t, m.Labels) assert.Empty(t, m.Labels)
assert.Equal(t, map[string]string{"bar": "baz"}, m.Vars, "instances should have matched") assert.Equal(t, map[string]string{"bar": "baz"}, m.Vars, "instances should have matched")
r2.VarsTemplate = "bar" r2.VarsTemplate = "bar"
_, err = Execute(r2, f) _, err = Execute(r2, f, true)
assert.Error(t, err) assert.Error(t, err)
r2.VarsTemplate = "{{" r2.VarsTemplate = "{{"
_, err = Execute(r2, f) _, err = Execute(r2, f, true)
assert.Error(t, err) assert.Error(t, err)
// //
@ -539,7 +539,7 @@ var-2=
"key-5": "", "key-5": "",
} }
m, err = Execute(r4, f) m, err = Execute(r4, f, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, expectedLabels, m.Labels, "instances should have matched") assert.Equal(t, expectedLabels, m.Labels, "instances should have matched")
@ -553,7 +553,7 @@ var-2=
}, },
} }
m, err = Execute(r4, f) m, err = Execute(r4, f, true)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, map[string]string(nil), m.Labels, "instances should have matched") assert.Equal(t, map[string]string(nil), m.Labels, "instances should have matched")
} }

View file

@ -0,0 +1,178 @@
/*
Copyright 2024 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 compat
//go:generate mockery --name=ArtifactClient --inpackage
import (
"context"
"encoding/json"
"fmt"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
oras "oras.land/oras-go/v2"
"oras.land/oras-go/v2/registry"
"oras.land/oras-go/v2/registry/remote"
"oras.land/oras-go/v2/registry/remote/auth"
"oras.land/oras-go/v2/registry/remote/retry"
"sigs.k8s.io/yaml"
compatv1alpha1 "sigs.k8s.io/node-feature-discovery/api/image-compatibility/v1alpha1"
)
// ArtifactClient interface contain set of functions to manipulate compatibility artfact.
type ArtifactClient interface {
// FetchCompatibilitySpec downloads the compatibility specifcation associated with the image.
FetchCompatibilitySpec(ctx context.Context) (*compatv1alpha1.Spec, error)
}
// Args holds command line arguments.
type Args struct {
PlainHttp bool
}
// Client represents a client that is reposnible for all artifact operations.
type Client struct {
Args Args
RegReference *registry.Reference
Platform *ocispec.Platform
orasClient *auth.Client
}
// New returns a new compatibility spec object.
func New(regReference *registry.Reference, opts ...ArtifactClientOpts) *Client {
c := &Client{
RegReference: regReference,
}
for _, opt := range opts {
opt.apply(c)
}
return c
}
// FetchCompatibilitySpec pulls the image compatibility specification associated with the image.
func (c *Client) FetchCompatibilitySpec(ctx context.Context) (*compatv1alpha1.Spec, error) {
repo, err := remote.NewRepository(c.RegReference.String())
if err != nil {
return nil, err
}
repo.Client = c.orasClient
repo.PlainHTTP = c.Args.PlainHttp
opts := oras.DefaultResolveOptions
if c.Platform != nil {
opts.TargetPlatform = c.Platform
}
targetDesc, err := oras.Resolve(ctx, repo, c.RegReference.Reference, opts)
if err != nil {
return nil, err
}
descs, err := registry.Referrers(ctx, repo, targetDesc, compatv1alpha1.ArtifactType)
if err != nil {
return nil, nil
} else if len(descs) < 1 {
return nil, fmt.Errorf("compatibility artifact not found")
}
artifactDesc := descs[len(descs)-1]
_, content, err := oras.FetchBytes(ctx, repo.Manifests(), artifactDesc.Digest.String(), oras.DefaultFetchBytesOptions)
if err != nil {
return nil, err
}
manifest := ocispec.Manifest{}
if err := json.Unmarshal(content, &manifest); err != nil {
return nil, err
}
// TODO: now it's a lazy check, verify in the future the media types and number of layers
if len(manifest.Layers) < 1 {
return nil, fmt.Errorf("compatibility layer not found")
}
specDesc := manifest.Layers[0]
_, compatSpecRaw, err := oras.FetchBytes(ctx, repo.Blobs(), specDesc.Digest.String(), oras.DefaultFetchBytesOptions)
if err != nil {
return nil, err
}
compatSpec := compatv1alpha1.Spec{}
err = yaml.Unmarshal(compatSpecRaw, &compatSpec)
if err != nil {
return nil, err
}
return &compatSpec, nil
}
// NodeValidatorOpts applies certain options to the node validator.
type ArtifactClientOpts interface {
apply(*Client)
}
type artifactClientOpt struct {
f func(*Client)
}
func (o *artifactClientOpt) apply(nv *Client) {
o.f(nv)
}
// WithArgs applies arguments to the artifact client.
func WithArgs(args Args) ArtifactClientOpts {
return &artifactClientOpt{f: func(c *Client) { c.Args = args }}
}
// WithPlatform applies OCI platform spec to the artifact client.
func WithPlatform(platform *ocispec.Platform) ArtifactClientOpts {
return &artifactClientOpt{f: func(c *Client) { c.Platform = platform }}
}
// WithAuthPassword initializes oras client with user and password.
func WithAuthPassword(username, password string) ArtifactClientOpts {
return &artifactClientOpt{f: func(c *Client) {
c.orasClient = &auth.Client{
Client: retry.DefaultClient,
Cache: auth.NewCache(),
Credential: auth.StaticCredential(c.RegReference.Registry, auth.Credential{
Username: username,
Password: password,
}),
}
}}
}
// WithAuthToken initializes oras client with auth token.
func WithAuthToken(token string) ArtifactClientOpts {
return &artifactClientOpt{f: func(c *Client) {
c.orasClient = &auth.Client{
Client: retry.DefaultClient,
Cache: auth.NewCache(),
Credential: auth.StaticCredential(c.RegReference.Registry, auth.Credential{
AccessToken: token,
}),
}
}}
}
// WithAuthDefault initializes the default oras client that does not authenticate.
func WithAuthDefault() ArtifactClientOpts {
return &artifactClientOpt{f: func(c *Client) { c.orasClient = auth.DefaultClient }}
}

View file

@ -0,0 +1,59 @@
// Code generated by mockery v2.42.0. DO NOT EDIT.
package compat
import (
context "context"
mock "github.com/stretchr/testify/mock"
v1alpha1 "sigs.k8s.io/node-feature-discovery/api/image-compatibility/v1alpha1"
)
// MockArtifactClient is an autogenerated mock type for the ArtifactClient type
type MockArtifactClient struct {
mock.Mock
}
// FetchCompatibilitySpec provides a mock function with given fields: ctx
func (_m *MockArtifactClient) FetchCompatibilitySpec(ctx context.Context) (*v1alpha1.Spec, error) {
ret := _m.Called(ctx)
if len(ret) == 0 {
panic("no return value specified for FetchCompatibilitySpec")
}
var r0 *v1alpha1.Spec
var r1 error
if rf, ok := ret.Get(0).(func(context.Context) (*v1alpha1.Spec, error)); ok {
return rf(ctx)
}
if rf, ok := ret.Get(0).(func(context.Context) *v1alpha1.Spec); ok {
r0 = rf(ctx)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*v1alpha1.Spec)
}
}
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = rf(ctx)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// NewMockArtifactClient creates a new instance of MockArtifactClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockArtifactClient(t interface {
mock.TestingT
Cleanup(func())
}) *MockArtifactClient {
mock := &MockArtifactClient{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View file

@ -0,0 +1,203 @@
/*
Copyright 2024 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 nodevalidator
import (
"context"
"slices"
"sort"
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
"sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/nodefeaturerule"
artifactcli "sigs.k8s.io/node-feature-discovery/pkg/client-nfd/compat/artifact-client"
"sigs.k8s.io/node-feature-discovery/source"
// register sources
_ "sigs.k8s.io/node-feature-discovery/source/cpu"
_ "sigs.k8s.io/node-feature-discovery/source/kernel"
_ "sigs.k8s.io/node-feature-discovery/source/memory"
_ "sigs.k8s.io/node-feature-discovery/source/network"
_ "sigs.k8s.io/node-feature-discovery/source/pci"
_ "sigs.k8s.io/node-feature-discovery/source/storage"
_ "sigs.k8s.io/node-feature-discovery/source/system"
_ "sigs.k8s.io/node-feature-discovery/source/usb"
)
// Args holds command line arguments.
type Args struct {
Tags []string
}
type nodeValidator struct {
args Args
artifactClient artifactcli.ArtifactClient
sources map[string]source.FeatureSource
}
// New builds a node validator with specified options.
func New(opts ...NodeValidatorOpts) nodeValidator {
n := nodeValidator{}
for _, opt := range opts {
opt.apply(&n)
}
return n
}
// Execute pulls the compatibility artifact to compare described features with the ones discovered on the node.
func (nv *nodeValidator) Execute(ctx context.Context) ([]*CompatibilityStatus, error) {
spec, err := nv.artifactClient.FetchCompatibilitySpec(ctx)
if err != nil {
return nil, err
}
for _, s := range nv.sources {
if err := s.Discover(); err != nil {
return nil, err
}
}
features := source.GetAllFeatures()
compats := []*CompatibilityStatus{}
for _, c := range spec.Compatibilties {
if len(nv.args.Tags) > 0 && !slices.Contains(nv.args.Tags, c.Tag) {
continue
}
compat := newCompatibilityStatus(&c)
for _, r := range c.Rules {
ruleOut, err := nodefeaturerule.Execute(&r, features, false)
if err != nil {
return nil, err
}
compat.Rules = append(compat.Rules, evaluateRuleStatus(&r, ruleOut.MatchStatus))
// Add the 'rule.matched' feature for backreference functionality
features.InsertAttributeFeatures(nfdv1alpha1.RuleBackrefDomain, nfdv1alpha1.RuleBackrefFeature, ruleOut.Labels)
features.InsertAttributeFeatures(nfdv1alpha1.RuleBackrefDomain, nfdv1alpha1.RuleBackrefFeature, ruleOut.Vars)
}
compats = append(compats, &compat)
}
return compats, nil
}
func evaluateRuleStatus(rule *nfdv1alpha1.Rule, matchStatus *nodefeaturerule.MatchStatus) ProcessedRuleStatus {
out := ProcessedRuleStatus{Name: rule.Name, IsMatch: matchStatus.IsMatch}
evaluateFeatureMatcher := func(featureMatcher, matchedFeatureTerms nfdv1alpha1.FeatureMatcher) []MatchedExpression {
out := []MatchedExpression{}
for _, term := range featureMatcher {
if term.MatchExpressions != nil {
for name, exp := range *term.MatchExpressions {
isMatch := false
// Check if the expression matches
for _, processedTerm := range matchedFeatureTerms {
if term.Feature != processedTerm.Feature || processedTerm.MatchExpressions == nil {
continue
}
pexp, ok := (*processedTerm.MatchExpressions)[name]
if isMatch = ok && exp.Op == pexp.Op && slices.Equal(exp.Value, pexp.Value); isMatch {
break
}
}
out = append(out, MatchedExpression{
Feature: term.Feature,
Name: name,
Expression: exp,
MatcherType: MatchExpressionType,
IsMatch: isMatch,
})
}
}
if term.MatchName != nil {
isMatch := false
for _, processedTerm := range matchStatus.MatchedFeaturesTerms {
if term.Feature != processedTerm.Feature || processedTerm.MatchName == nil {
continue
}
isMatch = term.MatchName.Op == processedTerm.MatchName.Op && slices.Equal(term.MatchName.Value, processedTerm.MatchName.Value)
if isMatch {
break
}
}
out = append(out, MatchedExpression{
Feature: term.Feature,
Name: "",
Expression: term.MatchName,
MatcherType: MatchNameType,
IsMatch: isMatch,
})
}
}
// For reproducible output sort by name, feature, expression.
sort.Slice(out, func(i, j int) bool {
if out[i].Feature != out[j].Feature {
return out[i].Feature < out[j].Feature
}
if out[i].Name != out[j].Name {
return out[i].Name < out[j].Name
}
return out[i].Expression.String() < out[j].Expression.String()
})
return out
}
if matchFeatures := rule.MatchFeatures; matchFeatures != nil {
out.MatchedExpressions = evaluateFeatureMatcher(matchFeatures, matchStatus.MatchedFeaturesTerms)
}
for i, matchAnyElem := range rule.MatchAny {
matchedExpressions := evaluateFeatureMatcher(matchAnyElem.MatchFeatures, matchStatus.MatchAny[i].MatchedFeaturesTerms)
out.MatchedAny = append(out.MatchedAny, MatchAnyElem{MatchedExpressions: matchedExpressions})
}
return out
}
// NodeValidatorOpts applies certain options to the node validator.
type NodeValidatorOpts interface {
apply(*nodeValidator)
}
type nodeValidatorOpt struct {
f func(*nodeValidator)
}
func (o *nodeValidatorOpt) apply(nv *nodeValidator) {
o.f(nv)
}
// WithArgs applies command line arguments to the node validator object.
func WithArgs(args *Args) NodeValidatorOpts {
return &nodeValidatorOpt{f: func(nv *nodeValidator) { nv.args = *args }}
}
// WithArtifactClient applies the client for all artifact operations.
func WithArtifactClient(cli artifactcli.ArtifactClient) NodeValidatorOpts {
return &nodeValidatorOpt{f: func(nv *nodeValidator) { nv.artifactClient = cli }}
}
// WithSources applies the list of enabled feature sources.
func WithSources(sources map[string]source.FeatureSource) NodeValidatorOpts {
return &nodeValidatorOpt{f: func(nv *nodeValidator) { nv.sources = sources }}
}

View file

@ -0,0 +1,418 @@
/*
Copyright 2024 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 nodevalidator
import (
"context"
"testing"
. "github.com/smartystreets/goconvey/convey"
compatv1alpha1 "sigs.k8s.io/node-feature-discovery/api/image-compatibility/v1alpha1"
"sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
artifactcli "sigs.k8s.io/node-feature-discovery/pkg/client-nfd/compat/artifact-client"
"sigs.k8s.io/node-feature-discovery/source"
"sigs.k8s.io/node-feature-discovery/source/fake"
)
func init() {
fs := source.GetConfigurableSource(fake.Name)
fs.SetConfig(fs.NewConfig())
}
func TestNodeValidator(t *testing.T) {
ctx := context.Background()
Convey("With a single compatibility set that contains flags, attributes and instances", t, func() {
spec := &compatv1alpha1.Spec{
Version: compatv1alpha1.Version,
Compatibilties: []compatv1alpha1.Compatibility{
{
Description: "Fake compatibility",
Rules: []v1alpha1.Rule{
{
Name: "fake_1",
MatchFeatures: v1alpha1.FeatureMatcher{
{
Feature: "fake.flag",
MatchName: &v1alpha1.MatchExpression{Op: v1alpha1.MatchInRegexp, Value: v1alpha1.MatchValue{"^flag"}},
},
},
},
{
Name: "fake_2",
MatchFeatures: v1alpha1.FeatureMatcher{
{
Feature: "fake.flag",
MatchExpressions: &v1alpha1.MatchExpressionSet{
"flag_unknown": &v1alpha1.MatchExpression{Op: v1alpha1.MatchExists},
},
},
{
Feature: "fake.attribute",
MatchExpressions: &v1alpha1.MatchExpressionSet{
"attr_1": &v1alpha1.MatchExpression{Op: v1alpha1.MatchIn, Value: v1alpha1.MatchValue{"true"}},
},
},
},
},
{
Name: "fake_3",
MatchFeatures: v1alpha1.FeatureMatcher{
{
Feature: "fake.instance",
MatchExpressions: &v1alpha1.MatchExpressionSet{
"name": &v1alpha1.MatchExpression{Op: v1alpha1.MatchIn, Value: v1alpha1.MatchValue{"instance_1"}},
"attr_1": &v1alpha1.MatchExpression{Op: v1alpha1.MatchIn, Value: v1alpha1.MatchValue{"true"}},
},
},
{
Feature: "fake.instance",
MatchExpressions: &v1alpha1.MatchExpressionSet{
"name": &v1alpha1.MatchExpression{Op: v1alpha1.MatchIn, Value: v1alpha1.MatchValue{"instance_2"}},
"attr_1": &v1alpha1.MatchExpression{Op: v1alpha1.MatchIn, Value: v1alpha1.MatchValue{"false"}},
},
},
},
},
{
Name: "fake_4",
MatchAny: []v1alpha1.MatchAnyElem{
{
MatchFeatures: v1alpha1.FeatureMatcher{
{
Feature: "fake.instance",
MatchExpressions: &v1alpha1.MatchExpressionSet{
"name": &v1alpha1.MatchExpression{Op: v1alpha1.MatchIn, Value: v1alpha1.MatchValue{"instance_1"}},
},
},
},
},
{
MatchFeatures: v1alpha1.FeatureMatcher{
{
Feature: "fake.instance",
MatchExpressions: &v1alpha1.MatchExpressionSet{
"name": &v1alpha1.MatchExpression{Op: v1alpha1.MatchIn, Value: v1alpha1.MatchValue{"instance_unknown"}},
},
},
},
},
},
},
},
},
},
}
// The output contains expressions in alphabetical order over the feature, name and expression string.
expectedOutput := []*CompatibilityStatus{
{
Description: "Fake compatibility",
Rules: []ProcessedRuleStatus{
{
Name: "fake_1",
IsMatch: true,
MatchedExpressions: []MatchedExpression{
{
Feature: "fake.flag",
Name: "",
Expression: &v1alpha1.MatchExpression{Op: v1alpha1.MatchInRegexp, Value: v1alpha1.MatchValue{"^flag"}},
MatcherType: MatchNameType,
IsMatch: true,
},
},
},
{
Name: "fake_2",
IsMatch: false,
MatchedExpressions: []MatchedExpression{
{
Feature: "fake.attribute",
Name: "attr_1",
Expression: &v1alpha1.MatchExpression{Op: v1alpha1.MatchIn, Value: v1alpha1.MatchValue{"true"}},
MatcherType: MatchExpressionType,
IsMatch: true,
},
{
Feature: "fake.flag",
Name: "flag_unknown",
Expression: &v1alpha1.MatchExpression{Op: v1alpha1.MatchExists},
MatcherType: MatchExpressionType,
IsMatch: false,
},
},
},
{
Name: "fake_3",
IsMatch: false,
MatchedExpressions: []MatchedExpression{
{
Feature: "fake.instance",
Name: "attr_1",
Expression: &v1alpha1.MatchExpression{Op: v1alpha1.MatchIn, Value: v1alpha1.MatchValue{"false"}},
MatcherType: MatchExpressionType,
IsMatch: false,
},
{
Feature: "fake.instance",
Name: "attr_1",
Expression: &v1alpha1.MatchExpression{Op: v1alpha1.MatchIn, Value: v1alpha1.MatchValue{"true"}},
MatcherType: MatchExpressionType,
IsMatch: true,
},
{
Feature: "fake.instance",
Name: "name",
Expression: &v1alpha1.MatchExpression{Op: v1alpha1.MatchIn, Value: v1alpha1.MatchValue{"instance_1"}},
MatcherType: MatchExpressionType,
IsMatch: true,
},
{
Feature: "fake.instance",
Name: "name",
Expression: &v1alpha1.MatchExpression{Op: v1alpha1.MatchIn, Value: v1alpha1.MatchValue{"instance_2"}},
MatcherType: MatchExpressionType,
IsMatch: true,
},
},
},
{
Name: "fake_4",
IsMatch: true,
MatchedAny: []MatchAnyElem{
{
MatchedExpressions: []MatchedExpression{
{
Feature: "fake.instance",
Name: "name",
Expression: &v1alpha1.MatchExpression{Op: v1alpha1.MatchIn, Value: v1alpha1.MatchValue{"instance_1"}},
MatcherType: MatchExpressionType,
IsMatch: true,
},
},
},
{
MatchedExpressions: []MatchedExpression{
{
Feature: "fake.instance",
Name: "name",
Expression: &v1alpha1.MatchExpression{Op: v1alpha1.MatchIn, Value: v1alpha1.MatchValue{"instance_unknown"}},
MatcherType: MatchExpressionType,
IsMatch: false,
},
},
},
},
},
},
},
}
validator := New(
WithArgs(&Args{}),
WithArtifactClient(newMock(ctx, spec)),
WithSources(map[string]source.FeatureSource{fake.Name: source.GetFeatureSource(fake.Name)}),
)
output, err := validator.Execute(ctx)
So(err, ShouldBeNil)
So(output, ShouldEqual, expectedOutput)
})
Convey("With multiple compatibility sets", t, func() {
spec := &compatv1alpha1.Spec{
Version: compatv1alpha1.Version,
Compatibilties: []compatv1alpha1.Compatibility{
{
Tag: "prefered",
Weight: 90,
Description: "Fake compatibility 1",
Rules: []v1alpha1.Rule{
{
Name: "fake_1",
MatchFeatures: v1alpha1.FeatureMatcher{
{
Feature: "fake.attribute",
MatchExpressions: &v1alpha1.MatchExpressionSet{
"attr_1": &v1alpha1.MatchExpression{Op: v1alpha1.MatchIn, Value: v1alpha1.MatchValue{"false"}},
},
},
},
},
},
},
{
Tag: "fallback",
Weight: 40,
Description: "Fake compatibility 2",
Rules: []v1alpha1.Rule{
{
Name: "fake_1",
MatchFeatures: v1alpha1.FeatureMatcher{
{
Feature: "fake.attribute",
MatchExpressions: &v1alpha1.MatchExpressionSet{
"attr_2": &v1alpha1.MatchExpression{Op: v1alpha1.MatchIn, Value: v1alpha1.MatchValue{"false"}},
},
},
},
},
},
},
},
}
expectedOutput := []*CompatibilityStatus{
{
Tag: "prefered",
Weight: 90,
Description: "Fake compatibility 1",
Rules: []ProcessedRuleStatus{
{
Name: "fake_1",
IsMatch: false,
MatchedExpressions: []MatchedExpression{
{
Feature: "fake.attribute",
Name: "attr_1",
Expression: &v1alpha1.MatchExpression{Op: v1alpha1.MatchIn, Value: v1alpha1.MatchValue{"false"}},
MatcherType: MatchExpressionType,
IsMatch: false,
},
},
},
},
},
{
Tag: "fallback",
Weight: 40,
Description: "Fake compatibility 2",
Rules: []ProcessedRuleStatus{
{
Name: "fake_1",
IsMatch: true,
MatchedExpressions: []MatchedExpression{
{
Feature: "fake.attribute",
Name: "attr_2",
Expression: &v1alpha1.MatchExpression{Op: v1alpha1.MatchIn, Value: v1alpha1.MatchValue{"false"}},
MatcherType: MatchExpressionType,
IsMatch: true,
},
},
},
},
},
}
validator := New(
WithArgs(&Args{}),
WithArtifactClient(newMock(ctx, spec)),
WithSources(map[string]source.FeatureSource{fake.Name: source.GetFeatureSource(fake.Name)}),
)
output, err := validator.Execute(ctx)
So(err, ShouldBeNil)
So(output, ShouldEqual, expectedOutput)
})
Convey("With compatibility sets filtered out by tags", t, func() {
spec := &compatv1alpha1.Spec{
Version: compatv1alpha1.Version,
Compatibilties: []compatv1alpha1.Compatibility{
{
Tag: "prefered",
Weight: 90,
Description: "Fake compatibility 1",
Rules: []v1alpha1.Rule{
{
Name: "fake_1",
MatchFeatures: v1alpha1.FeatureMatcher{
{
Feature: "fake.attribute",
MatchExpressions: &v1alpha1.MatchExpressionSet{
"attr_1": &v1alpha1.MatchExpression{Op: v1alpha1.MatchIn, Value: v1alpha1.MatchValue{"false"}},
},
},
},
},
},
},
{
Tag: "fallback",
Weight: 40,
Description: "Fake compatibility 2",
Rules: []v1alpha1.Rule{
{
Name: "fake_1",
MatchFeatures: v1alpha1.FeatureMatcher{
{
Feature: "fake.attribute",
MatchExpressions: &v1alpha1.MatchExpressionSet{
"attr_2": &v1alpha1.MatchExpression{Op: v1alpha1.MatchIn, Value: v1alpha1.MatchValue{"false"}},
},
},
},
},
},
},
},
}
expectedOutput := []*CompatibilityStatus{
{
Tag: "prefered",
Weight: 90,
Description: "Fake compatibility 1",
Rules: []ProcessedRuleStatus{
{
Name: "fake_1",
IsMatch: false,
MatchedExpressions: []MatchedExpression{
{
Feature: "fake.attribute",
Name: "attr_1",
Expression: &v1alpha1.MatchExpression{Op: v1alpha1.MatchIn, Value: v1alpha1.MatchValue{"false"}},
MatcherType: MatchExpressionType,
IsMatch: false,
},
},
},
},
},
}
validator := New(
WithArgs(&Args{
Tags: []string{"prefered"},
}),
WithArtifactClient(newMock(ctx, spec)),
WithSources(map[string]source.FeatureSource{fake.Name: source.GetFeatureSource(fake.Name)}),
)
output, err := validator.Execute(ctx)
So(err, ShouldBeNil)
So(output, ShouldEqual, expectedOutput)
})
}
func newMock(ctx context.Context, result *compatv1alpha1.Spec) *artifactcli.MockArtifactClient {
artifactClient := &artifactcli.MockArtifactClient{}
artifactClient.On("FetchCompatibilitySpec", ctx).Return(result, nil)
return artifactClient
}

View file

@ -0,0 +1,89 @@
/*
Copyright 2024 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 nodevalidator
import (
compatv1alpha1 "sigs.k8s.io/node-feature-discovery/api/image-compatibility/v1alpha1"
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
)
// MatcherType represents a type of the used matcher.
type MatcherType string
const (
// MatchExpressionType represents a matchExpression type.
MatchExpressionType MatcherType = "matchExpression"
// MatchNameType represents a matchName type.
MatchNameType MatcherType = "matchName"
)
// CompatibilityStatus represents the state of
// feature matching between the image and the host.
type CompatibilityStatus struct {
// Rules contain information about the matching status
// of all Node Feature Rules.
Rules []ProcessedRuleStatus `json:"rules"`
// Description of the compatibility set.
Description string `json:"description,omitempty"`
// Weight provides information about the priority of the compatibility set.
Weight int `json:"weight,omitempty"`
// Tag provides information about the tag assigned to the compatibility set.
Tag string `json:"tag,omitempty"`
}
func newCompatibilityStatus(c *compatv1alpha1.Compatibility) CompatibilityStatus {
cs := CompatibilityStatus{
Description: c.Description,
Weight: c.Weight,
Tag: c.Tag,
}
return cs
}
// ProcessedRuleStatus provides information whether the expressions succeeded on the host.
type ProcessedRuleStatus struct {
// Name of the rule.
Name string `json:"name"`
// IsMatch provides information if the rule matches with the host.
IsMatch bool `json:"isMatch"`
// MatchedExpressions represents the expressions that succeed on the host.
MatchedExpressions []MatchedExpression `json:"matchedExpressions,omitempty"`
// MatchAny represents an array of logical OR conditions between MatchedExpressions entries.
MatchedAny []MatchAnyElem `json:"matchedAny,omitempty"`
}
// MatchAnyElem represents a single object of MatchAny that contains MatchedExpression entries.
type MatchAnyElem struct {
// MatchedExpressions contains MatchedExpression entries.
MatchedExpressions []MatchedExpression `json:"matchedExpressions"`
}
// MatchedExpression represent all details about the expression that succeeded on the host.
type MatchedExpression struct {
// Feature which is available to be evaluated on the host.
Feature string `json:"feature"`
// Name of the element.
Name string `json:"name"`
// Expression represents the expression provided by users.
Expression *nfdv1alpha1.MatchExpression `json:"expression"`
// MatcherType represents the matcher type, e.g. MatchExpression, MatchName.
MatcherType MatcherType `json:"matcherType"`
// IsMatch provides information whether the expression suceeded on the host.
IsMatch bool `json:"isMatch"`
}

View file

@ -70,7 +70,7 @@ func processNodeFeatureRule(nodeFeatureRule nfdv1alpha1.NodeFeatureRule, nodeFea
for _, rule := range nodeFeatureRule.Spec.Rules { for _, rule := range nodeFeatureRule.Spec.Rules {
fmt.Println("Processing rule: ", rule.Name) fmt.Println("Processing rule: ", rule.Name)
ruleOut, err := nodefeaturerule.Execute(&rule, &nodeFeature.Features) ruleOut, err := nodefeaturerule.Execute(&rule, &nodeFeature.Features, true)
if err != nil { if err != nil {
errs = append(errs, fmt.Errorf("failed to process rule: %q - %w", rule.Name, err)) errs = append(errs, fmt.Errorf("failed to process rule: %q - %w", rule.Name, err))
continue continue

View file

@ -794,7 +794,7 @@ func (m *nfdMaster) nfdAPIUpdateNodeFeatureGroup(nfdClient nfdclientset.Interfac
nodeGroupValidator := make(map[string]bool) nodeGroupValidator := make(map[string]bool)
for _, rule := range nodeFeatureGroup.Spec.Rules { for _, rule := range nodeFeatureGroup.Spec.Rules {
for _, feature := range nodeFeaturesList { for _, feature := range nodeFeaturesList {
match, err := nodefeaturerule.ExecuteGroupRule(&rule, &feature.Spec.Features) match, err := nodefeaturerule.ExecuteGroupRule(&rule, &feature.Spec.Features, true)
if err != nil { if err != nil {
klog.ErrorS(err, "failed to evaluate rule", "ruleName", rule.Name) klog.ErrorS(err, "failed to evaluate rule", "ruleName", rule.Name)
continue continue
@ -1018,7 +1018,7 @@ func (m *nfdMaster) processNodeFeatureRule(nodeName string, features *nfdv1alpha
klog.InfoS("executing NodeFeatureRule", "nodefeaturerule", klog.KObj(spec), "nodeName", nodeName) klog.InfoS("executing NodeFeatureRule", "nodefeaturerule", klog.KObj(spec), "nodeName", nodeName)
} }
for _, rule := range spec.Spec.Rules { for _, rule := range spec.Spec.Rules {
ruleOut, err := nodefeaturerule.Execute(&rule, features) ruleOut, err := nodefeaturerule.Execute(&rule, features, true)
if err != nil { if err != nil {
klog.ErrorS(err, "failed to process rule", "ruleName", rule.Name, "nodefeaturerule", klog.KObj(spec), "nodeName", nodeName) klog.ErrorS(err, "failed to process rule", "ruleName", rule.Name, "nodefeaturerule", klog.KObj(spec), "nodeName", nodeName)
nfrProcessingErrors.Inc() nfrProcessingErrors.Inc()

View file

@ -93,7 +93,7 @@ func (s *customSource) GetLabels() (source.FeatureLabels, error) {
klog.V(2).InfoS("resolving custom features", "configuration", utils.DelayedDumper(allFeatureConfig)) klog.V(2).InfoS("resolving custom features", "configuration", utils.DelayedDumper(allFeatureConfig))
// Iterate over features // Iterate over features
for _, rule := range allFeatureConfig { for _, rule := range allFeatureConfig {
ruleOut, err := nodefeaturerule.Execute(&rule, features) ruleOut, err := nodefeaturerule.Execute(&rule, features, true)
if err != nil { if err != nil {
klog.ErrorS(err, "failed to execute rule") klog.ErrorS(err, "failed to execute rule")
continue continue