1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package registration
16
17 import (
18 "context"
19 "fmt"
20 "sync"
21 "time"
22
23 dclcontroller "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/dcl"
24 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/deletiondefender"
25 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/gsakeysecretgenerator"
26 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/iam/auditconfig"
27 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/iam/partialpolicy"
28 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/iam/policy"
29 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/iam/policymember"
30 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/tf"
31 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/unmanageddetector"
32 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/crd/crdgeneration"
33 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/conversion"
34 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/k8s"
35 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/servicemapping/servicemappingloader"
36
37 "github.com/GoogleCloudPlatform/declarative-resource-client-library/dcl"
38 tfschema "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
39 apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
40 "k8s.io/apimachinery/pkg/api/errors"
41 "k8s.io/apimachinery/pkg/runtime/schema"
42 "sigs.k8s.io/controller-runtime/pkg/client"
43 "sigs.k8s.io/controller-runtime/pkg/controller"
44 "sigs.k8s.io/controller-runtime/pkg/handler"
45 klog "sigs.k8s.io/controller-runtime/pkg/log"
46 "sigs.k8s.io/controller-runtime/pkg/manager"
47 "sigs.k8s.io/controller-runtime/pkg/reconcile"
48 "sigs.k8s.io/controller-runtime/pkg/source"
49 )
50
51 const controllerName = "registration-controller"
52 const serviceAccountKeyAPIGroup = "iam.cnrm.cloud.google.com"
53 const serviceAccountKeyKind = "IAMServiceAccountKey"
54
55 var logger = klog.Log.WithName(controllerName)
56
57
58
59 func Add(mgr manager.Manager, p *tfschema.Provider, smLoader *servicemappingloader.ServiceMappingLoader, dclConfig *dcl.Config, dclConverter *conversion.Converter, regFunc registrationFunc) error {
60 r := &ReconcileRegistration{
61 Client: mgr.GetClient(),
62 provider: p,
63 smLoader: smLoader,
64 dclConfig: dclConfig,
65 dclConverter: dclConverter,
66 mgr: mgr,
67 controllers: make(map[string]map[string]controllerContext),
68 registrationFunc: regFunc,
69 }
70 c, err := controller.New(controllerName, mgr,
71 controller.Options{
72 Reconciler: r,
73 MaxConcurrentReconciles: k8s.ControllerMaxConcurrentReconciles,
74 })
75 if err != nil {
76 return err
77 }
78 return c.Watch(&source.Kind{Type: &apiextensions.CustomResourceDefinition{}}, &handler.EnqueueRequestForObject{}, ManagedByKCCPredicate{})
79 }
80
81 var _ reconcile.Reconciler = &ReconcileRegistration{}
82
83
84 type ReconcileRegistration struct {
85 client.Client
86 provider *tfschema.Provider
87 smLoader *servicemappingloader.ServiceMappingLoader
88 dclConfig *dcl.Config
89 dclConverter *conversion.Converter
90 mgr manager.Manager
91 controllers map[string]map[string]controllerContext
92 registrationFunc registrationFunc
93 mu sync.Mutex
94 }
95
96 type controllerContext struct {
97 registered bool
98 schemaUpdater k8s.SchemaReferenceUpdater
99 }
100
101
102 type registrationFunc func(*ReconcileRegistration, *apiextensions.CustomResourceDefinition, schema.GroupVersionKind) (k8s.SchemaReferenceUpdater, error)
103
104 func (r *ReconcileRegistration) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) {
105
106 crd := &apiextensions.CustomResourceDefinition{}
107 err := r.Get(ctx, request.NamespacedName, crd)
108 if err != nil {
109 if errors.IsNotFound(err) {
110
111
112 return reconcile.Result{}, nil
113 }
114
115 return reconcile.Result{}, err
116 }
117
118 logger.Info("Waiting to obtain lock...", "kind", crd.Spec.Names.Kind)
119 start := time.Now()
120 r.mu.Lock()
121 logger.Info("Obtained lock", "kind", crd.Spec.Names.Kind, "elapsed (μs)", time.Since(start).Microseconds())
122 defer func() {
123 logger.Info("Releasing lock...", "kind", crd.Spec.Names.Kind)
124 r.mu.Unlock()
125 }()
126 gvk := schema.GroupVersionKind{
127 Group: crd.Spec.Group,
128 Version: k8s.GetVersionFromCRD(crd),
129 Kind: crd.Spec.Names.Kind,
130 }
131 if kindMapForGroup, exists := r.controllers[gvk.Group]; exists {
132 if kindMapForGroup[gvk.Kind].registered {
133 logger.Info("controller already registered for kind in API group", "group", gvk.Group, "version", gvk.Version, "kind", gvk.Kind)
134 if kindMapForGroup[gvk.Kind].schemaUpdater != nil {
135 logger.Info("updating schema for controller", "group", gvk.Group, "version", gvk.Version, "kind", gvk.Kind)
136 if err := kindMapForGroup[gvk.Kind].schemaUpdater.UpdateSchema(crd); err != nil {
137 logger.Info("error updating schema for controller", "group", gvk.Group, "version", gvk.Version, "kind", gvk.Kind)
138 }
139 }
140 return reconcile.Result{}, nil
141 }
142 } else {
143 r.controllers[gvk.Group] = make(map[string]controllerContext)
144 }
145
146 schemaUpdater, err := r.registrationFunc(r, crd, gvk)
147 if err != nil {
148 return reconcile.Result{}, fmt.Errorf("error registering controller: %w", err)
149 }
150
151 r.controllers[gvk.Group][gvk.Kind] = controllerContext{registered: true, schemaUpdater: schemaUpdater}
152 return reconcile.Result{}, nil
153 }
154
155 func isServiceAccountKeyCRD(crd *apiextensions.CustomResourceDefinition) bool {
156 return crd.Spec.Group == serviceAccountKeyAPIGroup && crd.Spec.Names.Kind == serviceAccountKeyKind
157 }
158
159 func RegisterDefaultController(r *ReconcileRegistration, crd *apiextensions.CustomResourceDefinition, gvk schema.GroupVersionKind) (k8s.SchemaReferenceUpdater, error) {
160 if _, ok := k8s.IgnoredKindList[crd.Spec.Names.Kind]; ok {
161 return nil, nil
162 }
163
164 var schemaUpdater k8s.SchemaReferenceUpdater
165 switch gvk.Kind {
166 case "IAMPolicy":
167 if err := policy.Add(r.mgr, r.provider, r.smLoader, r.dclConverter, r.dclConfig); err != nil {
168 return nil, err
169 }
170 case "IAMPartialPolicy":
171 if err := partialpolicy.Add(r.mgr, r.provider, r.smLoader, r.dclConverter, r.dclConfig); err != nil {
172 return nil, err
173 }
174 case "IAMPolicyMember":
175 if err := policymember.Add(r.mgr, r.provider, r.smLoader, r.dclConverter, r.dclConfig); err != nil {
176 return nil, err
177 }
178 case "IAMAuditConfig":
179 if err := auditconfig.Add(r.mgr, r.provider, r.smLoader, r.dclConverter, r.dclConfig); err != nil {
180 return nil, err
181 }
182 default:
183
184 if val, ok := crd.Labels[k8s.DCL2CRDLabel]; ok && val == "true" {
185 su, err := dclcontroller.Add(r.mgr, crd, r.dclConverter, r.dclConfig, r.smLoader)
186 if err != nil {
187 return nil, fmt.Errorf("error adding dcl controller for %v to a manager: %v", crd.Spec.Names.Kind, err)
188 }
189 return su, nil
190 }
191
192 if val, ok := crd.Labels[crdgeneration.TF2CRDLabel]; !ok || val != "true" {
193 logger.Info("unrecognized CRD; skipping controller registration", "group", gvk.Group, "version", gvk.Version, "kind", gvk.Kind)
194 return nil, nil
195 }
196 su, err := tf.Add(r.mgr, crd, r.provider, r.smLoader)
197 if err != nil {
198 return nil, fmt.Errorf("error adding terraform controller for %v to a manager: %v", crd.Spec.Names.Kind, err)
199 }
200 schemaUpdater = su
201
202 if isServiceAccountKeyCRD(crd) {
203 logger.Info("registering the GSA-Key-to-Secret generation controller")
204 if err := gsakeysecretgenerator.Add(r.mgr, crd); err != nil {
205 return nil, fmt.Errorf("error adding the gsa-to-secret generator for %v to a manager: %v", crd.Spec.Names.Kind, err)
206 }
207 }
208 }
209 return schemaUpdater, nil
210 }
211
212 func RegisterDeletionDefenderController(r *ReconcileRegistration, crd *apiextensions.CustomResourceDefinition, _ schema.GroupVersionKind) (k8s.SchemaReferenceUpdater, error) {
213 if _, ok := k8s.IgnoredKindList[crd.Spec.Names.Kind]; ok {
214 return nil, nil
215 }
216 if err := deletiondefender.Add(r.mgr, crd); err != nil {
217 return nil, fmt.Errorf("error registering deletion defender controller for '%v': %w", crd.GetName(), err)
218 }
219 return nil, nil
220 }
221
222 func RegisterUnmanagedDetectorController(r *ReconcileRegistration, crd *apiextensions.CustomResourceDefinition, _ schema.GroupVersionKind) (k8s.SchemaReferenceUpdater, error) {
223 if _, ok := k8s.IgnoredKindList[crd.Spec.Names.Kind]; ok {
224 return nil, nil
225 }
226 if err := unmanageddetector.Add(r.mgr, crd); err != nil {
227 return nil, fmt.Errorf("error registering unmanaged detector controller for '%v': %w", crd.GetName(), err)
228 }
229 return nil, nil
230 }
231
View as plain text