github.com/verrazzano/verrazzano@v1.7.0/application-operator/controllers/workloadselector/workloadselector.go (about)

     1  // Copyright (c) 2021, 2022, 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 workloadselector
     5  
     6  import (
     7  	"context"
     8  	"reflect"
     9  	"strings"
    10  
    11  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    12  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    13  	"k8s.io/apimachinery/pkg/labels"
    14  	"k8s.io/apimachinery/pkg/runtime/schema"
    15  	"k8s.io/client-go/kubernetes"
    16  )
    17  
    18  // WorkloadSelector type for accessing functions
    19  type WorkloadSelector struct {
    20  	KubeClient kubernetes.Interface
    21  }
    22  
    23  // DoesWorkloadMatch returns a boolean indicating whether an unstructured resource matches any of the criteria for a match.
    24  // The criteria used to match is a namespace label selector, object label selector, and group, version,
    25  // and kind of resource.
    26  func (w *WorkloadSelector) DoesWorkloadMatch(workload *unstructured.Unstructured, namespaceSelector *metav1.LabelSelector, objectSelector *metav1.LabelSelector, apiGroups []string, apiVersions []string, resources []string) (bool, error) {
    27  	// Check if we match the given namespace label selector
    28  	found, err := w.doesNamespaceMatch(workload, namespaceSelector)
    29  	if err != nil {
    30  		return false, err
    31  	}
    32  	if !found {
    33  		return false, nil
    34  	}
    35  
    36  	// If the namespace matches then check if we match the given object label selector
    37  	return w.doesObjectMatch(workload, objectSelector, apiGroups, apiVersions, resources)
    38  }
    39  
    40  // doesNamespaceMatch returns a boolean indicating whether an unstructured resource matches the namespace selector
    41  func (w *WorkloadSelector) doesNamespaceMatch(workload *unstructured.Unstructured, namespaceSelector *metav1.LabelSelector) (bool, error) {
    42  	// If the namespace label selector is not specified then we don't need to check the namespace
    43  	if namespaceSelector == nil || reflect.DeepEqual(namespaceSelector, metav1.LabelSelector{}) {
    44  		return true, nil
    45  	}
    46  
    47  	// Get the namespace object for the workload resource
    48  	namespace, err := w.KubeClient.CoreV1().Namespaces().Get(context.TODO(), workload.GetNamespace(), metav1.GetOptions{})
    49  	if err != nil {
    50  		return false, err
    51  	}
    52  
    53  	// Check if the namespace labels match the namespace label selector
    54  	label, err := metav1.LabelSelectorAsSelector(namespaceSelector)
    55  	if err != nil {
    56  		return false, err
    57  	}
    58  	if label.Matches(labels.Set(namespace.GetLabels())) {
    59  		return true, nil
    60  	}
    61  
    62  	return false, nil
    63  }
    64  
    65  // doesObjectMatch returns a boolean indicating whether an unstructured resource matches the criteria for a match.
    66  // The criteria used to match is an object label selector, and group, version, and kind values
    67  func (w *WorkloadSelector) doesObjectMatch(workload *unstructured.Unstructured, objectSelector *metav1.LabelSelector, apiGroups []string, apiVersions []string, resources []string) (bool, error) {
    68  	// Get the group and version of the workload resource
    69  	gv, err := schema.ParseGroupVersion(workload.GetAPIVersion())
    70  	if err != nil {
    71  		return false, nil
    72  	}
    73  
    74  	// Check that the workload resource GVK matches expected GVKs
    75  	if !checkMatch(gv.Version, apiVersions) || !checkMatch(gv.Group, apiGroups) || !checkMatch(workload.GetKind(), resources) {
    76  		return false, nil
    77  	}
    78  
    79  	// If the object label selector is not specified then we don't need to check the resource for a match
    80  	if objectSelector == nil || reflect.DeepEqual(objectSelector, metav1.LabelSelector{}) {
    81  		return true, nil
    82  	}
    83  	// Check if the workload resource labels match the object label selector
    84  	label, err := metav1.LabelSelectorAsSelector(objectSelector)
    85  	if err != nil {
    86  		return false, err
    87  	}
    88  	if label.Matches(labels.Set(workload.GetLabels())) {
    89  		return true, nil
    90  	}
    91  
    92  	return false, nil
    93  }
    94  
    95  // checkMatch checks for a matching string within a string array
    96  func checkMatch(match string, matches []string) bool {
    97  	if len(matches) == 0 {
    98  		return true
    99  	}
   100  	for _, value := range matches {
   101  		if value == "*" {
   102  			return true
   103  		}
   104  		if value == strings.ToLower(match) {
   105  			return true
   106  		}
   107  	}
   108  
   109  	return false
   110  }