Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding an admission controller #493

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions operator/PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ resources:
kind: SmartContractDeployment
path: github.com/kaleido-io/paladin/operator/api/v1alpha1
version: v1alpha1
webhooks:
validation: true
webhookVersion: v1
- api:
crdVersion: v1
namespaced: true
Expand Down Expand Up @@ -56,6 +59,9 @@ resources:
kind: PaladinRegistry
path: github.com/kaleido-io/paladin/operator/api/v1alpha1
version: v1alpha1
webhooks:
validation: true
webhookVersion: v1
- api:
crdVersion: v1
namespaced: true
Expand All @@ -74,6 +80,9 @@ resources:
kind: PaladinRegistration
path: github.com/kaleido-io/paladin/operator/api/v1alpha1
version: v1alpha1
webhooks:
validation: true
webhookVersion: v1
- api:
crdVersion: v1
namespaced: true
Expand All @@ -83,4 +92,7 @@ resources:
kind: TransactionInvoke
path: github.com/kaleido-io/paladin/operator/api/v1alpha1
version: v1alpha1
webhooks:
validation: true
webhookVersion: v1
version: "3"
54 changes: 54 additions & 0 deletions operator/api/v1alpha1/paladinregistration_webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
Copyright 2024.

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 v1alpha1

import (
"fmt"

"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)

// SetupWebhookWithManager will setup the manager to manage the webhooks
func (r *PaladinRegistration) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(r).
Complete()
}

// +kubebuilder:webhook:path=/validate-core-paladin-io-v1alpha1-paladinregistration,mutating=false,failurePolicy=fail,sideEffects=None,groups=core.paladin.io,resources=paladinregistrations,verbs=create;update;delete,versions=v1alpha1,name=vpaladinregistration.kb.io,admissionReviewVersions=v1

var _ webhook.Validator = &PaladinRegistration{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *PaladinRegistration) ValidateCreate() (admission.Warnings, error) {
// Allow creation
return nil, nil
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *PaladinRegistration) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
// Deny any update
return nil, fmt.Errorf("updates to PaladinRegistration resources are not allowed as they have already been submitted to the blockchain and cannot be modified")
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *PaladinRegistration) ValidateDelete() (admission.Warnings, error) {
// Deny any delete
return nil, fmt.Errorf("deletions of PaladinRegistration resources are not allowed as they have already been submitted to the blockchain and cannot be deleted")
}
89 changes: 89 additions & 0 deletions operator/api/v1alpha1/paladinregistration_webhook_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
Copyright 2024.

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 v1alpha1

import (
"context"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)

var _ = Describe("PaladinRegistration Webhook - Block Update & Delete", func() {

ctx := context.Background()

key := types.NamespacedName{
Name: "resource",
Namespace: "default",
}

pr := &PaladinRegistration{}

BeforeEach(func() {
// Create a valid PaladinRegistration that should pass creation validation
err := k8sClient.Get(ctx, key, pr)
if err != nil && errors.IsNotFound(err) {
pr = &PaladinRegistration{
ObjectMeta: metav1.ObjectMeta{
Name: key.Name,
Namespace: key.Namespace,
},
Spec: PaladinRegistrationSpec{
Registry: "evm-registry",
RegistryAdminNode: "node1",
RegistryAdminKey: "deployKey",
Node: "node1",
NodeKey: "registryAdmin",
Transports: []string{"grpc"},
},
}
Expect(k8sClient.Create(ctx, pr)).To(Succeed())
}
})

Context("When attempting to update an existing PaladinRegistration", func() {
It("Should block the update and return an error", func() {
// Fetch the newly created resource to ensure we have the latest version
fetched := &PaladinRegistration{}
err := k8sClient.Get(context.Background(), key, fetched)
Expect(err).NotTo(HaveOccurred())

fetched.Spec.Registry = "other-registry"

// Attempt to update, expecting the webhook to block
By("Updating PaladinRegistration, expecting a webhook error")
err = k8sClient.Update(context.Background(), fetched)
Expect(err).To(HaveOccurred())
// Check the error message from the webhook
Expect(err.Error()).To(ContainSubstring("updates to PaladinRegistration resources are not allowed"))
})
})

Context("When attempting to delete an existing PaladinRegistration", func() {
It("Should block the delete and return an error", func() {
// Attempt to delete, expecting the webhook to block
By("Deleting PaladinRegistration, expecting a webhook error")
err := k8sClient.Delete(context.Background(), pr)
Expect(err).To(HaveOccurred())
// Check the error message from the webhook
Expect(err.Error()).To(ContainSubstring("deletions of PaladinRegistration resources are not allowed"))
})
})
})
55 changes: 55 additions & 0 deletions operator/api/v1alpha1/paladinregistry_webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
Copyright 2024.

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 v1alpha1

