diff --git a/api/v1beta1/ocicluster_webhook.go b/api/v1beta1/ocicluster_webhook.go new file mode 100644 index 000000000..45c20a19d --- /dev/null +++ b/api/v1beta1/ocicluster_webhook.go @@ -0,0 +1,115 @@ +/* + * + * Copyright (c) 2022, Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + * + */ + +package v1beta1 + +import ( + "fmt" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/validation/field" + + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/webhook" +) + +var clusterlogger = ctrl.Log.WithName("ocicluster-resource") + +var ( + _ webhook.Validator = &OCICluster{} + _ webhook.Defaulter = &OCICluster{} +) + +// +kubebuilder:webhook:verbs=create;update,path=/validate-infrastructure-cluster-x-k8s-io-v1beta1-ocicluster,mutating=false,failurePolicy=fail,matchPolicy=Equivalent,groups=infrastructure.cluster.x-k8s.io,resources=ociclusters,versions=v1beta1,name=validation.ocicluster.infrastructure.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1beta1 +// +kubebuilder:webhook:verbs=create;update,path=/mutate-infrastructure-cluster-x-k8s-io-v1beta1-ocicluster,mutating=true,failurePolicy=fail,matchPolicy=Equivalent,groups=infrastructure.cluster.x-k8s.io,resources=ociclusters,versions=v1beta1,name=default.ocicluster.infrastructure.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1beta1 + +// Default implements webhook.Defaulter so a webhook will be registered for the type. +func (c *OCICluster) Default() {} + +func (c *OCICluster) SetupWebhookWithManager(mgr ctrl.Manager) error { + clusterlogger.Info("validate create cluster", "name", c.Name) + + return ctrl.NewWebhookManagedBy(mgr). + For(c). + Complete() +} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type. +func (c *OCICluster) ValidateCreate() error { + clusterlogger.Info("validate update cluster", "name", c.Name) + + var allErrs field.ErrorList + + allErrs = append(allErrs, c.validate()...) + + if len(allErrs) == 0 { + return nil + } + + return apierrors.NewInvalid(c.GroupVersionKind().GroupKind(), c.Name, allErrs) +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type. +func (c *OCICluster) ValidateDelete() error { + clusterlogger.Info("validate delete cluster", "name", c.Name) + + return nil +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type. +func (c *OCICluster) ValidateUpdate(old runtime.Object) error { + clusterlogger.Info("validate update cluster", "name", c.Name) + + var allErrs field.ErrorList + + oldCluster, ok := old.(*OCICluster) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected an OCICluster but got a %T", old)) + } + + if c.Spec.Region != oldCluster.Spec.Region { + allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "region"), c.Spec.Region, "field is immutable")) + } + + allErrs = append(allErrs, c.validate()...) + + if len(allErrs) == 0 { + return nil + } + + return apierrors.NewInvalid(c.GroupVersionKind().GroupKind(), c.Name, allErrs) +} + +func (c *OCICluster) validate() field.ErrorList { + var allErrs field.ErrorList + + // simple validity test for compartment + if !validOcid(c.Spec.CompartmentId) { + allErrs = append( + allErrs, + field.Invalid(field.NewPath("spec", "compartmentId"), c.Spec.CompartmentId, "field is invalid")) + } + + if len(allErrs) == 0 { + return nil + } + + return allErrs +} diff --git a/api/v1beta1/ocicluster_webhook_test.go b/api/v1beta1/ocicluster_webhook_test.go new file mode 100644 index 000000000..a955b6c02 --- /dev/null +++ b/api/v1beta1/ocicluster_webhook_test.go @@ -0,0 +1,124 @@ +/* + * + * Copyright (c) 2022, Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + * + */ + +package v1beta1 + +import ( + "testing" + + "github.com/onsi/gomega" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestOCICluster_ValidateCreate(t *testing.T) { + + tests := []struct { + name string + c *OCICluster + expectErr bool + }{ + { + name: "shouldn't allow bad ImageId", + c: &OCICluster{ + ObjectMeta: metav1.ObjectMeta{}, + Spec: OCIClusterSpec{ + CompartmentId: "badocid", + }, + }, + expectErr: true, + }, + { + name: "should succeed", + c: &OCICluster{ + ObjectMeta: metav1.ObjectMeta{}, + Spec: OCIClusterSpec{ + CompartmentId: "ocid", + }, + }, + expectErr: false, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + g := gomega.NewWithT(t) + + if test.expectErr { + g.Expect(test.c.ValidateCreate()).NotTo(gomega.Succeed()) + } else { + g.Expect(test.c.ValidateCreate()).To(gomega.Succeed()) + } + }) + } +} + +func TestOCICluster_ValidateUpdate(t *testing.T) { + tests := []struct { + name string + c *OCICluster + old *OCICluster + expectErr bool + }{ + { + name: "shouldn't region change", + c: &OCICluster{ + ObjectMeta: metav1.ObjectMeta{}, + Spec: OCIClusterSpec{ + Region: "new-region", + }, + }, + old: &OCICluster{ + ObjectMeta: metav1.ObjectMeta{}, + Spec: OCIClusterSpec{ + Region: "old-region", + }, + }, + expectErr: true, + }, + { + name: "should succeed", + c: &OCICluster{ + ObjectMeta: metav1.ObjectMeta{}, + Spec: OCIClusterSpec{ + CompartmentId: "ocid", + Region: "old-region", + }, + }, + old: &OCICluster{ + ObjectMeta: metav1.ObjectMeta{}, + Spec: OCIClusterSpec{ + Region: "old-region", + }, + }, + expectErr: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + g := gomega.NewWithT(t) + + if test.expectErr { + g.Expect(test.c.ValidateUpdate(test.old)).NotTo(gomega.Succeed()) + } else { + g.Expect(test.c.ValidateUpdate(test.old)).To(gomega.Succeed()) + } + }) + } + +} diff --git a/api/v1beta1/ocimachinetemplate_webhook.go b/api/v1beta1/ocimachinetemplate_webhook.go new file mode 100644 index 000000000..9a400c040 --- /dev/null +++ b/api/v1beta1/ocimachinetemplate_webhook.go @@ -0,0 +1,113 @@ +/* + * + * Copyright (c) 2022, Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + * + */ + +package v1beta1 + +import ( + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/validation/field" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/webhook" +) + +var ( + _ webhook.Validator = &OCIMachineTemplate{} + _ webhook.Defaulter = &OCIMachineTemplate{} +) + +// +kubebuilder:webhook:verbs=create;update,path=/validate-infrastructure-cluster-x-k8s-io-v1beta1-ocimachinetemplate,mutating=false,failurePolicy=fail,matchPolicy=Equivalent,groups=infrastructure.cluster.x-k8s.io,resources=ocimachinetemplates,versions=v1beta1,name=validation.ocimachinetemplate.infrastructure.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1beta1 +// +kubebuilder:webhook:verbs=create;update,path=/mutate-infrastructure-cluster-x-k8s-io-v1beta1-ocimachinetemplate,mutating=true,failurePolicy=fail,matchPolicy=Equivalent,groups=infrastructure.cluster.x-k8s.io,resources=ocimachinetemplates,versions=v1beta1,name=default.ocimachinetemplate.infrastructure.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1beta1 + +// Default implements webhook.Defaulter so a webhook will be registered for the type. +func (m *OCIMachineTemplate) Default() {} + +func (m *OCIMachineTemplate) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(m). + Complete() +} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type. +func (m *OCIMachineTemplate) ValidateCreate() error { + clusterlogger.Info("validate create machinetemplate", "name", m.Name) + + var allErrs field.ErrorList + + allErrs = append(allErrs, m.validate()...) + + if len(allErrs) == 0 { + return nil + } + + return apierrors.NewInvalid(m.GroupVersionKind().GroupKind(), m.Name, allErrs) +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type. +func (m *OCIMachineTemplate) ValidateDelete() error { + clusterlogger.Info("validate delete machinetemplate", "name", m.Name) + + return nil +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type. +func (m *OCIMachineTemplate) ValidateUpdate(old runtime.Object) error { + clusterlogger.Info("validate update machinetemplate", "name", m.Name) + + var allErrs field.ErrorList + + allErrs = append(allErrs, m.validate()...) + + if len(allErrs) == 0 { + return nil + } + + return apierrors.NewInvalid(m.GroupVersionKind().GroupKind(), m.Name, allErrs) +} + +func (m *OCIMachineTemplate) validate() field.ErrorList { + var allErrs field.ErrorList + + if !validOcid(m.Spec.Template.Spec.ImageId) { + allErrs = append( + allErrs, + field.Invalid( + field.NewPath("Spec", "Template", "Spec", "imageId"), + m.Spec.Template.Spec.ImageId, + "field is invalid"), + ) + } + + // simple validity test for compartment + if len(m.Spec.Template.Spec.CompartmentId) > 0 && !validOcid(m.Spec.Template.Spec.CompartmentId) { + allErrs = append( + allErrs, + field.Invalid( + field.NewPath("Spec", "Template", "Spec", "compartmentId"), + m.Spec.Template.Spec.CompartmentId, + "field is invalid"), + ) + } + + if len(allErrs) == 0 { + return nil + } + + return allErrs +} diff --git a/api/v1beta1/ocimachinetemplate_webhook_test.go b/api/v1beta1/ocimachinetemplate_webhook_test.go new file mode 100644 index 000000000..23da2913b --- /dev/null +++ b/api/v1beta1/ocimachinetemplate_webhook_test.go @@ -0,0 +1,105 @@ +/* + * + * Copyright (c) 2022, Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + * + */ + +package v1beta1 + +import ( + "testing" + + "github.com/onsi/gomega" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var tests = []struct { + name string + inputTemplate *OCIMachineTemplate + expectErr bool +}{ + { + name: "shouldn't allow bad ImageId", + inputTemplate: &OCIMachineTemplate{ + ObjectMeta: metav1.ObjectMeta{}, + Spec: OCIMachineTemplateSpec{ + Template: OCIMachineTemplateResource{ + Spec: OCIMachineSpec{ + ImageId: "badocid", + }, + }, + }, + }, + expectErr: true, + }, + { + name: "shouldn't allow bad CompartmentId", + inputTemplate: &OCIMachineTemplate{ + ObjectMeta: metav1.ObjectMeta{}, + Spec: OCIMachineTemplateSpec{ + Template: OCIMachineTemplateResource{ + Spec: OCIMachineSpec{ + CompartmentId: "badocid", + }, + }, + }, + }, + expectErr: true, + }, + { + name: "should succeed", + inputTemplate: &OCIMachineTemplate{ + ObjectMeta: metav1.ObjectMeta{}, + Spec: OCIMachineTemplateSpec{ + Template: OCIMachineTemplateResource{ + Spec: OCIMachineSpec{ + ImageId: "ocid", + CompartmentId: "ocid", + }, + }, + }, + }, + expectErr: false, + }, +} + +func TestOCIMachineTemplate_ValidateCreate(t *testing.T) { + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + g := gomega.NewWithT(t) + + if test.expectErr { + g.Expect(test.inputTemplate.ValidateCreate()).NotTo(gomega.Succeed()) + } else { + g.Expect(test.inputTemplate.ValidateCreate()).To(gomega.Succeed()) + } + }) + } +} + +func TestOCIMachineTemplate_ValidateUpdate(t *testing.T) { + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + g := gomega.NewWithT(t) + + if test.expectErr { + g.Expect(test.inputTemplate.ValidateUpdate(nil)).NotTo(gomega.Succeed()) + } else { + g.Expect(test.inputTemplate.ValidateUpdate(nil)).To(gomega.Succeed()) + } + }) + } +} diff --git a/api/v1beta1/webhooks.go b/api/v1beta1/webhooks.go new file mode 100644 index 000000000..afc0702c8 --- /dev/null +++ b/api/v1beta1/webhooks.go @@ -0,0 +1,29 @@ +/* + * + * Copyright (c) 2022, Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + * + */ + +package v1beta1 + +func validOcid(ocid string) bool { + + if len(ocid) >= 4 && ocid[:4] == "ocid" { + return true + } + + return false +} diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index f9cdf9ea6..5afe11ab2 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -2,7 +2,7 @@ // +build !ignore_autogenerated /* -Copyright (c) 2022 Oracle and/or its affiliates. +Copyright (c) 2022, Oracle and/or its affiliates. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ limitations under the License. package v1beta1 import ( - runtime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime" apiv1beta1 "sigs.k8s.io/cluster-api/api/v1beta1" "sigs.k8s.io/cluster-api/errors" ) diff --git a/config/certmanager/certificate.yaml b/config/certmanager/certificate.yaml new file mode 100644 index 000000000..476be88c1 --- /dev/null +++ b/config/certmanager/certificate.yaml @@ -0,0 +1,24 @@ +# The following manifests contain a self-signed issuer CR and a certificate CR. +# More document can be found at https://docs.cert-manager.io +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: selfsigned-issuer + namespace: system +spec: + selfSigned: {} +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml + namespace: system +spec: + # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize + dnsNames: + - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc + - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local + issuerRef: + kind: Issuer + name: selfsigned-issuer + secretName: $(SERVICE_NAME)-cert # this secret will not be prefixed, since it's not managed by kustomize \ No newline at end of file diff --git a/config/certmanager/kustomization.yaml b/config/certmanager/kustomization.yaml new file mode 100644 index 000000000..48e61b9c9 --- /dev/null +++ b/config/certmanager/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - certificate.yaml + +configurations: + - kustomizeconfig.yaml \ No newline at end of file diff --git a/config/certmanager/kustomizeconfig.yaml b/config/certmanager/kustomizeconfig.yaml new file mode 100644 index 000000000..cf148912c --- /dev/null +++ b/config/certmanager/kustomizeconfig.yaml @@ -0,0 +1,19 @@ +# This configuration is for teaching kustomize how to update name ref and var substitution +nameReference: + - kind: Issuer + group: cert-manager.io + fieldSpecs: + - kind: Certificate + group: cert-manager.io + path: spec/issuerRef/name + +varReference: + - kind: Certificate + group: cert-manager.io + path: spec/commonName + - kind: Certificate + group: cert-manager.io + path: spec/dnsNames + - kind: Certificate + group: cert-manager.io + path: spec/secretName \ No newline at end of file diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 5131ef34e..698803c3c 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -15,15 +15,15 @@ resources: patchesStrategicMerge: # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. # patches here are for enabling the conversion webhook for each CRD -#- patches/webhook_in_ociclusters.yaml -#- patches/webhook_in_ocimachines.yaml -#+kubebuilder:scaffold:crdkustomizewebhookpatch +- patches/webhook_in_ociclusters.yaml +- patches/webhook_in_ocimachines.yaml +# +kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix. # patches here are for enabling the CA injection for each CRD -#- patches/cainjection_in_ociclusters.yaml -#- patches/cainjection_in_ocimachines.yaml -#+kubebuilder:scaffold:crdkustomizecainjectionpatch +- patches/cainjection_in_ociclusters.yaml +- patches/cainjection_in_ocimachines.yaml +# +kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. configurations: diff --git a/config/crd/patches/webhook_in_ociclusters.yaml b/config/crd/patches/webhook_in_ociclusters.yaml index 713171771..c52d5b29b 100644 --- a/config/crd/patches/webhook_in_ociclusters.yaml +++ b/config/crd/patches/webhook_in_ociclusters.yaml @@ -14,3 +14,4 @@ spec: path: /convert conversionReviewVersions: - v1 + - v1beta1 diff --git a/config/crd/patches/webhook_in_ocimachines.yaml b/config/crd/patches/webhook_in_ocimachines.yaml index 0767de44a..479b8cde3 100644 --- a/config/crd/patches/webhook_in_ocimachines.yaml +++ b/config/crd/patches/webhook_in_ocimachines.yaml @@ -14,3 +14,4 @@ spec: path: /convert conversionReviewVersions: - v1 + - v1beta1 diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index 3e3be1a73..979bb0a4a 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -21,9 +21,9 @@ bases: - ../manager # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in # crd/kustomization.yaml -#- ../webhook +- ../webhook # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. -#- ../certmanager +- ../certmanager # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. #- ../prometheus @@ -41,39 +41,42 @@ patchesStrategicMerge: # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in # crd/kustomization.yaml -#- manager_webhook_patch.yaml +- manager_webhook_patch.yaml # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. # Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. # 'CERTMANAGER' needs to be enabled to use ca injection -#- webhookcainjection_patch.yaml +- webhookcainjection_patch.yaml # the following config is for teaching kustomize how to do var substitution vars: # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. -#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR -# objref: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # this name should match the one in certificate.yaml -# fieldref: -# fieldpath: metadata.namespace -#- name: CERTIFICATE_NAME -# objref: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # this name should match the one in certificate.yaml -#- name: SERVICE_NAMESPACE # namespace of the service -# objref: -# kind: Service -# version: v1 -# name: webhook-service -# fieldref: -# fieldpath: metadata.namespace -#- name: SERVICE_NAME -# objref: -# kind: Service -# version: v1 -# name: webhook-service +- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR + objref: + kind: Certificate + group: cert-manager.io + version: v1 + name: serving-cert # this name should match the one in certificate.yaml + fieldref: + fieldpath: metadata.namespace +- name: CERTIFICATE_NAME + objref: + kind: Certificate + group: cert-manager.io + version: v1 + name: serving-cert # this name should match the one in certificate.yaml +- name: SERVICE_NAMESPACE # namespace of the service + objref: + kind: Service + version: v1 + name: webhook-service + fieldref: + fieldpath: metadata.namespace +- name: SERVICE_NAME + objref: + kind: Service + version: v1 + name: webhook-service + +configurations: + - kustomizeconfig.yaml \ No newline at end of file diff --git a/config/default/kustomizeconfig.yaml b/config/default/kustomizeconfig.yaml new file mode 100644 index 000000000..2e04a4f01 --- /dev/null +++ b/config/default/kustomizeconfig.yaml @@ -0,0 +1,4 @@ +# This configuration is for teaching kustomize how to update name ref and var substitution +varReference: + - kind: Deployment + path: spec/template/spec/volumes/secret/secretName \ No newline at end of file diff --git a/config/default/manager_webhook_patch.yaml b/config/default/manager_webhook_patch.yaml new file mode 100644 index 000000000..e116610fa --- /dev/null +++ b/config/default/manager_webhook_patch.yaml @@ -0,0 +1,23 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: $(SERVICE_NAME)-cert \ No newline at end of file diff --git a/config/default/webhookcainjection_patch.yaml b/config/default/webhookcainjection_patch.yaml new file mode 100644 index 000000000..02ab515d4 --- /dev/null +++ b/config/default/webhookcainjection_patch.yaml @@ -0,0 +1,15 @@ +# This patch add annotation to admission webhook config and +# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: mutating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: validating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) diff --git a/config/webhook/kustomization.yaml b/config/webhook/kustomization.yaml new file mode 100644 index 000000000..fbd74f349 --- /dev/null +++ b/config/webhook/kustomization.yaml @@ -0,0 +1,6 @@ +resources: + - manifests.yaml + - service.yaml + +configurations: + - kustomizeconfig.yaml \ No newline at end of file diff --git a/config/webhook/kustomizeconfig.yaml b/config/webhook/kustomizeconfig.yaml new file mode 100644 index 000000000..15185025d --- /dev/null +++ b/config/webhook/kustomizeconfig.yaml @@ -0,0 +1,25 @@ +# the following config is for teaching kustomize where to look at when substituting vars. +# It requires kustomize v2.1.0 or newer to work properly. +nameReference: + - kind: Service + version: v1 + fieldSpecs: + - kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name + - kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name + +namespace: + - kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true + - kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true + +varReference: + - path: metadata/annotations \ No newline at end of file diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml new file mode 100644 index 000000000..95b140c6f --- /dev/null +++ b/config/webhook/manifests.yaml @@ -0,0 +1,100 @@ + +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + creationTimestamp: null + name: mutating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-infrastructure-cluster-x-k8s-io-v1beta1-ocicluster + failurePolicy: Fail + matchPolicy: Equivalent + name: default.ocicluster.infrastructure.cluster.x-k8s.io + rules: + - apiGroups: + - infrastructure.cluster.x-k8s.io + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - ociclusters + sideEffects: None +- admissionReviewVersions: + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-infrastructure-cluster-x-k8s-io-v1beta1-ocimachinetemplate + failurePolicy: Fail + matchPolicy: Equivalent + name: default.ocimachinetemplate.infrastructure.cluster.x-k8s.io + rules: + - apiGroups: + - infrastructure.cluster.x-k8s.io + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - ocimachinetemplates + sideEffects: None + +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + creationTimestamp: null + name: validating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-infrastructure-cluster-x-k8s-io-v1beta1-ocicluster + failurePolicy: Fail + matchPolicy: Equivalent + name: validation.ocicluster.infrastructure.cluster.x-k8s.io + rules: + - apiGroups: + - infrastructure.cluster.x-k8s.io + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - ociclusters + sideEffects: None +- admissionReviewVersions: + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-infrastructure-cluster-x-k8s-io-v1beta1-ocimachinetemplate + failurePolicy: Fail + matchPolicy: Equivalent + name: validation.ocimachinetemplate.infrastructure.cluster.x-k8s.io + rules: + - apiGroups: + - infrastructure.cluster.x-k8s.io + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - ocimachinetemplates + sideEffects: None diff --git a/config/webhook/service.yaml b/config/webhook/service.yaml new file mode 100644 index 000000000..9290b06f9 --- /dev/null +++ b/config/webhook/service.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Service +metadata: + name: webhook-service + namespace: system +spec: + ports: + - port: 443 + targetPort: webhook-server \ No newline at end of file diff --git a/hack/boilerplate.go.txt b/hack/boilerplate.go.txt index d094e7aca..8e805e6d7 100644 --- a/hack/boilerplate.go.txt +++ b/hack/boilerplate.go.txt @@ -1,5 +1,5 @@ /* -Copyright (c) 2022 Oracle and/or its affiliates. +Copyright (c) 2022, Oracle and/or its affiliates. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/main.go b/main.go index a6f347dbb..b7272d96f 100644 --- a/main.go +++ b/main.go @@ -39,8 +39,10 @@ import ( ) var ( - scheme = runtime.NewScheme() - setupLog = ctrl.Log.WithName("setup") + scheme = runtime.NewScheme() + setupLog = ctrl.Log.WithName("setup") + webhookPort int + webhookCertDir string ) const ( @@ -58,7 +60,7 @@ func main() { var metricsAddr string var enableLeaderElection bool var probeAddr string - var webhookPort int + //var webhookPort int flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") @@ -70,6 +72,8 @@ func main() { 9443, "Webhook Server port.", ) + flag.StringVar(&webhookCertDir, "webhook-cert-dir", "/tmp/k8s-webhook-server/serving-certs/", + "Webhook cert dir, only used when webhook-port is specified.") opts := zap.Options{ Development: true, @@ -86,6 +90,7 @@ func main() { HealthProbeBindAddress: probeAddr, LeaderElection: enableLeaderElection, LeaderElectionID: "controller-leader-elect-capoci", + CertDir: webhookCertDir, }) if err != nil { setupLog.Error(err, "unable to start manager") @@ -151,6 +156,17 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", scope.OCIMachineKind) os.Exit(1) } + + if err = (&infrastructurev1beta1.OCICluster{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "OCICluster") + os.Exit(1) + } + + if err = (&infrastructurev1beta1.OCIMachineTemplate{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "OCIMachineTemplate") + os.Exit(1) + } + //+kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {