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 }