istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/kube/multicluster/cluster.go (about) 1 // Copyright Istio Authors 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 multicluster 16 17 import ( 18 "crypto/sha256" 19 "time" 20 21 "go.uber.org/atomic" 22 corev1 "k8s.io/api/core/v1" 23 24 "istio.io/istio/pilot/pkg/features" 25 "istio.io/istio/pkg/cluster" 26 "istio.io/istio/pkg/config/mesh" 27 "istio.io/istio/pkg/kube" 28 "istio.io/istio/pkg/kube/kclient" 29 filter "istio.io/istio/pkg/kube/namespace" 30 "istio.io/istio/pkg/log" 31 ) 32 33 // Cluster defines cluster struct 34 type Cluster struct { 35 // ID of the cluster. 36 ID cluster.ID 37 // Client for accessing the cluster. 38 Client kube.Client 39 40 kubeConfigSha [sha256.Size]byte 41 42 stop chan struct{} 43 // initialSync is marked when RunAndWait completes 44 initialSync *atomic.Bool 45 // initialSyncTimeout is set when RunAndWait timed out 46 initialSyncTimeout *atomic.Bool 47 } 48 49 type ACTION int 50 51 const ( 52 Add ACTION = iota 53 Update 54 ) 55 56 func (a ACTION) String() string { 57 switch a { 58 case Add: 59 return "Add" 60 case Update: 61 return "Update" 62 } 63 return "Unknown" 64 } 65 66 // Run starts the cluster's informers and waits for caches to sync. Once caches are synced, we mark the cluster synced. 67 // This should be called after each of the handlers have registered informers, and should be run in a goroutine. 68 func (c *Cluster) Run(mesh mesh.Watcher, handlers []handler, action ACTION) { 69 if features.RemoteClusterTimeout > 0 { 70 time.AfterFunc(features.RemoteClusterTimeout, func() { 71 if !c.initialSync.Load() { 72 log.Errorf("remote cluster %s failed to sync after %v", c.ID, features.RemoteClusterTimeout) 73 timeouts.With(clusterLabel.Value(string(c.ID))).Increment() 74 } 75 c.initialSyncTimeout.Store(true) 76 }) 77 } 78 79 // Build a namespace watcher. This must have no filter, since this is our input to the filter itself. 80 // This must be done before we build components, so they can access the filter. 81 namespaces := kclient.New[*corev1.Namespace](c.Client) 82 // This will start a namespace informer and wait for it to be ready. So we must start it in a go routine to avoid blocking. 83 filter := filter.NewDiscoveryNamespacesFilter(namespaces, mesh, c.stop) 84 kube.SetObjectFilter(c.Client, filter) 85 86 syncers := make([]ComponentConstraint, 0, len(handlers)) 87 for _, h := range handlers { 88 switch action { 89 case Add: 90 syncers = append(syncers, h.clusterAdded(c)) 91 case Update: 92 syncers = append(syncers, h.clusterUpdated(c)) 93 } 94 } 95 if !c.Client.RunAndWait(c.stop) { 96 log.Warnf("remote cluster %s failed to sync", c.ID) 97 return 98 } 99 for _, h := range syncers { 100 if !kube.WaitForCacheSync("cluster"+string(c.ID), c.stop, h.HasSynced) { 101 log.Warnf("remote cluster %s failed to sync handler", c.ID) 102 return 103 } 104 } 105 106 c.initialSync.Store(true) 107 } 108 109 // Stop closes the stop channel, if is safe to be called multi times. 110 func (c *Cluster) Stop() { 111 select { 112 case <-c.stop: 113 return 114 default: 115 close(c.stop) 116 } 117 } 118 119 func (c *Cluster) HasSynced() bool { 120 // It could happen when a wrong credential provide, this cluster has no chance to run. 121 // In this case, the `initialSyncTimeout` will never be set 122 // In order not block istiod start up, check close as well. 123 if c.Closed() { 124 return true 125 } 126 return c.initialSync.Load() || c.initialSyncTimeout.Load() 127 } 128 129 func (c *Cluster) Closed() bool { 130 select { 131 case <-c.stop: 132 return true 133 default: 134 return false 135 } 136 } 137 138 func (c *Cluster) SyncDidTimeout() bool { 139 return !c.initialSync.Load() && c.initialSyncTimeout.Load() 140 }