github.com/cilium/cilium@v1.16.2/pkg/clustermesh/operator/remote_cluster.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package operator 5 6 import ( 7 "context" 8 "path" 9 10 "github.com/cilium/cilium/api/v1/models" 11 "github.com/cilium/cilium/pkg/clustermesh/common" 12 "github.com/cilium/cilium/pkg/clustermesh/types" 13 "github.com/cilium/cilium/pkg/clustermesh/wait" 14 "github.com/cilium/cilium/pkg/kvstore" 15 "github.com/cilium/cilium/pkg/kvstore/store" 16 "github.com/cilium/cilium/pkg/lock" 17 serviceStore "github.com/cilium/cilium/pkg/service/store" 18 ) 19 20 // remoteCluster implements the clustermesh business logic on top of 21 // common.RemoteCluster. 22 type remoteCluster struct { 23 // name is the name of the cluster 24 name string 25 26 globalServices *common.GlobalServiceCache 27 28 // remoteServices is the shared store representing services in remote clusters 29 remoteServices store.WatchStore 30 31 storeFactory store.Factory 32 33 clusterAddHooks []func(string) 34 clusterDeleteHooks []func(string) 35 36 // status is the function which fills the common part of the status. 37 status common.StatusFunc 38 39 // synced tracks the initial synchronization with the remote cluster. 40 synced synced 41 } 42 43 func (rc *remoteCluster) Run(ctx context.Context, backend kvstore.BackendOperations, config types.CiliumClusterConfig, ready chan<- error) { 44 var mgr store.WatchStoreManager 45 if config.Capabilities.SyncedCanaries { 46 mgr = rc.storeFactory.NewWatchStoreManager(backend, rc.name) 47 } else { 48 mgr = store.NewWatchStoreManagerImmediate(rc.name) 49 } 50 51 adapter := func(prefix string) string { return prefix } 52 if config.Capabilities.Cached { 53 adapter = kvstore.StateToCachePrefix 54 } 55 56 mgr.Register(adapter(serviceStore.ServiceStorePrefix), func(ctx context.Context) { 57 rc.remoteServices.Watch(ctx, backend, path.Join(adapter(serviceStore.ServiceStorePrefix), rc.name)) 58 }) 59 60 close(ready) 61 for _, clusterAddHook := range rc.clusterAddHooks { 62 clusterAddHook(rc.name) 63 } 64 mgr.Run(ctx) 65 } 66 67 func (rc *remoteCluster) Stop() { 68 rc.synced.Stop() 69 } 70 71 func (rc *remoteCluster) Remove(context.Context) { 72 for _, clusterDeleteHook := range rc.clusterDeleteHooks { 73 clusterDeleteHook(rc.name) 74 } 75 // Draining shall occur only when the configuration for the remote cluster 76 // is removed, and not in case the operator is shutting down, otherwise we 77 // would break existing connections on restart. 78 rc.remoteServices.Drain() 79 } 80 81 type synced struct { 82 wait.SyncedCommon 83 services *lock.StoppableWaitGroup 84 } 85 86 func newSynced() synced { 87 return synced{ 88 SyncedCommon: wait.NewSyncedCommon(), 89 services: lock.NewStoppableWaitGroup(), 90 } 91 } 92 93 // Services returns after that the initial list of shared services has been 94 // received from the remote cluster, the remote cluster is disconnected, 95 // or the given context is canceled. 96 func (s *synced) Services(ctx context.Context) error { 97 return s.Wait(ctx, s.services.WaitChannel()) 98 } 99 100 func (rc *remoteCluster) Status() *models.RemoteCluster { 101 status := rc.status() 102 103 status.NumSharedServices = int64(rc.remoteServices.NumEntries()) 104 105 status.Synced = &models.RemoteClusterSynced{ 106 Services: rc.remoteServices.Synced(), 107 // The operator does not watch nodes, endpoints and identities, hence 108 // let's pretend them to be synchronized by default. 109 Nodes: true, 110 Endpoints: true, 111 Identities: true, 112 } 113 114 status.Ready = status.Ready && 115 status.Synced.Nodes && status.Synced.Services && 116 status.Synced.Identities && status.Synced.Endpoints 117 118 return status 119 }