istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/kube/krt/helpers.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 krt 16 17 import ( 18 "fmt" 19 "reflect" 20 "strings" 21 "time" 22 23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 acmetav1 "k8s.io/client-go/applyconfigurations/meta/v1" 25 "k8s.io/client-go/tools/cache" 26 27 "istio.io/istio/pkg/kube/controllers" 28 "istio.io/istio/pkg/ptr" 29 ) 30 31 // GetKey returns the key for the provided object. 32 // If there is none, this will panic. 33 func GetKey[O any](a O) Key[O] { 34 if k, ok := tryGetKey[O](a); ok { 35 return k 36 } 37 38 // Kubernetes types are pointers, which means our types would be double pointers 39 // Allow flattening 40 ao, ok := any(&a).(controllers.Object) 41 if ok { 42 k, _ := cache.MetaNamespaceKeyFunc(ao) 43 return Key[O](k) 44 } 45 panic(fmt.Sprintf("Cannot get Key, got %T", a)) 46 } 47 48 // Named is a convenience struct. It is ideal to be embedded into a type that has a name and namespace, 49 // and will automatically implement the various interfaces to return the name, namespace, and a key based on these two. 50 type Named struct { 51 Name, Namespace string 52 } 53 54 // NewNamed builds a Named object from a Kubernetes object type. 55 func NewNamed(o metav1.Object) Named { 56 return Named{Name: o.GetName(), Namespace: o.GetNamespace()} 57 } 58 59 func (n Named) ResourceName() string { 60 return n.Namespace + "/" + n.Name 61 } 62 63 func (n Named) GetName() string { 64 return n.Name 65 } 66 67 func (n Named) GetNamespace() string { 68 return n.Namespace 69 } 70 71 // GetApplyConfigKey returns the key for the ApplyConfig. 72 // If there is none, this will return nil. 73 func GetApplyConfigKey[O any](a O) *Key[O] { 74 // Reflection is expensive; short circuit here 75 if !strings.HasSuffix(ptr.TypeName[O](), "ApplyConfiguration") { 76 return nil 77 } 78 val := reflect.ValueOf(a) 79 80 if val.Kind() == reflect.Ptr { 81 val = val.Elem() 82 } 83 if val.Kind() != reflect.Struct { 84 return nil 85 } 86 87 specField := val.FieldByName("ObjectMetaApplyConfiguration") 88 if !specField.IsValid() { 89 return nil 90 } 91 meta := specField.Interface().(*acmetav1.ObjectMetaApplyConfiguration) 92 if meta.Namespace != nil && len(*meta.Namespace) > 0 { 93 return ptr.Of(Key[O](*meta.Namespace + "/" + *meta.Name)) 94 } 95 return ptr.Of(Key[O](*meta.Name)) 96 } 97 98 // keyFunc is the internal API key function that returns "namespace"/"name" or 99 // "name" if "namespace" is empty 100 func keyFunc(name, namespace string) string { 101 if len(namespace) == 0 { 102 return name 103 } 104 return namespace + "/" + name 105 } 106 107 func waitForCacheSync(name string, stop <-chan struct{}, collections ...<-chan struct{}) (r bool) { 108 t := time.NewTicker(time.Second * 5) 109 defer t.Stop() 110 t0 := time.Now() 111 defer func() { 112 if r { 113 log.WithLabels("name", name, "time", time.Since(t0)).Debugf("sync complete") 114 } else { 115 log.WithLabels("name", name, "time", time.Since(t0)).Errorf("sync failed") 116 } 117 }() 118 for _, col := range collections { 119 for { 120 select { 121 case <-t.C: 122 log.WithLabels("name", name, "time", time.Since(t0)).Debugf("waiting for sync...") 123 continue 124 case <-stop: 125 return false 126 case <-col: 127 } 128 break 129 } 130 } 131 return true 132 }