Skip to content

Commit

Permalink
UPSTREAM: <carry>: STOR-1270: Admission plugin to deny deletion of st…
Browse files Browse the repository at this point in the history
…orages.operator.openshift.io
  • Loading branch information
dobsonj authored and soltysh committed Dec 8, 2023
1 parent f808224 commit d7885b8
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 0 deletions.
Expand Up @@ -19,6 +19,7 @@ import (
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/network"
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/node"
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/oauth"
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/operator"
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/project"
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/rolebindingrestriction"
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/route"
Expand All @@ -40,6 +41,7 @@ var AllCustomResourceValidators = []string{
oauth.PluginName,
project.PluginName,
config.PluginName,
operator.PluginName,
scheduler.PluginName,
clusterresourcequota.PluginName,
securitycontextconstraints.PluginName,
Expand Down Expand Up @@ -70,6 +72,7 @@ func RegisterCustomResourceValidation(plugins *admission.Plugins) {
oauth.Register(plugins)
project.Register(plugins)
config.Register(plugins)
operator.Register(plugins)
scheduler.Register(plugins)
kubecontrollermanager.Register(plugins)

Expand Down
@@ -0,0 +1,52 @@
package operator

import (
"context"
"fmt"
"io"

"k8s.io/apiserver/pkg/admission"
)

const PluginName = "operator.openshift.io/DenyDeleteClusterOperators"

// Register registers an admission plugin factory whose plugin prevents the deletion of cluster operator resources.
func Register(plugins *admission.Plugins) {
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return newAdmissionPlugin(), nil
})
}

var _ admission.ValidationInterface = &admissionPlugin{}

type admissionPlugin struct {
*admission.Handler
}

func newAdmissionPlugin() *admissionPlugin {
return &admissionPlugin{Handler: admission.NewHandler(admission.Delete)}
}

// Validate returns an error if there is an attempt to delete a cluster operator resource.
func (p *admissionPlugin) Validate(ctx context.Context, attributes admission.Attributes, _ admission.ObjectInterfaces) error {
if len(attributes.GetSubresource()) > 0 {
return nil
}
if attributes.GetResource().Group != "operator.openshift.io" {
return nil
}
switch attributes.GetResource().Resource {
// Deletion is denied for storages.operator.openshift.io objects named cluster,
// because MCO and KCM-O depend on this resource being present in order to
// correctly set environment variables on kubelet and kube-controller-manager.
case "storages":
if attributes.GetName() != "cluster" {
return nil
}
// Deletion is allowed for all other operator.openshift.io objects unless
// explicitly listed above.
default:
return nil
}
return admission.NewForbidden(attributes, fmt.Errorf("deleting required %s.%s resource, named %s, is not allowed", attributes.GetResource().Resource, attributes.GetResource().Group, attributes.GetName()))
}
@@ -0,0 +1,73 @@
package operator

import (
"context"
"testing"

"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/admission"
)

func TestAdmissionPlugin_Validate(t *testing.T) {
testCases := []struct {
tcName string
group string
resource string
name string
denyDelete bool
}{
{
tcName: "NotBlackListedResourceNamedCluster",
group: "operator.openshift.io",
resource: "notBlacklisted",
name: "cluster",
denyDelete: false,
},
{
tcName: "NotBlackListedResourceNamedNotCluster",
group: "operator.openshift.io",
resource: "notBlacklisted",
name: "notCluster",
denyDelete: false,
},
{
tcName: "StorageResourceNamedCluster",
group: "operator.openshift.io",
resource: "storages",
name: "cluster",
denyDelete: true,
},
{
tcName: "StorageResourceNamedNotCluster",
group: "operator.openshift.io",
resource: "storages",
name: "notCluster",
denyDelete: false,
},
{
tcName: "ClusterVersionNotVersion",
group: "config.openshift.io",
resource: "clusterversions",
name: "instance",
denyDelete: false,
},
{
tcName: "OtherGroup",
group: "not.operator.openshift.io",
resource: "notBlacklisted",
name: "cluster",
denyDelete: false,
},
}
for _, tc := range testCases {
t.Run(tc.tcName, func(t *testing.T) {
err := newAdmissionPlugin().Validate(context.TODO(), admission.NewAttributesRecord(
nil, nil, schema.GroupVersionKind{}, "",
tc.name, schema.GroupVersionResource{Group: tc.group, Resource: tc.resource},
"", admission.Delete, nil, false, nil), nil)
if tc.denyDelete != (err != nil) {
t.Error(tc.denyDelete, err)
}
})
}
}

0 comments on commit d7885b8

Please sign in to comment.