mirror of
https://github.com/open-cluster-management-io/ocm.git
synced 2026-08-23 14:17:00 +00:00
* 🌱 Copy TLS ConfigMap to addon namespaces in klusterlet operator Add AddonTLSConfigController that copies the ocm-tls-profile ConfigMap from the klusterlet operator namespace to addon namespaces (labeled with addon.open-cluster-management.io/namespace: "true"). This allows addon agents to read TLS profile settings without cross-namespace RBAC. The controller mirrors the existing addonsecretcontroller pattern: - Watches namespaces with the addon label via filtered informer - Copies ConfigMap data on namespace creation/update - Deletes target ConfigMap when source is removed - Skips update when target is already up-to-date Assisted by Claude Signed-off-by: zhujian <jiazhu@redhat.com> * 🌱 Fix ConfigMap update to preserve ResourceVersion and add stale-target test - Reuse existing ConfigMap object on update to preserve ResourceVersion, preventing optimistic concurrency conflicts - Add test case for stale target ConfigMap being updated Assisted by Claude Signed-off-by: zhujian <jiazhu@redhat.com> --------- Signed-off-by: zhujian <jiazhu@redhat.com>
183 lines
6.2 KiB
Go
183 lines
6.2 KiB
Go
package klusterlet
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/openshift/library-go/pkg/controller/controllercmd"
|
|
apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/fields"
|
|
versionutil "k8s.io/apimachinery/pkg/util/version"
|
|
"k8s.io/client-go/informers"
|
|
corev1informers "k8s.io/client-go/informers/core/v1"
|
|
"k8s.io/client-go/kubernetes"
|
|
"k8s.io/klog/v2"
|
|
|
|
operatorclient "open-cluster-management.io/api/client/operator/clientset/versioned"
|
|
operatorinformer "open-cluster-management.io/api/client/operator/informers/externalversions"
|
|
workclientset "open-cluster-management.io/api/client/work/clientset/versioned"
|
|
|
|
"open-cluster-management.io/ocm/pkg/operator/helpers"
|
|
"open-cluster-management.io/ocm/pkg/operator/operators/klusterlet/controllers/addonsecretcontroller"
|
|
"open-cluster-management.io/ocm/pkg/operator/operators/klusterlet/controllers/addontlsconfigcontroller"
|
|
"open-cluster-management.io/ocm/pkg/operator/operators/klusterlet/controllers/klusterletcontroller"
|
|
"open-cluster-management.io/ocm/pkg/operator/operators/klusterlet/controllers/ssarcontroller"
|
|
"open-cluster-management.io/ocm/pkg/operator/operators/klusterlet/controllers/statuscontroller"
|
|
)
|
|
|
|
type Options struct {
|
|
SkipPlaceholderHubSecret bool
|
|
ControlPlaneNodeLabelSelector string
|
|
DeploymentReplicas int32
|
|
DisableAddonNamespace bool
|
|
EnableSyncLabels bool
|
|
}
|
|
|
|
// RunKlusterletOperator starts a new klusterlet operator
|
|
func (o *Options) RunKlusterletOperator(ctx context.Context, controllerContext *controllercmd.ControllerContext) error {
|
|
// setting up contextual logger
|
|
logger := klog.NewKlogr()
|
|
podName := os.Getenv("POD_NAME")
|
|
if podName != "" {
|
|
logger = logger.WithValues("podName", podName)
|
|
}
|
|
ctx = klog.NewContext(ctx, logger)
|
|
|
|
// Build kube client and informer
|
|
kubeClient, err := kubernetes.NewForConfig(controllerContext.KubeConfig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
apiExtensionClient, err := apiextensionsclient.NewForConfig(controllerContext.KubeConfig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
version, err := kubeClient.ServerVersion()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
kubeVersion, err := versionutil.ParseGeneric(version.String())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
kubeInformer := informers.NewSharedInformerFactory(kubeClient, 5*time.Minute)
|
|
|
|
// to reduce cache size if there are larges number of secrets
|
|
newOneTermInformer := func(name string) informers.SharedInformerFactory {
|
|
return informers.NewSharedInformerFactoryWithOptions(kubeClient, 5*time.Minute,
|
|
informers.WithTweakListOptions(func(options *metav1.ListOptions) {
|
|
options.FieldSelector = fields.OneTermEqualSelector("metadata.name", name).String()
|
|
}))
|
|
}
|
|
|
|
hubConfigSecretInformer := newOneTermInformer(helpers.HubKubeConfig)
|
|
bootstrapConfigSecretInformer := newOneTermInformer(helpers.BootstrapHubKubeConfig)
|
|
externalConfigSecretInformer := newOneTermInformer(helpers.ExternalManagedKubeConfig)
|
|
|
|
secretInformers := map[string]corev1informers.SecretInformer{
|
|
helpers.HubKubeConfig: hubConfigSecretInformer.Core().V1().Secrets(),
|
|
helpers.BootstrapHubKubeConfig: bootstrapConfigSecretInformer.Core().V1().Secrets(),
|
|
helpers.ExternalManagedKubeConfig: externalConfigSecretInformer.Core().V1().Secrets(),
|
|
}
|
|
|
|
deploymentInformer := informers.NewSharedInformerFactoryWithOptions(kubeClient, 5*time.Minute,
|
|
informers.WithTweakListOptions(func(listOptions *metav1.ListOptions) {
|
|
selector := &metav1.LabelSelector{
|
|
MatchExpressions: []metav1.LabelSelectorRequirement{
|
|
{
|
|
Key: helpers.AgentLabelKey,
|
|
Operator: metav1.LabelSelectorOpExists,
|
|
},
|
|
},
|
|
}
|
|
listOptions.LabelSelector = metav1.FormatLabelSelector(selector)
|
|
}))
|
|
|
|
// Build operator client and informer
|
|
operatorClient, err := operatorclient.NewForConfig(controllerContext.KubeConfig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
operatorInformer := operatorinformer.NewSharedInformerFactory(operatorClient, 5*time.Minute)
|
|
|
|
workClient, err := workclientset.NewForConfig(controllerContext.KubeConfig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
klusterletController := klusterletcontroller.NewKlusterletController(
|
|
kubeClient,
|
|
apiExtensionClient,
|
|
operatorClient.OperatorV1().Klusterlets(),
|
|
operatorInformer.Operator().V1().Klusterlets(),
|
|
secretInformers,
|
|
deploymentInformer.Apps().V1().Deployments(),
|
|
workClient.WorkV1().AppliedManifestWorks(),
|
|
kubeVersion,
|
|
helpers.GetOperatorNamespace(),
|
|
o.ControlPlaneNodeLabelSelector,
|
|
o.DeploymentReplicas,
|
|
o.DisableAddonNamespace,
|
|
o.EnableSyncLabels)
|
|
|
|
klusterletCleanupController := klusterletcontroller.NewKlusterletCleanupController(
|
|
kubeClient,
|
|
apiExtensionClient,
|
|
operatorClient.OperatorV1().Klusterlets(),
|
|
operatorInformer.Operator().V1().Klusterlets(),
|
|
secretInformers,
|
|
deploymentInformer.Apps().V1().Deployments(),
|
|
workClient.WorkV1().AppliedManifestWorks(),
|
|
kubeVersion,
|
|
helpers.GetOperatorNamespace(),
|
|
o.ControlPlaneNodeLabelSelector,
|
|
o.DeploymentReplicas,
|
|
o.DisableAddonNamespace)
|
|
|
|
ssarController := ssarcontroller.NewKlusterletSSARController(
|
|
kubeClient,
|
|
operatorClient.OperatorV1().Klusterlets(),
|
|
operatorInformer.Operator().V1().Klusterlets(),
|
|
secretInformers,
|
|
)
|
|
|
|
statusController := statuscontroller.NewKlusterletStatusController(
|
|
kubeClient,
|
|
operatorClient.OperatorV1().Klusterlets(),
|
|
operatorInformer.Operator().V1().Klusterlets(),
|
|
deploymentInformer.Apps().V1().Deployments(),
|
|
)
|
|
|
|
addonController := addonsecretcontroller.NewAddonPullImageSecretController(
|
|
kubeClient,
|
|
helpers.GetOperatorNamespace(),
|
|
kubeInformer.Core().V1().Namespaces(),
|
|
)
|
|
|
|
addonTLSConfigController := addontlsconfigcontroller.NewAddonTLSConfigController(
|
|
kubeClient,
|
|
helpers.GetOperatorNamespace(),
|
|
kubeInformer.Core().V1().Namespaces(),
|
|
)
|
|
|
|
go operatorInformer.Start(ctx.Done())
|
|
go kubeInformer.Start(ctx.Done())
|
|
go hubConfigSecretInformer.Start(ctx.Done())
|
|
go bootstrapConfigSecretInformer.Start(ctx.Done())
|
|
go externalConfigSecretInformer.Start(ctx.Done())
|
|
go deploymentInformer.Start(ctx.Done())
|
|
go klusterletController.Run(ctx, 1)
|
|
go klusterletCleanupController.Run(ctx, 1)
|
|
go statusController.Run(ctx, 1)
|
|
go ssarController.Run(ctx, 1)
|
|
go addonController.Run(ctx, 1)
|
|
go addonTLSConfigController.Run(ctx, 1)
|
|
|
|
<-ctx.Done()
|
|
return nil
|
|
}
|