1
0
Fork 0
mirror of https://github.com/kubernetes-sigs/node-feature-discovery.git synced 2024-12-14 11:57:51 +00:00

Drop support for hooks

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
This commit is contained in:
Carlos Eduardo Arango Gutierrez 2024-11-04 10:25:47 +01:00
parent 65b5e0c255
commit 62f4eddce6
No known key found for this signature in database
GPG key ID: 42D9CB42F300A852
10 changed files with 19 additions and 234 deletions

View file

@ -19,9 +19,6 @@
- name: host-lib - name: host-lib
hostPath: hostPath:
path: "/lib" path: "/lib"
- name: source-d
hostPath:
path: "/etc/kubernetes/node-feature-discovery/source.d/"
- name: features-d - name: features-d
hostPath: hostPath:
path: "/etc/kubernetes/node-feature-discovery/features.d/" path: "/etc/kubernetes/node-feature-discovery/features.d/"
@ -50,9 +47,6 @@
- name: host-lib - name: host-lib
mountPath: "/host-lib" mountPath: "/host-lib"
readOnly: true readOnly: true
- name: source-d
mountPath: "/etc/kubernetes/node-feature-discovery/source.d/"
readOnly: true
- name: features-d - name: features-d
mountPath: "/etc/kubernetes/node-feature-discovery/features.d/" mountPath: "/etc/kubernetes/node-feature-discovery/features.d/"
readOnly: true readOnly: true

View file

@ -77,8 +77,6 @@
# - "class" # - "class"
# - "vendor" # - "vendor"
# - "device" # - "device"
# local:
# hooksEnabled: false
# custom: # custom:
# # The following feature demonstrates the capabilities of the matchFeatures # # The following feature demonstrates the capabilities of the matchFeatures
# - name: "my custom rule" # - name: "my custom rule"

View file

@ -141,9 +141,6 @@ spec:
mountPath: "/host-usr/src" mountPath: "/host-usr/src"
readOnly: true readOnly: true
{{- end }} {{- end }}
- name: source-d
mountPath: "/etc/kubernetes/node-feature-discovery/source.d/"
readOnly: true
- name: features-d - name: features-d
mountPath: "/etc/kubernetes/node-feature-discovery/features.d/" mountPath: "/etc/kubernetes/node-feature-discovery/features.d/"
readOnly: true readOnly: true
@ -174,9 +171,6 @@ spec:
hostPath: hostPath:
path: "/usr/src" path: "/usr/src"
{{- end }} {{- end }}
- name: source-d
hostPath:
path: "/etc/kubernetes/node-feature-discovery/source.d/"
- name: features-d - name: features-d
hostPath: hostPath:
path: "/etc/kubernetes/node-feature-discovery/features.d/" path: "/etc/kubernetes/node-feature-discovery/features.d/"

View file

@ -255,8 +255,6 @@ worker:
# - "class" # - "class"
# - "vendor" # - "vendor"
# - "device" # - "device"
# local:
# hooksEnabled: false
# custom: # custom:
# # The following feature demonstrates the capabilities of the matchFeatures # # The following feature demonstrates the capabilities of the matchFeatures
# - name: "my custom rule" # - name: "my custom rule"

View file

