Skip to content
Open
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
62 changes: 43 additions & 19 deletions pkg/operator/nodekubeconfigcontroller/nodekubeconfigcontroller.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/base64"
"fmt"
"reflect"
"strings"
"time"

Expand All @@ -20,12 +21,17 @@ import (
"github.com/openshift/library-go/pkg/operator/resource/resourceread"
"github.com/openshift/library-go/pkg/operator/v1helpers"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/client-go/kubernetes"
coreclientv1 "k8s.io/client-go/kubernetes/typed/core/v1"
corev1listers "k8s.io/client-go/listers/core/v1"
)

const workQueueKey = "key"
const (
workQueueKey = "key"
kubeApiserverServerCA = "kube-apiserver-server-ca"
nodeSystemAdminClient = "node-system-admin-client"
)

type NodeKubeconfigController struct {
operatorClient v1helpers.StaticPodOperatorClient
Expand All @@ -40,24 +46,37 @@ func NewNodeKubeconfigController(
operatorClient v1helpers.StaticPodOperatorClient,
kubeInformersForNamespaces v1helpers.KubeInformersForNamespaces,
kubeClient kubernetes.Interface,
infrastuctureInformer configv1informers.InfrastructureInformer,
infrastructureInformer configv1informers.InfrastructureInformer,
eventRecorder events.Recorder,
) factory.Controller {
c := &NodeKubeconfigController{
operatorClient: operatorClient,
kubeClient: kubeClient,
configMapLister: kubeInformersForNamespaces.ConfigMapLister(),
secretLister: kubeInformersForNamespaces.SecretLister(),
infrastructureLister: infrastuctureInformer.Lister(),
}

return factory.New().WithInformers(
infrastructureLister: infrastructureInformer.Lister(),
}

return factory.New().WithFilteredEventsInformers(
func(obj interface{}) bool {
if cm, ok := obj.(*corev1.ConfigMap); ok {
if cm.Namespace == operatorclient.TargetNamespace && cm.Name == kubeApiserverServerCA {
return true
}
return false
}
if secret, ok := obj.(*corev1.Secret); ok {
if secret.Namespace == operatorclient.OperatorNamespace && secret.Name == nodeSystemAdminClient {
return true
}
return false
}
return true
},
operatorClient.Informer(),
kubeInformersForNamespaces.InformersFor(operatorclient.OperatorNamespace).Core().V1().ConfigMaps().Informer(),
kubeInformersForNamespaces.InformersFor(operatorclient.TargetNamespace).Core().V1().ConfigMaps().Informer(),
kubeInformersForNamespaces.InformersFor(operatorclient.OperatorNamespace).Core().V1().Secrets().Informer(),
kubeInformersForNamespaces.InformersFor(operatorclient.TargetNamespace).Core().V1().Secrets().Informer(),
infrastuctureInformer.Informer(),
infrastructureInformer.Informer(),
).WithSync(c.sync).WithSyncDegradedOnError(c.operatorClient).ResyncEvery(5*time.Minute).ToController("NodeKubeconfigController", eventRecorder.WithComponentSuffix("node-kubeconfig-controller"))
}

Expand Down Expand Up @@ -99,27 +118,27 @@ func (c NodeKubeconfigController) sync(ctx context.Context, syncContext factory.
func ensureNodeKubeconfigs(ctx context.Context, client coreclientv1.CoreV1Interface, secretLister corev1listers.SecretLister, configmapLister corev1listers.ConfigMapLister, infrastructureLister configv1listers.InfrastructureLister, recorder events.Recorder) error {
requiredSecret := resourceread.ReadSecretV1OrDie(bindata.MustAsset("assets/kube-apiserver/node-kubeconfigs.yaml"))

systemAdminCredsSecret, err := secretLister.Secrets(operatorclient.OperatorNamespace).Get("node-system-admin-client")
systemAdminCredsSecret, err := secretLister.Secrets(operatorclient.OperatorNamespace).Get(nodeSystemAdminClient)
if err != nil {
return err
}

systemAdminClientCert := systemAdminCredsSecret.Data[corev1.TLSCertKey]
if len(systemAdminClientCert) == 0 {
return fmt.Errorf("system:admin client certificate missing from secret %s/node-system-admin-client", operatorclient.OperatorNamespace)
return fmt.Errorf("system:admin client certificate missing from secret %s/%s", operatorclient.OperatorNamespace, nodeSystemAdminClient)
}
systemAdminClientKey := systemAdminCredsSecret.Data[corev1.TLSPrivateKeyKey]
if len(systemAdminClientKey) == 0 {
return fmt.Errorf("system:admin client private key missing from secret %s/node-system-admin-client", operatorclient.OperatorNamespace)
return fmt.Errorf("system:admin client private key missing from secret %s/%s", operatorclient.OperatorNamespace, nodeSystemAdminClient)
}

servingCABundleCM, err := configmapLister.ConfigMaps(operatorclient.TargetNamespace).Get("kube-apiserver-server-ca")
servingCABundleCM, err := configmapLister.ConfigMaps(operatorclient.TargetNamespace).Get(kubeApiserverServerCA)
if err != nil {
return err
}
servingCABundleData := servingCABundleCM.Data["ca-bundle.crt"]
if len(servingCABundleData) == 0 {
return fmt.Errorf("serving CA bundle missing from configmap %s/kube-apiserver-server-ca", operatorclient.TargetNamespace)
return fmt.Errorf("serving CA bundle missing from configmap %s/%s", operatorclient.TargetNamespace, kubeApiserverServerCA)
}

infrastructure, err := infrastructureLister.Get("cluster")
Expand Down Expand Up @@ -161,10 +180,15 @@ func ensureNodeKubeconfigs(ctx context.Context, client coreclientv1.CoreV1Interf
requiredSecret.Annotations[certrotation.CertificateNotAfterAnnotation] = systemAdminCredsSecret.Annotations[certrotation.CertificateNotAfterAnnotation]
}

_, _, err = resourceapply.ApplySecret(ctx, client, recorder, requiredSecret)
if err != nil {
return err
actualSecret, err := secretLister.Secrets(requiredSecret.Namespace).Get(requiredSecret.Name)
if !apierrors.IsNotFound(err) {
if err != nil {
return err
}
if reflect.DeepEqual(actualSecret.Data, requiredSecret.Data) && reflect.DeepEqual(actualSecret.Annotations, requiredSecret.Annotations) {
return nil
}
}

return nil
_, _, err = resourceapply.ApplySecret(ctx, client, recorder, requiredSecret)
return err
}
101 changes: 101 additions & 0 deletions pkg/operator/nodekubeconfigcontroller/nodekubeconfigcontroller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,107 @@ func TestEnsureNodeKubeconfigs(t *testing.T) {
},
},
},
{
name: "annotations only update",
existingObjects: []runtime.Object{
&corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: "openshift-kube-apiserver",
Name: "kube-apiserver-server-ca",
},
Data: map[string]string{
"ca-bundle.crt": "kube-apiserver-server-ca certificate",
},
},
&corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: "openshift-kube-apiserver-operator",
Name: "node-system-admin-client",
Annotations: map[string]string{
certrotation.CertificateNotBeforeAnnotation: certNotBefore,
certrotation.CertificateNotAfterAnnotation: certNotAfter,
},
},
Data: map[string][]byte{
"tls.crt": []byte(publicKey),
"tls.key": []byte(privateKey),
},
},
&corev1.Secret{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Secret",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "openshift-kube-apiserver",
Name: "node-kubeconfigs",
Annotations: map[string]string{
annotations.OpenShiftComponent: "kube-apiserver",
certrotation.CertificateNotBeforeAnnotation: "some-old-not-before",
certrotation.CertificateNotAfterAnnotation: "some-old-not-after",
},
},
Data: map[string][]byte{
"localhost.kubeconfig": generateKubeConfig("localhost", "https://localhost:6443"),
"localhost-recovery.kubeconfig": generateKubeConfig("localhost-recovery", "https://localhost:6443"),
"lb-ext.kubeconfig": generateKubeConfig("lb-ext", lbExtServer),
"lb-int.kubeconfig": generateKubeConfig("lb-int", lbIntServer),
},
},
},
infrastructure: &configv1.Infrastructure{
ObjectMeta: metav1.ObjectMeta{
Namespace: "",
Name: "cluster",
},
Status: configv1.InfrastructureStatus{
APIServerURL: lbExtServer,
APIServerInternalURL: lbIntServer,
},
},
expectedErr: nil,
expectedActions: []clienttesting.Action{
clienttesting.DeleteActionImpl{
ActionImpl: clienttesting.ActionImpl{
Namespace: "openshift-kube-apiserver",
Verb: "delete",
Resource: corev1.SchemeGroupVersion.WithResource("secrets"),
},
Name: "node-kubeconfigs",
},
clienttesting.CreateActionImpl{
ActionImpl: clienttesting.ActionImpl{
Namespace: "openshift-kube-apiserver",
Verb: "create",
Resource: corev1.SchemeGroupVersion.WithResource("secrets"),
},
Object: &corev1.Secret{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Secret",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "openshift-kube-apiserver",
Name: "node-kubeconfigs",
Labels: map[string]string{},
OwnerReferences: []metav1.OwnerReference{},
Annotations: map[string]string{
annotations.OpenShiftComponent: "kube-apiserver",
certrotation.CertificateNotBeforeAnnotation: certNotBefore,
certrotation.CertificateNotAfterAnnotation: certNotAfter,
},
},
Data: map[string][]byte{
"localhost.kubeconfig": generateKubeConfig("localhost", "https://localhost:6443"),
"localhost-recovery.kubeconfig": generateKubeConfig("localhost-recovery", "https://localhost:6443"),
"lb-ext.kubeconfig": generateKubeConfig("lb-ext", lbExtServer),
"lb-int.kubeconfig": generateKubeConfig("lb-int", lbIntServer),
},
Type: corev1.SecretTypeOpaque,
},
},
},
},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
Expand Down