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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ Available Commands:
Common Flags:
--config-dir string Directory containing optional l8k-config.yaml and presets/ overrides
--enabled-plugins string Comma-separated list of plugins to enable (default "network-operator")
--image-pull-secrets strings Image pull secret names for NicClusterPolicy (comma-separated)
--image-pull-secrets strings Image pull secret names for Network Operator components (comma-separated)
--kubeconfig string Path to kubeconfig file for cluster deployment (required when using --deploy; falls back to $KUBECONFIG, then ~/.kube/config)
--network-operator-namespace string Override the network operator namespace from the config file
--network-operator-release string Network Operator release line to deploy (MAJOR.MINOR). Selects component image tags + repository from a built-in catalog and drives version-gated template sections. Supported: 26.1, 26.4, 26.7
Expand Down
4 changes: 2 additions & 2 deletions docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Run `l8k <command> --help` for the authoritative flag list. Run `l8k schema` for
| `--node-selector` | Selector persisted for generated resources. It does not filter discovery scheduling. |
| `--keep-namespace` | Keep the temporary `nvidia-k8s-launch-kit` namespace and daemon workload for inspection. |
| `--collapse-nic-rails` | Collapse eligible multi-port NICs into one rail. Enabled by default; known dual-port models retain a rail per port. |
| `--image-pull-secrets` | Secret names used to pull the discovery daemon and propagated into generated policy. |
| `--image-pull-secrets` | Secret names used to pull the discovery daemon and propagated into generated policies and Network Operator Helm values. |
| `--enabled-plugins` | Comma-separated plugins. The supported deployment plugin is `network-operator`. |

Discovery also accepts the profile and Spectrum-X flags below. Explicit flags override values from `--user-config` and discovered defaults.
Expand Down Expand Up @@ -84,7 +84,7 @@ Discovery also accepts the profile and Spectrum-X flags below. Explicit flags ov
| `--network-namespaces` | Namespaces that receive secondary-network resources and example workloads. |
| `--workload-manifest` | Replace the profile's example workload with a Pod or workload-controller manifest. |
| `--enable-doca-driver` | Override `docaDriver.enable` and include the DOCA driver deployment. |
| `--image-pull-secrets` | Secret names propagated into generated Network Operator policy. |
| `--image-pull-secrets` | Secret names propagated into generated Network Operator policies and Helm values. |
| `--deploy` | Deploy immediately after generation. |
| `--kubeconfig` | Kubeconfig used with `--deploy`. |
| `--dry-run` | Preview the deploy stage used with `--deploy`. |
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ The release line fills Network Operator versions, component image tags, DOCA dri
| `operatorRepository` | Registry path for the Network Operator controller image. |
| `helmRepoURL` | Chart repository used by `l8k deploy`. Empty means Helm phase 0 is skipped. |
| `namespace` | Namespace for the Helm release and namespaced Network Operator resources. |
| `imagePullSecrets` | Secret names propagated into `NicClusterPolicy` and per-group `NicNodePolicy` specifications. |
| `imagePullSecrets` | Secret names propagated into the discovery daemon, generated policies, and Helm values for the Network Operator and enabled subcharts. |

