...

Source file src/github.com/GoogleCloudPlatform/k8s-config-connector/pkg/webhook/management_conflict_annotation_defaulter.go

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

     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 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  // managementConflictAnnotationDefaulter implements inject.Client.
    56  var _ inject.Client = &managementConflictAnnotationDefaulter{}
    57  
    58  // InjectClient injects the client into the managementConflictAnnotationDefaulter
    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