Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions api/v1beta1/nodemodulesconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ type ModuleItem struct {
//+optional
// tolerations define which tolerations should be added for every load/unload pod running on the node
Tolerations []v1.Toleration `json:"tolerations,omitempty"`
//+optional
// Version is the version of the kernel module that should be loaded
Version string `json:"version,omitempty"`
}

type NodeModuleSpec struct {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,10 @@ spec:
type: string
type: object
type: array
version:
description: Version is the version of the kernel module that
should be loaded
type: string
required:
- config
- name
Expand Down Expand Up @@ -415,6 +419,10 @@ spec:
type: string
type: object
type: array
version:
description: Version is the version of the kernel module that
should be loaded
type: string
required:
- name
- namespace
Expand Down
8 changes: 8 additions & 0 deletions config/crd/bases/kmm.sigs.x-k8s.io_nodemodulesconfigs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,10 @@ spec:
type: string
type: object
type: array
version:
description: Version is the version of the kernel module that
should be loaded
type: string
required:
- config
- name
Expand Down Expand Up @@ -415,6 +419,10 @@ spec:
type: string
type: object
type: array
version:
description: Version is the version of the kernel module that
should be loaded
type: string
required:
- name
- namespace
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ metadata:
name: modulebuildsignconfig-sample
spec:
images:
- image: quay.io/myorg/my-kernel-module:latest
- image: <kmod container image URL>
kernelVersion: 4.18.0-372.32.1.el8_6.x86_64
action: BuildImage
build:
Expand Down
2 changes: 1 addition & 1 deletion config/samples/kmm.sigs.x-k8s.io_moduleimagesconfigs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ metadata:
name: moduleimagesconfig-sample
spec:
images:
- image: quay.io/myorg/my-kernel-module:v1.0.0
- image: <kmod container image URL>
kernelVersion: 4.18.0-372.32.1.el8_6.x86_64
imagePullPolicy: IfNotPresent
pushBuiltImage: false
2 changes: 1 addition & 1 deletion config/samples/kmm.sigs.x-k8s.io_nodemodulesconfigs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ spec:
serviceAccountName: kmm-operator-controller
config:
kernelVersion: 4.18.0-372.32.1.el8_6.x86_64
containerImage: quay.io/myorg/my-kernel-module:latest
containerImage: <kmod container image URL>
imagePullPolicy: IfNotPresent
insecurePull: false
modprobe:
Expand Down
5 changes: 5 additions & 0 deletions docs/mkdocs/documentation/deploy_kmod.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ on the target node(s) to run the necessary action.
The operator monitors the outcome of those Pods and records that information.
It uses it to label `Node` objects when the module was successfully loaded, and to run the device plugin (if
configured).
The label can be found in the node's label with the following format:
```
kmm.node.kubernetes.io/<namespace>.<modulename>.ready
```
but it is strongly recommended to use `GetKernelModuleReadyNodeLabel` function from the `labels` package in order to construct the correct label

### Worker Pods

Expand Down
10 changes: 10 additions & 0 deletions docs/mkdocs/documentation/ordered_upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ Steps 3, 4 and 5 are then unified into one step: update the
`kmm.node.kubernetes.io/version-module.<module-namespace>.<module-name>` label value to new `$moduleVersion` as set in
the `Module`.

It is strongly recommended to use `GetModuleVersionLabelName` function from the `labels` package in order to construct the correct label used in the step 1
of the upgrade flow

### Indicator that the new version is ready to be used

The operator will label the node with a "version.ready" label to indicate that the new version of the kernel module is loaded
and ready to be used:
`kmm.node.kubernetes.io/<module-namespace>.<module-name>.version.ready=<module-version>`
It is strongly recommended to use `GetKernelModuleVersionReadyNodeLabel` function from the `labels` package in order to construct the correct label

## Implementation details

### Components
Expand Down
18 changes: 16 additions & 2 deletions internal/controllers/mock_nmc_reconciler.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

153 changes: 89 additions & 64 deletions internal/controllers/nmc_reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,15 +102,16 @@ func (r *NMCReconciler) Reconcile(ctx context.Context, req reconcile.Request) (r
}

errs := make([]error, 0, len(nmcObj.Spec.Modules)+len(nmcObj.Status.Modules))
var readyLabelsToRemove []string
readyLabelsToRemove := make(map[string]string)
for _, mod := range nmcObj.Spec.Modules {
moduleNameKey := mod.Namespace + "/" + mod.Name

logger := logger.WithValues("module", moduleNameKey)

// skipping handling NMC spec module until node is ready
if !r.nodeAPI.IsNodeSchedulable(&node, mod.Tolerations) {
readyLabelsToRemove = append(readyLabelsToRemove, utils.GetKernelModuleReadyNodeLabel(mod.Namespace, mod.Name))
readyLabelsToRemove[utils.GetKernelModuleReadyNodeLabel(mod.Namespace, mod.Name)] = ""
readyLabelsToRemove[utils.GetKernelModuleVersionReadyNodeLabel(mod.Namespace, mod.Name)] = ""
delete(statusMap, moduleNameKey)
continue
}
Expand Down Expand Up @@ -141,7 +142,7 @@ func (r *NMCReconciler) Reconcile(ctx context.Context, req reconcile.Request) (r
}

// removing label of loaded kmods
if readyLabelsToRemove != nil {
if len(readyLabelsToRemove) != 0 {
if err := r.nodeAPI.UpdateLabels(ctx, &node, nil, readyLabelsToRemove); err != nil {
return ctrl.Result{}, fmt.Errorf("could remove node %s labels: %v", node.Name, err)
}
Expand Down Expand Up @@ -574,6 +575,8 @@ func (h *nmcReconcilerHelperImpl) SyncStatus(ctx context.Context, nmcObj *kmmv1b

status.BootId = node.Status.NodeInfo.BootID

status.Version = h.podManager.GetModuleVersionAnnotation(&p)

nmc.SetModuleStatus(&nmcObj.Status.Modules, *status)

podsToDelete = append(podsToDelete, p)
Expand All @@ -599,63 +602,6 @@ func (h *nmcReconcilerHelperImpl) SyncStatus(ctx context.Context, nmcObj *kmmv1b
return errors.Join(errs...)
}

type labelPreparationHelper interface {
getDeprecatedKernelModuleReadyLabels(node v1.Node) sets.Set[string]
getNodeKernelModuleReadyLabels(node v1.Node) sets.Set[types.NamespacedName]
getSpecLabelsAndTheirConfigs(nmc *kmmv1beta1.NodeModulesConfig) map[types.NamespacedName]kmmv1beta1.ModuleConfig
getStatusLabelsAndTheirConfigs(nmc *kmmv1beta1.NodeModulesConfig) map[types.NamespacedName]kmmv1beta1.ModuleConfig
addEqualLabels(nodeModuleReadyLabels sets.Set[types.NamespacedName],
specLabels, statusLabels map[types.NamespacedName]kmmv1beta1.ModuleConfig) []types.NamespacedName
removeOrphanedLabels(nodeModuleReadyLabels sets.Set[types.NamespacedName],
specLabels, statusLabels map[types.NamespacedName]kmmv1beta1.ModuleConfig) []types.NamespacedName
}
type labelPreparationHelperImpl struct{}

func newLabelPreparationHelper() labelPreparationHelper {
return &labelPreparationHelperImpl{}
}

func (lph *labelPreparationHelperImpl) getNodeKernelModuleReadyLabels(node v1.Node) sets.Set[types.NamespacedName] {
nodeModuleReadyLabels := sets.New[types.NamespacedName]()

for label := range node.GetLabels() {
if ok, namespace, name := utils.IsKernelModuleReadyNodeLabel(label); ok {
nodeModuleReadyLabels.Insert(types.NamespacedName{Namespace: namespace, Name: name})
}
}
return nodeModuleReadyLabels
}

func (lph *labelPreparationHelperImpl) getDeprecatedKernelModuleReadyLabels(node v1.Node) sets.Set[string] {
deprecatedNodeModuleReadyLabels := sets.New[string]()

for label := range node.GetLabels() {
if utils.IsDeprecatedKernelModuleReadyNodeLabel(label) {
deprecatedNodeModuleReadyLabels.Insert(label)
}
}
return deprecatedNodeModuleReadyLabels
}

func (lph *labelPreparationHelperImpl) getSpecLabelsAndTheirConfigs(nmc *kmmv1beta1.NodeModulesConfig) map[types.NamespacedName]kmmv1beta1.ModuleConfig {
specLabels := make(map[types.NamespacedName]kmmv1beta1.ModuleConfig)

for _, module := range nmc.Spec.Modules {
specLabels[types.NamespacedName{Namespace: module.Namespace, Name: module.Name}] = module.Config
}
return specLabels
}

func (lph *labelPreparationHelperImpl) getStatusLabelsAndTheirConfigs(nmc *kmmv1beta1.NodeModulesConfig) map[types.NamespacedName]kmmv1beta1.ModuleConfig {
statusLabels := make(map[types.NamespacedName]kmmv1beta1.ModuleConfig)

for _, module := range nmc.Status.Modules {
label := types.NamespacedName{Namespace: module.Namespace, Name: module.Name}
statusLabels[label] = module.Config
}
return statusLabels
}

func (h *nmcReconcilerHelperImpl) UpdateNodeLabels(ctx context.Context, nmc *kmmv1beta1.NodeModulesConfig, node *v1.Node) ([]types.NamespacedName, []types.NamespacedName, error) {

// get all the kernel module ready labels of the node
Expand All @@ -668,20 +614,30 @@ func (h *nmcReconcilerHelperImpl) UpdateNodeLabels(ctx context.Context, nmc *kmm
// get status labels and their config
statusLabels := h.lph.getStatusLabelsAndTheirConfigs(nmc)

// get the versions per the name/namespace of the module
statusVersions := h.lph.getStatusVersions(nmc)

// label in node but not in spec or status - should be removed
nsnLabelsToBeRemoved := h.lph.removeOrphanedLabels(nodeModuleReadyLabels, specLabels, statusLabels)

// label in spec and status and config equal - should be added
nsnLabelsToBeLoaded := h.lph.addEqualLabels(nodeModuleReadyLabels, specLabels, statusLabels)

var loadedLabels []string
unloadedLabels := deprecatedNodeModuleReadyLabels.UnsortedList()
loadedLabels := make(map[string]string)
unloadedLabels := deprecatedNodeModuleReadyLabels

for _, label := range nsnLabelsToBeRemoved {
unloadedLabels = append(unloadedLabels, utils.GetKernelModuleReadyNodeLabel(label.Namespace, label.Name))
unloadedLabels[utils.GetKernelModuleReadyNodeLabel(label.Namespace, label.Name)] = ""
// unload the kernel ready version label also. if it does not exists, that's ok, the code won't fail.It also means
// that the version label will not be in labelsToAdd, since status and spec are missing
unloadedLabels[utils.GetKernelModuleVersionReadyNodeLabel(label.Namespace, label.Name)] = ""
}

for _, label := range nsnLabelsToBeLoaded {
loadedLabels = append(loadedLabels, utils.GetKernelModuleReadyNodeLabel(label.Namespace, label.Name))
loadedLabels[utils.GetKernelModuleReadyNodeLabel(label.Namespace, label.Name)] = ""
if version, ok := statusVersions[label]; ok {
loadedLabels[utils.GetKernelModuleVersionReadyNodeLabel(label.Namespace, label.Name)] = version
}
}

if err := h.nodeAPI.UpdateLabels(ctx, node, loadedLabels, unloadedLabels); err != nil {
Expand Down Expand Up @@ -714,6 +670,75 @@ func (h *nmcReconcilerHelperImpl) RecordEvents(node *v1.Node, loadedModules, unl
}
}

type labelPreparationHelper interface {
getDeprecatedKernelModuleReadyLabels(node v1.Node) map[string]string
getNodeKernelModuleReadyLabels(node v1.Node) sets.Set[types.NamespacedName]
getSpecLabelsAndTheirConfigs(nmc *kmmv1beta1.NodeModulesConfig) map[types.NamespacedName]kmmv1beta1.ModuleConfig
getStatusLabelsAndTheirConfigs(nmc *kmmv1beta1.NodeModulesConfig) map[types.NamespacedName]kmmv1beta1.ModuleConfig
getStatusVersions(nmc *kmmv1beta1.NodeModulesConfig) map[types.NamespacedName]string
addEqualLabels(nodeModuleReadyLabels sets.Set[types.NamespacedName],
specLabels, statusLabels map[types.NamespacedName]kmmv1beta1.ModuleConfig) []types.NamespacedName
removeOrphanedLabels(nodeModuleReadyLabels sets.Set[types.NamespacedName],
specLabels, statusLabels map[types.NamespacedName]kmmv1beta1.ModuleConfig) []types.NamespacedName
}
type labelPreparationHelperImpl struct{}

func newLabelPreparationHelper() labelPreparationHelper {
return &labelPreparationHelperImpl{}
}

func (lph *labelPreparationHelperImpl) getNodeKernelModuleReadyLabels(node v1.Node) sets.Set[types.NamespacedName] {
nodeModuleReadyLabels := sets.New[types.NamespacedName]()

for label := range node.GetLabels() {
if ok, namespace, name := utils.IsKernelModuleReadyNodeLabel(label); ok {
nodeModuleReadyLabels.Insert(types.NamespacedName{Namespace: namespace, Name: name})
}
}
return nodeModuleReadyLabels
}

func (lph *labelPreparationHelperImpl) getDeprecatedKernelModuleReadyLabels(node v1.Node) map[string]string {
deprecatedLabels := make(map[string]string)

for key, val := range node.GetLabels() {
if utils.IsDeprecatedKernelModuleReadyNodeLabel(key) {
deprecatedLabels[key] = val
}
}
return deprecatedLabels
}

func (lph *labelPreparationHelperImpl) getSpecLabelsAndTheirConfigs(nmc *kmmv1beta1.NodeModulesConfig) map[types.NamespacedName]kmmv1beta1.ModuleConfig {
specLabels := make(map[types.NamespacedName]kmmv1beta1.ModuleConfig)

for _, module := range nmc.Spec.Modules {
specLabels[types.NamespacedName{Namespace: module.Namespace, Name: module.Name}] = module.Config
}
return specLabels
}

func (lph *labelPreparationHelperImpl) getStatusLabelsAndTheirConfigs(nmc *kmmv1beta1.NodeModulesConfig) map[types.NamespacedName]kmmv1beta1.ModuleConfig {
statusLabels := make(map[types.NamespacedName]kmmv1beta1.ModuleConfig)

for _, module := range nmc.Status.Modules {
label := types.NamespacedName{Namespace: module.Namespace, Name: module.Name}
statusLabels[label] = module.Config
}
return statusLabels
}

func (lph *labelPreparationHelperImpl) getStatusVersions(nmc *kmmv1beta1.NodeModulesConfig) map[types.NamespacedName]string {
versions := make(map[types.NamespacedName]string)

for _, module := range nmc.Status.Modules {
if module.Version != "" {
versions[types.NamespacedName{Namespace: module.Namespace, Name: module.Name}] = module.Version
}
}
return versions
}

func (lph *labelPreparationHelperImpl) removeOrphanedLabels(nodeModuleReadyLabels sets.Set[types.NamespacedName],
specLabels, statusLabels map[types.NamespacedName]kmmv1beta1.ModuleConfig) []types.NamespacedName {

Expand Down
Loading