feat(manager): implement import-renderers (#1317)
Scorecard supply-chain security / Scorecard analysis (push) Failing after 1m40s
Post / images (amd64, addon-manager) (push) Failing after 52s
Post / images (amd64, placement) (push) Failing after 46s
Post / images (amd64, registration-operator) (push) Failing after 47s
Post / images (amd64, work) (push) Failing after 47s
Post / images (arm64, addon-manager) (push) Failing after 49s
Post / images (arm64, placement) (push) Failing after 48s
Post / images (arm64, registration) (push) Failing after 46s
Post / images (arm64, registration-operator) (push) Failing after 48s
Post / images (arm64, work) (push) Failing after 49s
Post / images (amd64, registration) (push) Failing after 14m11s
Post / coverage (push) Failing after 40m4s
Post / image manifest (addon-manager) (push) Has been skipped
Post / image manifest (placement) (push) Has been skipped
Post / image manifest (registration) (push) Has been skipped
Post / image manifest (registration-operator) (push) Has been skipped
Post / image manifest (work) (push) Has been skipped
Post / trigger clusteradm e2e (push) Has been skipped
Close stale issues and PRs / stale (push) Successful in 3s

Signed-off-by: Guilhem Lettron <glettron@akamai.com>
This commit is contained in:
Guilhem Lettron
2026-01-09 07:38:35 +00:00
committed by GitHub
parent c6aa931619
commit ac5f34839d
4 changed files with 157 additions and 0 deletions
@@ -70,6 +70,9 @@ spec:
{{if .ClusterImporterEnabled}}
- "--agent-image={{ .AgentImage }}"
- "--bootstrap-serviceaccount={{ .OperatorNamespace }}/agent-registration-bootstrap"
{{if .ImporterRenderers}}
- "--import-renderers={{ .ImporterRenderers }}"
{{end}}
{{end}}
{{ if .HostedMode }}
- "--kubeconfig=/var/run/secrets/hub/kubeconfig"
+1
View File
@@ -26,6 +26,7 @@ type HubConfig struct {
AgentImage string
CloudEventsDriverEnabled bool
ClusterImporterEnabled bool
ImporterRenderers string
WorkDriver string
AutoApproveUsers string
ImagePullSecret string
@@ -196,6 +196,9 @@ func (n *clusterManagerController) sync(ctx context.Context, controllerContext f
config.ClusterImporterEnabled = helpers.FeatureGateEnabled(registrationFeatureGates, ocmfeature.DefaultHubRegistrationFeatureGates, ocmfeature.ClusterImporter)
if config.ClusterImporterEnabled {
config.AgentImage = os.Getenv("AGENT_IMAGE")
if clusterManager.Spec.RegistrationConfiguration != nil && clusterManager.Spec.RegistrationConfiguration.ImporterConfiguration != nil {
config.ImporterRenderers = strings.Join(clusterManager.Spec.RegistrationConfiguration.ImporterConfiguration.Renderers, ",")
}
}
var workFeatureGates []operatorapiv1.FeatureGate
@@ -1057,6 +1057,156 @@ func TestGRPCServiceLoadBalancerType(t *testing.T) {
}
}
// TestImporterRenderersConfiguration tests that importer renderers are correctly rendered in the registration deployment
func TestImporterRenderersConfiguration(t *testing.T) {
tests := []struct {
name string
clusterManager *operatorapiv1.ClusterManager
expectedRenderers string
description string
}{
{
name: "ImporterRenderers with single renderer",
clusterManager: func() *operatorapiv1.ClusterManager {
cm := newClusterManager("testhub")
cm.Spec.RegistrationConfiguration = &operatorapiv1.RegistrationHubConfiguration{
FeatureGates: []operatorapiv1.FeatureGate{
{Feature: string(ocmfeature.ClusterImporter), Mode: operatorapiv1.FeatureGateModeTypeEnable},
},
ImporterConfiguration: &operatorapiv1.ImporterConfiguration{
Renderers: []string{"render-auto"},
},
}
return cm
}(),
expectedRenderers: "render-auto",
description: "Single renderer should be passed correctly",
},
{
name: "ImporterRenderers with multiple renderers",
clusterManager: func() *operatorapiv1.ClusterManager {
cm := newClusterManager("testhub")
cm.Spec.RegistrationConfiguration = &operatorapiv1.RegistrationHubConfiguration{
FeatureGates: []operatorapiv1.FeatureGate{
{Feature: string(ocmfeature.ClusterImporter), Mode: operatorapiv1.FeatureGateModeTypeEnable},
},
ImporterConfiguration: &operatorapiv1.ImporterConfiguration{
Renderers: []string{"render-auto", "render-from-config-secret"},
},
}
return cm
}(),
expectedRenderers: "render-auto,render-from-config-secret",
description: "Multiple renderers should be comma-separated",
},
{
name: "ImporterRenderers with empty renderers",
clusterManager: func() *operatorapiv1.ClusterManager {
cm := newClusterManager("testhub")
cm.Spec.RegistrationConfiguration = &operatorapiv1.RegistrationHubConfiguration{
FeatureGates: []operatorapiv1.FeatureGate{
{Feature: string(ocmfeature.ClusterImporter), Mode: operatorapiv1.FeatureGateModeTypeEnable},
},
ImporterConfiguration: &operatorapiv1.ImporterConfiguration{
Renderers: []string{},
},
}
return cm
}(),
expectedRenderers: "",
description: "Empty renderers should result in no argument",
},
{
name: "ImporterRenderers with nil ImporterConfiguration",
clusterManager: func() *operatorapiv1.ClusterManager {
cm := newClusterManager("testhub")
cm.Spec.RegistrationConfiguration = &operatorapiv1.RegistrationHubConfiguration{
FeatureGates: []operatorapiv1.FeatureGate{
{Feature: string(ocmfeature.ClusterImporter), Mode: operatorapiv1.FeatureGateModeTypeEnable},
},
// ImporterConfiguration is nil
}
return cm
}(),
expectedRenderers: "",
description: "Nil ImporterConfiguration should result in no argument",
},
{
name: "ImporterRenderers with ClusterImporter feature gate disabled",
clusterManager: func() *operatorapiv1.ClusterManager {
cm := newClusterManager("testhub")
cm.Spec.RegistrationConfiguration = &operatorapiv1.RegistrationHubConfiguration{
FeatureGates: []operatorapiv1.FeatureGate{
{Feature: string(ocmfeature.ClusterImporter), Mode: operatorapiv1.FeatureGateModeTypeDisable},
},
ImporterConfiguration: &operatorapiv1.ImporterConfiguration{
Renderers: []string{"render-auto"},
},
}
return cm
}(),
expectedRenderers: "",
description: "Disabled ClusterImporter feature gate should result in no argument",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
tc := newTestController(t, test.clusterManager)
clusterManagerNamespace := helpers.ClusterManagerNamespace(test.clusterManager.Name, test.clusterManager.Spec.DeployOption.Mode)
cd := setDeployment(test.clusterManager.Name, clusterManagerNamespace)
setup(t, tc, cd)
syncContext := testingcommon.NewFakeSyncContext(t, test.clusterManager.Name)
err := tc.clusterManagerController.sync(ctx, syncContext, test.clusterManager.Name)
if err != nil {
t.Fatalf("Expected no error when sync, %v", err)
}
// Find the registration deployment and check for --import-renderers argument
registrationDeploymentName := test.clusterManager.Name + "-registration-controller"
var registrationDeploymentFound bool
var importRenderersArg string
kubeActions := append(tc.hubKubeClient.Actions(), tc.managementKubeClient.Actions()...)
for _, action := range kubeActions {
verb := action.GetVerb()
if verb == createVerb || verb == "update" {
var deployment *appsv1.Deployment
if verb == createVerb {
object := action.(clienttesting.CreateActionImpl).Object
deployment, _ = object.(*appsv1.Deployment)
} else {
object := action.(clienttesting.UpdateActionImpl).Object
deployment, _ = object.(*appsv1.Deployment)
}
if deployment != nil && deployment.Name == registrationDeploymentName && deployment.Namespace == clusterManagerNamespace {
registrationDeploymentFound = true
for _, arg := range deployment.Spec.Template.Spec.Containers[0].Args {
if after, ok := strings.CutPrefix(arg, "--import-renderers="); ok {
importRenderersArg = after
break
}
}
break
}
}
}
if !registrationDeploymentFound {
t.Fatalf("Registration deployment %s not found in namespace %s", registrationDeploymentName, clusterManagerNamespace)
}
if importRenderersArg != test.expectedRenderers {
t.Errorf("Test %q failed: %s. Expected import-renderers %q, but got %q",
test.name, test.description, test.expectedRenderers, importRenderersArg)
}
})
}
}
// TestWorkControllerEnabledByFeatureGates tests that work controller is enabled when specific feature gates are enabled
func TestWorkControllerEnabledByFeatureGates(t *testing.T) {
tests := []struct {