mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-09-06 16:17:08 +00:00
Pull controller-go back into this repository as internal/controllerlib.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
Copyright 2020 VMware, Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
corev1informers "k8s.io/client-go/informers/core/v1"
|
||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
"k8s.io/client-go/tools/events"
|
||||
|
||||
"github.com/suzerain-io/pinniped/internal/controllerlib"
|
||||
"github.com/suzerain-io/pinniped/internal/controllerlib/test/integration/examplecontroller/api"
|
||||
)
|
||||
|
||||
func NewExampleUpdatingController(
|
||||
services corev1informers.ServiceInformer,
|
||||
secrets corev1informers.SecretInformer,
|
||||
secretClient corev1client.SecretsGetter,
|
||||
recorder events.EventRecorder,
|
||||
secretData string,
|
||||
) controllerlib.Controller {
|
||||
serviceLister := services.Lister()
|
||||
secretLister := secrets.Lister()
|
||||
|
||||
// note that these functions do not need to be inlined
|
||||
// this just demonstrates that for simple Syncer implementations, everything can be in one place
|
||||
|
||||
toServiceName := func(secret *corev1.Secret) (string, bool) {
|
||||
serviceName := secret.Annotations[api.ServiceNameAnnotation]
|
||||
return serviceName, len(serviceName) != 0
|
||||
}
|
||||
|
||||
ensureSecretData := func(service *corev1.Service, secretCopy *corev1.Secret) bool {
|
||||
var needsUpdate bool
|
||||
|
||||
expectedData := map[string][]byte{
|
||||
api.SecretDataKey: []byte(secretData),
|
||||
}
|
||||
if !reflect.DeepEqual(secretCopy.Data, expectedData) {
|
||||
secretCopy.Data = expectedData
|
||||
needsUpdate = true
|
||||
}
|
||||
|
||||
expectedOwnerReferences := []metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: "v1",
|
||||
Kind: "Service",
|
||||
Name: service.Name,
|
||||
UID: service.UID,
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(secretCopy.OwnerReferences, expectedOwnerReferences) {
|
||||
secretCopy.OwnerReferences = expectedOwnerReferences
|
||||
needsUpdate = true
|
||||
}
|
||||
|
||||
return needsUpdate
|
||||
}
|
||||
|
||||
isSecretValidForService := func(service *corev1.Service, secret *corev1.Secret) bool {
|
||||
if service.Annotations[api.SecretNameAnnotation] != secret.Name {
|
||||
return false
|
||||
}
|
||||
if secret.Annotations[api.ServiceUIDAnnotation] != string(service.UID) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
getServiceForSecret := func(secret *corev1.Secret) (*corev1.Service, error) {
|
||||
serviceName, ok := toServiceName(secret)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
service, err := serviceLister.Services(secret.Namespace).Get(serviceName)
|
||||
if apierrors.IsNotFound(err) {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get service %s/%s: %w", secret.Namespace, serviceName, err)
|
||||
}
|
||||
return service, nil
|
||||
}
|
||||
|
||||
syncer := controllerlib.SyncFunc(func(ctx controllerlib.Context) error {
|
||||
secret, err := secretLister.Secrets(ctx.Key.Namespace).Get(ctx.Key.Name)
|
||||
if apierrors.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get the secret %s/%s: %w", secret.Namespace, secret.Name, err)
|
||||
}
|
||||
|
||||
service, err := getServiceForSecret(secret)
|
||||
if err != nil || service == nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isSecretValidForService(service, secret) {
|
||||
//nolint: goerr113
|
||||
utilruntime.HandleError(fmt.Errorf("secret %s/%s does not have corresponding service UID %v", secret.Namespace, secret.Name, service.UID))
|
||||
return nil
|
||||
}
|
||||
|
||||
// make a copy to avoid mutating cache state
|
||||
secretCopy := secret.DeepCopy()
|
||||
|
||||
if needsUpdate := ensureSecretData(service, secretCopy); needsUpdate {
|
||||
_, updateErr := secretClient.Secrets(secretCopy.Namespace).Update(context.TODO(), secretCopy, metav1.UpdateOptions{})
|
||||
return updateErr
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
config := controllerlib.Config{
|
||||
Name: "example-controller-updating",
|
||||
Syncer: syncer,
|
||||
}
|
||||
|
||||
addSecret := func(obj metav1.Object) bool {
|
||||
secret := obj.(*corev1.Secret)
|
||||
_, ok := toServiceName(secret)
|
||||
return ok
|
||||
}
|
||||
|
||||
return controllerlib.New(config,
|
||||
controllerlib.WithInformer(services, controllerlib.FilterFuncs{}, controllerlib.InformerOption{SkipEvents: true}),
|
||||
|
||||
controllerlib.WithInformer(secrets, controllerlib.FilterFuncs{
|
||||
AddFunc: addSecret,
|
||||
UpdateFunc: func(oldObj, newObj metav1.Object) bool {
|
||||
return addSecret(newObj) || addSecret(oldObj)
|
||||
},
|
||||
}, controllerlib.InformerOption{}),
|
||||
|
||||
controllerlib.WithRecorder(recorder), // TODO actually use the recorder
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user