github.com/caos/orbos@v1.5.14-0.20221103111702-e6cd0cea7ad4/internal/utils/clientgo/resource.go (about)

     1  package clientgo
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/caos/orbos/mntr"
     9  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    10  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    11  	"k8s.io/apimachinery/pkg/runtime/schema"
    12  	"k8s.io/client-go/discovery"
    13  	"k8s.io/client-go/dynamic"
    14  )
    15  
    16  var (
    17  	ignoredResources = []string{
    18  		"componentstatuses",
    19  		"endpoints",
    20  		"bindings",
    21  		"nodes",
    22  		"nodes",
    23  		"replicationcontrollers",
    24  		"podtemplates",
    25  		"limitranges",
    26  		"apiservices",
    27  		"controllerrevisions",
    28  		"leases",
    29  		"backendconfigs",
    30  		"updateinfos",
    31  		"runtimeclasses",
    32  		"storagestates",
    33  		"storageversionmigrations",
    34  		"csidrivers",
    35  		"csinodes",
    36  		"localsubjectaccessreviews",
    37  		"selfsubjectaccessreviews",
    38  		"selfsubjectrulesreviews",
    39  		"subjectaccessreviews",
    40  		"tokenreviews",
    41  		"scalingpolicies",
    42  		"priorityclasses",
    43  	}
    44  	ignoredGroupResources = []string{
    45  		// "metrics.k8s.io/pods",
    46  		"metrics.k8s.io/nodes",
    47  		"networking.gke.io/managedcertificates",
    48  		"networking.gke.io/ingresses",
    49  		"networking.gke.io/networkpolicies",
    50  	}
    51  )
    52  
    53  var (
    54  	Limit int64 = 50
    55  )
    56  
    57  type ResourceInfo struct {
    58  	Group      string
    59  	Version    string
    60  	Resource   string
    61  	Namespaced bool
    62  }
    63  
    64  type Resource struct {
    65  	Group     string
    66  	Version   string
    67  	Resource  string
    68  	Kind      string
    69  	Name      string
    70  	Namespace string
    71  	Labels    map[string]string
    72  }
    73  
    74  type ResourceSorter []*Resource
    75  
    76  func (a ResourceSorter) Len() int      { return len(a) }
    77  func (a ResourceSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
    78  func (a ResourceSorter) Less(i, j int) bool {
    79  	return (a[i].Group < a[j].Group ||
    80  		(a[i].Group == a[j].Group && a[i].Version < a[j].Version) ||
    81  		(a[i].Group == a[j].Group && a[i].Version == a[j].Version && a[i].Resource < a[j].Resource) ||
    82  		(a[i].Group == a[j].Group && a[i].Version == a[j].Version && a[i].Resource == a[j].Resource && a[i].Kind < a[j].Kind) ||
    83  		(a[i].Group == a[j].Group && a[i].Version == a[j].Version && a[i].Resource == a[j].Resource && a[i].Kind == a[j].Kind && a[i].Name < a[j].Name) ||
    84  		(a[i].Group == a[j].Group && a[i].Version == a[j].Version && a[i].Resource == a[j].Resource && a[i].Kind == a[j].Kind && a[i].Name == a[j].Name && a[i].Namespace < a[j].Namespace))
    85  }
    86  
    87  func GetResource(monitor mntr.Monitor, group, version, resource, namespace, name string) (*Resource, error) {
    88  	res := schema.GroupVersionResource{Group: group, Version: version, Resource: resource}
    89  
    90  	conf, err := GetClusterConfig(monitor, "")
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  
    95  	clientset, err := dynamic.NewForConfig(conf)
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  
   100  	result, err := clientset.Resource(res).Namespace(namespace).Get(context.Background(), name, metav1.GetOptions{})
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  
   105  	return &Resource{
   106  		Kind:      result.GetKind(),
   107  		Name:      result.GetName(),
   108  		Namespace: result.GetNamespace(),
   109  		Labels:    result.GetLabels(),
   110  	}, nil
   111  }
   112  
   113  func DeleteResource(monitor mntr.Monitor, resource *Resource) error {
   114  	res := schema.GroupVersionResource{Group: resource.Group, Version: resource.Version, Resource: resource.Resource}
   115  	conf, err := GetClusterConfig(monitor, "")
   116  	if err != nil {
   117  		return err
   118  	}
   119  
   120  	client, err := dynamic.NewForConfig(conf)
   121  	if err != nil {
   122  		return err
   123  	}
   124  
   125  	deletePolicy := metav1.DeletePropagationForeground
   126  	deleteOptions := &metav1.DeleteOptions{
   127  		PropagationPolicy: &deletePolicy,
   128  	}
   129  
   130  	clientRes := client.Resource(res)
   131  	if resource.Namespace != "" {
   132  		err = clientRes.Namespace(resource.Namespace).Delete(context.Background(), resource.Name, *deleteOptions)
   133  	} else {
   134  		err = clientRes.Delete(context.Background(), resource.Name, *deleteOptions)
   135  	}
   136  
   137  	if err != nil {
   138  		return fmt.Errorf("error while deleting %s: %w", resource.Name, err)
   139  	}
   140  	return nil
   141  }
   142  
   143  func GetGroupVersionsResources(monitor mntr.Monitor, filtersResources []string) ([]*ResourceInfo, error) {
   144  	listMonitor := monitor.WithFields(map[string]interface{}{
   145  		"action": "groupVersionResources",
   146  	})
   147  
   148  	conf, err := GetClusterConfig(monitor, "")
   149  	if err != nil {
   150  		return nil, fmt.Errorf("getting cluster config failed: %w", err)
   151  	}
   152  
   153  	client, err := discovery.NewDiscoveryClientForConfig(conf)
   154  	if err != nil {
   155  		return nil, fmt.Errorf("creating discovery client failed: %w", err)
   156  	}
   157  
   158  	apiGroups, err := client.ServerGroups()
   159  	if err != nil {
   160  		return nil, fmt.Errorf("getting supported groups and versions failed: %w", err)
   161  	}
   162  	resourceInfoList := make([]*ResourceInfo, 0)
   163  	for _, apiGroup := range apiGroups.Groups {
   164  		version := apiGroup.PreferredVersion
   165  		apiResources, err := client.ServerResourcesForGroupVersion(version.GroupVersion)
   166  		if err != nil {
   167  			return nil, fmt.Errorf("getting supported resources failed for %s: %w", version.GroupVersion, err)
   168  		}
   169  
   170  		for _, apiResource := range apiResources.APIResources {
   171  
   172  			if filtersResources != nil &&
   173  				len(filtersResources) > 0 &&
   174  				containsFilter(filtersResources, apiGroup.Name, version.Version, apiResource.Kind) {
   175  				continue
   176  			}
   177  
   178  			resourceInfo := &ResourceInfo{
   179  				Group:      apiGroup.Name,
   180  				Version:    version.Version,
   181  				Resource:   apiResource.Name,
   182  				Namespaced: apiResource.Namespaced,
   183  			}
   184  
   185  			groupResource := strings.Join([]string{resourceInfo.Group, resourceInfo.Resource}, "/")
   186  			if !contains(ignoredResources, resourceInfo.Resource) &&
   187  				!contains(ignoredGroupResources, groupResource) {
   188  				resourceInfoList = append(resourceInfoList, resourceInfo)
   189  			}
   190  		}
   191  	}
   192  	listMonitor.WithField("count", len(resourceInfoList)).Debug("Listed groupVersionsResources")
   193  	return resourceInfoList, nil
   194  }
   195  
   196  func contains(s []string, e string) bool {
   197  	for _, a := range s {
   198  		if a == e {
   199  			return true
   200  		}
   201  	}
   202  	return false
   203  }
   204  
   205  func ListResources(monitor mntr.Monitor, resourceInfoList []*ResourceInfo, labels map[string]string) ([]*Resource, error) {
   206  	listMonitor := monitor.WithFields(map[string]interface{}{
   207  		"action": "listResources",
   208  	})
   209  	conf, err := GetClusterConfig(monitor, "")
   210  	if err != nil {
   211  		return nil, err
   212  	}
   213  
   214  	client, err := dynamic.NewForConfig(conf)
   215  	if err != nil {
   216  		return nil, err
   217  	}
   218  
   219  	labelSelector := ""
   220  	for k, v := range labels {
   221  		if labelSelector == "" {
   222  			labelSelector = strings.Join([]string{k, v}, "=")
   223  		} else {
   224  			keyValue := strings.Join([]string{k, v}, "=")
   225  			labelSelector = strings.Join([]string{labelSelector, keyValue}, ", ")
   226  		}
   227  	}
   228  
   229  	listMonitor.WithFields(map[string]interface{}{
   230  		"labelSelector": labelSelector,
   231  	}).Debug(fmt.Sprintf("Used labels"))
   232  
   233  	resourceList := make([]*Resource, 0)
   234  	for _, resourceInfo := range resourceInfoList {
   235  
   236  		gvr := schema.GroupVersionResource{Group: resourceInfo.Group, Version: resourceInfo.Version, Resource: resourceInfo.Resource}
   237  
   238  		listOpt := metav1.ListOptions{
   239  			LabelSelector: labelSelector,
   240  			Limit:         Limit,
   241  		}
   242  		list, err := client.Resource(gvr).List(context.Background(), listOpt)
   243  		if err != nil {
   244  			continue
   245  		}
   246  
   247  		resList, err := getResourcesFromList(resourceInfo.Group, resourceInfo.Version, resourceInfo.Resource, list.Items)
   248  		if err != nil {
   249  			return nil, err
   250  		}
   251  
   252  		for list.GetContinue() != "" {
   253  			listOpt.Continue = list.GetContinue()
   254  			listInternal, err := client.Resource(gvr).List(context.Background(), listOpt)
   255  			if err != nil {
   256  				continue
   257  			}
   258  			list = listInternal
   259  
   260  			resListContinue, err := getResourcesFromList(resourceInfo.Group, resourceInfo.Version, resourceInfo.Resource, list.Items)
   261  			if err != nil {
   262  				return nil, err
   263  			}
   264  			resList = append(resList, resListContinue...)
   265  		}
   266  
   267  		listMonitor.WithFields(map[string]interface{}{
   268  			"group":    resourceInfo.Group,
   269  			"version":  resourceInfo.Version,
   270  			"resource": resourceInfo.Resource,
   271  			"count":    len(resList),
   272  		}).Debug("Listed resources")
   273  		resourceList = append(resourceList, resList...)
   274  	}
   275  
   276  	listMonitor.WithFields(map[string]interface{}{
   277  		"count": len(resourceList),
   278  	}).Info("All current resources")
   279  	return resourceList, nil
   280  }
   281  
   282  func getResourcesFromList(group, version, resource string, list []unstructured.Unstructured) ([]*Resource, error) {
   283  
   284  	resourceList := make([]*Resource, 0)
   285  	for _, item := range list {
   286  
   287  		name, found, err := unstructured.NestedString(item.Object, "metadata", "name")
   288  		if err != nil || !found {
   289  			return nil, err
   290  		}
   291  
   292  		kind, _, err := unstructured.NestedString(item.Object, "kind")
   293  		if err != nil {
   294  			return nil, err
   295  		}
   296  
   297  		namespace, _, err := unstructured.NestedString(item.Object, "metadata", "namespace")
   298  		if err != nil {
   299  			return nil, err
   300  		}
   301  
   302  		labels, _, err := unstructured.NestedMap(item.Object, "metadata", "labels")
   303  		if err != nil {
   304  			return nil, err
   305  		}
   306  
   307  		_, found, err = unstructured.NestedSlice(item.Object, "metadata", "ownerReferences")
   308  		if err != nil {
   309  			return nil, err
   310  		}
   311  		if found == true {
   312  			continue
   313  		}
   314  
   315  		labelStrs := make(map[string]string)
   316  		for k, label := range labels {
   317  			labelStrs[k] = label.(string)
   318  		}
   319  
   320  		resourceList = append(resourceList, &Resource{
   321  			Group:     group,
   322  			Version:   version,
   323  			Resource:  resource,
   324  			Name:      name,
   325  			Kind:      kind,
   326  			Namespace: namespace,
   327  			Labels:    labelStrs,
   328  		})
   329  	}
   330  
   331  	return resourceList, nil
   332  }
   333  
   334  func GetFilter(group, version, kind string) string {
   335  	return strings.Join([]string{group, version, kind}, "/")
   336  }
   337  
   338  func containsFilter(filters []string, group, version, kind string) bool {
   339  	compFilter := GetFilter(group, version, kind)
   340  	for _, filter := range filters {
   341  		if filter == compFilter {
   342  			return true
   343  		}
   344  	}
   345  	return false
   346  }