1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package webhook
16
17 import (
18 "context"
19 "fmt"
20 "net/http"
21
22 dclmetadata "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/metadata"
23 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/schema/dclschemaloader"
24 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/k8s"
25 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/servicemapping/servicemappingloader"
26
27 tfschema "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
28 "github.com/hashicorp/terraform-provider-google-beta/google-beta"
29 corev1 "k8s.io/api/core/v1"
30 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
31 apimachinerytypes "k8s.io/apimachinery/pkg/types"
32 "k8s.io/klog/v2"
33 "sigs.k8s.io/controller-runtime/pkg/client"
34 "sigs.k8s.io/controller-runtime/pkg/runtime/inject"
35 "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
36 )
37
38 type managementConflictAnnotationDefaulter struct {
39 client client.Client
40 dclSchemaLoader dclschemaloader.DCLSchemaLoader
41 serviceMetadataLoader dclmetadata.ServiceMetadataLoader
42 smLoader *servicemappingloader.ServiceMappingLoader
43 tfResourceMap map[string]*tfschema.Resource
44 }
45
46 func NewManagementConflictAnnotationDefaulter(smLoader *servicemappingloader.ServiceMappingLoader, dclSchemaLoader dclschemaloader.DCLSchemaLoader, serviceMetadataLoader dclmetadata.ServiceMetadataLoader) *managementConflictAnnotationDefaulter {
47 return &managementConflictAnnotationDefaulter{
48 smLoader: smLoader,
49 serviceMetadataLoader: serviceMetadataLoader,
50 dclSchemaLoader: dclSchemaLoader,
51 tfResourceMap: google.ResourceMap(),
52 }
53 }
54
55
56 var _ inject.Client = &managementConflictAnnotationDefaulter{}
57
58
59 func (a *managementConflictAnnotationDefaulter) InjectClient(c client.Client) error {
60 a.client = c
61 return nil
62 }
63
64 func (a *managementConflictAnnotationDefaulter) Handle(ctx context.Context, req admission.Request) admission.Response {
65 deserializer := codecs.UniversalDeserializer()
66 obj := &unstructured.Unstructured{}
67 if _, _, err := deserializer.Decode(req.AdmissionRequest.Object.Raw, nil, obj); err != nil {
68 klog.Error(err)
69 return admission.Errored(http.StatusBadRequest,
70 fmt.Errorf("error decoding object: %v", err))
71 }
72 ns := &corev1.Namespace{}
73 if err := a.client.Get(ctx, apimachinerytypes.NamespacedName{Name: obj.GetNamespace()}, ns); err != nil {
74 return admission.Errored(http.StatusInternalServerError,
75 fmt.Errorf("error getting Namespace %v: %v", obj.GetNamespace(), err))
76 }
77 if dclmetadata.IsDCLBasedResourceKind(obj.GroupVersionKind(), a.serviceMetadataLoader) {
78 return defaultManagementConflictAnnotationForDCLBasedResources(obj, ns, a.dclSchemaLoader, a.serviceMetadataLoader)
79 }
80 return defaultManagementConflictAnnotationForTFBasedResources(obj, ns, a.smLoader, a.tfResourceMap)
81 }
82
83 func defaultManagementConflictAnnotationForDCLBasedResources(obj *unstructured.Unstructured, ns *corev1.Namespace, dclSchemaLoader dclschemaloader.DCLSchemaLoader, serviceMetadataLoader dclmetadata.ServiceMetadataLoader) admission.Response {
84 gvk := obj.GroupVersionKind()
85 stv, err := dclmetadata.ToServiceTypeVersion(gvk, serviceMetadataLoader)
86 if err != nil {
87 return admission.Errored(http.StatusInternalServerError,
88 fmt.Errorf("error getting DCL ServiceTypeVersion for GroupVersionKind %v: %v", gvk, err))
89 }
90 schema, err := dclSchemaLoader.GetDCLSchema(stv)
91 if err != nil {
92 return admission.Errored(http.StatusInternalServerError,
93 fmt.Errorf("error getting the DCL Schema for GroupVersionKind %v: %v", gvk, err))
94 }
95 newObj := obj.DeepCopy()
96 if err := k8s.ValidateOrDefaultManagementConflictPreventionAnnotationForDCLBasedResource(newObj, ns, schema); err != nil {
97 return admission.Errored(http.StatusBadRequest, fmt.Errorf("error validating or defaulting management conflict policy annotation: %v", err))
98 }
99 return constructPatchResponse(obj, newObj)
100 }
101
102 func defaultManagementConflictAnnotationForTFBasedResources(obj *unstructured.Unstructured, ns *corev1.Namespace, smLoader *servicemappingloader.ServiceMappingLoader, tfResourceMap map[string]*tfschema.Resource) admission.Response {
103 rc, err := smLoader.GetResourceConfig(obj)
104 if err != nil {
105 return admission.Errored(http.StatusBadRequest,
106 fmt.Errorf("error getting ResourceConfig for kind %v: %v", obj.GetKind(), err))
107 }
108 newObj := obj.DeepCopy()
109 if err := k8s.ValidateOrDefaultManagementConflictPreventionAnnotationForTFBasedResource(newObj, ns, rc, tfResourceMap); err != nil {
110 return admission.Errored(http.StatusBadRequest, fmt.Errorf("error validating or defaulting management conflict policy annotation: %v", err))
111 }
112 return constructPatchResponse(obj, newObj)
113 }
114
View as plain text