import (
"fmt"

"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)

// SetupWebhookWithManager will setup the manager to manage the webhooks
func (r *PaladinRegistry) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(r).
Complete()
}

// +kubebuilder:webhook:path=/validate-core-paladin-io-v1alpha1-paladinregistry,mutating=false,failurePolicy=fail,sideEffects=None,groups=core.paladin.io,resources=paladinregistries,verbs=create;update;delete,versions=v1alpha1,name=vpaladinregistry.kb.io,admissionReviewVersions=v1

var _ webhook.Validator = &PaladinRegistry{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *PaladinRegistry) ValidateCreate() (admission.Warnings, error) {
// allow creation
return nil, nil
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *PaladinRegistry) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
// Deny any update
return nil, fmt.Errorf("updates to PaladinRegistry resources are not allowed as they have already been submitted to the blockchain and cannot be modified")
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *PaladinRegistry) ValidateDelete() (admission.Warnings, error) {
// Deny any delete
return nil, fmt.Errorf("deletions of PaladinRegistry resources are not allowed as they have already been submitted to the blockchain and cannot be deleted")
}
96 changes: 96 additions & 0 deletions operator/api/v1alpha1/paladinregistry_webhook_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
Copyright 2024.

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 v1alpha1

import (
"context"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)

var _ = Describe("PaladinRegistry Webhook - Block Update & Delete", func() {

ctx := context.Background()

key := types.NamespacedName{
Name: "resource",
Namespace: "default",
}

registry := &PaladinRegistry{}

BeforeEach(func() {
// Attempt to fetch the resource. If it doesn't exist, create a valid one.
err := k8sClient.Get(ctx, key, registry)
if err != nil && errors.IsNotFound(err) {
registry = &PaladinRegistry{
ObjectMeta: metav1.ObjectMeta{
Name: key.Name,
Namespace: key.Namespace,
},
Spec: PaladinRegistrySpec{
Type: RegistryTypeEVM,
EVM: EVMRegistryConfig{
SmartContractDeployment: "registry",
},
Plugin: PluginConfig{
Type: "c-shared",
Library: "/app/registries/libevm.so",
},
ConfigJSON: "{}",
},
}
Expect(k8sClient.Create(ctx, registry)).To(Succeed())
} else {
// If any other error occurred, fail the test immediately
Expect(err).NotTo(HaveOccurred())
}
})

Context("When attempting to update an existing PaladinRegistry", func() {
It("Should block the update and return an error", func() {
fetched := &PaladinRegistry{}
// Fetch the resource to ensure we have the latest version
err := k8sClient.Get(ctx, key, fetched)
Expect(err).NotTo(HaveOccurred())

// Attempt to change something in the spec
fetched.Spec.ConfigJSON = "{\"some\":\"update\"}"

// Update should fail due to the webhook blocking updates
By("Updating PaladinRegistry, expecting a webhook error")
err = k8sClient.Update(ctx, fetched)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("updates to PaladinRegistry resources are not allowed"))
})
})

Context("When attempting to delete an existing PaladinRegistry", func() {
It("Should block the delete and return an error", func() {
// Attempt to delete, expecting the webhook to block
By("Deleting PaladinRegistry, expecting a webhook error")
err := k8sClient.Delete(ctx, registry)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("deletions of PaladinRegistry resources are not allowed"))
})
})
})
52 changes: 52 additions & 0 deletions operator/api/v1alpha1/smartcontractdeployment_webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
Copyright 2024.

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 v1alpha1

import (
"fmt"

"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)

// SetupWebhookWithManager will setup the manager to manage the webhooks
func (r *SmartContractDeployment) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(r).
Complete()
}

//+kubebuilder:webhook:path=/validate-core-paladin-io-v1alpha1-smartcontractdeployment,mutating=false,failurePolicy=fail,sideEffects=None,groups=core.paladin.io,resources=smartcontractdeployments,verbs=create;update;delete,versions=v1alpha1,name=vsmartcontractdeployment.kb.io,admissionReviewVersions=v1

var _ webhook.Validator = &SmartContractDeployment{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *SmartContractDeployment) ValidateCreate() (admission.Warnings, error) {
return nil, nil
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *SmartContractDeployment) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
return nil, fmt.Errorf("updates to SmartContractDeployment resources are not allowed as they have already been submitted to the blockchain and cannot be modified")
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *SmartContractDeployment) ValidateDelete() (admission.Warnings, error) {
return nil, fmt.Errorf("deletions of SmartContractDeployment resources are not allowed as they have already been submitted to the blockchain and cannot be deleted")
}
Loading
Loading