github.com/fafucoder/cilium@v1.6.11/pkg/clustermesh/clustermesh.go (about) 1 // Copyright 2018 Authors of Cilium 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package clustermesh 16 17 import ( 18 "fmt" 19 20 "github.com/cilium/cilium/pkg/allocator" 21 "github.com/cilium/cilium/pkg/controller" 22 "github.com/cilium/cilium/pkg/kvstore" 23 "github.com/cilium/cilium/pkg/kvstore/store" 24 "github.com/cilium/cilium/pkg/lock" 25 nodemanager "github.com/cilium/cilium/pkg/node/manager" 26 nodeStore "github.com/cilium/cilium/pkg/node/store" 27 "github.com/cilium/cilium/pkg/option" 28 ) 29 30 const ( 31 // configNotificationsChannelSize is the size of the channel used to 32 // notify a clustermesh of configuration changes 33 configNotificationsChannelSize = 512 34 ) 35 36 // Configuration is the configuration that must be provided to 37 // NewClusterMesh() 38 type Configuration struct { 39 // Name is the name of the remote cluster cache. This is for logging 40 // purposes only 41 Name string 42 43 // ConfigDirectory is the path to the directory that will be watched for etcd 44 // configuration files to appear 45 ConfigDirectory string 46 47 // NodeKeyCreator is the function used to create node instances as 48 // nodes are being discovered in remote clusters 49 NodeKeyCreator store.KeyCreator 50 51 // ServiceMerger is the interface responsible to merge service and 52 // endpoints into an existing cache 53 ServiceMerger ServiceMerger 54 55 // NodeManager is the node manager to manage all discovered remote 56 // nodes 57 NodeManager *nodemanager.Manager 58 59 nodeObserver store.Observer 60 61 // RemoteIdentityWatcher provides identities that have been allocated on a 62 // remote cluster. 63 RemoteIdentityWatcher RemoteIdentityWatcher 64 } 65 66 // RemoteIdentityWatcher is any type which provides identities that have been 67 // allocated on a remote cluster. 68 type RemoteIdentityWatcher interface { 69 // WatchRemoteIdentities starts watching for identities in another kvstore and 70 // syncs all identities to the local identity cache. 71 WatchRemoteIdentities(backend kvstore.BackendOperations) (*allocator.RemoteCache, error) 72 73 // Close stops the watcher. 74 Close() 75 } 76 77 // NodeObserver returns the node store observer of the configuration 78 func (c *Configuration) NodeObserver() store.Observer { 79 if c.nodeObserver != nil { 80 return c.nodeObserver 81 } 82 83 return nodeStore.NewNodeObserver(c.NodeManager) 84 } 85 86 // ClusterMesh is a cache of multiple remote clusters 87 type ClusterMesh struct { 88 // conf is the configuration, it is immutable after NewClusterMesh() 89 conf Configuration 90 91 mutex lock.RWMutex 92 clusters map[string]*remoteCluster 93 controllers *controller.Manager 94 configWatcher *configDirectoryWatcher 95 96 // globalServices is a list of all global services. The datastructure 97 // is protected by its own mutex inside of the structure. 98 globalServices *globalServiceCache 99 } 100 101 // NewClusterMesh creates a new remote cluster cache based on the 102 // provided configuration 103 func NewClusterMesh(c Configuration) (*ClusterMesh, error) { 104 cm := &ClusterMesh{ 105 conf: c, 106 clusters: map[string]*remoteCluster{}, 107 controllers: controller.NewManager(), 108 globalServices: newGlobalServiceCache(), 109 } 110 111 w, err := createConfigDirectoryWatcher(c.ConfigDirectory, cm) 112 if err != nil { 113 return nil, fmt.Errorf("unable to create config directory watcher: %s", err) 114 } 115 116 cm.configWatcher = w 117 118 if err := cm.configWatcher.watch(); err != nil { 119 return nil, err 120 } 121 122 return cm, nil 123 } 124 125 // Close stops watching for remote cluster configuration files to appear and 126 // will close all connections to remote clusters 127 func (cm *ClusterMesh) Close() { 128 cm.mutex.Lock() 129 defer cm.mutex.Unlock() 130 131 if cm.configWatcher != nil { 132 cm.configWatcher.close() 133 } 134 135 for name, cluster := range cm.clusters { 136 cluster.onRemove() 137 delete(cm.clusters, name) 138 } 139 140 cm.controllers.RemoveAllAndWait() 141 } 142 143 func (cm *ClusterMesh) newRemoteCluster(name, path string) *remoteCluster { 144 return &remoteCluster{ 145 name: name, 146 configPath: path, 147 mesh: cm, 148 changed: make(chan bool, configNotificationsChannelSize), 149 controllers: controller.NewManager(), 150 } 151 } 152 153 func (cm *ClusterMesh) add(name, path string) { 154 if name == option.Config.ClusterName { 155 log.WithField(fieldClusterName, name).Debug("Ignoring configuration for own cluster") 156 return 157 } 158 159 inserted := false 160 cm.mutex.Lock() 161 cluster, ok := cm.clusters[name] 162 if !ok { 163 cluster = cm.newRemoteCluster(name, path) 164 cm.clusters[name] = cluster 165 inserted = true 166 } 167 cm.mutex.Unlock() 168 169 log.WithField(fieldClusterName, name).Debug("Remote cluster configuration added") 170 171 if inserted { 172 cluster.onInsert() 173 } else { 174 // signal a change in configuration 175 cluster.changed <- true 176 } 177 } 178 179 func (cm *ClusterMesh) remove(name string) { 180 cm.mutex.Lock() 181 if cluster, ok := cm.clusters[name]; ok { 182 cluster.onRemove() 183 delete(cm.clusters, name) 184 185 cm.globalServices.onClusterDelete(name) 186 } 187 cm.mutex.Unlock() 188 189 log.WithField(fieldClusterName, name).Debug("Remote cluster configuration removed") 190 } 191 192 // NumReadyClusters returns the number of remote clusters to which a connection 193 // has been established 194 func (cm *ClusterMesh) NumReadyClusters() int { 195 cm.mutex.RLock() 196 defer cm.mutex.RUnlock() 197 198 nready := 0 199 for _, cm := range cm.clusters { 200 if cm.isReady() { 201 nready++ 202 } 203 } 204 205 return nready 206 }