Skip to content

Commit

Permalink
feat: add validating webhook for KeptnTaskDefinition (#1514)
Browse files Browse the repository at this point in the history
Signed-off-by: odubajDT <ondrej.dubaj@dynatrace.com>
Signed-off-by: geoffrey1330 <israelgeoffrey13@gmail.com>
Signed-off-by: Geoffrey Israel <israelgeoffrey13@gmail.com>
Co-authored-by: odubajDT <93584209+odubajDT@users.noreply.github.com>
Co-authored-by: Rakshit Gondwal <98955085+rakshitgondwal@users.noreply.github.com>
Co-authored-by: Florian Bacher <florian.bacher@dynatrace.com>
  • Loading branch information
4 people committed Jun 7, 2023
1 parent 069e049 commit d55a7ef
Show file tree
Hide file tree
Showing 12 changed files with 485 additions and 112 deletions.
3 changes: 3 additions & 0 deletions operator/PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,9 @@ resources:
kind: KeptnTaskDefinition
path: github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3
version: v1alpha3
webhooks:
validation: true
webhookVersion: v1
- api:
crdVersion: v1
namespaced: true
Expand Down
98 changes: 98 additions & 0 deletions operator/apis/lifecycle/v1alpha3/keptntaskdefinition_webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
Copyright 2022.
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 v1alpha3

import (
"github.com/pkg/errors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
ctrl "sigs.k8s.io/controller-runtime"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook"
)

// log is for logging in this package.
var keptntaskdefinitionlog = logf.Log.WithName("keptntaskdefinition-resource")

func (r *KeptnTaskDefinition) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(r).
Complete()
}

//+kubebuilder:webhook:path=/validate-lifecycle-keptn-sh-v1alpha3-keptntaskdefinition,mutating=false,failurePolicy=fail,sideEffects=None,groups=lifecycle.keptn.sh,resources=keptntaskdefinitions,verbs=create;update,versions=v1alpha3,name=vkeptntaskdefinition.kb.io,admissionReviewVersions=v1

var _ webhook.Validator = &KeptnTaskDefinition{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *KeptnTaskDefinition) ValidateCreate() error {
keptntaskdefinitionlog.Info("validate create", "name", r.Name)

return r.validateKeptnTaskDefinition()
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *KeptnTaskDefinition) ValidateUpdate(old runtime.Object) error {
keptntaskdefinitionlog.Info("validate update", "name", r.Name)

return r.validateKeptnTaskDefinition()
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *KeptnTaskDefinition) ValidateDelete() error {
keptntaskdefinitionlog.Info("validate delete", "name", r.Name)

return nil
}

func (r *KeptnTaskDefinition) validateKeptnTaskDefinition() error {
var allErrs field.ErrorList //defined as a list to allow returning multiple validation errors
var err *field.Error
if err = r.validateFields(); err != nil {
allErrs = append(allErrs, err)
}
if len(allErrs) == 0 {
return nil
}

return apierrors.NewInvalid(
schema.GroupKind{Group: "lifecycle.keptn.sh", Kind: "KeptnTaskDefinition"},
r.Name,
allErrs)
}
func (r *KeptnTaskDefinition) validateFields() *field.Error {

if r.Spec.Function == nil && r.Spec.Container == nil {
return field.Invalid(
field.NewPath("spec"),
r.Spec,
errors.New("Forbidden! Either Function or Container field must be defined").Error(),
)
}

if r.Spec.Function != nil && r.Spec.Container != nil {
return field.Invalid(
field.NewPath("spec"),
r.Spec,
errors.New("Forbidden! Both Function and Container fields cannot be defined simultaneously").Error(),
)
}

return nil
}
121 changes: 121 additions & 0 deletions operator/apis/lifecycle/v1alpha3/keptntaskdefinition_webhook_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package v1alpha3

import (
"testing"

"github.com/pkg/errors"
"github.com/stretchr/testify/require"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
)

