Skip to content

Commit

Permalink
Merge pull request #841 from stuggi/one_ctlplane
Browse files Browse the repository at this point in the history
Only allow a single ctlplane per namespace
  • Loading branch information
openshift-merge-bot[bot] authored Jun 12, 2024
2 parents 9351c6f + 547257e commit 114839d
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 0 deletions.
40 changes: 40 additions & 0 deletions apis/core/v1beta1/openstackcontrolplane_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package v1beta1

import (
"context"
"fmt"
"strings"

Expand All @@ -26,11 +27,14 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)

var ctlplaneWebhookClient client.Client

// OpenStackControlPlaneDefaults -
type OpenStackControlPlaneDefaults struct {
RabbitMqImageURL string
Expand All @@ -49,6 +53,10 @@ func SetupOpenStackControlPlaneDefaults(defaults OpenStackControlPlaneDefaults)

// SetupWebhookWithManager sets up the Webhook with the Manager.
func (r *OpenStackControlPlane) SetupWebhookWithManager(mgr ctrl.Manager) error {
if ctlplaneWebhookClient == nil {
ctlplaneWebhookClient = mgr.GetClient()
}

return ctrl.NewWebhookManagedBy(mgr).
For(r).
Complete()
Expand All @@ -64,6 +72,38 @@ func (r *OpenStackControlPlane) ValidateCreate() (admission.Warnings, error) {

var allErrs field.ErrorList
basePath := field.NewPath("spec")

ctlplaneList := &OpenStackControlPlaneList{}
listOpts := []client.ListOption{
client.InNamespace(r.Namespace),
}
if err := ctlplaneWebhookClient.List(context.TODO(), ctlplaneList, listOpts...); err != nil {
return nil, apierrors.NewForbidden(
schema.GroupResource{
Group: GroupVersion.WithKind("OpenStackControlPlane").Group,
Resource: GroupVersion.WithKind("OpenStackControlPlane").Kind,
}, r.GetName(), &field.Error{
Type: field.ErrorTypeForbidden,
Field: "",
BadValue: r.Name,
Detail: err.Error(),
},
)
}
if len(ctlplaneList.Items) >= 1 {
return nil, apierrors.NewForbidden(
schema.GroupResource{
Group: GroupVersion.WithKind("OpenStackControlPlane").Group,
Resource: GroupVersion.WithKind("OpenStackControlPlane").Kind,
}, r.GetName(), &field.Error{
Type: field.ErrorTypeForbidden,
Field: "",
BadValue: r.Name,
Detail: "Only one OpenStackControlPlane instance per namespace is supported at this time.",
},
)
}

if err := r.ValidateCreateServices(basePath); err != nil {
allErrs = append(allErrs, err...)
}
Expand Down
35 changes: 35 additions & 0 deletions tests/functional/openstackoperator_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1673,6 +1673,41 @@ var _ = Describe("OpenStackOperator controller", func() {

var _ = Describe("OpenStackOperator Webhook", func() {

It("Blocks creating multiple ctlplane CRs in the same namespace", func() {
spec := GetDefaultOpenStackControlPlaneSpec()
spec["tls"] = GetTLSPublicSpec()
DeferCleanup(
th.DeleteInstance,
CreateOpenStackControlPlane(names.OpenStackControlplaneName, spec),
)

OSCtlplane := GetOpenStackControlPlane(names.OpenStackControlplaneName)
Expect(OSCtlplane.Labels).Should(Not(BeNil()))
Expect(OSCtlplane.Labels).Should(HaveKeyWithValue("core.openstack.org/openstackcontrolplane", ""))

raw := map[string]interface{}{
"apiVersion": "core.openstack.org/v1beta1",
"kind": "OpenStackControlPlane",
"metadata": map[string]interface{}{
"name": "foo",
"namespace": OSCtlplane.GetNamespace(),
},
"spec": spec,
}

unstructuredObj := &unstructured.Unstructured{Object: raw}
_, err := controllerutil.CreateOrPatch(
th.Ctx, th.K8sClient, unstructuredObj, func() error { return nil })
Expect(err).Should(HaveOccurred())
var statusError *k8s_errors.StatusError
Expect(errors.As(err, &statusError)).To(BeTrue())
Expect(statusError.ErrStatus.Details.Kind).To(Equal("OpenStackControlPlane"))
Expect(statusError.ErrStatus.Message).To(
ContainSubstring(
"Forbidden: Only one OpenStackControlPlane instance per namespace is supported at this time."),
)
})

It("Adds default label via defaulting webhook", func() {
spec := GetDefaultOpenStackControlPlaneSpec()
spec["tls"] = GetTLSPublicSpec()
Expand Down

0 comments on commit 114839d

Please sign in to comment.