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  }