1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package unmanageddetector
16
17 import (
18 "context"
19 "fmt"
20 "strings"
21
22 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/lifecyclehandler"
23 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/k8s"
24
25 "github.com/go-logr/logr"
26 v1 "k8s.io/api/apps/v1"
27 apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
28 "k8s.io/apimachinery/pkg/api/errors"
29 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
30 "k8s.io/apimachinery/pkg/labels"
31 "k8s.io/apimachinery/pkg/runtime/schema"
32 "sigs.k8s.io/controller-runtime/pkg/builder"
33 "sigs.k8s.io/controller-runtime/pkg/client"
34 "sigs.k8s.io/controller-runtime/pkg/controller"
35 klog "sigs.k8s.io/controller-runtime/pkg/log"
36 "sigs.k8s.io/controller-runtime/pkg/manager"
37 "sigs.k8s.io/controller-runtime/pkg/reconcile"
38 )
39
40 var logger = klog.Log
41
42 type Reconciler struct {
43 lifecyclehandler.LifecycleHandler
44 mgr manager.Manager
45 crd *apiextensions.CustomResourceDefinition
46 gvk schema.GroupVersionKind
47 logger logr.Logger
48 }
49
50 func Add(mgr manager.Manager, crd *apiextensions.CustomResourceDefinition) error {
51 kind := crd.Spec.Names.Kind
52 apiVersion := k8s.GetAPIVersionFromCRD(crd)
53 controllerName := fmt.Sprintf("%v-unmanaged-detector", strings.ToLower(kind))
54 r, err := NewReconciler(mgr, crd)
55 if err != nil {
56 return err
57 }
58 obj := &unstructured.Unstructured{
59 Object: map[string]interface{}{
60 "kind": kind,
61 "apiVersion": apiVersion,
62 },
63 }
64 _, err = builder.
65 ControllerManagedBy(mgr).
66 Named(controllerName).
67 WithOptions(controller.Options{MaxConcurrentReconciles: k8s.ControllerMaxConcurrentReconciles}).
68 For(obj, builder.OnlyMetadata, builder.WithPredicates(UnmanagedDetectorPredicate{})).
69 Build(r)
70 if err != nil {
71 return fmt.Errorf("error creating new controller: %v", err)
72 }
73 logger.Info("Registered unmanaged detector controller", "kind", kind, "apiVersion", apiVersion)
74 return nil
75 }
76
77 func NewReconciler(mgr manager.Manager, crd *apiextensions.CustomResourceDefinition) (*Reconciler, error) {
78 controllerName := fmt.Sprintf("%v-unmanaged-detector", strings.ToLower(crd.Spec.Names.Kind))
79 return &Reconciler{
80 LifecycleHandler: lifecyclehandler.NewLifecycleHandlerWithFieldOwner(
81 mgr.GetClient(),
82 mgr.GetEventRecorderFor(controllerName),
83 k8s.UnmanagedDetectorFieldManager,
84 ),
85 mgr: mgr,
86 crd: crd,
87 gvk: schema.GroupVersionKind{
88 Group: crd.Spec.Group,
89 Version: k8s.GetVersionFromCRD(crd),
90 Kind: crd.Spec.Names.Kind,
91 },
92 logger: logger.WithName(controllerName),
93 }, nil
94 }
95
96 func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (res reconcile.Result, err error) {
97 r.logger.Info("starting reconcile", "resource", req.NamespacedName)
98
99 u := &unstructured.Unstructured{}
100 u.SetGroupVersionKind(r.gvk)
101
102 if err := r.Get(ctx, req.NamespacedName, u); err != nil {
103 if errors.IsNotFound(err) {
104 r.logger.Info("resource not found in API server; finishing reconcile", "resource", req.NamespacedName)
105 return reconcile.Result{}, nil
106 }
107 return reconcile.Result{}, err
108 }
109
110 yes, err := controllerExistsForNamespace(ctx, u.GetNamespace(), r)
111 if err != nil {
112 return reconcile.Result{}, fmt.Errorf("error determining if controller exists for namespace %v: %v", u.GetNamespace(), err)
113 }
114 if yes {
115
116
117 r.logger.Info("controller found for resource's namespace; finishing reconcile", "resource", k8s.GetNamespacedName(u))
118 return reconcile.Result{}, nil
119 }
120
121 resource, err := k8s.NewResource(u)
122 if err != nil {
123 return reconcile.Result{}, fmt.Errorf("could not parse resource %v: %v", k8s.GetNamespacedName(u), err)
124 }
125
126
127
128 r.logger.Info("controller not found for resource's namespace; finishing reconcile", "resource", k8s.GetNamespacedName(u))
129 return reconcile.Result{}, r.HandleUnmanaged(ctx, resource)
130 }
131
132 func controllerExistsForNamespace(ctx context.Context, namespace string, c client.Client) (yes bool, err error) {
133 stsLabelSelectorRaw := fmt.Sprintf("%v=%v,%v=%v",
134 k8s.KCCComponentLabel, k8s.ControllerManagerNamePrefix,
135 k8s.ScopedNamespaceLabel, namespace,
136 )
137 stsLabelSelector, err := labels.Parse(stsLabelSelectorRaw)
138 if err != nil {
139 return false, fmt.Errorf("error parsing '%v' as a label selector: %v", stsLabelSelectorRaw, err)
140 }
141 stsList := &v1.StatefulSetList{}
142 stsOpts := &client.ListOptions{
143 Namespace: k8s.SystemNamespace,
144 LabelSelector: stsLabelSelector,
145 Limit: 1,
146 }
147 if err := c.List(ctx, stsList, stsOpts); err != nil {
148 return false, fmt.Errorf("error listing controller manager StatefulSets: %w", err)
149 }
150 return len(stsList.Items) > 0, nil
151 }
152
View as plain text