...

Source file src/github.com/GoogleCloudPlatform/k8s-config-connector/operator/pkg/controllers/utils.go

Documentation: github.com/GoogleCloudPlatform/k8s-config-connector/operator/pkg/controllers

     1  // Copyright 2022 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package controllers
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"strings"
    21  
    22  	customizev1alpha1 "github.com/GoogleCloudPlatform/k8s-config-connector/operator/pkg/apis/core/customize/v1alpha1"
    23  	corev1beta1 "github.com/GoogleCloudPlatform/k8s-config-connector/operator/pkg/apis/core/v1beta1"
    24  	"github.com/GoogleCloudPlatform/k8s-config-connector/operator/pkg/k8s"
    25  
    26  	corev1 "k8s.io/api/core/v1"
    27  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    30  	"k8s.io/apimachinery/pkg/runtime/schema"
    31  	"k8s.io/apimachinery/pkg/types"
    32  	"sigs.k8s.io/controller-runtime/pkg/client"
    33  	"sigs.k8s.io/kubebuilder-declarative-pattern/pkg/patterns/declarative/pkg/manifest"
    34  )
    35  
    36  var (
    37  	ValidConfigConnectorNamespacedName = types.NamespacedName{Name: k8s.ConfigConnectorAllowedName}
    38  )
    39  
    40  func GetConfigConnector(ctx context.Context, client client.Client, nn types.NamespacedName) (*corev1beta1.ConfigConnector, error) {
    41  	cc := &corev1beta1.ConfigConnector{}
    42  	if err := client.Get(ctx, nn, cc); err != nil {
    43  		return nil, err
    44  	}
    45  	return cc, nil
    46  }
    47  
    48  func RemoveOperatorFinalizer(o metav1.Object) (found bool) {
    49  	var finalizers []string
    50  	for _, f := range o.GetFinalizers() {
    51  		if f != k8s.OperatorFinalizer {
    52  			finalizers = append(finalizers, f)
    53  		} else {
    54  			found = true
    55  		}
    56  	}
    57  	if found {
    58  		o.SetFinalizers(finalizers)
    59  	}
    60  	return found
    61  }
    62  
    63  func EnsureOperatorFinalizer(o metav1.Object) (found bool) {
    64  	for _, f := range o.GetFinalizers() {
    65  		if f == k8s.OperatorFinalizer {
    66  			return true
    67  		}
    68  	}
    69  	o.SetFinalizers(append(o.GetFinalizers(), k8s.OperatorFinalizer))
    70  	return false
    71  }
    72  
    73  func AnnotateServiceAccountObject(object *manifest.Object, gsa string) (*manifest.Object, error) {
    74  	u := object.UnstructuredObject()
    75  	annotations := u.GetAnnotations()
    76  	if annotations == nil {
    77  		annotations = make(map[string]string)
    78  	}
    79  	annotations[k8s.WorkloadIdentityAnnotation] = gsa
    80  	u.SetAnnotations(annotations)
    81  	return manifest.NewObject(u)
    82  }
    83  
    84  func DeleteObject(ctx context.Context, c client.Client, obj client.Object) error {
    85  	kind := obj.GetObjectKind().GroupVersionKind().Kind
    86  	name := obj.(metav1.Object).GetName()
    87  	if err := c.Delete(ctx, obj, &client.DeleteOptions{}); err != nil {
    88  		if apierrors.IsNotFound(err) {
    89  			return nil
    90  		}
    91  		return fmt.Errorf("error deleting %v %v: %v", kind, name, err)
    92  	}
    93  	return removeOperatorFinalizerIfPresent(ctx, c, obj)
    94  }
    95  
    96  /*
    97   * some of the critical resources, such as role bindings, are in the customer namespace, we protect them with an
    98   * operator finalizer to ensure that the operator can control the time when they are removed.
    99   */
   100  func removeOperatorFinalizerIfPresent(ctx context.Context, c client.Client, obj client.Object) error {
   101  	found := RemoveOperatorFinalizer(obj)
   102  	if !found {
   103  		return nil
   104  	}
   105  	if err := c.Update(ctx, obj); err != nil {
   106  		return fmt.Errorf("error removing operator finalizer from %v %v: %w",
   107  			obj.GetObjectKind().GroupVersionKind().Kind, obj.GetName(), err)
   108  	}
   109  	return nil
   110  }
   111  
   112  func IsControllerManagerStatefulSet(obj *manifest.Object) bool {
   113  	if obj.Kind != "StatefulSet" {
   114  		return false
   115  	}
   116  	labels := obj.UnstructuredObject().GetLabels()
   117  	return labels[k8s.KCCSystemComponentLabel] == k8s.KCCControllerManagerComponent
   118  }
   119  
   120  func IsControllerManagerService(obj *manifest.Object) bool {
   121  	if obj.Kind != "Service" {
   122  		return false
   123  	}
   124  	return obj.GetName() == k8s.NamespacedManagerServiceTmpl
   125  }
   126  
   127  func GetControllerResource(ctx context.Context, c client.Client, name string) (*customizev1alpha1.ControllerResource, error) {
   128  	obj := &customizev1alpha1.ControllerResource{}
   129  	if err := c.Get(ctx, types.NamespacedName{Name: name}, obj); err != nil {
   130  		return nil, err
   131  	}
   132  	return obj, nil
   133  }
   134  
   135  // ListControllerResources lists all ControllerResources.
   136  func ListControllerResources(ctx context.Context, c client.Client) ([]customizev1alpha1.ControllerResource, error) {
   137  	list := &customizev1alpha1.ControllerResourceList{}
   138  	if err := c.List(ctx, list); err != nil {
   139  		return nil, err
   140  	}
   141  	return list.Items, nil
   142  }
   143  
   144  // ApplyContainerResourceCustomization applies container resource customizations specified in ControllerResource / NamespacedControllerResource CR.
   145  func ApplyContainerResourceCustomization(isNamespaced bool, m *manifest.Objects, controllerName string, controllerGVK schema.GroupVersionKind, containers []customizev1alpha1.ContainerResourceSpec) error {
   146  	cMap := make(map[string]corev1.ResourceRequirements, len(containers)) // cMap is a map of container name to its corresponding resource customization.
   147  	cMapApplied := make(map[string]bool)                                  // cMapApplied is a map of container name to a boolean indicating whether the customization for this container is applied.
   148  	for _, c := range containers {
   149  		cMap[c.Name] = c.Resources
   150  		cMapApplied[c.Name] = false
   151  	}
   152  	// apply customization to the matching controller in the manifest
   153  	for _, item := range m.Items {
   154  		if item.GroupVersionKind() == controllerGVK { // match GVK
   155  			if item.GetName() == controllerName { // match exact controller name for cluster-scoped controller
   156  				// apply container resource customization for this controller.
   157  				item.MutateContainers(customizeContainerResourcesFn(cMap, cMapApplied))
   158  				break // we already found the match, no need to keep looking.
   159  			}
   160  		}
   161  	}
   162  	// check if all container resource customizations are applied
   163  	var notApplied []string
   164  	for c, applied := range cMapApplied {
   165  		if !applied {
   166  			notApplied = append(notApplied, c)
   167  		}
   168  	}
   169  	if len(notApplied) > 0 {
   170  		return fmt.Errorf("resource customization failed for the following containers because there are no matching containers in the manifest: %s", strings.Join(notApplied, ", "))
   171  	}
   172  	return nil
   173  }
   174  
   175  // customizeContainerResourcesFn returns a function to customize container resources.
   176  func customizeContainerResourcesFn(cMap map[string]corev1.ResourceRequirements, cMapApplied map[string]bool) func(container map[string]interface{}) error {
   177  	return func(container map[string]interface{}) error {
   178  		name, _, err := unstructured.NestedString(container, "name")
   179  		if err != nil {
   180  			return fmt.Errorf("error reading container name: %v", err)
   181  		}
   182  		r, found := cMap[name]
   183  		if !found {
   184  			return nil
   185  		}
   186  		if r.Limits != nil && !r.Limits.Cpu().IsZero() {
   187  			if err := unstructured.SetNestedField(container, r.Limits.Cpu().String(), "resources", "limits", "cpu"); err != nil {
   188  				return fmt.Errorf("error setting cpu limit: %v", err)
   189  			}
   190  		}
   191  		if r.Limits != nil && !r.Limits.Memory().IsZero() {
   192  			if err := unstructured.SetNestedField(container, r.Limits.Memory().String(), "resources", "limits", "memory"); err != nil {
   193  				return fmt.Errorf("error setting memory limit: %v", err)
   194  			}
   195  		}
   196  		if r.Requests != nil && !r.Requests.Cpu().IsZero() {
   197  			if err := unstructured.SetNestedField(container, r.Requests.Cpu().String(), "resources", "requests", "cpu"); err != nil {
   198  				return fmt.Errorf("error setting cpu request: %v", err)
   199  			}
   200  		}
   201  		if r.Requests != nil && !r.Requests.Memory().IsZero() {
   202  			if err := unstructured.SetNestedField(container, r.Requests.Memory().String(), "resources", "requests", "memory"); err != nil {
   203  				return fmt.Errorf("error setting memory request: %v", err)
   204  			}
   205  		}
   206  		cMapApplied[name] = true
   207  		return nil
   208  	}
   209  }
   210  

View as plain text