github.com/verrazzano/verrazzano@v1.7.0/pkg/k8s/resource/resource.go (about)

     1  // Copyright (c) 2022, 2023, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package resource
     5  
     6  import (
     7  	"context"
     8  	spiErrors "github.com/verrazzano/verrazzano/pkg/controller/errors"
     9  	"github.com/verrazzano/verrazzano/pkg/log/vzlog"
    10  	"github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/spi"
    11  	"k8s.io/apimachinery/pkg/api/errors"
    12  	"k8s.io/apimachinery/pkg/api/meta"
    13  	"k8s.io/apimachinery/pkg/types"
    14  	"reflect"
    15  	"sigs.k8s.io/controller-runtime/pkg/client"
    16  )
    17  
    18  type Resource struct {
    19  	Namespace string
    20  	Name      string
    21  	Client    client.Client
    22  	Object    client.Object
    23  	Log       vzlog.VerrazzanoLogger
    24  }
    25  
    26  // CleanupResources deletes and removes finalizers from resources
    27  func CleanupResources(ctx spi.ComponentContext, objects []client.Object) error {
    28  	for _, object := range objects {
    29  		err := Resource{
    30  			Name:      object.GetName(),
    31  			Namespace: object.GetNamespace(),
    32  			Client:    ctx.Client(),
    33  			Object:    object,
    34  			Log:       ctx.Log(),
    35  		}.RemoveFinalizersAndDelete()
    36  		if err != nil {
    37  			ctx.Log().ErrorfThrottled("Unexpected error cleaning up resource %s/%s still exists: %s", object.GetName(), object.GetNamespace(), err.Error())
    38  			return spiErrors.RetryableError{
    39  				Source:    ctx.GetComponent(),
    40  				Operation: ctx.GetOperation(),
    41  				Cause:     err,
    42  			}
    43  		}
    44  	}
    45  	return nil
    46  }
    47  
    48  // VerifyResourcesDeleted verifies resources have been fully cleaned up
    49  func VerifyResourcesDeleted(ctx spi.ComponentContext, objects []client.Object) error {
    50  	for _, object := range objects {
    51  		exists, err := Resource{
    52  			Name:      object.GetName(),
    53  			Namespace: object.GetNamespace(),
    54  			Client:    ctx.Client(),
    55  			Object:    object,
    56  			Log:       ctx.Log(),
    57  		}.Exists()
    58  		if err != nil {
    59  			ctx.Log().ErrorfThrottled("Unexpected error checking if resource %s/%s still exists: %s", object.GetName(), object.GetNamespace(), err.Error())
    60  			return spiErrors.RetryableError{
    61  				Source:    ctx.GetComponent(),
    62  				Operation: ctx.GetOperation(),
    63  				Cause:     err,
    64  			}
    65  		}
    66  		if exists {
    67  			return spiErrors.RetryableError{
    68  				Source: ctx.GetComponent(),
    69  			}
    70  		}
    71  		ctx.Log().Debugf("Verified that resource %s/%s has been successfully deleted", object.GetName(), object.GetNamespace())
    72  	}
    73  	return nil
    74  }
    75  
    76  // Delete deletes a resource if it exists, not found error is ignored
    77  func (r Resource) Delete() error {
    78  	r.Object.SetName(r.Name)
    79  	r.Object.SetNamespace(r.Namespace)
    80  	val := reflect.ValueOf(r.Object)
    81  	kind := val.Elem().Type().Name()
    82  	if kind == "Unstructured" {
    83  		kind = r.Object.GetObjectKind().GroupVersionKind().Kind
    84  	}
    85  	err := r.Client.Delete(context.TODO(), r.Object)
    86  	if err != nil {
    87  		// Ignore if CRD doesn't exist
    88  		if _, ok := err.(*meta.NoKindMatchError); ok {
    89  			return nil
    90  		}
    91  		if client.IgnoreNotFound(err) == nil {
    92  			return nil
    93  		}
    94  		return r.Log.ErrorfNewErr("Failed to delete the resource of type %s, named %s/%s: %v", kind, r.Object.GetNamespace(), r.Object.GetName(), err)
    95  	}
    96  	r.Log.Oncef("Successfully deleted %s %s/%s", kind, r.Object.GetNamespace(), r.Object.GetName())
    97  	return nil
    98  }
    99  
   100  // RemoveFinializersAndDelete removes all finalizers from a resource and deletes the resource
   101  func (r Resource) RemoveFinalizersAndDelete() error {
   102  	// always delete first, then remove finalizer to reduce the chance that a Rancher webhook
   103  	// will add it back (since the deletion timestamp will be non-zero)
   104  	err := r.Delete()
   105  	if err != nil {
   106  		return err
   107  	}
   108  	return r.RemoveFinalizers()
   109  }
   110  
   111  // RemoveFinalizers removes all finalizers from a resource
   112  func (r Resource) RemoveFinalizers() error {
   113  	val := reflect.ValueOf(r.Object)
   114  	kind := val.Elem().Type().Name()
   115  	if kind == "Unstructured" {
   116  		kind = r.Object.GetObjectKind().GroupVersionKind().Kind
   117  	}
   118  	err := r.Client.Get(context.TODO(), types.NamespacedName{Namespace: r.Namespace, Name: r.Name}, r.Object)
   119  	if client.IgnoreNotFound(err) != nil {
   120  		return r.Log.ErrorfNewErr("Failed to get the resource of type %s, named %s/%s: %v", kind, r.Object.GetNamespace(), r.Object.GetName(), err)
   121  	} else if err == nil {
   122  		if len(r.Object.GetFinalizers()) != 0 {
   123  			r.Object.SetFinalizers([]string{})
   124  			err = r.Client.Update(context.TODO(), r.Object)
   125  			if err != nil {
   126  				return r.Log.ErrorfNewErr("Failed to update the resource of type %s, named %s/%s: %v", kind, r.Object.GetNamespace(), r.Object.GetName(), err)
   127  			}
   128  		}
   129  	}
   130  	return nil
   131  }
   132  
   133  // Exists returns true if the Resource exists
   134  func (r Resource) Exists() (bool, error) {
   135  	err := r.Client.Get(context.TODO(), types.NamespacedName{Namespace: r.Namespace, Name: r.Name}, r.Object)
   136  	if err != nil {
   137  		if errors.IsNotFound(err) {
   138  			return false, nil
   139  		}
   140  		// Ignore if CRD doesn't exist
   141  		if _, ok := err.(*meta.NoKindMatchError); ok {
   142  			return false, nil
   143  		}
   144  		return false, err
   145  	}
   146  	return true, nil
   147  }