Skip to content

Commit 88a2873

Browse files
dobsonjbertinatto
authored andcommitted
UPSTREAM: <carry>: STOR-1270: Admission plugin to deny deletion of storages.operator.openshift.io
1 parent 1cc1d84 commit 88a2873

File tree

3 files changed

+128
-0
lines changed

3 files changed

+128
-0
lines changed

openshift-kube-apiserver/admission/customresourcevalidation/customresourcevalidationregistration/cr_validation_registration.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/network"
2020
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/node"
2121
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/oauth"
22+
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/operator"
2223
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/project"
2324
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/rolebindingrestriction"
2425
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/route"
@@ -40,6 +41,7 @@ var AllCustomResourceValidators = []string{
4041
oauth.PluginName,
4142
project.PluginName,
4243
config.PluginName,
44+
operator.PluginName,
4345
scheduler.PluginName,
4446
clusterresourcequota.PluginName,
4547
securitycontextconstraints.PluginName,
@@ -70,6 +72,7 @@ func RegisterCustomResourceValidation(plugins *admission.Plugins) {
7072
oauth.Register(plugins)
7173
project.Register(plugins)
7274
config.Register(plugins)
75+
operator.Register(plugins)
7376
scheduler.Register(plugins)
7477
kubecontrollermanager.Register(plugins)
7578

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package operator
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"io"
7+
8+
"k8s.io/apiserver/pkg/admission"
9+
)
10+
11+
const PluginName = "operator.openshift.io/DenyDeleteClusterOperators"
12+
13+
// Register registers an admission plugin factory whose plugin prevents the deletion of cluster operator resources.
14+
func Register(plugins *admission.Plugins) {
15+
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
16+
return newAdmissionPlugin(), nil
17+
})
18+
}
19+
20+
var _ admission.ValidationInterface = &admissionPlugin{}
21+
22+
type admissionPlugin struct {
23+
*admission.Handler
24+
}
25+
26+
func newAdmissionPlugin() *admissionPlugin {
27+
return &admissionPlugin{Handler: admission.NewHandler(admission.Delete)}
28+
}
29+
30+
// Validate returns an error if there is an attempt to delete a cluster operator resource.
31+
func (p *admissionPlugin) Validate(ctx context.Context, attributes admission.Attributes, _ admission.ObjectInterfaces) error {
32+
if len(attributes.GetSubresource()) > 0 {
33+
return nil
34+
}
35+
if attributes.GetResource().Group != "operator.openshift.io" {
36+
return nil
37+
}
38+
switch attributes.GetResource().Resource {
39+
// Deletion is denied for storages.operator.openshift.io objects named cluster,
40+
// because MCO and KCM-O depend on this resource being present in order to
41+
// correctly set environment variables on kubelet and kube-controller-manager.
42+
case "storages":
43+
if attributes.GetName() != "cluster" {
44+
return nil
45+
}
46+
// Deletion is allowed for all other operator.openshift.io objects unless
47+
// explicitly listed above.
48+
default:
49+
return nil
50+
}
51+
return admission.NewForbidden(attributes, fmt.Errorf("deleting required %s.%s resource, named %s, is not allowed", attributes.GetResource().Resource, attributes.GetResource().Group, attributes.GetName()))
52+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package operator
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"k8s.io/apimachinery/pkg/runtime/schema"
8+
"k8s.io/apiserver/pkg/admission"
9+
)
10+
11+
func TestAdmissionPlugin_Validate(t *testing.T) {
12+
testCases := []struct {
13+
tcName string
14+
group string
15+
resource string
16+
name string
17+
denyDelete bool
18+
}{
19+
{
20+
tcName: "NotBlackListedResourceNamedCluster",
21+
group: "operator.openshift.io",
22+
resource: "notBlacklisted",
23+
name: "cluster",
24+
denyDelete: false,
25+
},
26+
{
27+
tcName: "NotBlackListedResourceNamedNotCluster",
28+
group: "operator.openshift.io",
29+
resource: "notBlacklisted",
30+
name: "notCluster",
31+
denyDelete: false,
32+
},
33+
{
34+
tcName: "StorageResourceNamedCluster",
35+
group: "operator.openshift.io",
36+
resource: "storages",
37+
name: "cluster",
38+
denyDelete: true,
39+
},
40+
{
41+
tcName: "StorageResourceNamedNotCluster",
42+
group: "operator.openshift.io",
43+
resource: "storages",
44+
name: "notCluster",
45+
denyDelete: false,
46+
},
47+
{
48+
tcName: "ClusterVersionNotVersion",
49+
group: "config.openshift.io",
50+
resource: "clusterversions",
51+
name: "instance",
52+
denyDelete: false,
53+
},
54+
{
55+
tcName: "OtherGroup",
56+
group: "not.operator.openshift.io",
57+
resource: "notBlacklisted",
58+
name: "cluster",
59+
denyDelete: false,
60+
},
61+
}
62+
for _, tc := range testCases {
63+
t.Run(tc.tcName, func(t *testing.T) {
64+
err := newAdmissionPlugin().Validate(context.TODO(), admission.NewAttributesRecord(
65+
nil, nil, schema.GroupVersionKind{}, "",
66+
tc.name, schema.GroupVersionResource{Group: tc.group, Resource: tc.resource},
67+
"", admission.Delete, nil, false, nil), nil)
68+
if tc.denyDelete != (err != nil) {
69+
t.Error(tc.denyDelete, err)
70+
}
71+
})
72+
}
73+
}

0 commit comments

Comments
 (0)