github.com/kyma-project/kyma-environment-broker@v0.0.1/cmd/subscriptioncleanup/job/cleaner.go (about)

     1  package job
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/kyma-project/kyma-environment-broker/cmd/subscriptioncleanup/cloudprovider"
     8  	"github.com/kyma-project/kyma-environment-broker/cmd/subscriptioncleanup/model"
     9  	"github.com/kyma-project/kyma-environment-broker/common/gardener"
    10  	"github.com/sirupsen/logrus"
    11  	apiv1 "k8s.io/api/core/v1"
    12  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    13  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    14  	"k8s.io/client-go/dynamic"
    15  	"k8s.io/client-go/kubernetes"
    16  )
    17  
    18  type Type string
    19  
    20  type Cleaner interface {
    21  	Do() error
    22  }
    23  
    24  func NewCleaner(context context.Context,
    25  	kubernetesInterface kubernetes.Interface,
    26  	secretBindingsClient dynamic.ResourceInterface,
    27  	shootClient dynamic.ResourceInterface,
    28  	providerFactory cloudprovider.ProviderFactory) Cleaner {
    29  
    30  	return &cleaner{
    31  		kubernetesInterface:  kubernetesInterface,
    32  		secretBindingsClient: secretBindingsClient,
    33  		providerFactory:      providerFactory,
    34  		shootClient:          shootClient,
    35  		context:              context,
    36  	}
    37  }
    38  
    39  type cleaner struct {
    40  	kubernetesInterface  kubernetes.Interface
    41  	secretBindingsClient dynamic.ResourceInterface
    42  	providerFactory      cloudprovider.ProviderFactory
    43  	shootClient          dynamic.ResourceInterface
    44  	context              context.Context
    45  }
    46  
    47  func (p *cleaner) Do() error {
    48  	logrus.Info("Started releasing resources")
    49  	secretBindings, err := p.getSecretBindingsToRelease()
    50  	if err != nil {
    51  		return err
    52  	}
    53  	for _, secretBinding := range secretBindings {
    54  		canRelease, err := p.checkIfSecretCanBeReleased(secretBinding)
    55  		if err != nil {
    56  			logrus.Errorf("Failed to list shoots for '%s' secret binding: %s", secretBinding.GetName(), err.Error())
    57  			continue
    58  		}
    59  
    60  		if !canRelease {
    61  			logrus.Warnf("Cannot release secret binding: %s. Still in use", secretBinding.GetName())
    62  			continue
    63  		}
    64  
    65  		err = p.releaseResources(secretBinding)
    66  		if err != nil {
    67  			logrus.Errorf("Failed to release resources for '%s' secret binding: %s", secretBinding.GetName(), err.Error())
    68  			continue
    69  		}
    70  		err = p.returnSecretBindingToThePool(secretBinding)
    71  		if err != nil {
    72  			logrus.Errorf("Failed returning '%s' secret binding to the pool: %s", secretBinding.GetName(), err.Error())
    73  			continue
    74  		}
    75  		logrus.Infof("Resources released for '%s' secret binding", secretBinding.GetName())
    76  	}
    77  
    78  	logrus.Info("Finished releasing resources")
    79  	return nil
    80  }
    81  
    82  func (p *cleaner) releaseResources(secretBinding unstructured.Unstructured) error {
    83  	hyperscalerType, err := model.NewHyperscalerType(secretBinding.GetLabels()["hyperscalerType"])
    84  	if err != nil {
    85  		return fmt.Errorf("starting releasing resources: %w", err)
    86  	}
    87  
    88  	secret, err := p.getBoundSecret(secretBinding)
    89  	if err != nil {
    90  		return fmt.Errorf("getting referenced secret: %w", err)
    91  	}
    92  
    93  	cleaner, err := p.providerFactory.New(hyperscalerType, secret.Data)
    94  	if err != nil {
    95  		return fmt.Errorf("initializing cloud provider cleaner: %w", err)
    96  	}
    97  
    98  	return cleaner.Do()
    99  }
   100  
   101  func (p *cleaner) getBoundSecret(sb unstructured.Unstructured) (*apiv1.Secret, error) {
   102  	secretBinding := gardener.SecretBinding{sb}
   103  	secret, err := p.kubernetesInterface.CoreV1().
   104  		Secrets(secretBinding.GetSecretRefNamespace()).
   105  		Get(p.context, secretBinding.GetSecretRefName(), metav1.GetOptions{})
   106  	if err != nil {
   107  		return nil, fmt.Errorf("getting %s/%s secret: %w", secretBinding.GetSecretRefNamespace(),
   108  			secretBinding.GetSecretRefName(), err)
   109  	}
   110  	return secret, nil
   111  }
   112  
   113  func (p *cleaner) returnSecretBindingToThePool(secretBinding unstructured.Unstructured) error {
   114  	sb, err := p.secretBindingsClient.Get(p.context, secretBinding.GetName(), metav1.GetOptions{})
   115  	if err != nil {
   116  		return err
   117  	}
   118  
   119  	l := sb.GetLabels()
   120  	delete(l, "dirty")
   121  	delete(l, "tenantName")
   122  	sb.SetLabels(l)
   123  
   124  	_, err = p.secretBindingsClient.Update(p.context, sb, metav1.UpdateOptions{})
   125  	if err != nil {
   126  		return fmt.Errorf("failed to return secret binding to the hyperscaler account pool: %w", err)
   127  	}
   128  
   129  	return nil
   130  }
   131  
   132  func (p *cleaner) getSecretBindingsToRelease() ([]unstructured.Unstructured, error) {
   133  	labelSelector := fmt.Sprintf("dirty=true")
   134  
   135  	return getSecretBindings(p.context, p.secretBindingsClient, labelSelector)
   136  }
   137  
   138  // Checks if there are no clusters tied to the secret binding
   139  func (p *cleaner) checkIfSecretCanBeReleased(binding unstructured.Unstructured) (bool, error) {
   140  	list, err := p.shootClient.List(p.context, metav1.ListOptions{})
   141  	if err != nil {
   142  		return false, fmt.Errorf("failed to list shoots: %w", err)
   143  	}
   144  
   145  	for _, sh := range list.Items {
   146  		shoot := gardener.Shoot{sh}
   147  		if shoot.GetSpecSecretBindingName() == binding.GetName() {
   148  			return false, nil
   149  		}
   150  	}
   151  
   152  	return true, nil
   153  }
   154  
   155  func getSecretBindings(ctx context.Context, secretBindingsClient dynamic.ResourceInterface, labelSelector string) ([]unstructured.Unstructured, error) {
   156  	secrets, err := secretBindingsClient.List(ctx, metav1.ListOptions{
   157  		LabelSelector: labelSelector,
   158  	})
   159  	if err != nil {
   160  		return nil, fmt.Errorf("listing secrets bindings for LabelSelector: %s: %w", labelSelector, err)
   161  	}
   162  
   163  	return secrets.Items, nil
   164  }