@ -24,8 +24,8 @@ For backwards compatibility a container image tag with suffix `-minimal`
## Full ## Full
This image is based on [debian:bookworm-slim](https://hub.docker.com/_/debian) This image is based on [debian:bookworm-slim](https://hub.docker.com/_/debian)
and contains a full Linux system for running shell-based nfd-worker hooks and and contains a full Linux system for doing live debugging and diagnosis
doing live debugging and diagnosis of the NFD images. of the NFD images.
The container image tag has suffix `-full` The container image tag has suffix `-full`
(e.g. `{{ site.container_image }}-full`). (e.g. `{{ site.container_image }}-full`).

View file

@ -71,7 +71,7 @@ Feature discovery is divided into domain-specific feature sources:
- System - System
- USB - USB
- Custom (rule-based custom features) - Custom (rule-based custom features)
- Local (hooks for user-specific features) - Local (features files)
Each feature source is responsible for detecting a set of features which. in Each feature source is responsible for detecting a set of features which. in
turn, are turned into node feature labels. Feature labels are prefixed with turn, are turned into node feature labels. Feature labels are prefixed with

View file

@ -304,32 +304,6 @@ sources:
### sources.local ### sources.local
### sources.local.hooksEnabled
**DEPRECATED**: Hooks are DEPRECATED since v0.12.0 release and support (and
this configuration option) will be removed in NFD v0.17. Use
[feature files](../usage//customization-guide.md#feature-files) instead.
Configuration option to disable/enable hooks execution. Disabled by default.
> **NOTE:** The default NFD container image only supports statically linked
> binaries. Use the [full](../deployment/image-variants.md#full) image variant
> for a slightly more extensive environment that additionally supports bash and
> perl runtimes.
GitHub tracking issue:
[Drop support for hooks (#856)](https://github.com/kubernetes-sigs/node-feature-discovery/issues/856).
Default: false
Example:
```yaml
sources:
local:
hooksEnabled: true
```
### sources.pci ### sources.pci
#### sources.pci.deviceClassWhitelist #### sources.pci.deviceClassWhitelist

View file

@ -26,7 +26,7 @@ labeling:
- [`NodeFeatureRule`](#nodefeaturerule-custom-resource) objects provide a way to - [`NodeFeatureRule`](#nodefeaturerule-custom-resource) objects provide a way to
deploy custom labeling rules via the Kubernetes API. deploy custom labeling rules via the Kubernetes API.
- [`local`](#local-feature-source) feature source of nfd-worker creates - [`local`](#local-feature-source) feature source of nfd-worker creates
labels by reading text files and executing hooks. labels by reading text files.
- [`custom`](#custom-feature-source) feature source of nfd-worker creates - [`custom`](#custom-feature-source) feature source of nfd-worker creates
labels based on user-specified rules. labels based on user-specified rules.
@ -232,13 +232,12 @@ point for external feature detectors. It provides a mechanism for pluggable
extensions, allowing the creation of new user-specific features and even extensions, allowing the creation of new user-specific features and even
overriding built-in labels. overriding built-in labels.
The `local` feature source has two methods for detecting features, feature The `local` feature source uses feature files. The features discovered by the
files and hooks (hooks are deprecated and slated for removal in NFD v0.17). The `local` source can further be used in label rules specified in
features discovered by the `local` source can further be used in label rules [`NodeFeatureRule`](#nodefeaturerule-custom-resource) objects and
specified in [`NodeFeatureRule`](#nodefeaturerule-custom-resource) objects and
the [`custom`](#custom-feature-source) feature source. the [`custom`](#custom-feature-source) feature source.
> **NOTE:** Be careful when creating and/or updating hook or feature files > **NOTE:** Be careful when creating and/or updating feature files
> while NFD is running. To avoid race conditions you should write > while NFD is running. To avoid race conditions you should write
> into a temporary file, and atomically create/update the original file by > into a temporary file, and atomically create/update the original file by
> doing a file rename operation. NFD ignores dot files, > doing a file rename operation. NFD ignores dot files,
@ -250,9 +249,7 @@ the [`custom`](#custom-feature-source) feature source.
Consider a plaintext file Consider a plaintext file
`/etc/kubernetes/node-feature-discovery/features.d/my-features` `/etc/kubernetes/node-feature-discovery/features.d/my-features`
having the following contents (or alternatively a shell script having the following contents:
`/etc/kubernetes/node-feature-discovery/source.d/my-hook.sh` having the
following stdout output):
```plaintext ```plaintext
feature.node.kubernetes.io/my-feature.1 feature.node.kubernetes.io/my-feature.1
@ -274,47 +271,9 @@ The `local` source reads files found in
`/etc/kubernetes/node-feature-discovery/features.d/`. File content is parsed `/etc/kubernetes/node-feature-discovery/features.d/`. File content is parsed
and translated into node labels, see the [input format below](#input-format). and translated into node labels, see the [input format below](#input-format).
### Hooks
**DEPRECATED** Hooks are deprecated and will be completely removed in NFD
v0.17.
The `local` source executes hooks found in
`/etc/kubernetes/node-feature-discovery/source.d/`. The hook files must be
executable and they are supposed to print all discovered features in `stdout`.
Since NFD v0.13 the default container image only supports statically linked ELF
binaries.
`stderr` output of hooks is propagated to NFD log so it can be used for
debugging and logging.
NFD tries to execute any regular files found from the hooks directory.
Any additional data files the hook might need (e.g. a configuration file)
should be placed in a separate directory to avoid NFD unnecessarily
trying to execute them. A subdirectory under the hooks directory can be used,
for example `/etc/kubernetes/node-feature-discovery/source.d/conf/`.
> **NOTE:** Starting from release v0.14 hooks are disabled by default and can
> be enabled via `sources.local.hooksEnabled` field in the worker
> configuration.
```yaml
sources:
local:
hooksEnabled: true # true by default at this point
```
> **NOTE:** NFD will blindly run any executables placed/mounted in the hooks
> directory. It is the user's responsibility to review the hooks for e.g.
> possible security implications.
>
> **NOTE:** The [full](../deployment/image-variants.md#full) image variant
> provides backwards-compatibility with older NFD versions by including a more
> expanded environment, supporting bash and perl runtimes.
### Input format ### Input format
The hook stdout and feature files are expected to contain features in simple The feature files are expected to contain features in simple
key-value pairs, separated by newlines: key-value pairs, separated by newlines:
```plaintext ```plaintext
@ -410,19 +369,16 @@ vendor.io/my-feature=value
### Mounts ### Mounts
The standard NFD deployments contain `hostPath` mounts for The standard NFD deployments contain `hostPath` mounts for
`/etc/kubernetes/node-feature-discovery/source.d/` and
`/etc/kubernetes/node-feature-discovery/features.d/`, making these directories `/etc/kubernetes/node-feature-discovery/features.d/`, making these directories
from the host available inside the nfd-worker container. from the host available inside the nfd-worker container.
#### Injecting labels from other pods #### Injecting labels from other pods
One use case for the feature files and hooks is detecting features in other One use case for the feature files is detecting features in other
Pods outside NFD, e.g. in Kubernetes device plugins. By using the same Pods outside NFD, e.g. in Kubernetes device plugins. By using the same
`hostPath` mounts for `/etc/kubernetes/node-feature-discovery/source.d/` and `hostPath` mounts `/etc/kubernetes/node-feature-discovery/features.d/`
`/etc/kubernetes/node-feature-discovery/features.d/` in the side-car (e.g. in the side-car (e.g. device plugin) creates a shared area for
device plugin) creates a shared area for deploying feature files and hooks to deploying feature files to NFD.
NFD. NFD periodically scans the directories and reads any feature files and
runs any hooks it finds.
## Custom feature source ## Custom feature source
@ -1000,8 +956,8 @@ The following features are available for matching:
| | | **`major`** | int | First component of the kernel version (e.g. 4') | | | | **`major`** | int | First component of the kernel version (e.g. 4') |
| | | **`minor`** | int | Second component of the kernel version (e.g. 5') | | | | **`minor`** | int | Second component of the kernel version (e.g. 5') |
| | | **`revision`** | int | Third component of the kernel version (e.g. 6') | | | | **`revision`** | int | Third component of the kernel version (e.g. 6') |
| **`local.label`** | attribute | | | Labels from feature files and hooks, i.e. labels from the [*local* feature source](#local-feature-source) | | **`local.label`** | attribute | | | Labels from feature files, i.e. labels from the [*local* feature source](#local-feature-source) |
| **`local.feature`** | attribute | | | Features from feature files and hooks, i.e. features from the [*local* feature source](#local-feature-source) | | **`local.feature`** | attribute | | | Features from feature files, i.e. features from the [*local* feature source](#local-feature-source) |
| | | **`<label-name>`** | string | Label `<label-name>` created by the local feature source, value equals the value of the label | | | | **`<label-name>`** | string | Label `<label-name>` created by the local feature source, value equals the value of the label |
| **`memory.nv`** | instance | | | NVDIMM devices present in the system | | **`memory.nv`** | instance | | | NVDIMM devices present in the system |
| | | **`<sysfs-attribute>`** | string | Value of the sysfs device attribute, available attributes: `devtype`, `mode` | | | | **`<sysfs-attribute>`** | string | Value of the sysfs device attribute, available attributes: `devtype`, `mode` |

View file

@ -572,7 +572,7 @@ func getFeatureLabels(source source.LabelSource, labelWhiteList regexp.Regexp) (
name := k name := k
switch sourceName := source.Name(); sourceName { switch sourceName := source.Name(); sourceName {
case "local", "custom": case "local", "custom":
// No mangling of labels from the custom rules, hooks or feature files // No mangling of labels from the custom rules or feature files
default: default:
// Prefix for labels from other sources // Prefix for labels from other sources
if !strings.Contains(name, "/") { if !strings.Contains(name, "/") {

View file

@ -20,7 +20,6 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"strings" "strings"
"time" "time"
@ -64,7 +63,6 @@ const MaxFeatureFileSize = 65536
// Config // Config
var ( var (
featureFilesDir = "/etc/kubernetes/node-feature-discovery/features.d/" featureFilesDir = "/etc/kubernetes/node-feature-discovery/features.d/"
hookDir = "/etc/kubernetes/node-feature-discovery/source.d/"
) )
// localSource implements the FeatureSource and LabelSource interfaces. // localSource implements the FeatureSource and LabelSource interfaces.
@ -74,7 +72,6 @@ type localSource struct {
} }
type Config struct { type Config struct {
HooksEnabled bool `json:"hooksEnabled,omitempty"`
} }
// parsingOpts contains options used for directives parsing // parsingOpts contains options used for directives parsing
@ -86,7 +83,7 @@ type parsingOpts struct {
// Singleton source instance // Singleton source instance
var ( var (
src = localSource{config: newDefaultConfig()} src = localSource{}
_ source.FeatureSource = &src _ source.FeatureSource = &src
_ source.LabelSource = &src _ source.LabelSource = &src
_ source.ConfigurableSource = &src _ source.ConfigurableSource = &src
@ -96,7 +93,7 @@ var (
func (s *localSource) Name() string { return Name } func (s *localSource) Name() string { return Name }
// NewConfig method of the LabelSource interface // NewConfig method of the LabelSource interface
func (s *localSource) NewConfig() source.Config { return newDefaultConfig() } func (s *localSource) NewConfig() source.Config { return &Config{} }
// GetConfig method of the LabelSource interface // GetConfig method of the LabelSource interface
func (s *localSource) GetConfig() source.Config { return s.config } func (s *localSource) GetConfig() source.Config { return s.config }
@ -125,13 +122,6 @@ func (s *localSource) GetLabels() (source.FeatureLabels, error) {
return labels, nil return labels, nil
} }
// newDefaultConfig returns a new config with pre-populated defaults
func newDefaultConfig() *Config {
return &Config{
HooksEnabled: false,
}
}
// Discover method of the FeatureSource interface // Discover method of the FeatureSource interface
func (s *localSource) Discover() error { func (s *localSource) Discover() error {
s.features = nfdv1alpha1.NewFeatures() s.features = nfdv1alpha1.NewFeatures()
@ -141,33 +131,6 @@ func (s *localSource) Discover() error {
klog.ErrorS(err, "failed to read feature files") klog.ErrorS(err, "failed to read feature files")
} }
if s.config.HooksEnabled {
klog.InfoS("starting hooks...")
klog.InfoS("NOTE: hooks are deprecated and will be completely removed in a future release.")
featuresFromHooks, labelsFromHooks, err := getFeaturesFromHooks()
if err != nil {
klog.ErrorS(err, "failed to run hooks")
}
// Merge features from hooks and files
for k, v := range featuresFromHooks {
if old, ok := featuresFromFiles[k]; ok {
klog.InfoS("overriding feature value", "featureKey", k, "oldValue", old, "newValue", v)
}
featuresFromFiles[k] = v
}
// Merge labels from hooks and files
for k, v := range labelsFromHooks {
if old, ok := labelsFromFiles[k]; ok {
klog.InfoS("overriding label value", "labelKey", k, "oldValue", old, "newValue", v)
}
labelsFromFiles[k] = v
}
}
s.features.Attributes[LabelFeature] = nfdv1alpha1.NewAttributeFeatures(labelsFromFiles) s.features.Attributes[LabelFeature] = nfdv1alpha1.NewAttributeFeatures(labelsFromFiles)
s.features.Attributes[RawFeature] = nfdv1alpha1.NewAttributeFeatures(featuresFromFiles) s.features.Attributes[RawFeature] = nfdv1alpha1.NewAttributeFeatures(featuresFromFiles)
@ -279,98 +242,6 @@ func updateFeatures(m map[string]string, lineSplit []string) {
} }
} }
// Run all hooks and get features
func getFeaturesFromHooks() (map[string]string, map[string]string, error) {
features := make(map[string]string)
labels := make(map[string]string)
files, err := os.ReadDir(hookDir)
if err != nil {
if os.IsNotExist(err) {
klog.InfoS("hook directory does not exist", "path", hookDir)
return features, labels, nil
}
return features, labels, fmt.Errorf("unable to access %v: %w", hookDir, err)
}
if len(files) > 0 {
klog.InfoS("hooks are DEPRECATED since v0.12.0 and support will be removed in a future release; use feature files instead")
}
for _, file := range files {
fileName := file.Name()
// ignore hidden feature file
if strings.HasPrefix(fileName, ".") {
continue
}
lines, err := runHook(fileName)
if err != nil {
klog.ErrorS(err, "failed to run hook", "fileName", fileName)
continue
}
// Append features
fileFeatures, fileLabels := parseFeatureFile(lines, fileName)
klog.V(4).InfoS("hook executed", "fileName", fileName, "features", utils.DelayedDumper(fileFeatures), "labels", utils.DelayedDumper(fileLabels))
for k, v := range fileFeatures {
if old, ok := features[k]; ok {
klog.InfoS("overriding feature value from another hook", "featureKey", k, "oldValue", old, "newValue", v, "fileName", fileName)
}
features[k] = v
}
for k, v := range fileLabels {
if old, ok := labels[k]; ok {
klog.InfoS("overriding label value from another hook", "labelKey", k, "oldValue", old, "newValue", v, "fileName", fileName)
}
labels[k] = v
}
}
return features, labels, nil
}
// Run one hook
func runHook(file string) ([][]byte, error) {
var lines [][]byte
path := filepath.Join(hookDir, file)
filestat, err := os.Stat(path)
if err != nil {
klog.ErrorS(err, "failed to get filestat, skipping hook", "path", path)
return lines, err
}
if filestat.Mode().IsRegular() {
cmd := exec.Command(path)
var stdout bytes.Buffer
var stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
// Run hook
err = cmd.Run()
// Forward stderr to our logger
errLines := bytes.Split(stderr.Bytes(), []byte("\n"))
for i, line := range errLines {
if i == len(errLines)-1 && len(line) == 0 {
// Don't print the last empty string
break
}
klog.InfoS(fmt.Sprintf("%s: %s", file, line))
}
// Do not return any lines if an error occurred
if err != nil {
return lines, err
}
lines = bytes.Split(stdout.Bytes(), []byte("\n"))
}
return lines, nil
}
// Read all files to get features // Read all files to get features
func getFeaturesFromFiles() (map[string]string, map[string]string, error) { func getFeaturesFromFiles() (map[string]string, map[string]string, error) {
features := make(map[string]string) features := make(map[string]string)