Merge pull request #48707 from danielfm/node-restriction-pod-eviction-subresource

Automatic merge from submit-queue

Allow nodes to create evictions for its own pods in NodeRestriction admission controller

**What this PR does / why we need it**: This PR adds support for `pods/eviction` sub-resource to the NodeRestriction admission controller so it allows a node to evict pods bound to itself.

**Which issue this PR fixes**: fixes #48666

**Special notes for your reviewer**: The NodeRestriction already allows nodes to delete pods bound to itself, so allowing nodes to also delete pods via the Eviction API probably makes sense.

```release-note
NodeRestriction allows a node to evict pods bound to itself
```
This commit is contained in:
Kubernetes Submit Queue 2017-07-23 04:16:51 -07:00 committed by GitHub
commit 4d2a721223
7 changed files with 314 additions and 4 deletions

View File

@ -15,6 +15,7 @@ go_library(
deps = [ deps = [
"//pkg/api:go_default_library", "//pkg/api:go_default_library",
"//pkg/api/pod:go_default_library", "//pkg/api/pod:go_default_library",
"//pkg/apis/policy:go_default_library",
"//pkg/auth/nodeidentifier:go_default_library", "//pkg/auth/nodeidentifier:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library", "//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library", "//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
@ -32,6 +33,7 @@ go_test(
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = [
"//pkg/api:go_default_library", "//pkg/api:go_default_library",
"//pkg/apis/policy:go_default_library",
"//pkg/auth/nodeidentifier:go_default_library", "//pkg/auth/nodeidentifier:go_default_library",
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library", "//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library", "//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",

View File

@ -25,6 +25,7 @@ import (
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
podutil "k8s.io/kubernetes/pkg/api/pod" podutil "k8s.io/kubernetes/pkg/api/pod"
"k8s.io/kubernetes/pkg/apis/policy"
"k8s.io/kubernetes/pkg/auth/nodeidentifier" "k8s.io/kubernetes/pkg/auth/nodeidentifier"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
coreinternalversion "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" coreinternalversion "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
@ -102,6 +103,8 @@ func (c *nodePlugin) Admit(a admission.Attributes) error {
return c.admitPod(nodeName, a) return c.admitPod(nodeName, a)
case "status": case "status":
return c.admitPodStatus(nodeName, a) return c.admitPodStatus(nodeName, a)
case "eviction":
return c.admitPodEviction(nodeName, a)
default: default:
return admission.NewForbidden(a, fmt.Errorf("unexpected pod subresource %q", a.GetSubresource())) return admission.NewForbidden(a, fmt.Errorf("unexpected pod subresource %q", a.GetSubresource()))
} }
@ -161,6 +164,9 @@ func (c *nodePlugin) admitPod(nodeName string, a admission.Attributes) error {
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
// wasn't found in the server cache, do a live lookup before forbidding // wasn't found in the server cache, do a live lookup before forbidding
existingPod, err = c.podsGetter.Pods(a.GetNamespace()).Get(a.GetName(), v1.GetOptions{}) existingPod, err = c.podsGetter.Pods(a.GetNamespace()).Get(a.GetName(), v1.GetOptions{})
if errors.IsNotFound(err) {
return err
}
} }
if err != nil { if err != nil {
return admission.NewForbidden(a, err) return admission.NewForbidden(a, err)
@ -195,6 +201,45 @@ func (c *nodePlugin) admitPodStatus(nodeName string, a admission.Attributes) err
} }
} }
func (c *nodePlugin) admitPodEviction(nodeName string, a admission.Attributes) error {
switch a.GetOperation() {
case admission.Create:
// require eviction to an existing pod object
eviction, ok := a.GetObject().(*policy.Eviction)
if !ok {
return admission.NewForbidden(a, fmt.Errorf("unexpected type %T", a.GetObject()))
}
// use pod name from the admission attributes, if set, rather than from the submitted Eviction object
podName := a.GetName()
if len(podName) == 0 {
if len(eviction.Name) == 0 {
return admission.NewForbidden(a, fmt.Errorf("could not determine pod from request data"))
}
podName = eviction.Name
}
// get the existing pod from the server cache
existingPod, err := c.podsGetter.Pods(a.GetNamespace()).Get(podName, v1.GetOptions{ResourceVersion: "0"})
if errors.IsNotFound(err) {
// wasn't found in the server cache, do a live lookup before forbidding
existingPod, err = c.podsGetter.Pods(a.GetNamespace()).Get(podName, v1.GetOptions{})
if errors.IsNotFound(err) {
return err
}
}
if err != nil {
return admission.NewForbidden(a, err)
}
// only allow a node to evict a pod bound to itself
if existingPod.Spec.NodeName != nodeName {
return admission.NewForbidden(a, fmt.Errorf("node %s can only evict pods with spec.nodeName set to itself", nodeName))
}
return nil
default:
return admission.NewForbidden(a, fmt.Errorf("unexpected operation %s", a.GetOperation()))
}
}
func (c *nodePlugin) admitNode(nodeName string, a admission.Attributes) error { func (c *nodePlugin) admitNode(nodeName string, a admission.Attributes) error {
requestedName := a.GetName() requestedName := a.GetName()

View File

@ -24,6 +24,8 @@ import (
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/authentication/user" "k8s.io/apiserver/pkg/authentication/user"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/policy"
policyapi "k8s.io/kubernetes/pkg/apis/policy"
"k8s.io/kubernetes/pkg/auth/nodeidentifier" "k8s.io/kubernetes/pkg/auth/nodeidentifier"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
coreinternalversion "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" coreinternalversion "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
@ -40,6 +42,12 @@ func makeTestPod(namespace, name, node string, mirror bool) *api.Pod {
return pod return pod
} }
func makeTestPodEviction(name string) *policy.Eviction {
eviction := &policy.Eviction{}
eviction.Name = name
return eviction
}
func Test_nodePlugin_Admit(t *testing.T) { func Test_nodePlugin_Admit(t *testing.T) {
var ( var (
mynode = &user.DefaultInfo{Name: "system:node:mynode", Groups: []string{"system:nodes"}} mynode = &user.DefaultInfo{Name: "system:node:mynode", Groups: []string{"system:nodes"}}
@ -54,12 +62,22 @@ func Test_nodePlugin_Admit(t *testing.T) {
mypod = makeTestPod("ns", "mypod", "mynode", false) mypod = makeTestPod("ns", "mypod", "mynode", false)
otherpod = makeTestPod("ns", "otherpod", "othernode", false) otherpod = makeTestPod("ns", "otherpod", "othernode", false)
unboundpod = makeTestPod("ns", "unboundpod", "", false) unboundpod = makeTestPod("ns", "unboundpod", "", false)
unnamedpod = makeTestPod("ns", "", "mynode", false)
mymirrorpodEviction = makeTestPodEviction("mymirrorpod")
othermirrorpodEviction = makeTestPodEviction("othermirrorpod")
unboundmirrorpodEviction = makeTestPodEviction("unboundmirrorpod")
mypodEviction = makeTestPodEviction("mypod")
otherpodEviction = makeTestPodEviction("otherpod")
unboundpodEviction = makeTestPodEviction("unboundpod")
unnamedEviction = makeTestPodEviction("")
configmapResource = api.Resource("configmap").WithVersion("v1") configmapResource = api.Resource("configmap").WithVersion("v1")
configmapKind = api.Kind("ConfigMap").WithVersion("v1") configmapKind = api.Kind("ConfigMap").WithVersion("v1")
podResource = api.Resource("pods").WithVersion("v1") podResource = api.Resource("pods").WithVersion("v1")
podKind = api.Kind("Pod").WithVersion("v1") podKind = api.Kind("Pod").WithVersion("v1")
evictionKind = policyapi.Kind("Eviction").WithVersion("v1beta1")
nodeResource = api.Resource("nodes").WithVersion("v1") nodeResource = api.Resource("nodes").WithVersion("v1")
nodeKind = api.Kind("Node").WithVersion("v1") nodeKind = api.Kind("Node").WithVersion("v1")
@ -123,6 +141,30 @@ func Test_nodePlugin_Admit(t *testing.T) {
attributes: admission.NewAttributesRecord(nil, nil, podKind, mymirrorpod.Namespace, mymirrorpod.Name, podResource, "status", admission.Delete, mynode), attributes: admission.NewAttributesRecord(nil, nil, podKind, mymirrorpod.Namespace, mymirrorpod.Name, podResource, "status", admission.Delete, mynode),
err: "forbidden: unexpected operation", err: "forbidden: unexpected operation",
}, },
{
name: "allow create of eviction for mirror pod bound to self",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(mymirrorpodEviction, nil, evictionKind, mymirrorpod.Namespace, mymirrorpod.Name, podResource, "eviction", admission.Create, mynode),
err: "",
},
{
name: "forbid update of eviction for mirror pod bound to self",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(mymirrorpodEviction, nil, evictionKind, mymirrorpod.Namespace, mymirrorpod.Name, podResource, "eviction", admission.Update, mynode),
err: "forbidden: unexpected operation",
},
{
name: "forbid delete of eviction for mirror pod bound to self",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(mymirrorpodEviction, nil, evictionKind, mymirrorpod.Namespace, mymirrorpod.Name, podResource, "eviction", admission.Delete, mynode),
err: "forbidden: unexpected operation",
},
{
name: "allow create of unnamed eviction for mirror pod bound to self",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(unnamedEviction, nil, evictionKind, mymirrorpod.Namespace, mymirrorpod.Name, podResource, "eviction", admission.Create, mynode),
err: "",
},
// Mirror pods bound to another node // Mirror pods bound to another node
{ {
@ -161,6 +203,30 @@ func Test_nodePlugin_Admit(t *testing.T) {
attributes: admission.NewAttributesRecord(nil, nil, podKind, othermirrorpod.Namespace, othermirrorpod.Name, podResource, "status", admission.Delete, mynode), attributes: admission.NewAttributesRecord(nil, nil, podKind, othermirrorpod.Namespace, othermirrorpod.Name, podResource, "status", admission.Delete, mynode),
err: "forbidden: unexpected operation", err: "forbidden: unexpected operation",
}, },
{
name: "forbid create of eviction for mirror pod bound to another",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(othermirrorpodEviction, nil, evictionKind, othermirrorpod.Namespace, othermirrorpod.Name, podResource, "eviction", admission.Create, mynode),
err: "spec.nodeName set to itself",
},
{
name: "forbid update of eviction for mirror pod bound to another",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(othermirrorpodEviction, nil, evictionKind, othermirrorpod.Namespace, othermirrorpod.Name, podResource, "eviction", admission.Update, mynode),
err: "forbidden: unexpected operation",
},
{
name: "forbid delete of eviction for mirror pod bound to another",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(othermirrorpodEviction, nil, evictionKind, othermirrorpod.Namespace, othermirrorpod.Name, podResource, "eviction", admission.Delete, mynode),
err: "forbidden: unexpected operation",
},
{
name: "forbid create of unnamed eviction for mirror pod to another",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(unnamedEviction, nil, evictionKind, othermirrorpod.Namespace, othermirrorpod.Name, podResource, "eviction", admission.Create, mynode),
err: "spec.nodeName set to itself",
},
// Mirror pods not bound to any node // Mirror pods not bound to any node
{ {
@ -199,6 +265,30 @@ func Test_nodePlugin_Admit(t *testing.T) {
attributes: admission.NewAttributesRecord(nil, nil, podKind, unboundmirrorpod.Namespace, unboundmirrorpod.Name, podResource, "status", admission.Delete, mynode), attributes: admission.NewAttributesRecord(nil, nil, podKind, unboundmirrorpod.Namespace, unboundmirrorpod.Name, podResource, "status", admission.Delete, mynode),
err: "forbidden: unexpected operation", err: "forbidden: unexpected operation",
}, },
{
name: "forbid create of eviction for mirror pod unbound",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(unboundmirrorpodEviction, nil, evictionKind, unboundmirrorpod.Namespace, unboundmirrorpod.Name, podResource, "eviction", admission.Create, mynode),
err: "spec.nodeName set to itself",
},
{
name: "forbid update of eviction for mirror pod unbound",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(unboundmirrorpodEviction, nil, evictionKind, unboundmirrorpod.Namespace, unboundmirrorpod.Name, podResource, "eviction", admission.Update, mynode),
err: "forbidden: unexpected operation",
},
{
name: "forbid delete of eviction for mirror pod unbound",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(unboundmirrorpodEviction, nil, evictionKind, unboundmirrorpod.Namespace, unboundmirrorpod.Name, podResource, "eviction", admission.Delete, mynode),
err: "forbidden: unexpected operation",
},
{
name: "forbid create of unnamed eviction for mirror pod unbound",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(unnamedEviction, nil, evictionKind, unboundmirrorpod.Namespace, unboundmirrorpod.Name, podResource, "eviction", admission.Create, mynode),
err: "spec.nodeName set to itself",
},
// Normal pods bound to us // Normal pods bound to us
{ {
@ -237,6 +327,24 @@ func Test_nodePlugin_Admit(t *testing.T) {
attributes: admission.NewAttributesRecord(nil, nil, podKind, mypod.Namespace, mypod.Name, podResource, "status", admission.Delete, mynode), attributes: admission.NewAttributesRecord(nil, nil, podKind, mypod.Namespace, mypod.Name, podResource, "status", admission.Delete, mynode),
err: "forbidden: unexpected operation", err: "forbidden: unexpected operation",
}, },
{
name: "forbid update of eviction for normal pod bound to self",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(mypodEviction, nil, evictionKind, mypod.Namespace, mypod.Name, podResource, "eviction", admission.Update, mynode),
err: "forbidden: unexpected operation",
},
{
name: "forbid delete of eviction for normal pod bound to self",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(mypodEviction, nil, evictionKind, mypod.Namespace, mypod.Name, podResource, "eviction", admission.Delete, mynode),
err: "forbidden: unexpected operation",
},
{
name: "allow create of unnamed eviction for normal pod bound to self",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(unnamedEviction, nil, evictionKind, mypod.Namespace, mypod.Name, podResource, "eviction", admission.Create, mynode),
err: "",
},
// Normal pods bound to another // Normal pods bound to another
{ {
@ -275,6 +383,30 @@ func Test_nodePlugin_Admit(t *testing.T) {
attributes: admission.NewAttributesRecord(nil, nil, podKind, otherpod.Namespace, otherpod.Name, podResource, "status", admission.Delete, mynode), attributes: admission.NewAttributesRecord(nil, nil, podKind, otherpod.Namespace, otherpod.Name, podResource, "status", admission.Delete, mynode),
err: "forbidden: unexpected operation", err: "forbidden: unexpected operation",
}, },
{
name: "forbid create of eviction for normal pod bound to another",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(otherpodEviction, nil, evictionKind, otherpodEviction.Namespace, otherpodEviction.Name, podResource, "eviction", admission.Create, mynode),
err: "spec.nodeName set to itself",
},
{
name: "forbid update of eviction for normal pod bound to another",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(otherpodEviction, nil, evictionKind, otherpodEviction.Namespace, otherpodEviction.Name, podResource, "eviction", admission.Update, mynode),
err: "forbidden: unexpected operation",
},
{
name: "forbid delete of eviction for normal pod bound to another",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(otherpodEviction, nil, evictionKind, otherpodEviction.Namespace, otherpodEviction.Name, podResource, "eviction", admission.Delete, mynode),
err: "forbidden: unexpected operation",
},
{
name: "forbid create of eviction for normal pod bound to another",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(unnamedEviction, nil, evictionKind, otherpod.Namespace, otherpod.Name, podResource, "eviction", admission.Create, mynode),
err: "spec.nodeName set to itself",
},
// Normal pods not bound to any node // Normal pods not bound to any node
{ {
@ -313,6 +445,30 @@ func Test_nodePlugin_Admit(t *testing.T) {
attributes: admission.NewAttributesRecord(nil, nil, podKind, unboundpod.Namespace, unboundpod.Name, podResource, "status", admission.Delete, mynode), attributes: admission.NewAttributesRecord(nil, nil, podKind, unboundpod.Namespace, unboundpod.Name, podResource, "status", admission.Delete, mynode),
err: "forbidden: unexpected operation", err: "forbidden: unexpected operation",
}, },
{
name: "forbid create of eviction for normal pod unbound",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(unboundpodEviction, nil, evictionKind, unboundpod.Namespace, unboundpod.Name, podResource, "eviction", admission.Create, mynode),
err: "spec.nodeName set to itself",
},
{
name: "forbid update of eviction for normal pod unbound",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(unboundpodEviction, nil, evictionKind, unboundpod.Namespace, unboundpod.Name, podResource, "eviction", admission.Update, mynode),
err: "forbidden: unexpected operation",
},
{
name: "forbid delete of eviction for normal pod unbound",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(unboundpodEviction, nil, evictionKind, unboundpod.Namespace, unboundpod.Name, podResource, "eviction", admission.Delete, mynode),
err: "forbidden: unexpected operation",
},
{
name: "forbid create of unnamed eviction for normal unbound",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(unnamedEviction, nil, evictionKind, unboundpod.Namespace, unboundpod.Name, podResource, "eviction", admission.Create, mynode),
err: "spec.nodeName set to itself",
},
// Missing pod // Missing pod
{ {
@ -321,6 +477,57 @@ func Test_nodePlugin_Admit(t *testing.T) {
attributes: admission.NewAttributesRecord(nil, nil, podKind, unboundpod.Namespace, unboundpod.Name, podResource, "", admission.Delete, mynode), attributes: admission.NewAttributesRecord(nil, nil, podKind, unboundpod.Namespace, unboundpod.Name, podResource, "", admission.Delete, mynode),
err: "not found", err: "not found",
}, },
{
name: "forbid create of eviction for unknown pod",
podsGetter: noExistingPods,
attributes: admission.NewAttributesRecord(mypodEviction, nil, evictionKind, mypod.Namespace, mypod.Name, podResource, "eviction", admission.Create, mynode),
err: "not found",
},
{
name: "forbid update of eviction for unknown pod",
podsGetter: noExistingPods,
attributes: admission.NewAttributesRecord(mypodEviction, nil, evictionKind, mypod.Namespace, mypod.Name, podResource, "eviction", admission.Update, mynode),
err: "forbidden: unexpected operation",
},
{
name: "forbid delete of eviction for unknown pod",
podsGetter: noExistingPods,
attributes: admission.NewAttributesRecord(mypodEviction, nil, evictionKind, mypod.Namespace, mypod.Name, podResource, "eviction", admission.Delete, mynode),
err: "forbidden: unexpected operation",
},
{
name: "forbid create of unnamed eviction for unknown pod",
podsGetter: noExistingPods,
attributes: admission.NewAttributesRecord(unnamedEviction, nil, evictionKind, mypod.Namespace, mypod.Name, podResource, "eviction", admission.Create, mynode),
err: "not found",
},
// Eviction for unnamed pod
{
name: "allow create of eviction for unnamed pod",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(mypodEviction, nil, evictionKind, unnamedpod.Namespace, unnamedpod.Name, podResource, "eviction", admission.Create, mynode),
// use the submitted eviction resource name as the pod name
err: "",
},
{
name: "forbid update of eviction for unnamed pod",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(mypodEviction, nil, evictionKind, unnamedpod.Namespace, unnamedpod.Name, podResource, "eviction", admission.Update, mynode),
err: "forbidden: unexpected operation",
},
{
name: "forbid delete of eviction for unnamed pod",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(mypodEviction, nil, evictionKind, unnamedpod.Namespace, unnamedpod.Name, podResource, "eviction", admission.Delete, mynode),
err: "forbidden: unexpected operation",
},
{
name: "forbid create of unnamed eviction for unnamed pod",
podsGetter: existingPods,
attributes: admission.NewAttributesRecord(unnamedEviction, nil, evictionKind, unnamedpod.Namespace, unnamedpod.Name, podResource, "eviction", admission.Create, mynode),
err: "could not determine pod from request data",
},
// Resource pods // Resource pods
{ {

View File

@ -113,6 +113,9 @@ func NodeRules() []rbac.PolicyRule {
// Needed for the node to report status of pods it is running. // Needed for the node to report status of pods it is running.
// Use the NodeRestriction admission plugin to limit a node to updating status of pods bound to itself. // Use the NodeRestriction admission plugin to limit a node to updating status of pods bound to itself.
rbac.NewRule("update").Groups(legacyGroup).Resources("pods/status").RuleOrDie(), rbac.NewRule("update").Groups(legacyGroup).Resources("pods/status").RuleOrDie(),
// Needed for the node to create pod evictions.
// Use the NodeRestriction admission plugin to limit a node to creating evictions for pods bound to itself.
rbac.NewRule("create").Groups(legacyGroup).Resources("pods/eviction").RuleOrDie(),
// Needed for imagepullsecrets, rbd/ceph and secret volumes, and secrets in envs // Needed for imagepullsecrets, rbd/ceph and secret volumes, and secrets in envs
// Needed for configmap volume and envs // Needed for configmap volume and envs

View File

@ -686,6 +686,12 @@ items:
- pods/status - pods/status
verbs: verbs:
- update - update
- apiGroups:
- ""
resources:
- pods/eviction
verbs:
- create
- apiGroups: - apiGroups:
- "" - ""
resources: resources:

View File

@ -28,6 +28,7 @@ go_test(
"//pkg/apis/authorization:go_default_library", "//pkg/apis/authorization:go_default_library",
"//pkg/apis/autoscaling:go_default_library", "//pkg/apis/autoscaling:go_default_library",
"//pkg/apis/extensions:go_default_library", "//pkg/apis/extensions:go_default_library",
"//pkg/apis/policy:go_default_library",
"//pkg/apis/rbac:go_default_library", "//pkg/apis/rbac:go_default_library",
"//pkg/auth/authorizer/abac:go_default_library", "//pkg/auth/authorizer/abac:go_default_library",
"//pkg/auth/nodeidentifier:go_default_library", "//pkg/auth/nodeidentifier:go_default_library",

View File

@ -32,6 +32,7 @@ import (
"k8s.io/apiserver/pkg/authentication/user" "k8s.io/apiserver/pkg/authentication/user"
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/policy"
"k8s.io/kubernetes/pkg/auth/nodeidentifier" "k8s.io/kubernetes/pkg/auth/nodeidentifier"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion" informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
@ -224,6 +225,30 @@ func TestNodeAuthorizer(t *testing.T) {
deleteNode2 := func(client clientset.Interface) error { deleteNode2 := func(client clientset.Interface) error {
return client.Core().Nodes().Delete("node2", nil) return client.Core().Nodes().Delete("node2", nil)
} }
createNode2NormalPodEviction := func(client clientset.Interface) error {
return client.Policy().Evictions("ns").Evict(&policy.Eviction{
TypeMeta: metav1.TypeMeta{
APIVersion: "policy/v1beta1",
Kind: "Eviction",
},
ObjectMeta: metav1.ObjectMeta{
Name: "node2normalpod",
Namespace: "ns",
},
})
}
createNode2MirrorPodEviction := func(client clientset.Interface) error {
return client.Policy().Evictions("ns").Evict(&policy.Eviction{
TypeMeta: metav1.TypeMeta{
APIVersion: "policy/v1beta1",
Kind: "Eviction",
},
ObjectMeta: metav1.ObjectMeta{
Name: "node2mirrorpod",
Namespace: "ns",
},
})
}
nodeanonClient := clientsetForToken(tokenNodeUnknown, clientConfig) nodeanonClient := clientsetForToken(tokenNodeUnknown, clientConfig)
node1Client := clientsetForToken(tokenNode1, clientConfig) node1Client := clientsetForToken(tokenNode1, clientConfig)
@ -237,7 +262,9 @@ func TestNodeAuthorizer(t *testing.T) {
expectForbidden(t, getPV(nodeanonClient)) expectForbidden(t, getPV(nodeanonClient))
expectForbidden(t, createNode2NormalPod(nodeanonClient)) expectForbidden(t, createNode2NormalPod(nodeanonClient))
expectForbidden(t, createNode2MirrorPod(nodeanonClient)) expectForbidden(t, createNode2MirrorPod(nodeanonClient))
expectForbidden(t, deleteNode2NormalPod(nodeanonClient))
expectForbidden(t, deleteNode2MirrorPod(nodeanonClient)) expectForbidden(t, deleteNode2MirrorPod(nodeanonClient))
expectForbidden(t, createNode2MirrorPodEviction(nodeanonClient))
expectForbidden(t, createNode2(nodeanonClient)) expectForbidden(t, createNode2(nodeanonClient))
expectForbidden(t, updateNode2Status(nodeanonClient)) expectForbidden(t, updateNode2Status(nodeanonClient))
expectForbidden(t, deleteNode2(nodeanonClient)) expectForbidden(t, deleteNode2(nodeanonClient))
@ -249,7 +276,8 @@ func TestNodeAuthorizer(t *testing.T) {
expectForbidden(t, getPV(node1Client)) expectForbidden(t, getPV(node1Client))
expectForbidden(t, createNode2NormalPod(nodeanonClient)) expectForbidden(t, createNode2NormalPod(nodeanonClient))
expectForbidden(t, createNode2MirrorPod(node1Client)) expectForbidden(t, createNode2MirrorPod(node1Client))
expectForbidden(t, deleteNode2MirrorPod(node1Client)) expectNotFound(t, deleteNode2MirrorPod(node1Client))
expectNotFound(t, createNode2MirrorPodEviction(node1Client))
expectForbidden(t, createNode2(node1Client)) expectForbidden(t, createNode2(node1Client))
expectForbidden(t, updateNode2Status(node1Client)) expectForbidden(t, updateNode2Status(node1Client))
expectForbidden(t, deleteNode2(node1Client)) expectForbidden(t, deleteNode2(node1Client))
@ -264,6 +292,8 @@ func TestNodeAuthorizer(t *testing.T) {
// mirror pod and self node lifecycle is allowed // mirror pod and self node lifecycle is allowed
expectAllowed(t, createNode2MirrorPod(node2Client)) expectAllowed(t, createNode2MirrorPod(node2Client))
expectAllowed(t, deleteNode2MirrorPod(node2Client)) expectAllowed(t, deleteNode2MirrorPod(node2Client))
expectAllowed(t, createNode2MirrorPod(node2Client))
expectAllowed(t, createNode2MirrorPodEviction(node2Client))
expectAllowed(t, createNode2(node2Client)) expectAllowed(t, createNode2(node2Client))
expectAllowed(t, updateNode2Status(node2Client)) expectAllowed(t, updateNode2Status(node2Client))
expectAllowed(t, deleteNode2(node2Client)) expectAllowed(t, deleteNode2(node2Client))
@ -280,8 +310,10 @@ func TestNodeAuthorizer(t *testing.T) {
expectForbidden(t, createNode2NormalPod(nodeanonClient)) expectForbidden(t, createNode2NormalPod(nodeanonClient))
expectForbidden(t, updateNode2NormalPodStatus(nodeanonClient)) expectForbidden(t, updateNode2NormalPodStatus(nodeanonClient))
expectForbidden(t, deleteNode2NormalPod(nodeanonClient)) expectForbidden(t, deleteNode2NormalPod(nodeanonClient))
expectForbidden(t, createNode2NormalPodEviction(nodeanonClient))
expectForbidden(t, createNode2MirrorPod(nodeanonClient)) expectForbidden(t, createNode2MirrorPod(nodeanonClient))
expectForbidden(t, deleteNode2MirrorPod(nodeanonClient)) expectForbidden(t, deleteNode2MirrorPod(nodeanonClient))
expectForbidden(t, createNode2MirrorPodEviction(nodeanonClient))
expectForbidden(t, getSecret(node1Client)) expectForbidden(t, getSecret(node1Client))
expectForbidden(t, getPVSecret(node1Client)) expectForbidden(t, getPVSecret(node1Client))
@ -291,8 +323,10 @@ func TestNodeAuthorizer(t *testing.T) {
expectForbidden(t, createNode2NormalPod(node1Client)) expectForbidden(t, createNode2NormalPod(node1Client))
expectForbidden(t, updateNode2NormalPodStatus(node1Client)) expectForbidden(t, updateNode2NormalPodStatus(node1Client))
expectForbidden(t, deleteNode2NormalPod(node1Client)) expectForbidden(t, deleteNode2NormalPod(node1Client))
expectForbidden(t, createNode2NormalPodEviction(node1Client))
expectForbidden(t, createNode2MirrorPod(node1Client)) expectForbidden(t, createNode2MirrorPod(node1Client))
expectForbidden(t, deleteNode2MirrorPod(node1Client)) expectNotFound(t, deleteNode2MirrorPod(node1Client))
expectNotFound(t, createNode2MirrorPodEviction(node1Client))
// node2 can get referenced objects now // node2 can get referenced objects now
expectAllowed(t, getSecret(node2Client)) expectAllowed(t, getSecret(node2Client))
@ -305,6 +339,11 @@ func TestNodeAuthorizer(t *testing.T) {
expectAllowed(t, deleteNode2NormalPod(node2Client)) expectAllowed(t, deleteNode2NormalPod(node2Client))
expectAllowed(t, createNode2MirrorPod(node2Client)) expectAllowed(t, createNode2MirrorPod(node2Client))
expectAllowed(t, deleteNode2MirrorPod(node2Client)) expectAllowed(t, deleteNode2MirrorPod(node2Client))
// recreate as an admin to test eviction
expectAllowed(t, createNode2NormalPod(superuserClient))
expectAllowed(t, createNode2MirrorPod(superuserClient))
expectAllowed(t, createNode2NormalPodEviction(node2Client))
expectAllowed(t, createNode2MirrorPodEviction(node2Client))
} }
func expectForbidden(t *testing.T, err error) { func expectForbidden(t *testing.T, err error) {
@ -314,6 +353,13 @@ func expectForbidden(t *testing.T, err error) {
} }
} }
func expectNotFound(t *testing.T, err error) {
if !errors.IsNotFound(err) {
_, file, line, _ := runtime.Caller(1)
t.Errorf("%s:%d: Expected notfound error, got %v", filepath.Base(file), line, err)
}
}
func expectAllowed(t *testing.T, err error) { func expectAllowed(t *testing.T, err error) {
if err != nil { if err != nil {
_, file, line, _ := runtime.Caller(1) _, file, line, _ := runtime.Caller(1)