github.com/banzaicloud/operator-tools@v0.28.10/pkg/wait/wait.go (about) 1 // Copyright © 2020 Banzai Cloud 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 wait 16 17 import ( 18 "context" 19 "fmt" 20 "strings" 21 22 "emperror.dev/errors" 23 "github.com/go-logr/logr" 24 "k8s.io/apimachinery/pkg/api/meta" 25 "k8s.io/apimachinery/pkg/runtime" 26 "k8s.io/apimachinery/pkg/runtime/schema" 27 "k8s.io/apimachinery/pkg/util/wait" 28 "sigs.k8s.io/controller-runtime/pkg/client" 29 "sigs.k8s.io/controller-runtime/pkg/client/apiutil" 30 ) 31 32 type Backoff = wait.Backoff 33 34 type ResourceConditionChecks struct { 35 client client.Client 36 backoff wait.Backoff 37 log logr.Logger 38 scheme *runtime.Scheme 39 } 40 41 func NewResourceConditionChecks(client client.Client, backoff Backoff, log logr.Logger, scheme *runtime.Scheme) *ResourceConditionChecks { 42 return &ResourceConditionChecks{ 43 client: client, 44 backoff: wait.Backoff(backoff), 45 log: log, 46 scheme: scheme, 47 } 48 } 49 50 func (c *ResourceConditionChecks) WaitForCustomConditionChecks(id string, checkFuncs ...CustomResourceConditionCheck) error { 51 log := c.log.WithName(id) 52 53 sink := log.GetSink() 54 if l, ok := sink.(interface{ Grouped(state bool) }); ok { 55 l.Grouped(true) 56 defer l.Grouped(false) 57 log = log.WithSink(sink) 58 } 59 60 log.Info("waiting") 61 62 err := wait.ExponentialBackoff(c.backoff, func() (bool, error) { 63 for _, fn := range checkFuncs { 64 if ok, err := fn(); !ok { 65 if err != nil { 66 return false, err 67 } 68 return false, nil 69 } 70 } 71 72 return true, nil 73 }) 74 if err != nil { 75 return err 76 } 77 78 log.Info("done") 79 80 return nil 81 } 82 83 func (c *ResourceConditionChecks) WaitForResources(id string, objects []runtime.Object, checkFuncs ...ResourceConditionCheck) error { 84 if len(objects) == 0 || len(checkFuncs) == 0 { 85 return nil 86 } 87 88 log := c.log.WithName(id) 89 90 sink := log.GetSink() 91 if l, ok := sink.(interface{ Grouped(state bool) }); ok { 92 l.Grouped(true) 93 defer l.Grouped(false) 94 log = log.WithSink(sink) 95 } 96 97 log.Info("waiting") 98 99 for _, o := range objects { 100 err := c.waitForResourceConditions(o, log, checkFuncs...) 101 if err != nil { 102 return err 103 } 104 } 105 106 log.Info("done") 107 108 return nil 109 } 110 111 func (c *ResourceConditionChecks) waitForResourceConditions(object runtime.Object, log logr.Logger, checkFuncs ...ResourceConditionCheck) error { 112 resource := object.DeepCopyObject() 113 114 m, err := meta.Accessor(resource) 115 if err != nil { 116 return errors.WrapIf(err, "failed to get object key") 117 } 118 key := client.ObjectKey{Namespace: m.GetNamespace(), Name: m.GetName()} 119 log = log.WithValues(c.resourceDetails(resource)...) 120 121 log.V(1).Info("pending") 122 err = wait.ExponentialBackoff(c.backoff, func() (bool, error) { 123 err := c.client.Get(context.Background(), key, resource.(client.Object)) 124 for _, fn := range checkFuncs { 125 ok := fn(resource, err) 126 if !ok { 127 if err != nil { 128 c.log.V(2).Info("still waiting", "error", err) 129 } 130 return false, nil 131 } 132 } 133 134 return true, nil 135 }) 136 137 if err != nil { 138 return err 139 } 140 141 log.V(1).Info("ok") 142 143 return nil 144 } 145 146 func (r *ResourceConditionChecks) resourceDetails(desired runtime.Object) (values []interface{}) { 147 m, err := meta.Accessor(desired) 148 key := client.ObjectKey{Namespace: m.GetNamespace(), Name: m.GetName()} 149 if err == nil { 150 values = append(values, "name", key.Name) 151 if key.Namespace != "" { 152 values = append(values, "namespace", key.Namespace) 153 } 154 } 155 156 gvk := desired.GetObjectKind().GroupVersionKind() 157 if gvk.Kind == "" { 158 gvko, err := apiutil.GVKForObject(desired, r.scheme) 159 if err == nil { 160 gvk = gvko 161 } 162 } 163 164 values = append(values, 165 "apiVersion", gvk.GroupVersion().String(), 166 "kind", gvk.Kind) 167 168 return 169 } 170 171 func GetFormattedName(name, namespace string, gvk schema.GroupVersionKind) string { 172 var group string 173 if gvk.Group != "" { 174 group = "." + gvk.Group 175 } 176 177 if namespace != "" { 178 namespace += "/" 179 } 180 return fmt.Sprintf("%s%s:%s%s", strings.ToLower(gvk.Kind), group, namespace, name) 181 }