Skip to content

Commit

Permalink
Merge pull request #78 from rabbitmq/vhost-policy-user-webhooks
Browse files Browse the repository at this point in the history
validation webhooks for users, vhosts, and policies
  • Loading branch information
ChunyiLyu committed Mar 22, 2021
2 parents aadc5da + db1ffb2 commit dc8357e
Show file tree
Hide file tree
Showing 22 changed files with 454 additions and 27 deletions.
2 changes: 1 addition & 1 deletion api/v1alpha1/exchange_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ var _ = Describe("exchange webhook", func() {

var exchange = Exchange{
ObjectMeta: metav1.ObjectMeta{
Name: "update-binding",
Name: "test-exchange",
},
Spec: ExchangeSpec{
Name: "test",
Expand Down
8 changes: 8 additions & 0 deletions api/v1alpha1/policy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)

// PolicySpec defines the desired state of Policy
Expand Down Expand Up @@ -66,6 +67,13 @@ type PolicyList struct {
Items []Policy `json:"items"`
}

func (p *Policy) GroupResource() schema.GroupResource {
return schema.GroupResource{
Group: p.GroupVersionKind().Group,
Resource: p.GroupVersionKind().Kind,
}
}

func init() {
SchemeBuilder.Register(&Policy{}, &PolicyList{})
}
55 changes: 55 additions & 0 deletions api/v1alpha1/policy_webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package v1alpha1

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"
)

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

// +kubebuilder:webhook:verbs=create;update,path=/validate-rabbitmq-com-v1alpha1-policy,mutating=false,failurePolicy=fail,groups=rabbitmq.com,resources=policies,versions=v1alpha1,name=vpolicy.kb.io,sideEffects=none,admissionReviewVersions=v1

var _ webhook.Validator = &Policy{}

// no validation on create
func (p *Policy) ValidateCreate() error {
return nil
}

// returns error type 'forbidden' for updates on policy name, vhost and rabbitmqClusterReference
func (p *Policy) ValidateUpdate(old runtime.Object) error {
oldPolicy, ok := old.(*Policy)
if !ok {
return apierrors.NewBadRequest(fmt.Sprintf("expected a policy but got a %T", old))
}

detailMsg := "updates on name, vhost and rabbitmqClusterReference are all forbidden"
if p.Spec.Name != oldPolicy.Spec.Name {
return apierrors.NewForbidden(p.GroupResource(), p.Name,
field.Forbidden(field.NewPath("spec", "name"), detailMsg))
}

if p.Spec.Vhost != oldPolicy.Spec.Vhost {
return apierrors.NewForbidden(p.GroupResource(), p.Name,
field.Forbidden(field.NewPath("spec", "vhost"), detailMsg))
}

if p.Spec.RabbitmqClusterReference != oldPolicy.Spec.RabbitmqClusterReference {
return apierrors.NewForbidden(p.GroupResource(), p.Name,
field.Forbidden(field.NewPath("spec", "rabbitmqClusterReference"), detailMsg))
}
return nil
}

// no validation on delete
func (p *Policy) ValidateDelete() error {
return nil
}
73 changes: 73 additions & 0 deletions api/v1alpha1/policy_webhook_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package v1alpha1

import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)

var _ = Describe("policy webhook", func() {
var policy = Policy{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
},
Spec: PolicySpec{
Name: "test",
Vhost: "/test",
Pattern: "a-pattern",
ApplyTo: "all",
Priority: 0,
RabbitmqClusterReference: RabbitmqClusterReference{
Name: "a-cluster",
Namespace: "default",
},
},
}

It("does not allow updates on policy name", func() {
newPolicy := policy.DeepCopy()
newPolicy.Spec.Name = "new-name"
Expect(apierrors.IsForbidden(newPolicy.ValidateUpdate(&policy))).To(BeTrue())
})

It("does not allow updates on vhost", func() {
newPolicy := policy.DeepCopy()
newPolicy.Spec.Vhost = "new-vhost"
Expect(apierrors.IsForbidden(newPolicy.ValidateUpdate(&policy))).To(BeTrue())
})

It("does not allow updates on RabbitmqClusterReference", func() {
newPolicy := policy.DeepCopy()
newPolicy.Spec.RabbitmqClusterReference = RabbitmqClusterReference{
Name: "new-cluster",
Namespace: "default",
}
Expect(apierrors.IsForbidden(newPolicy.ValidateUpdate(&policy))).To(BeTrue())
})

It("allows updates on policy.spec.pattern", func() {
newPolicy := policy.DeepCopy()
newPolicy.Spec.Pattern = "new-pattern"
Expect(newPolicy.ValidateUpdate(&policy)).To(Succeed())
})

It("allows updates on policy.spec.applyTo", func() {
newPolicy := policy.DeepCopy()
newPolicy.Spec.ApplyTo = "queues"
Expect(newPolicy.ValidateUpdate(&policy)).To(Succeed())
})

It("allows updates on policy.spec.priority", func() {
newPolicy := policy.DeepCopy()
newPolicy.Spec.Priority = 1000
Expect(newPolicy.ValidateUpdate(&policy)).To(Succeed())
})

It("allows updates on policy.spec.definition", func() {
newPolicy := policy.DeepCopy()
newPolicy.Spec.Definition = &runtime.RawExtension{Raw: []byte(`{"key":"new-definition-value"}`)}
Expect(newPolicy.ValidateUpdate(&policy)).To(Succeed())
})
})
8 changes: 8 additions & 0 deletions api/v1alpha1/user_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)

