github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/caas/kubernetes/provider/teardown.go (about) 1 // Copyright 2020 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package provider 5 6 import ( 7 "context" 8 "sync" 9 "time" 10 11 jujuclock "github.com/juju/clock" 12 "github.com/juju/errors" 13 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 14 k8slabels "k8s.io/apimachinery/pkg/labels" 15 16 "github.com/juju/juju/caas/kubernetes/provider/utils" 17 "github.com/juju/juju/core/watcher" 18 ) 19 20 func (k *kubernetesClient) deleteClusterScopeResourcesModelTeardown(ctx context.Context, wg *sync.WaitGroup, errChan chan<- error) { 21 defer wg.Done() 22 23 labels := utils.LabelsForModel(k.CurrentModel(), k.IsLegacyLabels()) 24 selector := k8slabels.NewSelector().Add( 25 labelSetToRequirements(labels)..., 26 ) 27 28 // TODO(caas): Fix to only delete cluster wide resources created by this controller. 29 tasks := []teardownResources{ 30 k.deleteClusterRoleBindingsModelTeardown, 31 k.deleteClusterRolesModelTeardown, 32 k.deleteClusterScopeAPIExtensionResourcesModelTeardown, 33 k.deleteMutatingWebhookConfigurationsModelTeardown, 34 k.deleteValidatingWebhookConfigurationsModelTeardown, 35 k.deleteStorageClassesModelTeardown, 36 } 37 var subwg sync.WaitGroup 38 subwg.Add(len(tasks)) 39 defer subwg.Wait() 40 41 for _, f := range tasks { 42 go f(ctx, selector, k.clock, &subwg, errChan) 43 } 44 } 45 46 type teardownResources func( 47 context.Context, 48 k8slabels.Selector, 49 jujuclock.Clock, 50 *sync.WaitGroup, 51 chan<- error, 52 ) 53 54 func (k *kubernetesClient) deleteClusterRoleBindingsModelTeardown( 55 ctx context.Context, 56 selector k8slabels.Selector, 57 clk jujuclock.Clock, 58 wg *sync.WaitGroup, 59 errChan chan<- error, 60 ) { 61 ensureResourcesDeletedFunc(ctx, selector, clk, wg, errChan, 62 k.deleteClusterRoleBindings, func(selector k8slabels.Selector) error { 63 _, err := k.listClusterRoleBindings(selector) 64 return err 65 }, 66 ) 67 } 68 69 func (k *kubernetesClient) deleteClusterRolesModelTeardown( 70 ctx context.Context, 71 selector k8slabels.Selector, 72 clk jujuclock.Clock, 73 wg *sync.WaitGroup, 74 errChan chan<- error, 75 ) { 76 ensureResourcesDeletedFunc(ctx, selector, clk, wg, errChan, 77 k.deleteClusterRoles, func(selector k8slabels.Selector) error { 78 _, err := k.listClusterRoles(selector) 79 return err 80 }, 81 ) 82 } 83 84 func (k *kubernetesClient) deleteClusterScopeAPIExtensionResourcesModelTeardown( 85 ctx context.Context, 86 selector k8slabels.Selector, 87 clk jujuclock.Clock, 88 wg *sync.WaitGroup, 89 errChan chan<- error, 90 ) { 91 defer wg.Done() 92 93 var subwg sync.WaitGroup 94 subwg.Add(2) 95 defer subwg.Wait() 96 97 selector = mergeSelectors(selector, lifecycleModelTeardownSelector) 98 // Delete CRs first then CRDs. 99 k.deleteClusterScopeCustomResourcesModelTeardown(ctx, selector, clk, &subwg, errChan) 100 k.deleteCustomResourceDefinitionsModelTeardown(ctx, selector, clk, &subwg, errChan) 101 } 102 103 func (k *kubernetesClient) deleteClusterScopeCustomResourcesModelTeardown( 104 ctx context.Context, 105 selector k8slabels.Selector, 106 clk jujuclock.Clock, 107 wg *sync.WaitGroup, 108 errChan chan<- error, 109 ) { 110 getSelector := func(crd apiextensionsv1.CustomResourceDefinition) k8slabels.Selector { 111 if !isCRDScopeNamespaced(crd.Spec.Scope) { 112 // We only delete cluster scope CRs here, namespaced CRs are deleted by namespace destroy process. 113 return selector 114 } 115 return k8slabels.NewSelector() 116 } 117 ensureResourcesDeletedFunc(ctx, selector, clk, wg, errChan, 118 func(_ k8slabels.Selector) error { 119 return k.deleteCustomResources(getSelector) 120 }, 121 func(_ k8slabels.Selector) error { 122 _, err := k.listCustomResources(getSelector) 123 return err 124 }, 125 ) 126 } 127 128 func (k *kubernetesClient) deleteCustomResourceDefinitionsModelTeardown( 129 ctx context.Context, 130 selector k8slabels.Selector, 131 clk jujuclock.Clock, 132 wg *sync.WaitGroup, 133 errChan chan<- error, 134 ) { 135 ensureResourcesDeletedFunc(ctx, selector, clk, wg, errChan, 136 k.deleteCustomResourceDefinitions, func(selector k8slabels.Selector) error { 137 _, err := k.listCustomResourceDefinitions(selector) 138 return err 139 }, 140 ) 141 } 142 143 func (k *kubernetesClient) deleteMutatingWebhookConfigurationsModelTeardown( 144 ctx context.Context, 145 selector k8slabels.Selector, 146 clk jujuclock.Clock, 147 wg *sync.WaitGroup, 148 errChan chan<- error, 149 ) { 150 ensureResourcesDeletedFunc(ctx, selector, clk, wg, errChan, 151 k.deleteMutatingWebhookConfigurations, func(selector k8slabels.Selector) error { 152 _, err := k.listMutatingWebhookConfigurations(selector) 153 return err 154 }, 155 ) 156 } 157 158 func (k *kubernetesClient) deleteValidatingWebhookConfigurationsModelTeardown( 159 ctx context.Context, 160 selector k8slabels.Selector, 161 clk jujuclock.Clock, 162 wg *sync.WaitGroup, 163 errChan chan<- error, 164 ) { 165 ensureResourcesDeletedFunc(ctx, selector, clk, wg, errChan, 166 k.deleteValidatingWebhookConfigurations, func(selector k8slabels.Selector) error { 167 _, err := k.listValidatingWebhookConfigurations(selector) 168 return err 169 }, 170 ) 171 } 172 func (k *kubernetesClient) deleteStorageClassesModelTeardown( 173 ctx context.Context, 174 selector k8slabels.Selector, 175 clk jujuclock.Clock, 176 wg *sync.WaitGroup, 177 errChan chan<- error, 178 ) { 179 ensureResourcesDeletedFunc(ctx, selector, clk, wg, errChan, 180 k.deleteStorageClasses, func(selector k8slabels.Selector) error { 181 _, err := k.ListStorageClasses(selector) 182 return err 183 }, 184 ) 185 } 186 187 type deleterChecker func(k8slabels.Selector) error 188 189 func ensureResourcesDeletedFunc( 190 ctx context.Context, 191 selector k8slabels.Selector, 192 clk jujuclock.Clock, 193 wg *sync.WaitGroup, 194 errChan chan<- error, 195 deleter, checker deleterChecker, 196 ) { 197 defer wg.Done() 198 199 var err error 200 defer func() { 201 if err != nil { 202 select { 203 case errChan <- err: 204 default: 205 } 206 } 207 }() 208 209 if err = deleter(selector); err != nil { 210 if errors.IsNotFound(err) { 211 err = nil 212 } 213 return 214 } 215 216 interval := 1 * time.Second 217 ticker := clk.NewTimer(interval) 218 defer ticker.Stop() 219 for { 220 select { 221 case <-ctx.Done(): 222 err = errors.Trace(ctx.Err()) 223 return 224 case <-ticker.Chan(): 225 err = checker(selector) 226 if errors.IsNotFound(err) { 227 // Deleted already. 228 err = nil 229 return 230 } 231 if err != nil { 232 err = errors.Trace(err) 233 return 234 } 235 } 236 // Keep checking. 237 ticker.Reset(interval) 238 } 239 } 240 241 func (k *kubernetesClient) deleteNamespaceModelTeardown(ctx context.Context, wg *sync.WaitGroup, errChan chan<- error) { 242 defer wg.Done() 243 244 var err error 245 defer func() { 246 if err != nil { 247 select { 248 case errChan <- err: 249 default: 250 } 251 } 252 }() 253 254 var w watcher.NotifyWatcher 255 if w, err = k.WatchNamespace(); err != nil { 256 err = errors.Annotatef(err, "watching namespace %q", k.namespace) 257 return 258 } 259 defer w.Kill() 260 261 if err = k.deleteNamespace(); err != nil { 262 err = errors.Annotatef(err, "deleting model namespace %q", k.namespace) 263 return 264 } 265 for { 266 select { 267 case <-ctx.Done(): 268 err = errors.Annotatef(ctx.Err(), "tearing down namespace %q", k.namespace) 269 return 270 case <-w.Changes(): 271 // Ensures the namespace to be deleted - notfound error expected. 272 _, err = k.GetNamespace(k.namespace) 273 if errors.IsNotFound(err) { 274 // Namespace has been deleted. 275 err = nil 276 return 277 } 278 if err != nil { 279 err = errors.Trace(err) 280 return 281 } 282 logger.Debugf("namespace %q is still been terminating", k.namespace) 283 } 284 } 285 }