sync labels from klusterlet to all agent resources (#475)

Signed-off-by: Zhiwei Yin <zyin@redhat.com>
This commit is contained in:
Zhiwei Yin
2024-06-04 14:49:49 +08:00
committed by GitHub
parent b53012561d
commit 7fd733082f
41 changed files with 350 additions and 60 deletions

View File

@@ -52,6 +52,9 @@ const (
// DefaultAddonNamespace is the default namespace for agent addon
DefaultAddonNamespace = "open-cluster-management-agent-addon"
// AgentLabelKey is used to filter resources in informers
AgentLabelKey = "createdByKlusterlet"
)
const (
@@ -666,7 +669,7 @@ func AgentPriorityClassName(klusterlet *operatorapiv1.Klusterlet, kubeVersion *v
// https://github.com/openshift/library-go/blob/d9cdfbd844ea08465b938c46a16bed2ea23207e4/pkg/operator/resource/resourceapply/core.go#L357,
// add an addition targetClient parameter to support sync secret to another cluster.
func SyncSecret(ctx context.Context, client, targetClient coreclientv1.SecretsGetter, recorder events.Recorder,
sourceNamespace, sourceName, targetNamespace, targetName string, ownerRefs []metav1.OwnerReference) (*corev1.Secret, bool, error) {
sourceNamespace, sourceName, targetNamespace, targetName string, ownerRefs []metav1.OwnerReference, labels map[string]string) (*corev1.Secret, bool, error) {
source, err := client.Secrets(sourceNamespace).Get(ctx, sourceName, metav1.GetOptions{})
switch {
case errors.IsNotFound(err):
@@ -707,6 +710,7 @@ func SyncSecret(ctx context.Context, client, targetClient coreclientv1.SecretsGe
source.Name = targetName
source.ResourceVersion = ""
source.OwnerReferences = ownerRefs
source.Labels = labels
return resourceapply.ApplySecret(ctx, targetClient, recorder, source)
}
}
@@ -821,3 +825,24 @@ func GetOperatorNamespace() string {
}
return operatorNamespace
}
func GetKlusterletAgentLabels(klusterlet *operatorapiv1.Klusterlet) map[string]string {
labels := klusterlet.GetLabels()
if labels == nil {
labels = map[string]string{}
}
// This label key is used to filter resources in deployment informer
labels[AgentLabelKey] = klusterlet.GetName()
return labels
}
func MapCompare(required, existing map[string]string) bool {
for k, v := range required {
if existing[k] != v {
return false
}
}
return true
}

View File

@@ -1477,7 +1477,8 @@ func TestSyncSecret(t *testing.T) {
clientTarget := fakekube.NewSimpleClientset()
secret, changed, err := SyncSecret(
context.TODO(), client.CoreV1(), clientTarget.CoreV1(),
events.NewInMemoryRecorder("test"), tc.sourceNamespace, tc.sourceName, tc.targetNamespace, tc.targetName, tc.ownerRefs)
events.NewInMemoryRecorder("test"), tc.sourceNamespace, tc.sourceName,
tc.targetNamespace, tc.targetName, tc.ownerRefs, nil)
if (err == nil && len(tc.expectedErr) != 0) || (err != nil && err.Error() != tc.expectedErr) {
t.Errorf("%s: expected error %v, got %v", tc.name, tc.expectedErr, err)

View File

@@ -104,12 +104,12 @@ func SATokenCreater(ctx context.Context, saName, saNamespace string, saClient ku
func SyncKubeConfigSecret(ctx context.Context, secretName, secretNamespace, kubeconfigPath string,
templateKubeconfig *rest.Config, secretClient coreclientv1.SecretsGetter,
tokenGetter TokenGetterFunc, recorder events.Recorder) error {
tokenGetter TokenGetterFunc, recorder events.Recorder, labels map[string]string) error {
secret, err := secretClient.Secrets(secretNamespace).Get(ctx, secretName, metav1.GetOptions{})
switch {
case errors.IsNotFound(err):
return applyKubeconfigSecret(ctx, templateKubeconfig, secretName, secretNamespace,
kubeconfigPath, secretClient, tokenGetter, recorder)
kubeconfigPath, secretClient, tokenGetter, recorder, labels)
case err != nil:
return err
}
@@ -118,7 +118,8 @@ func SyncKubeConfigSecret(ctx context.Context, secretName, secretNamespace, kube
return nil
}
return applyKubeconfigSecret(ctx, templateKubeconfig, secretName, secretNamespace, kubeconfigPath, secretClient, tokenGetter, recorder)
return applyKubeconfigSecret(ctx, templateKubeconfig, secretName, secretNamespace, kubeconfigPath,
secretClient, tokenGetter, recorder, labels)
}
func tokenValid(secret *corev1.Secret, tokenGetter TokenGetterFunc) bool {
@@ -200,7 +201,7 @@ func clusterInfoNotChanged(secret *corev1.Secret, templateKubeconfig *rest.Confi
// applyKubeconfigSecret would render saToken to a secret.
func applyKubeconfigSecret(ctx context.Context, templateKubeconfig *rest.Config, secretName, secretNamespace,
kubeconfigPath string, secretClient coreclientv1.SecretsGetter, tokenGetter TokenGetterFunc,
recorder events.Recorder) error {
recorder events.Recorder, labels map[string]string) error {
token, expiration, additionalData, err := tokenGetter()
if err != nil {
@@ -239,6 +240,7 @@ func applyKubeconfigSecret(ctx context.Context, templateKubeconfig *rest.Config,
ObjectMeta: metav1.ObjectMeta{
Namespace: secretNamespace,
Name: secretName,
Labels: labels,
},
Data: map[string][]byte{
"kubeconfig": kubeconfigContent,

View File

@@ -270,7 +270,7 @@ func TestApplyKubeconfigSecret(t *testing.T) {
err := SyncKubeConfigSecret(
context.TODO(), secretName, secretNamespace,
"/tmp/kubeconfig", tkc, client.CoreV1(), tokenGetter,
eventstesting.NewTestingEventRecorder(t))
eventstesting.NewTestingEventRecorder(t), nil)
if err != nil && !tt.wantErr {
t.Error(err)
}

View File

@@ -347,7 +347,7 @@ func ensureSAKubeconfigs(ctx context.Context, clusterManagerName, clusterManager
TLSClientConfig: rest.TLSClientConfig{
CAData: hubKubeConfig.CAData,
},
}, managementClient.CoreV1(), tokenGetter, recorder)
}, managementClient.CoreV1(), tokenGetter, recorder, nil)
if err != nil {
return err
}

View File

@@ -53,6 +53,7 @@ func (c *secretReconcile) reconcile(ctx context.Context, cm *operatorapiv1.Clust
config.ClusterManagerNamespace,
secretName,
[]metav1.OwnerReference{},
nil,
); err != nil {
syncedErrs = append(syncedErrs, fmt.Errorf("failed to sync secret %s: %v", secretName, err))
}

View File

@@ -79,6 +79,7 @@ func (c *addonPullImageSecretController) sync(ctx context.Context, controllerCon
namespace,
helpers.ImagePullSecret,
[]metav1.OwnerReference{},
nil,
)
if err != nil {
return err

View File

@@ -8,6 +8,7 @@ import (
"github.com/openshift/library-go/pkg/controller/factory"
"github.com/openshift/library-go/pkg/operator/events"
"github.com/openshift/library-go/pkg/operator/resource/resourceapply"
"github.com/openshift/library-go/pkg/operator/resource/resourcemerge"
corev1 "k8s.io/api/core/v1"
apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/apimachinery/pkg/api/errors"
@@ -166,6 +167,9 @@ type klusterletConfig struct {
// DisableAddonNamespace is the flag to disable the creationg of default addon namespace.
DisableAddonNamespace bool
// Labels of the agents are synced from klusterlet CR.
Labels map[string]string
}
func (n *klusterletController) sync(ctx context.Context, controllerContext factory.SyncContext) error {
@@ -221,6 +225,7 @@ func (n *klusterletController) sync(ctx context.Context, controllerContext facto
ResourceRequirementResourceType: helpers.ResourceType(klusterlet),
ResourceRequirements: resourceRequirements,
DisableAddonNamespace: n.disableAddonNamespace,
Labels: helpers.GetKlusterletAgentLabels(klusterlet),
}
managedClusterClients, err := n.managedClusterClientsBuilder.
@@ -418,6 +423,7 @@ func syncPullSecret(ctx context.Context, sourceClient, targetClient kubernetes.I
namespace,
helpers.ImagePullSecret,
[]metav1.OwnerReference{},
helpers.GetKlusterletAgentLabels(klusterlet),
)
if err != nil {
@@ -436,6 +442,8 @@ func ensureNamespace(
kubeClient kubernetes.Interface,
klusterlet *operatorapiv1.Klusterlet,
namespace string, labels map[string]string, recorder events.Recorder) error {
modified := resourcemerge.BoolPtr(false)
resourcemerge.MergeMap(modified, &labels, helpers.GetKlusterletAgentLabels(klusterlet))
_, _, err := resourceapply.ApplyNamespace(ctx, kubeClient.CoreV1(), recorder, &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: namespace,

View File

@@ -463,6 +463,11 @@ func ensureObject(t *testing.T, object runtime.Object, klusterlet *operatorapiv1
return
}
if !helpers.MapCompare(helpers.GetKlusterletAgentLabels(klusterlet), access.GetLabels()) {
t.Errorf("the labels of klusterlet are not synced to %v", access.GetName())
return
}
namespace := helpers.AgentNamespace(klusterlet)
switch o := object.(type) {
case *appsv1.Deployment:
@@ -560,6 +565,7 @@ func TestSyncDeploy(t *testing.T) {
func TestSyncDeploySingleton(t *testing.T) {
klusterlet := newKlusterlet("klusterlet", "testns", "cluster1")
klusterlet.SetLabels(map[string]string{"test": "test", "abc": "abc"})
klusterlet.Spec.DeployOption.Mode = operatorapiv1.InstallModeSingleton
bootStrapSecret := newSecret(helpers.BootstrapHubKubeConfig, "testns")
hubKubeConfigSecret := newSecret(helpers.HubKubeConfig, "testns")

View File

@@ -164,6 +164,7 @@ func (r *managedReconcile) createAggregationRule(ctx context.Context, klusterlet
},
Rules: []rbacv1.PolicyRule{},
}
aggregateClusterRole.SetLabels(helpers.GetKlusterletAgentLabels(klusterlet))
_, createErr := r.managedClusterClients.kubeClient.RbacV1().ClusterRoles().Create(ctx, aggregateClusterRole, metav1.CreateOptions{})
return createErr
}

View File

@@ -198,9 +198,10 @@ func (r *runtimeReconcile) createManagedClusterKubeconfig(
klusterlet *operatorapiv1.Klusterlet,
klusterletNamespace, agentNamespace, saName, secretName string,
recorder events.Recorder) error {
labels := helpers.GetKlusterletAgentLabels(klusterlet)
tokenGetter := helpers.SATokenGetter(ctx, saName, klusterletNamespace, r.managedClusterClients.kubeClient)
err := helpers.SyncKubeConfigSecret(ctx, secretName, agentNamespace, "/spoke/config/kubeconfig",
r.managedClusterClients.kubeconfig, r.kubeClient.CoreV1(), tokenGetter, recorder)
r.managedClusterClients.kubeconfig, r.kubeClient.CoreV1(), tokenGetter, recorder, labels)
if err != nil {
meta.SetStatusCondition(&klusterlet.Status.Conditions, metav1.Condition{
Type: operatorapiv1.ConditionKlusterletApplied, Status: metav1.ConditionFalse, Reason: operatorapiv1.ReasonKlusterletApplyFailed,

View File

@@ -73,8 +73,16 @@ func (o *Options) RunKlusterletOperator(ctx context.Context, controllerContext *
}
deploymentInformer := informers.NewSharedInformerFactoryWithOptions(kubeClient, 5*time.Minute,
informers.WithTweakListOptions(func(options *metav1.ListOptions) {
options.LabelSelector = "createdBy=klusterlet"
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