istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/kube/multicluster/component.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 "sync" 19 20 "istio.io/istio/pkg/cluster" 21 "istio.io/istio/pkg/kube/controllers" 22 "istio.io/istio/pkg/kube/kclient" 23 "istio.io/istio/pkg/kube/kubetypes" 24 "istio.io/istio/pkg/maps" 25 "istio.io/istio/pkg/slices" 26 ) 27 28 type ComponentConstraint interface { 29 Close() 30 HasSynced() bool 31 } 32 33 type Component[T ComponentConstraint] struct { 34 mu sync.RWMutex 35 constructor func(cluster *Cluster) T 36 clusters map[cluster.ID]T 37 } 38 39 func (m *Component[T]) ForCluster(clusterID cluster.ID) *T { 40 m.mu.RLock() 41 defer m.mu.RUnlock() 42 t, f := m.clusters[clusterID] 43 if !f { 44 return nil 45 } 46 return &t 47 } 48 49 func (m *Component[T]) All() []T { 50 m.mu.RLock() 51 defer m.mu.RUnlock() 52 return maps.Values(m.clusters) 53 } 54 55 func (m *Component[T]) clusterAdded(cluster *Cluster) ComponentConstraint { 56 comp := m.constructor(cluster) 57 m.mu.Lock() 58 defer m.mu.Unlock() 59 m.clusters[cluster.ID] = comp 60 return comp 61 } 62 63 func (m *Component[T]) clusterUpdated(cluster *Cluster) ComponentConstraint { 64 // Build outside of the lock, in case its slow 65 comp := m.constructor(cluster) 66 old, f := m.clusters[cluster.ID] 67 m.mu.Lock() 68 m.clusters[cluster.ID] = comp 69 m.mu.Unlock() 70 // Close outside of the lock, in case its slow 71 if f { 72 old.Close() 73 } 74 return comp 75 } 76 77 func (m *Component[T]) clusterDeleted(cluster cluster.ID) { 78 m.mu.Lock() 79 defer m.mu.Unlock() 80 // If there is an old one, close it 81 if old, f := m.clusters[cluster]; f { 82 old.Close() 83 } 84 delete(m.clusters, cluster) 85 } 86 87 func (m *Component[T]) HasSynced() bool { 88 for _, c := range m.All() { 89 if !c.HasSynced() { 90 return false 91 } 92 } 93 return true 94 } 95 96 type KclientComponent[T controllers.ComparableObject] struct { 97 internal *Component[kclientInternalComponent[T]] 98 } 99 100 type kclientInternalComponent[T controllers.ComparableObject] struct { 101 client kclient.Client[T] 102 } 103 104 func (k kclientInternalComponent[T]) Close() { 105 k.client.ShutdownHandlers() 106 } 107 108 func (k kclientInternalComponent[T]) HasSynced() bool { 109 return k.client.HasSynced() 110 } 111 112 // BuildMultiClusterKclientComponent builds a simple Component that just wraps a single kclient.Client 113 func BuildMultiClusterKclientComponent[T controllers.ComparableObject](c ComponentBuilder, filter kubetypes.Filter) *KclientComponent[T] { 114 res := BuildMultiClusterComponent[kclientInternalComponent[T]](c, func(cluster *Cluster) kclientInternalComponent[T] { 115 return kclientInternalComponent[T]{kclient.NewFiltered[T](cluster.Client, filter)} 116 }) 117 return &KclientComponent[T]{res} 118 } 119 120 // ForCluster returns the client for the requests cluster 121 // Note: this may return nil. 122 func (m *KclientComponent[T]) ForCluster(clusterID cluster.ID) kclient.Client[T] { 123 c := m.internal.ForCluster(clusterID) 124 if c == nil { 125 return nil 126 } 127 return c.client 128 } 129 130 func (m *KclientComponent[T]) All() []kclient.Client[T] { 131 return slices.Map(m.internal.All(), func(e kclientInternalComponent[T]) kclient.Client[T] { 132 return e.client 133 }) 134 }