// UserSpec defines the desired state of User.
Expand Down Expand Up @@ -74,6 +75,13 @@ type UserList struct {
Items []User `json:"items"`
}

func (u *User) GroupResource() schema.GroupResource {
return schema.GroupResource{
Group: u.GroupVersionKind().Group,
Resource: u.GroupVersionKind().Kind,
}
}

func init() {
SchemeBuilder.Register(&User{}, &UserList{})
}
45 changes: 45 additions & 0 deletions api/v1alpha1/user_webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package v1alpha1

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"
)

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

// +kubebuilder:webhook:verbs=create;update,path=/validate-rabbitmq-com-v1alpha1-user,mutating=false,failurePolicy=fail,groups=rabbitmq.com,resources=users,versions=v1alpha1,name=vuser.kb.io,sideEffects=none,admissionReviewVersions=v1

var _ webhook.Validator = &User{}

// no validation on create
func (u *User) ValidateCreate() error {
return nil
}

// returns error type 'forbidden' for updates on rabbitmqClusterReference
// user.spec.tags can be updated
func (u *User) ValidateUpdate(old runtime.Object) error {
oldUser, ok := old.(*User)
if !ok {
return apierrors.NewBadRequest(fmt.Sprintf("expected a user but got a %T", old))
}

if u.Spec.RabbitmqClusterReference != oldUser.Spec.RabbitmqClusterReference {
return apierrors.NewForbidden(u.GroupResource(), u.Name,
field.Forbidden(field.NewPath("spec", "rabbitmqClusterReference"), "update on rabbitmqClusterReference is forbidden"))
}
return nil
}

// no validation on delete
func (u *User) ValidateDelete() error {
return nil
}
38 changes: 38 additions & 0 deletions api/v1alpha1/user_webhook_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package v1alpha1