func TestKeptnTaskDefinition_ValidateFields(t *testing.T) {

specWithFunctionAndContainer := KeptnTaskDefinitionSpec{
Function: &FunctionSpec{},
Container: &ContainerSpec{},
}

emptySpec := KeptnTaskDefinitionSpec{}

tests := []struct {
name string
spec KeptnTaskDefinitionSpec
want error
verb string
oldSpec runtime.Object
}{
{
name: "with-no-function-or-container",
spec: emptySpec,
want: apierrors.NewInvalid(
schema.GroupKind{Group: "lifecycle.keptn.sh", Kind: "KeptnTaskDefinition"},
"with-no-function-or-container",
[]*field.Error{field.Invalid(
field.NewPath("spec"),
emptySpec,
errors.New("Forbidden! Either Function or Container field must be defined").Error(),
)},
),
verb: "create",
},
{
name: "with-both-function-and-container",
spec: specWithFunctionAndContainer,
verb: "create",
want: apierrors.NewInvalid(
schema.GroupKind{Group: "lifecycle.keptn.sh", Kind: "KeptnTaskDefinition"},
"with-both-function-and-container",
[]*field.Error{field.Invalid(
field.NewPath("spec"),
specWithFunctionAndContainer,
errors.New("Forbidden! Both Function and Container fields cannot be defined simultaneously").Error(),
)},
),
},
{
name: "with-function-only",
spec: KeptnTaskDefinitionSpec{
Function: &FunctionSpec{},
},
verb: "create",
},
{
name: "with-container-only",
spec: KeptnTaskDefinitionSpec{
Container: &ContainerSpec{},
},
verb: "create",
},
{
name: "update-with-both-function-and-container",
spec: specWithFunctionAndContainer,
want: apierrors.NewInvalid(
schema.GroupKind{Group: "lifecycle.keptn.sh", Kind: "KeptnTaskDefinition"},
"update-with-both-function-and-container",
[]*field.Error{field.Invalid(
field.NewPath("spec"),
specWithFunctionAndContainer,
errors.New("Forbidden! Both Function and Container fields cannot be defined simultaneously").Error(),
)},
),
oldSpec: &KeptnTaskDefinition{
Spec: KeptnTaskDefinitionSpec{},
},
verb: "update",
},
{
name: "delete",
verb: "delete",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ktd := &KeptnTaskDefinition{
ObjectMeta: metav1.ObjectMeta{Name: tt.name},
Spec: tt.spec,
}

var got error
switch tt.verb {
case "create":
got = ktd.ValidateCreate()
case "update":
got = ktd.ValidateUpdate(tt.oldSpec)
case "delete":
got = ktd.ValidateDelete()
}

if tt.want != nil {
require.NotNil(t, got)
require.EqualValues(t, tt.want, got)
} else {
require.Nil(t, got)
}
})
}
}
2 changes: 1 addition & 1 deletion operator/apis/lifecycle/v1alpha3/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions operator/config/default/manager_webhook_patch.yaml
Original file line number Diff line number Diff line change
@@ -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: webhook-server-cert
29 changes: 29 additions & 0 deletions operator/config/webhook/manifests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,32 @@ webhooks:
resources:
- pods
sideEffects: None
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
creationTimestamp: null
name: lifecycle-validating-webhook-configuration
labels:
keptn.sh/inject-cert: "true"
webhooks:
- admissionReviewVersions:
- v1
clientConfig:
service:
name: lifecycle-webhook-service
namespace: system
path: /validate-lifecycle-keptn-sh-v1alpha3-keptntaskdefinition
failurePolicy: Fail
name: vkeptntaskdefinition.kb.io
rules:
- apiGroups:
- lifecycle.keptn.sh
apiVersions:
- v1alpha3
operations:
- CREATE
- UPDATE
resources:
- keptntaskdefinitions
sideEffects: None
Loading

0 comments on commit d55a7ef

Please sign in to comment.