...

Source file src/github.com/redhat-developer/odo/pkg/kclient/dynamic.go

Documentation: github.com/redhat-developer/odo/pkg/kclient

     1  package kclient
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"time"
     9  
    10  	kerrors "k8s.io/apimachinery/pkg/api/errors"
    11  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    12  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    13  	"k8s.io/apimachinery/pkg/runtime/schema"
    14  	"k8s.io/apimachinery/pkg/types"
    15  	"k8s.io/apimachinery/pkg/watch"
    16  	"k8s.io/klog"
    17  )
    18  
    19  // PatchDynamicResource patches a dynamic custom resource and returns true
    20  // if the generation of the resource increased or the resource is created
    21  func (c *Client) PatchDynamicResource(resource unstructured.Unstructured) (bool, error) {
    22  	klog.V(5).Infoln("Applying resource via server-side apply:")
    23  	klog.V(5).Infoln(resourceAsJson(resource.Object))
    24  	unversionedResource := resource.DeepCopy()
    25  	unversionedResource.SetResourceVersion("")
    26  	data, err := json.Marshal(unversionedResource.Object)
    27  	if err != nil {
    28  		return false, fmt.Errorf("unable to marshal resource: %w", err)
    29  	}
    30  
    31  	gvr, err := c.GetRestMappingFromUnstructured(*unversionedResource)
    32  	if err != nil {
    33  		return false, err
    34  	}
    35  
    36  	var previousGeneration int64 = -1
    37  	// Get the generation of the current resource
    38  	previous, err := c.DynamicClient.Resource(gvr.Resource).Namespace(c.Namespace).Get(context.TODO(), unversionedResource.GetName(), metav1.GetOptions{})
    39  	if err != nil {
    40  		if !kerrors.IsNotFound(err) {
    41  			return false, err
    42  		}
    43  	} else {
    44  		previousGeneration = previous.GetGeneration()
    45  	}
    46  
    47  	// Patch the dynamic resource
    48  	current, err := c.DynamicClient.Resource(gvr.Resource).Namespace(c.Namespace).Patch(context.TODO(), unversionedResource.GetName(), types.ApplyPatchType, data, metav1.PatchOptions{FieldManager: FieldManager, Force: Bool(true)})
    49  	if err != nil {
    50  		return false, err
    51  	}
    52  	newGeneration := current.GetGeneration()
    53  
    54  	return newGeneration > previousGeneration, nil
    55  }
    56  
    57  // ListDynamicResources returns an unstructured list of instances of a Custom
    58  // Resource currently deployed in the specified namespace of the cluster. The current namespace is used if the namespace is not specified.
    59  // If a selector is passed, then it will be used as a label selector to list the resources.
    60  func (c *Client) ListDynamicResources(namespace string, gvr schema.GroupVersionResource, selector string) (*unstructured.UnstructuredList, error) {
    61  
    62  	if c.DynamicClient == nil {
    63  		return nil, nil
    64  	}
    65  
    66  	ns := namespace
    67  	if ns == "" {
    68  		ns = c.Namespace
    69  	}
    70  
    71  	listOptions := metav1.ListOptions{}
    72  	if selector != "" {
    73  		listOptions.LabelSelector = selector
    74  	}
    75  
    76  	list, err := c.DynamicClient.Resource(gvr).Namespace(ns).List(context.TODO(), listOptions)
    77  	if err != nil {
    78  		if kerrors.IsNotFound(err) {
    79  			// Assume this is a cluster scoped resource (not namespace scoped) and skip it
    80  			return &unstructured.UnstructuredList{}, nil
    81  		}
    82  		return nil, err
    83  	}
    84  
    85  	return list, nil
    86  }
    87  
    88  // ListClusterWideDynamicResources returns an unstructured list of instances of a Custom
    89  // Resource currently deployed cluster-wide in the cluster.
    90  // If a selector is passed, then it will be used as a label selector to list the resources.
    91  func (c *Client) ListClusterWideDynamicResources(gvr schema.GroupVersionResource, selector string) (*unstructured.UnstructuredList, error) {
    92  
    93  	if c.DynamicClient == nil {
    94  		return nil, nil
    95  	}
    96  
    97  	listOptions := metav1.ListOptions{}
    98  	if selector != "" {
    99  		listOptions.LabelSelector = selector
   100  	}
   101  
   102  	list, err := c.DynamicClient.Resource(gvr).List(context.TODO(), listOptions)
   103  	if err != nil {
   104  		if kerrors.IsNotFound(err) {
   105  			return &unstructured.UnstructuredList{}, nil
   106  		}
   107  		return nil, err
   108  	}
   109  
   110  	return list, nil
   111  }
   112  
   113  // GetDynamicResource returns an unstructured instance of a Custom Resource currently deployed in the active namespace
   114  func (c *Client) GetDynamicResource(gvr schema.GroupVersionResource, name string) (*unstructured.Unstructured, error) {
   115  	res, err := c.DynamicClient.Resource(gvr).Namespace(c.Namespace).Get(context.TODO(), name, metav1.GetOptions{})
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  	return res, nil
   120  }
   121  
   122  // UpdateDynamicResource updates a dynamic resource
   123  func (c *Client) UpdateDynamicResource(gvr schema.GroupVersionResource, name string, u *unstructured.Unstructured) error {
   124  	_, err := c.DynamicClient.Resource(gvr).Namespace(c.Namespace).Update(context.TODO(), u, metav1.UpdateOptions{})
   125  	if err != nil {
   126  		return err
   127  	}
   128  	return nil
   129  }
   130  
   131  type GVRN struct {
   132  	gvr  schema.GroupVersionResource
   133  	name string
   134  }
   135  
   136  // DeleteDynamicResource deletes an instance, specified by name, of a Custom Resource
   137  // if wait is true, it will set the PropagationPolicy to DeletePropagationForeground
   138  // to wait for owned resources to be deleted (only for resources with a BlockOwnerDeletion set to true)
   139  func (c *Client) DeleteDynamicResource(name string, gvr schema.GroupVersionResource, wait bool) error {
   140  
   141  	doDeleteResource := func() error {
   142  		return c.DynamicClient.Resource(gvr).Namespace(c.Namespace).Delete(context.TODO(), name, metav1.DeleteOptions{
   143  			PropagationPolicy: func(f metav1.DeletionPropagation) *metav1.DeletionPropagation {
   144  				if wait {
   145  					return &f
   146  				}
   147  				return nil
   148  			}(metav1.DeletePropagationForeground),
   149  		})
   150  	}
   151  
   152  	if !wait {
   153  		return doDeleteResource()
   154  	}
   155  
   156  	// Search resources referencing this resource without BlockOwnerDeletion, to handle waiting their deletion here
   157  	thisRes, err := c.GetDynamicResource(gvr, name)
   158  	if err != nil {
   159  		return err
   160  	}
   161  	all, err := c.GetAllResourcesFromSelector("", c.Namespace)
   162  	if err != nil {
   163  		return err
   164  	}
   165  
   166  	var toWait []GVRN
   167  	for _, res := range all {
   168  		ownerRefs := res.GetOwnerReferences()
   169  		for _, ownerRef := range ownerRefs {
   170  			if ownerRef.UID == thisRes.GetUID() {
   171  				if ownerRef.BlockOwnerDeletion == nil || !*ownerRef.BlockOwnerDeletion {
   172  					mapping, err2 := c.GetRestMappingFromUnstructured(res)
   173  					if err2 != nil {
   174  						return err2
   175  					}
   176  					toWait = append(toWait, GVRN{
   177  						gvr:  mapping.Resource,
   178  						name: res.GetName(),
   179  					})
   180  				}
   181  			}
   182  		}
   183  	}
   184  
   185  	err = doDeleteResource()
   186  	if err != nil {
   187  		return err
   188  	}
   189  	err = c.WaitDynamicResourceDeleted(gvr, name)
   190  	if err != nil {
   191  		return err
   192  	}
   193  	for _, wait := range toWait {
   194  		err = c.WaitDynamicResourceDeleted(wait.gvr, wait.name)
   195  		if err != nil {
   196  			return err
   197  		}
   198  	}
   199  	return nil
   200  }
   201  
   202  // WaitDynamicResourceDeleted waits for the given resource to be deleted, with a timeout
   203  func (c *Client) WaitDynamicResourceDeleted(gvr schema.GroupVersionResource, name string) error {
   204  
   205  	watcher, err := c.DynamicClient.Resource(gvr).Namespace(c.Namespace).Watch(context.TODO(), metav1.ListOptions{FieldSelector: "metadata.name=" + name})
   206  	if err != nil {
   207  		return err
   208  	}
   209  	defer watcher.Stop()
   210  
   211  	_, err = c.GetDynamicResource(gvr, name)
   212  	if err != nil {
   213  		// deletion is done if the resource does not exist
   214  		if kerrors.IsNotFound(err) {
   215  			return nil
   216  		}
   217  		return err
   218  	}
   219  
   220  	for {
   221  		select {
   222  		case <-time.After(time.Minute):
   223  			return fmt.Errorf("timeout while waiting for %q resource to be deleted", name)
   224  
   225  		case val, ok := <-watcher.ResultChan():
   226  			if !ok {
   227  				return errors.New("error getting value from resultchan")
   228  			}
   229  			if val.Type == watch.Deleted {
   230  				return nil
   231  			}
   232  		}
   233  	}
   234  }
   235  

View as plain text