import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var _ = Describe("user webhook", func() {
var user = User{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
},
Spec: UserSpec{
Tags: []UserTag{"policymaker"},
RabbitmqClusterReference: RabbitmqClusterReference{
Name: "a-cluster",
Namespace: "default",
},
},
}

It("does not allow updates on RabbitmqClusterReference", func() {
new := user.DeepCopy()
new.Spec.RabbitmqClusterReference = RabbitmqClusterReference{
Name: "new-cluster",
Namespace: "default",
}
Expect(apierrors.IsForbidden(new.ValidateUpdate(&user))).To(BeTrue())
})

It("allows update on tags", func() {
new := user.DeepCopy()
new.Spec.Tags = []UserTag{"monitoring"}
Expect(new.ValidateUpdate(&user)).To(Succeed())
})
})
8 changes: 8 additions & 0 deletions api/v1alpha1/vhost_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ package v1alpha1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)

// VhostSpec defines the desired state of Vhost
Expand Down Expand Up @@ -54,6 +55,13 @@ type VhostList struct {
Items []Vhost `json:"items"`
}

func (v *Vhost) GroupResource() schema.GroupResource {
return schema.GroupResource{
Group: v.GroupVersionKind().Group,
Resource: v.GroupVersionKind().Kind,
}
}

func init() {
SchemeBuilder.Register(&Vhost{}, &VhostList{})
}
52 changes: 52 additions & 0 deletions api/v1alpha1/vhost_webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package v1alpha1

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"
)

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

// +kubebuilder:webhook:verbs=create;update,path=/validate-rabbitmq-com-v1alpha1-vhost,mutating=false,failurePolicy=fail,groups=rabbitmq.com,resources=vhosts,versions=v1alpha1,name=vvhost.kb.io,sideEffects=none,admissionReviewVersions=v1

var _ webhook.Validator = &Vhost{}

// no validation on create
func (v *Vhost) ValidateCreate() error {
return nil
}

// returns error type 'forbidden' for updates on vhost name and rabbitmqClusterReference
// vhost.spec.tracing can be updated
func (v *Vhost) ValidateUpdate(old runtime.Object) error {
oldVhost, ok := old.(*Vhost)
if !ok {
return apierrors.NewBadRequest(fmt.Sprintf("expected a vhost but got a %T", old))
}

detailMsg := "updates on name and rabbitmqClusterReference are all forbidden"
if v.Spec.Name != oldVhost.Spec.Name {
return apierrors.NewForbidden(v.GroupResource(), v.Name,
field.Forbidden(field.NewPath("spec", "name"), detailMsg))
}

if v.Spec.RabbitmqClusterReference != oldVhost.Spec.RabbitmqClusterReference {
return apierrors.NewForbidden(v.GroupResource(), v.Name,
field.Forbidden(field.NewPath("spec", "rabbitmqClusterReference"), detailMsg))
}

return nil
}

// no validation on delete
func (v *Vhost) ValidateDelete() error {
return nil
}
46 changes: 46 additions & 0 deletions api/v1alpha1/vhost_webhook_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package v1alpha1

import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var _ = Describe("vhost webhook", func() {

var vhost = Vhost{
ObjectMeta: metav1.ObjectMeta{
Name: "test-vhost",
},
Spec: VhostSpec{
Name: "test",
Tracing: false,
RabbitmqClusterReference: RabbitmqClusterReference{
Name: "a-cluster",
Namespace: "default",
},
},
}

It("does not allow updates on vhost name", func() {
newVhost := vhost.DeepCopy()
newVhost.Spec.Name = "new-name"
Expect(apierrors.IsForbidden(newVhost.ValidateUpdate(&vhost))).To(BeTrue())
})

It("does not allow updates on RabbitmqClusterReference", func() {
newVhost := vhost.DeepCopy()
newVhost.Spec.RabbitmqClusterReference = RabbitmqClusterReference{
Name: "new-cluster",
Namespace: "default",
}
Expect(apierrors.IsForbidden(newVhost.ValidateUpdate(&vhost))).To(BeTrue())
})

It("allows updates on vhost.spec.tracing", func() {
newVhost := vhost.DeepCopy()
newVhost.Spec.Tracing = true
Expect(newVhost.ValidateUpdate(&vhost)).To(Succeed())
})
})

0 comments on commit dc8357e

Please sign in to comment.