When `selectedRelease` is set, catalog values replace explicit version and repository fields so the cohort remains consistent.

Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/discover.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ func init() {
fmt.Sprintf("Network Operator release line to deploy (MAJOR.MINOR). Supported: %s",
strings.Join(releases.SupportedReleases(), ", ")))
discoverCmd.Flags().StringVar(&nodeSelector, "node-selector", "feature.node.kubernetes.io/pci-15b3.present=true", "Node selector written into the saved cluster-config (used at deploy time). Does NOT gate discovery scheduling — the daemon runs on all nodes and NIC nodes are detected via a sysfs PCI-vendor probe")
discoverCmd.Flags().StringSliceVar(&imagePullSecrets, "image-pull-secrets", nil, "Image pull secret names for NicClusterPolicy (comma-separated)")
discoverCmd.Flags().StringSliceVar(&imagePullSecrets, "image-pull-secrets", nil, "Image pull secret names for Network Operator components (comma-separated)")
discoverCmd.Flags().StringVar(&enabledPlugins, "enabled-plugins", "network-operator", "Comma-separated list of plugins to enable")
discoverCmd.Flags().BoolVar(&keepNamespace, "keep-namespace", false, "Skip teardown of the nvidia-k8s-launch-kit namespace (for debugging)")
discoverCmd.Flags().BoolVar(&collapseNicRails, "collapse-nic-rails", true, collapseNicRailsFlagHelp)
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ func init() {
generateCmd.Flags().StringVar(&networkOperatorRelease, "network-operator-release", "",
fmt.Sprintf("Network Operator release line to deploy (MAJOR.MINOR). Supported: %s",
strings.Join(releases.SupportedReleases(), ", ")))
generateCmd.Flags().StringSliceVar(&imagePullSecrets, "image-pull-secrets", nil, "Image pull secret names for NicClusterPolicy (comma-separated)")
generateCmd.Flags().StringSliceVar(&imagePullSecrets, "image-pull-secrets", nil, "Image pull secret names for Network Operator components (comma-separated)")
generateCmd.Flags().StringVar(&enabledPlugins, "enabled-plugins", "network-operator", "Comma-separated list of plugins to enable")

// Deploy (optional)
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ func init() {
rootCmd.Flags().StringVar(&nodeSelector, "node-selector", "feature.node.kubernetes.io/pci-15b3.present=true", "Node selector written into the saved cluster-config (used at deploy time). Does NOT gate discovery scheduling — the daemon runs on all nodes and NIC nodes are detected via a sysfs PCI-vendor probe")
rootCmd.Flags().BoolVar(&collapseNicRails, "collapse-nic-rails", true, collapseNicRailsFlagHelp)
rootCmd.Flags().StringVar(&forPreset, "for", "", forFlagHelp())
rootCmd.Flags().StringSliceVar(&imagePullSecrets, "image-pull-secrets", nil, "Image pull secret names for NicClusterPolicy (comma-separated)")
rootCmd.Flags().StringSliceVar(&imagePullSecrets, "image-pull-secrets", nil, "Image pull secret names for Network Operator components (comma-separated)")
rootCmd.Flags().StringVar(&saveDeploymentFiles, "save-deployment-files", "./deployment", "Save generated deployment files to the specified directory")
rootCmd.Flags().StringSliceVar(&networkNamespaces, "network-namespaces", nil, "Comma-separated namespaces for the secondary-network CRs and example test DaemonSets. One independent copy is rendered per namespace (shared resources like IPPools and NodePolicies are NOT duplicated). Overrides config networkNamespaces; default: 'default'.")
rootCmd.Flags().BoolVar(&enableDocaDriver, "enable-doca-driver", false, "Enable DOCA driver deployment (overrides config file docaDriver.enable)")
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ var schemaCmd = &cobra.Command{
},
"--image-pull-secrets": {
Type: "[]string",
Description: "Image pull secret names for NicClusterPolicy (comma-separated)",
Description: "Image pull secret names for Network Operator components (comma-separated)",
},
"--network-operator-release": {
Type: "string",
Expand Down
171 changes: 171 additions & 0 deletions pkg/networkoperatorplugin/helm_values_render_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// Copyright 2026 NVIDIA CORPORATION & AFFILIATES
//
// 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.
//
// SPDX-License-Identifier: Apache-2.0

package networkoperatorplugin

import (
"os"
"path/filepath"
"testing"

"github.com/go-logr/logr"
"github.com/nvidia/k8s-launch-kit/pkg/config"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/yaml"
)

type helmPullSecretValues struct {
ImagePullSecrets []string `json:"imagePullSecrets"`
NodeFeatureDiscovery struct {
ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets"`
} `json:"node-feature-discovery"`
MaintenanceOperatorChart struct {
ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets"`
} `json:"maintenance-operator-chart"`
SriovNetworkOperator struct {
ImagePullSecrets []string `json:"imagePullSecrets"`
} `json:"sriov-network-operator"`
}

func TestHelmValuesImagePullSecretShapes(t *testing.T) {
profiles := []struct {
name string
deploysSriov bool
}{
{name: "host-device-rdma", deploysSriov: false},
{name: "ipoib-rdma-shared", deploysSriov: false},
{name: "macvlan-rdma-shared", deploysSriov: false},
{name: "spectrum-x-ra2.1", deploysSriov: true},
{name: "spectrum-x-ra2.2", deploysSriov: true},
{name: "spectrum-x", deploysSriov: true},
{name: "sriov-ethernet-rdma", deploysSriov: true},
{name: "sriov-ib-rdma", deploysSriov: true},
}
secretNames := []string{"registry-secret", "true", "null", "123"}
secretRefs := []corev1.LocalObjectReference{
{Name: "registry-secret"},
{Name: "true"},
{Name: "null"},
{Name: "123"},
}

for _, profile := range profiles {
t.Run(profile.name, func(t *testing.T) {
templatePath, err := filepath.Abs(filepath.Join(
"..", "..", "profiles", profile.name, helmValuesTemplateName))
require.NoError(t, err)

cfg := &config.LaunchKitConfig{
NetworkOperator: &config.NetworkOperatorConfig{
Version: "v26.7.0-beta.5",
SelectedRelease: "26.7",
OperatorRepository: "nvcr.io/nvstaging/mellanox",
ImagePullSecrets: secretNames,
},
Maintenance: config.DefaultMaintenanceConfig(),
Profile: &config.Profile{},
}

rendered, err := ProcessTemplate(templatePath, cfg, "")
require.NoError(t, err)

var values helmPullSecretValues
require.NoError(t, yaml.Unmarshal(
[]byte(rendered[helmValuesTemplateName]), &values))

assert.Equal(t, secretNames, values.ImagePullSecrets)
assert.Equal(t, secretRefs, values.NodeFeatureDiscovery.ImagePullSecrets)
assert.Equal(t, secretRefs, values.MaintenanceOperatorChart.ImagePullSecrets)
if profile.deploysSriov {
assert.Equal(t, secretNames, values.SriovNetworkOperator.ImagePullSecrets)
} else {
assert.Empty(t, values.SriovNetworkOperator.ImagePullSecrets)
}
})
}
}

func TestProfilePolicyImagePullSecretsRemainStrings(t *testing.T) {
configPath, err := filepath.Abs(filepath.Join(
"testdata", "grouping", "mixed-same-type.yaml"))
require.NoError(t, err)

cfg, err := config.LoadFullConfig(configPath, logr.Discard())
require.NoError(t, err)
cfg.NetworkOperator.SelectedRelease = "26.7"
cfg.NetworkOperator.ImagePullSecrets = []string{"registry-secret", "true", "null", "123"}
cfg.Profile = &config.Profile{Multirail: true}

profiles := []string{
"host-device-rdma",
"ipoib-rdma-shared",
"macvlan-rdma-shared",
"spectrum-x-ra2.1",
"spectrum-x-ra2.2",
"spectrum-x",
"sriov-ethernet-rdma",
"sriov-ib-rdma",
}

for _, profile := range profiles {
for _, templateName := range []string{"10-nicclusterpolicy.yaml", "11-nicnodepolicy.yaml"} {
t.Run(profile+"/"+templateName, func(t *testing.T) {
templatePath, err := filepath.Abs(filepath.Join(
"..", "..", "profiles", profile, templateName))
require.NoError(t, err)
_, err = os.Stat(templatePath)
if os.IsNotExist(err) {
t.Skip("profile does not use this policy kind")
}
require.NoError(t, err)

rendered, err := ProcessTemplate(templatePath, cfg, "")
require.NoError(t, err)
for fileName, content := range rendered {
var document map[string]any
require.NoError(t, yaml.Unmarshal([]byte(content), &document), fileName)
assertImagePullSecretItemsAreStrings(t, document, fileName)
}
})
}
}
}

func assertImagePullSecretItemsAreStrings(t *testing.T, value any, path string) {
t.Helper()

switch typed := value.(type) {
case map[string]any:
for key, child := range typed {
childPath := path + "." + key
if key == "imagePullSecrets" {
items, ok := child.([]any)
require.True(t, ok, "%s must be a list", childPath)
for index, item := range items {
assert.IsType(t, "", item, "%s[%d] must remain a string", childPath, index)
}
continue
}
assertImagePullSecretItemsAreStrings(t, child, childPath)
}
case []any:
for _, child := range typed {
assertImagePullSecretItemsAreStrings(t, child, path)
}
}
}
2 changes: 1 addition & 1 deletion pkg/nicconfigdaemon/assets/daemon.yaml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ spec:
{{- if .ImagePullSecrets }}
imagePullSecrets:
{{- range .ImagePullSecrets }}
- name: {{ . }}
- name: "{{ . }}"
{{- end }}
{{- end }}
containers:
Expand Down
10 changes: 7 additions & 3 deletions pkg/nicconfigdaemon/bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ func TestEnsure_SkipsCRDsWhenPresent(t *testing.T) {
func TestEnsure_AppliesDaemonSetWithExpectedImage(t *testing.T) {
c := newFakeClient(t)

pullSecrets := []string{"my-registry-creds"}
pullSecrets := []string{"my-registry-creds", "true", "null", "123"}
require.NoError(t, Ensure(context.Background(), c, Options{
Repository: testRepo,
Version: testVersion,
Expand Down Expand Up @@ -195,8 +195,12 @@ func TestEnsure_AppliesDaemonSetWithExpectedImage(t *testing.T) {
assert.Equal(t, "debug", logLevelEnv)

// ImagePullSecrets surfaced on the pod spec
require.Len(t, ds.Spec.Template.Spec.ImagePullSecrets, 1)
assert.Equal(t, "my-registry-creds", ds.Spec.Template.Spec.ImagePullSecrets[0].Name)
assert.Equal(t, []corev1.LocalObjectReference{
{Name: "my-registry-creds"},
{Name: "true"},
{Name: "null"},
{Name: "123"},
}, ds.Spec.Template.Spec.ImagePullSecrets)

// No nodeSelector: discovery runs on every node (the NFD
// pci-15b3.present label may not exist yet at discover time). NIC-bearing
Expand Down
16 changes: 15 additions & 1 deletion profiles/host-device-rdma/00-values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ or upgrade the network-operator release in the configured namespace
*/ -}}
nfd:
enabled: true
{{- if .NetworkOperator.ImagePullSecrets }}

node-feature-discovery:
imagePullSecrets:
{{- range .NetworkOperator.ImagePullSecrets }}
- name: "{{ . }}"
{{- end }}
{{- end }}

upgradeCRDs: true

Expand All @@ -24,14 +32,20 @@ operator:

imagePullSecrets:
{{- range .NetworkOperator.ImagePullSecrets }}
- name: {{ . }}
- "{{ . }}"
{{- end }}
{{- end }}

maintenanceOperator:
enabled: {{ if or .NicConfigurationOperator (versionGE .NetworkOperator.SelectedRelease "26.1") }}true{{ else }}false{{ end }}

maintenance-operator-chart:
{{- if .NetworkOperator.ImagePullSecrets }}
imagePullSecrets:
{{- range .NetworkOperator.ImagePullSecrets }}
- name: "{{ . }}"
{{- end }}
{{- end }}
operatorConfig:
deploy: true
# Keep scalar overrides quoted: upstream truthiness guards omit numeric 0.
Expand Down
6 changes: 3 additions & 3 deletions profiles/host-device-rdma/10-nicclusterpolicy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ spec:
global:
imagePullSecrets:
{{- range .NetworkOperator.ImagePullSecrets }}
- {{ . }}
- "{{ . }}"
{{- end }}
{{- end }}
{{- if and .NicConfigurationOperator (or .NicConfigurationOperator.DeployNicInterfaceNameTemplate .NicConfigurationOperator.UpdateFW) }}
Expand Down Expand Up @@ -56,7 +56,7 @@ spec:
{{- if .NetworkOperator.ImagePullSecrets }}
imagePullSecrets:
{{- range .NetworkOperator.ImagePullSecrets }}
- {{ . }}
- "{{ . }}"
{{- end }}
{{- end }}
version: "{{.DOCADriver.Version}}"
Expand Down Expand Up @@ -93,7 +93,7 @@ spec:
{{- if .NetworkOperator.ImagePullSecrets }}
imagePullSecrets:
{{- range .NetworkOperator.ImagePullSecrets }}
- {{ . }}
- "{{ . }}"
{{- end }}
{{- end }}
version: "{{.NetworkOperator.ComponentVersion}}"
Expand Down
4 changes: 2 additions & 2 deletions profiles/host-device-rdma/11-nicnodepolicy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ spec:
{{- if .NetworkOperator.ImagePullSecrets }}
imagePullSecrets:
{{- range .NetworkOperator.ImagePullSecrets }}
- {{ . }}
- "{{ . }}"
{{- end }}
{{- end }}
version: "{{.DOCADriver.Version}}"
Expand Down Expand Up @@ -57,7 +57,7 @@ spec:
{{- if .NetworkOperator.ImagePullSecrets }}
imagePullSecrets:
{{- range .NetworkOperator.ImagePullSecrets }}
- {{ . }}
- "{{ . }}"
{{- end }}
{{- end }}
version: "{{.NetworkOperator.ComponentVersion}}"
Expand Down
16 changes: 15 additions & 1 deletion profiles/ipoib-rdma-shared/00-values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ or upgrade the network-operator release in the configured namespace
*/ -}}
nfd:
enabled: true
{{- if .NetworkOperator.ImagePullSecrets }}

node-feature-discovery:
imagePullSecrets:
{{- range .NetworkOperator.ImagePullSecrets }}
- name: "{{ . }}"
{{- end }}
{{- end }}

upgradeCRDs: true

Expand All @@ -24,14 +32,20 @@ operator:

imagePullSecrets:
{{- range .NetworkOperator.ImagePullSecrets }}
- name: {{ . }}
- "{{ . }}"
{{- end }}
{{- end }}

maintenanceOperator:
enabled: {{ if or .NicConfigurationOperator (versionGE .NetworkOperator.SelectedRelease "26.1") }}true{{ else }}false{{ end }}

maintenance-operator-chart:
{{- if .NetworkOperator.ImagePullSecrets }}
imagePullSecrets:
{{- range .NetworkOperator.ImagePullSecrets }}
- name: "{{ . }}"
{{- end }}
{{- end }}
operatorConfig:
deploy: true
# Keep scalar overrides quoted: upstream truthiness guards omit numeric 0.
Expand Down
Loading