1
2
3
4
5
6
7
8
9
10
11
12
13
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
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
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
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))
147 cMapApplied := make(map[string]bool)
148 for _, c := range containers {
149 cMap[c.Name] = c.Resources
150 cMapApplied[c.Name] = false
151 }
152
153 for _, item := range m.Items {
154 if item.GroupVersionKind() == controllerGVK {
155 if item.GetName() == controllerName {
156
157 item.MutateContainers(customizeContainerResourcesFn(cMap, cMapApplied))
158 break
159 }
160 }
161 }
162
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
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