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

controllers: webhook for restricting storageclaims to auto created ones #292

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
9 changes: 9 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,15 @@ func main() {
}},
)

setupLog.Info("registering StorageClaim validating webhook endpoint")
hookServer.Register("/validate-storageclim", &webhook.Admission{
Handler: &admwebhook.StorageClaimAdmission{
Client: mgr.GetClient(),
Decoder: admission.NewDecoder(mgr.GetScheme()),
Log: mgr.GetLogger().WithName("webhook.storageclaim"),
}},
)

if err = (&controller.StorageClientReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Expand Down
14 changes: 11 additions & 3 deletions internal/controller/operatorconfigmap_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,6 @@ func (c *OperatorConfigMapReconciler) reconcileSubscriptionValidatingWebhook() e
whConfig := &admrv1.ValidatingWebhookConfiguration{}
whConfig.Name = templates.SubscriptionWebhookName

// TODO (lgangava): after change to configmap controller, need to remove webhook during deletion
err := c.createOrUpdate(whConfig, func() error {

// openshift fills in the ca on finding this annotation
Expand All @@ -555,7 +554,7 @@ func (c *OperatorConfigMapReconciler) reconcileSubscriptionValidatingWebhook() e

var caBundle []byte
if len(whConfig.Webhooks) == 0 {
whConfig.Webhooks = make([]admrv1.ValidatingWebhook, 1)
whConfig.Webhooks = make([]admrv1.ValidatingWebhook, 2)
} else {
// do not mutate CA bundle that was injected by openshift
caBundle = whConfig.Webhooks[0].ClientConfig.CABundle
Expand All @@ -565,7 +564,7 @@ func (c *OperatorConfigMapReconciler) reconcileSubscriptionValidatingWebhook() e
var wh *admrv1.ValidatingWebhook = &whConfig.Webhooks[0]
templates.SubscriptionValidatingWebhook.DeepCopyInto(wh)

wh.Name = whConfig.Name
wh.Name = templates.SubscriptionWebhookName
// only send requests received from own namespace
wh.NamespaceSelector = &metav1.LabelSelector{
MatchLabels: map[string]string{
Expand All @@ -583,6 +582,15 @@ func (c *OperatorConfigMapReconciler) reconcileSubscriptionValidatingWebhook() e
// send request to the service running in own namespace
wh.ClientConfig.Service.Namespace = c.OperatorNamespace

wh = &whConfig.Webhooks[1]
templates.StorageClaimValidatingWebhook.DeepCopyInto(wh)

wh.Name = templates.StorageClaimWebhookName
// preserve the existing (injected) CA bundle if any
// reusing same caBundle as the certs are being generated by same signer
wh.ClientConfig.CABundle = caBundle
// send request to the service running in own namespace
wh.ClientConfig.Service.Namespace = c.OperatorNamespace
return nil
})

Expand Down
27 changes: 27 additions & 0 deletions pkg/templates/validatingwebhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

const (
SubscriptionWebhookName = "subscription.ocs.openshift.io"
StorageClaimWebhookName = "storageclaim.ocs.openshift.io"
)

var SubscriptionValidatingWebhook = admrv1.ValidatingWebhook{
Expand Down Expand Up @@ -34,3 +35,29 @@ var SubscriptionValidatingWebhook = admrv1.ValidatingWebhook{
// fail the validation if webhook can't be reached
FailurePolicy: ptr.To(admrv1.Fail),
}

var StorageClaimValidatingWebhook = admrv1.ValidatingWebhook{
ClientConfig: admrv1.WebhookClientConfig{
Service: &admrv1.ServiceReference{
Name: "ocs-client-operator-webhook-server",
Path: ptr.To("/validate-storageclaim"),
Port: ptr.To(int32(443)),
},
},
Rules: []admrv1.RuleWithOperations{
{
Rule: admrv1.Rule{
APIGroups: []string{"ocs.openshift.io"},
APIVersions: []string{"v1alpha1"},
Resources: []string{"storageclaims"},
Scope: ptr.To(admrv1.ClusterScope),
},
Operations: []admrv1.OperationType{admrv1.Create},
},
},
SideEffects: ptr.To(admrv1.SideEffectClassNone),
TimeoutSeconds: ptr.To(int32(30)),
AdmissionReviewVersions: []string{"v1"},
// fail the validation if webhook can't be reached
FailurePolicy: ptr.To(admrv1.Fail),
}
56 changes: 56 additions & 0 deletions pkg/webhook/storageclaim.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package webhook

import (
"context"
"fmt"
"net/http"

"github.com/red-hat-storage/ocs-client-operator/api/v1alpha1"

"github.com/go-logr/logr"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)

var (
rbdFormatWithSuffix = "%s-ceph-rbd"
cephFsFormatWithSuffix = "%s-cephfs"
)

type StorageClaimAdmission struct {
Client client.Client
Decoder admission.Decoder
Log logr.Logger
}

func (s *StorageClaimAdmission) Handle(ctx context.Context, req admission.Request) admission.Response {
s.Log.Info("Request received for admission review")

// review should be for a storageClaim
storageClaim := &v1alpha1.StorageClaim{}
if err := s.Decoder.Decode(req, storageClaim); err != nil {
s.Log.Error(err, "failed to decode admission review as storageclaim")
return admission.Errored(http.StatusBadRequest, fmt.Errorf("only storageclaims admission reviews are supported: %v", err))
}

storageClients := &v1alpha1.StorageClientList{}
if err := s.Client.List(ctx, storageClients); err != nil {
s.Log.Error(err, "failed to list storageclients")
return admission.Errored(http.StatusInternalServerError, fmt.Errorf("failed to list storageclients for validating subscription request: %v", err))
}

for idx := range storageClients.Items {
if storageClients.Items[idx].Status.Phase != v1alpha1.StorageClientFailed {
clientName := storageClients.Items[idx].Name
supportedRbdClaim := fmt.Sprintf(rbdFormatWithSuffix, clientName)
supportedCephFsClaim := fmt.Sprintf(cephFsFormatWithSuffix, clientName)
if storageClaim.Name == supportedRbdClaim || storageClaim.Name == supportedCephFsClaim {
s.Log.Info("Allowing review request as the storageclaim has one of the supported names")
return admission.Allowed("valid storageclaim")
}
}
}

s.Log.Info("Rejecting review request as the storageclaim name doesn't match any of the supported names")
return admission.Denied(fmt.Sprintf("unsupported storageclaim with name %s", storageClaim.Name))
}
Loading