mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-06 10:43:56 +00:00
integration: RBAC tests for /resize request
The test confirms that the subject can successfully resize the Pod resources but not the entire pod.
This commit is contained in:
parent
ce0f4597d3
commit
7e3ae1060f
@ -2589,7 +2589,7 @@ func Test_ValidateSecondaryAuthorization(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "serviceaccount is authorized for custom verb on current resource",
|
name: "serviceaccount is authorized for custom verb on current resource",
|
||||||
extraAccountFn: serviceAccountClient("default", "extra-acct"),
|
extraAccountFn: authutil.ServiceAccountClient("default", "extra-acct"),
|
||||||
extraAccountRbac: &rbacv1.PolicyRule{
|
extraAccountRbac: &rbacv1.PolicyRule{
|
||||||
Verbs: []string{"anthropomorphize"},
|
Verbs: []string{"anthropomorphize"},
|
||||||
APIGroups: []string{""},
|
APIGroups: []string{""},
|
||||||
@ -2985,8 +2985,6 @@ contexts:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type clientFn func(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset
|
|
||||||
|
|
||||||
func secondaryAuthorizationUserClient(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset {
|
func secondaryAuthorizationUserClient(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset {
|
||||||
clientConfig = rest.CopyConfig(clientConfig)
|
clientConfig = rest.CopyConfig(clientConfig)
|
||||||
clientConfig.Impersonate = rest.ImpersonationConfig{
|
clientConfig.Impersonate = rest.ImpersonationConfig{
|
||||||
@ -3002,29 +3000,7 @@ func secondaryAuthorizationUserClient(t *testing.T, adminClient *clientset.Clien
|
|||||||
}
|
}
|
||||||
|
|
||||||
func secondaryAuthorizationServiceAccountClient(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset {
|
func secondaryAuthorizationServiceAccountClient(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset {
|
||||||
return serviceAccountClient("default", "test-service-acct")(t, adminClient, clientConfig, rules)
|
return authutil.ServiceAccountClient("default", "test-service-acct")(t, adminClient, clientConfig, rules)
|
||||||
}
|
|
||||||
|
|
||||||
func serviceAccountClient(namespace, name string) clientFn {
|
|
||||||
return func(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset {
|
|
||||||
clientConfig = rest.CopyConfig(clientConfig)
|
|
||||||
sa, err := adminClient.CoreV1().ServiceAccounts(namespace).Create(context.TODO(), &v1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: name}}, metav1.CreateOptions{})
|
|
||||||
if err != nil && !apierrors.IsAlreadyExists(err) {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
uid := sa.UID
|
|
||||||
|
|
||||||
clientConfig.Impersonate = rest.ImpersonationConfig{
|
|
||||||
UserName: "system:serviceaccount:" + namespace + ":" + name,
|
|
||||||
UID: string(uid),
|
|
||||||
}
|
|
||||||
client := clientset.NewForConfigOrDie(clientConfig)
|
|
||||||
|
|
||||||
for _, rule := range rules {
|
|
||||||
authutil.GrantServiceAccountAuthorization(t, context.TODO(), adminClient, name, namespace, rule)
|
|
||||||
}
|
|
||||||
return client
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func withWaitReadyConstraintAndExpression(policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
|
func withWaitReadyConstraintAndExpression(policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
|
||||||
|
@ -23,12 +23,15 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
authorizationv1 "k8s.io/api/authorization/v1"
|
authorizationv1 "k8s.io/api/authorization/v1"
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
authorizationv1client "k8s.io/client-go/kubernetes/typed/authorization/v1"
|
authorizationv1client "k8s.io/client-go/kubernetes/typed/authorization/v1"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WaitForNamedAuthorizationUpdate checks if the given user can perform the named verb and action on the named resource.
|
// WaitForNamedAuthorizationUpdate checks if the given user can perform the named verb and action on the named resource.
|
||||||
@ -132,3 +135,27 @@ func grantAuthorization(t *testing.T, ctx context.Context, adminClient clientset
|
|||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type clientFn func(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset
|
||||||
|
|
||||||
|
func ServiceAccountClient(namespace, name string) clientFn {
|
||||||
|
return func(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset {
|
||||||
|
clientConfig = rest.CopyConfig(clientConfig)
|
||||||
|
sa, err := adminClient.CoreV1().ServiceAccounts(namespace).Create(context.TODO(), &v1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: name}}, metav1.CreateOptions{})
|
||||||
|
if err != nil && !apierrors.IsAlreadyExists(err) {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
uid := sa.UID
|
||||||
|
|
||||||
|
clientConfig.Impersonate = rest.ImpersonationConfig{
|
||||||
|
UserName: "system:serviceaccount:" + namespace + ":" + name,
|
||||||
|
UID: string(uid),
|
||||||
|
}
|
||||||
|
client := clientset.NewForConfigOrDie(clientConfig)
|
||||||
|
|
||||||
|
for _, rule := range rules {
|
||||||
|
GrantServiceAccountAuthorization(t, context.TODO(), adminClient, name, namespace, rule)
|
||||||
|
}
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -23,16 +23,20 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
typedv1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
typedv1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
|
kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
|
||||||
|
rbachelper "k8s.io/kubernetes/pkg/apis/rbac/v1"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
"k8s.io/kubernetes/pkg/features"
|
||||||
"k8s.io/kubernetes/test/integration"
|
"k8s.io/kubernetes/test/integration"
|
||||||
|
"k8s.io/kubernetes/test/integration/authutil"
|
||||||
"k8s.io/kubernetes/test/integration/framework"
|
"k8s.io/kubernetes/test/integration/framework"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -679,16 +683,114 @@ func TestPodUpdateEphemeralContainers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPodResize(t *testing.T) {
|
func TestPodResizeRBAC(t *testing.T) {
|
||||||
// Disable ServiceAccount admission plugin as we don't have serviceaccount controller running.
|
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||||
server := kubeapiservertesting.StartTestServerOrDie(t, nil,
|
server := kubeapiservertesting.StartTestServerOrDie(t, nil,
|
||||||
append(framework.DefaultTestServerFlags(), "--feature-gates=InPlacePodVerticalScaling=true"),
|
append(framework.DefaultTestServerFlags(), "--authorization-mode=RBAC"), framework.SharedEtcd())
|
||||||
framework.SharedEtcd())
|
defer server.TearDownFn()
|
||||||
|
adminClient := clientset.NewForConfigOrDie(server.ClientConfig)
|
||||||
|
|
||||||
|
ns := framework.CreateNamespaceOrDie(adminClient, "pod-resize", t)
|
||||||
|
defer framework.DeleteNamespaceOrDie(adminClient, ns, t)
|
||||||
|
|
||||||
|
testPod := func(name string) *v1.Pod {
|
||||||
|
return &v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "fake-name",
|
||||||
|
Image: "fakeimage",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testcases := []struct {
|
||||||
|
name string
|
||||||
|
serviceAccountFn func(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset
|
||||||
|
serviceAccountRBAC rbacv1.PolicyRule
|
||||||
|
allowResize bool
|
||||||
|
allowUpdate bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "pod-mutator",
|
||||||
|
serviceAccountFn: authutil.ServiceAccountClient(ns.Name, "pod-mutator"),
|
||||||
|
serviceAccountRBAC: rbachelper.NewRule("get", "update", "patch").Groups("").Resources("pods").RuleOrDie(),
|
||||||
|
allowResize: false,
|
||||||
|
allowUpdate: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "pod-resizer",
|
||||||
|
serviceAccountFn: authutil.ServiceAccountClient(ns.Name, "pod-resizer"),
|
||||||
|
serviceAccountRBAC: rbachelper.NewRule("get", "update", "patch").Groups("").Resources("pods/resize").RuleOrDie(),
|
||||||
|
allowResize: true,
|
||||||
|
allowUpdate: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range testcases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
// 1. Create a test pod.
|
||||||
|
pod := testPod(fmt.Sprintf("resize-%d", i))
|
||||||
|
resp, err := adminClient.CoreV1().Pods(ns.Name).Create(context.TODO(), pod, metav1.CreateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error when creating pod: %v", err)
|
||||||
|
integration.DeletePodOrErrorf(t, adminClient, ns.Name, pod.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Create a service account and fetch its client.
|
||||||
|
saClient := tc.serviceAccountFn(t, adminClient, server.ClientConfig, []rbacv1.PolicyRule{tc.serviceAccountRBAC})
|
||||||
|
|
||||||
|
// 3. Update pod and check whether it should be allowed.
|
||||||
|
resp.Spec.Containers[0].Image = "updated-image"
|
||||||
|
if _, err := saClient.CoreV1().Pods(ns.Name).Update(context.TODO(), resp, metav1.UpdateOptions{}); err == nil && !tc.allowUpdate {
|
||||||
|
t.Fatalf("Unexpected allowed pod update")
|
||||||
|
integration.DeletePodOrErrorf(t, adminClient, ns.Name, pod.Name)
|
||||||
|
} else if err != nil && tc.allowUpdate {
|
||||||
|
t.Fatalf("Unexpected error when updating pod container resources: %v", err)
|
||||||
|
integration.DeletePodOrErrorf(t, adminClient, ns.Name, pod.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Resize pod container resource and check whether it should be allowed.
|
||||||
|
resp, err = adminClient.CoreV1().Pods(ns.Name).Get(context.TODO(), resp.Name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error when fetching the pod: %v", err)
|
||||||
|
integration.DeletePodOrErrorf(t, adminClient, ns.Name, pod.Name)
|
||||||
|
}
|
||||||
|
resp.Spec.Containers[0].Resources = v1.ResourceRequirements{
|
||||||
|
Requests: v1.ResourceList{
|
||||||
|
v1.ResourceEphemeralStorage: resource.MustParse("2Gi"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err = saClient.CoreV1().Pods(ns.Name).Resize(context.TODO(), resp.Name, resp, metav1.UpdateOptions{})
|
||||||
|
if tc.allowResize && err != nil {
|
||||||
|
t.Fatalf("Unexpected pod resize failure: %v", err)
|
||||||
|
integration.DeletePodOrErrorf(t, adminClient, ns.Name, pod.Name)
|
||||||
|
}
|
||||||
|
if !tc.allowResize && err == nil {
|
||||||
|
t.Fatalf("Unexpected pod resize success")
|
||||||
|
integration.DeletePodOrErrorf(t, adminClient, ns.Name, pod.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Delete the test pod.
|
||||||
|
integration.DeletePodOrErrorf(t, adminClient, ns.Name, pod.Name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPodResize(t *testing.T) {
|
||||||
|
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||||
|
// Disable ServiceAccount admission plugin as we don't have serviceaccount controller running.
|
||||||
|
server := kubeapiservertesting.StartTestServerOrDie(t, nil, framework.DefaultTestServerFlags(), framework.SharedEtcd())
|
||||||
defer server.TearDownFn()
|
defer server.TearDownFn()
|
||||||
|
|
||||||
client := clientset.NewForConfigOrDie(server.ClientConfig)
|
client := clientset.NewForConfigOrDie(server.ClientConfig)
|
||||||
|
|
||||||
ns := framework.CreateNamespaceOrDie(client, "pod-create-ephemeral-containers", t)
|
ns := framework.CreateNamespaceOrDie(client, "pod-resize", t)
|
||||||
defer framework.DeleteNamespaceOrDie(client, ns, t)
|
defer framework.DeleteNamespaceOrDie(client, ns, t)
|
||||||
|
|
||||||
testPod := func(name string) *v1.Pod {
|
testPod := func(name string) *v1.Pod {
|
||||||
@ -773,6 +875,7 @@ func TestPodResize(t *testing.T) {
|
|||||||
integration.DeletePodOrErrorf(t, client, ns.Name, pod.Name)
|
integration.DeletePodOrErrorf(t, client, ns.Name, pod.Name)
|
||||||
} else if !strings.Contains(err.Error(), "spec: Forbidden: pod updates may not change fields other than") {
|
} else if !strings.Contains(err.Error(), "spec: Forbidden: pod updates may not change fields other than") {
|
||||||
t.Fatalf("Unexpected error when updating pod container resources: %v", err)
|
t.Fatalf("Unexpected error when updating pod container resources: %v", err)
|
||||||
|
integration.DeletePodOrErrorf(t, client, ns.Name, pod.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err = client.CoreV1().Pods(ns.Name).Resize(context.TODO(), resp.Name, resp, metav1.UpdateOptions{})
|
resp, err = client.CoreV1().Pods(ns.Name).Resize(context.TODO(), resp.Name, resp, metav1.UpdateOptions{})
|
||||||
|
Loading…
Reference in New Issue
Block a user