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

feat(webhook): add validation for namspace delete requests #1754

Merged
merged 3 commits into from
Sep 14, 2020
Merged
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
1 change: 1 addition & 0 deletions changelogs/unreleased/1754-shubham14bajpai
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
feat(webhook): add validation for namspace delete requests
9 changes: 8 additions & 1 deletion cmd/admission-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"k8s.io/klog"

clientset "github.com/openebs/maya/pkg/client/generated/clientset/versioned"
ndmclientset "github.com/openebs/maya/pkg/client/generated/openebs.io/ndm/v1alpha1/clientset/internalclientset"
snapclientset "github.com/openebs/maya/pkg/client/generated/openebs.io/snapshot/v1/clientset/internalclientset"
)

Expand Down Expand Up @@ -75,6 +76,12 @@ func main() {
klog.Fatalf("Error building openebs snapshot clientset: %s", err.Error())
}

// Building NDM Clientset
ndmClient, err := ndmclientset.NewForConfig(cfg)
if err != nil {
klog.Fatalf("Error building ndm clientset: %s", err.Error())
}

// Fetch a reference to the admission server deployment object
ownerReference, err := webhook.GetAdmissionReference()
if err != nil {
Expand All @@ -85,7 +92,7 @@ func main() {
klog.Fatal(validatorErr, "failed to initialize validation server")
}

wh, err := webhook.New(parameters, kubeClient, openebsClient, snapClient)
wh, err := webhook.New(parameters, kubeClient, openebsClient, snapClient, ndmClient)
if err != nil {
klog.Fatalf("failed to create validation webhook: %s", err.Error())
}
Expand Down
18 changes: 18 additions & 0 deletions pkg/webhook/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ var (
addCSPCDeleteRule,
addCVCWithUpdateRule,
addSPCWithDeleteRule,
addNSWithDeleteRule,
}
cvcRuleWithOperations = v1beta1.RuleWithOperations{
Operations: []v1beta1.OperationType{
Expand All @@ -102,6 +103,16 @@ var (
Resources: []string{"storagepoolclaims"},
},
}
nsRuleWithOperations = v1beta1.RuleWithOperations{
Operations: []v1beta1.OperationType{
v1beta1.Delete,
},
Rule: v1beta1.Rule{
APIGroups: []string{"*"},
APIVersions: []string{"*"},
Resources: []string{"namespaces"},
},
}
)

// createWebhookService creates our webhook Service resource if it does not
Expand Down Expand Up @@ -214,6 +225,7 @@ func createValidatingWebhookConfig(
},
cvcRuleWithOperations,
spcRuleWithOperations,
nsRuleWithOperations,
},
ClientConfig: v1beta1.WebhookClientConfig{
Service: &v1beta1.ServiceReference{
Expand Down Expand Up @@ -507,6 +519,12 @@ func addSPCWithDeleteRule(config *v1beta1.ValidatingWebhookConfiguration) {
}
}

func addNSWithDeleteRule(config *v1beta1.ValidatingWebhookConfiguration) {
if util.IsCurrentLessThanNewVersion(config.Labels[string(apis.OpenEBSVersionKey)], "2.1.0") {
config.Webhooks[0].Rules = append(config.Webhooks[0].Rules, nsRuleWithOperations)
}
}

func getOldService(openebsNamespace string) (*corev1.ServiceList, error) {
v100SVCName := "admission-server-svc"
// fetch service 1.1.0 onwards based on label
Expand Down
76 changes: 75 additions & 1 deletion pkg/webhook/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
snapshot "github.com/openebs/maya/pkg/apis/openebs.io/snapshot/v1"
"github.com/openebs/maya/pkg/apis/openebs.io/v1alpha1"
clientset "github.com/openebs/maya/pkg/client/generated/clientset/versioned"
ndmclientset "github.com/openebs/maya/pkg/client/generated/openebs.io/ndm/v1alpha1/clientset/internalclientset"
snapclient "github.com/openebs/maya/pkg/client/generated/openebs.io/snapshot/v1/clientset/internalclientset"
"github.com/pkg/errors"
"k8s.io/api/admission/v1beta1"
Expand Down Expand Up @@ -80,6 +81,8 @@ type webhook struct {

// snapClientSet is a snaphot custom resource package generated from custom API group.
snapClientSet snapclient.Interface

ndmClientset ndmclientset.Interface
}

// Parameters are server configures parameters
Expand All @@ -105,7 +108,8 @@ func init() {
// set up secret (for TLS certs) k8s resource. This function runs forever.
func New(p Parameters, kubeClient kubernetes.Interface,
openebsClient clientset.Interface,
snapClient snapclient.Interface) (
snapClient snapclient.Interface,
ndmClient ndmclientset.Interface) (
*webhook, error) {

admNamespace, err := getOpenebsNamespace()
Expand Down Expand Up @@ -173,6 +177,7 @@ func New(p Parameters, kubeClient kubernetes.Interface,
kubeClient: kubeClient,
clientset: openebsClient,
snapClientSet: snapClient,
ndmClientset: ndmClient,
}
return wh, nil
}
Expand Down Expand Up @@ -421,6 +426,9 @@ func (wh *webhook) validate(ar *v1beta1.AdmissionReview) *v1beta1.AdmissionRespo
response.Allowed = true
klog.Info("Admission webhook request received")
switch req.Kind.Kind {
case "Namespace":
klog.V(2).Infof("Admission webhook request for type %s", req.Kind.Kind)
return wh.validateNamespace(ar)
Copy link
Contributor

@prateekpandey14 prateekpandey14 Oct 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this validate kicks in for any namespace deletion , can we use the webhook namespace itself to get the openebs namespace and skip for other namespace validations

case "PersistentVolumeClaim":
klog.V(2).Infof("Admission webhook request for type %s", req.Kind.Kind)
return wh.validatePVC(ar)
Expand Down Expand Up @@ -453,6 +461,72 @@ func (wh *webhook) validatePVC(ar *v1beta1.AdmissionReview) *v1beta1.AdmissionRe
return response
}

func (wh *webhook) validateNamespace(ar *v1beta1.AdmissionReview) *v1beta1.AdmissionResponse {
req := ar.Request
response := &v1beta1.AdmissionResponse{}
response.Allowed = true
// validates only if requested operation is DELETE
if req.Operation == v1beta1.Delete {
return wh.validateNamespaceDeleteRequest(req)
}
klog.V(2).Info("Admission wehbook for Namespace module not " +
"configured for operations other than DELETE")
return response
}

func (wh *webhook) validateNamespaceDeleteRequest(req *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
response := &v1beta1.AdmissionResponse{}
response.Allowed = true
svcLabel := "openebs.io/controller-service=jiva-controller-svc"

msg := fmt.Sprintf("either BDCs or services with the label %s exists in the namespace %s.", svcLabel, req.Name)

// ignore the Delete request of Namespace if resource name is empty
if req.Name == "" {
return response
}

bdcList, err := wh.ndmClientset.OpenebsV1alpha1().
BlockDeviceClaims(req.Name).
List(metav1.ListOptions{})
if err != nil {
response.Allowed = false
response.Result = &metav1.Status{
Message: fmt.Sprintf("error listing BDC in namespace %s: %v", req.Name, err.Error()),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Failure is here in case of helm chart deletion

}
return response
}

if len(bdcList.Items) != 0 {
response.Allowed = false
response.Result = &metav1.Status{
Message: msg,
}
return response
}

svcList, err := wh.kubeClient.CoreV1().Services(req.Name).
List(metav1.ListOptions{
LabelSelector: svcLabel,
})
if err != nil {
response.Allowed = false
response.Result = &metav1.Status{
Message: fmt.Sprintf("error listing svc in namespace %s: %v", req.Name, err.Error()),
}
return response
}

if len(svcList.Items) != 0 {
response.Allowed = false
response.Result = &metav1.Status{
Message: msg,
}
return response
}
return response
}

// getCstorVolumes gets the list of CstorVolumes based in the source-volume labels
func (wh *webhook) getCstorVolumes(listOptions metav1.ListOptions) (*v1alpha1.CStorVolumeList, error) {
return wh.clientset.OpenebsV1alpha1().CStorVolumes("").List(listOptions)
Expand Down