linkerd2/controller/cmd/service-mirror/config_watcher.go

162 lines
4.7 KiB
Go

package servicemirror
import (
"fmt"
"sync"
"time"
"github.com/linkerd/linkerd2/controller/k8s"
log "github.com/sirupsen/logrus"
corev1 "k8s.io/api/core/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/clientcmd"
consts "github.com/linkerd/linkerd2/pkg/k8s"
sm "github.com/linkerd/linkerd2/pkg/servicemirror"
)
// RemoteClusterConfigWatcher watches for secrets of type MirrorSecretType
// and upon the detection of such secret created starts a RemoteClusterServiceWatcher
type RemoteClusterConfigWatcher struct {
serviceMirrorNamespace string
k8sAPI *k8s.API
clusterWatchers map[string]*RemoteClusterServiceWatcher
requeueLimit int
repairPeriod time.Duration
sync.RWMutex
}
// NewRemoteClusterConfigWatcher Creates a new config watcher
func NewRemoteClusterConfigWatcher(serviceMirrorNamespace string, secretsInformer cache.SharedIndexInformer, k8sAPI *k8s.API, requeueLimit int, repairPeriod time.Duration) *RemoteClusterConfigWatcher {
rcw := &RemoteClusterConfigWatcher{
serviceMirrorNamespace: serviceMirrorNamespace,
k8sAPI: k8sAPI,
clusterWatchers: map[string]*RemoteClusterServiceWatcher{},
requeueLimit: requeueLimit,
repairPeriod: repairPeriod,
}
secretsInformer.AddEventHandler(
cache.FilteringResourceEventHandler{
FilterFunc: func(obj interface{}) bool {
switch object := obj.(type) {
case *corev1.Secret:
return object.Type == consts.MirrorSecretType
case cache.DeletedFinalStateUnknown:
if secret, ok := object.Obj.(*corev1.Secret); ok {
return secret.Type == consts.MirrorSecretType
}
return false
default:
return false
}
},
Handler: cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
secret := obj.(*corev1.Secret)
if err := rcw.registerRemoteCluster(secret); err != nil {
log.Errorf("Cannot register target cluster: %s", err)
}
},
DeleteFunc: func(obj interface{}) {
secret, ok := obj.(*corev1.Secret)
if !ok {
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
if !ok {
log.Errorf("couldn't get object from DeletedFinalStateUnknown %#v", obj)
return
}
secret, ok = tombstone.Obj.(*corev1.Secret)
if !ok {
log.Errorf("DeletedFinalStateUnknown contained object that is not a Secret %#v", obj)
return
}
}
if err := rcw.unregisterRemoteCluster(secret, true); err != nil {
log.Errorf("Cannot unregister target cluster: %s", err)
}
},
UpdateFunc: func(old, new interface{}) {
oldSecret := old.(*corev1.Secret)
newSecret := new.(*corev1.Secret)
if oldSecret.ResourceVersion != newSecret.ResourceVersion {
if err := rcw.unregisterRemoteCluster(oldSecret, false); err != nil {
log.Errorf("Cannot unregister target cluster: %s", err)
return
}
if err := rcw.registerRemoteCluster(newSecret); err != nil {
log.Errorf("Cannot register target cluster: %s", err)
}
}
//TODO: Handle update (it might be that the credentials have changed...)
},
},
},
)
return rcw
}
// Stop Shuts down all created config and cluster watchers
func (rcw *RemoteClusterConfigWatcher) Stop() {
rcw.Lock()
defer rcw.Unlock()
for _, watcher := range rcw.clusterWatchers {
watcher.Stop(false)
}
}
func (rcw *RemoteClusterConfigWatcher) registerRemoteCluster(secret *corev1.Secret) error {
config, err := sm.ParseRemoteClusterSecret(secret)
if err != nil {
return err
}
clientConfig, err := clientcmd.RESTConfigFromKubeConfig(config.APIConfig)
if err != nil {
return fmt.Errorf("unable to parse kube config: %s", err)
}
rcw.Lock()
defer rcw.Unlock()
if _, ok := rcw.clusterWatchers[config.ClusterName]; ok {
return fmt.Errorf("there is already a cluster with name %s being watcher. Please delete its config before attempting to register a new one", config.ClusterName)
}
watcher, err := NewRemoteClusterServiceWatcher(rcw.serviceMirrorNamespace, rcw.k8sAPI, clientConfig, config.ClusterName, rcw.requeueLimit, rcw.repairPeriod, config.ClusterDomain)
if err != nil {
return err
}
rcw.clusterWatchers[config.ClusterName] = watcher
if err := watcher.Start(); err != nil {
return err
}
return nil
}
func (rcw *RemoteClusterConfigWatcher) unregisterRemoteCluster(secret *corev1.Secret, cleanState bool) error {
config, err := sm.ParseRemoteClusterSecret(secret)
if err != nil {
return err
}
rcw.Lock()
defer rcw.Unlock()
if watcher, ok := rcw.clusterWatchers[config.ClusterName]; ok {
watcher.Stop(cleanState)
} else {
return fmt.Errorf("cannot find watcher for cluser: %s", config.ClusterName)
}
delete(rcw.clusterWatchers, config.ClusterName)
return nil
}