Unverified Commit 0b6ad8bc authored by Kubernetes Prow Robot's avatar Kubernetes Prow Robot Committed by GitHub

Merge pull request #77563 from jpbetz/admission-webhook-options

Pass {Operation}Options to Webhooks
parents 0e224ad3 900d652a
...@@ -63,7 +63,8 @@ type AdmissionRequest struct { ...@@ -63,7 +63,8 @@ type AdmissionRequest struct {
// Namespace is the namespace associated with the request (if any). // Namespace is the namespace associated with the request (if any).
// +optional // +optional
Namespace string Namespace string
// Operation is the operation being performed // Operation is the operation being performed. This may be different than the operation
// requested. e.g. a patch can result in either a CREATE or UPDATE Operation.
Operation Operation Operation Operation
// UserInfo is information about the requesting user // UserInfo is information about the requesting user
UserInfo authentication.UserInfo UserInfo authentication.UserInfo
...@@ -78,6 +79,13 @@ type AdmissionRequest struct { ...@@ -78,6 +79,13 @@ type AdmissionRequest struct {
// Defaults to false. // Defaults to false.
// +optional // +optional
DryRun *bool DryRun *bool
// Options is the operation option structure of the operation being performed.
// e.g. `meta.k8s.io/v1.DeleteOptions` or `meta.k8s.io/v1.CreateOptions`. This may be
// different than the options the caller provided. e.g. for a patch request the performed
// Operation might be a CREATE, in which case the Options will a
// `meta.k8s.io/v1.CreateOptions` even though the caller provided `meta.k8s.io/v1.PatchOptions`.
// +optional
Options runtime.Object
} }
// AdmissionResponse describes an admission response. // AdmissionResponse describes an admission response.
......
...@@ -90,6 +90,9 @@ func autoConvert_v1beta1_AdmissionRequest_To_admission_AdmissionRequest(in *v1be ...@@ -90,6 +90,9 @@ func autoConvert_v1beta1_AdmissionRequest_To_admission_AdmissionRequest(in *v1be
return err return err
} }
out.DryRun = (*bool)(unsafe.Pointer(in.DryRun)) out.DryRun = (*bool)(unsafe.Pointer(in.DryRun))
if err := runtime.Convert_runtime_RawExtension_To_runtime_Object(&in.Options, &out.Options, s); err != nil {
return err
}
return nil return nil
} }
...@@ -117,6 +120,9 @@ func autoConvert_admission_AdmissionRequest_To_v1beta1_AdmissionRequest(in *admi ...@@ -117,6 +120,9 @@ func autoConvert_admission_AdmissionRequest_To_v1beta1_AdmissionRequest(in *admi
return err return err
} }
out.DryRun = (*bool)(unsafe.Pointer(in.DryRun)) out.DryRun = (*bool)(unsafe.Pointer(in.DryRun))
if err := runtime.Convert_runtime_Object_To_runtime_RawExtension(&in.Options, &out.Options, s); err != nil {
return err
}
return nil return nil
} }
......
...@@ -42,6 +42,9 @@ func (in *AdmissionRequest) DeepCopyInto(out *AdmissionRequest) { ...@@ -42,6 +42,9 @@ func (in *AdmissionRequest) DeepCopyInto(out *AdmissionRequest) {
*out = new(bool) *out = new(bool)
**out = **in **out = **in
} }
if in.Options != nil {
out.Options = in.Options.DeepCopyObject()
}
return return
} }
......
...@@ -22,6 +22,7 @@ go_test( ...@@ -22,6 +22,7 @@ go_test(
embed = [":go_default_library"], embed = [":go_default_library"],
deps = [ deps = [
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
], ],
) )
......
...@@ -19,13 +19,14 @@ package admit ...@@ -19,13 +19,14 @@ package admit
import ( import (
"testing" "testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
) )
func TestAdmissionNonNilAttribute(t *testing.T) { func TestAdmissionNonNilAttribute(t *testing.T) {
handler := NewAlwaysAdmit() handler := NewAlwaysAdmit()
err := handler.(*alwaysAdmit).Admit(admission.NewAttributesRecord(nil, nil, api.Kind("kind").WithVersion("version"), "namespace", "name", api.Resource("resource").WithVersion("version"), "subresource", admission.Create, false, nil), nil) err := handler.(*alwaysAdmit).Admit(admission.NewAttributesRecord(nil, nil, api.Kind("kind").WithVersion("version"), "namespace", "name", api.Resource("resource").WithVersion("version"), "subresource", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if err != nil { if err != nil {
t.Errorf("Unexpected error returned from admission handler") t.Errorf("Unexpected error returned from admission handler")
} }
......
...@@ -47,7 +47,7 @@ func TestAdmission(t *testing.T) { ...@@ -47,7 +47,7 @@ func TestAdmission(t *testing.T) {
}, },
}, },
} }
err := handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, false, nil), nil) err := handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if err != nil { if err != nil {
t.Errorf("Unexpected error returned from admission handler") t.Errorf("Unexpected error returned from admission handler")
} }
...@@ -84,7 +84,7 @@ func TestValidate(t *testing.T) { ...@@ -84,7 +84,7 @@ func TestValidate(t *testing.T) {
}, },
} }
expectedError := `pods "123" is forbidden: spec.initContainers[0].imagePullPolicy: Unsupported value: "": supported values: "Always"` expectedError := `pods "123" is forbidden: spec.initContainers[0].imagePullPolicy: Unsupported value: "": supported values: "Always"`
err := handler.Validate(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, false, nil), nil) err := handler.Validate(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if err == nil { if err == nil {
t.Fatal("missing expected error") t.Fatal("missing expected error")
} }
...@@ -139,7 +139,7 @@ func TestOtherResources(t *testing.T) { ...@@ -139,7 +139,7 @@ func TestOtherResources(t *testing.T) {
for _, tc := range tests { for _, tc := range tests {
handler := &AlwaysPullImages{} handler := &AlwaysPullImages{}
err := handler.Admit(admission.NewAttributesRecord(tc.object, nil, api.Kind(tc.kind).WithVersion("version"), namespace, name, api.Resource(tc.resource).WithVersion("version"), tc.subresource, admission.Create, false, nil), nil) err := handler.Admit(admission.NewAttributesRecord(tc.object, nil, api.Kind(tc.kind).WithVersion("version"), namespace, name, api.Resource(tc.resource).WithVersion("version"), tc.subresource, admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if tc.expectError { if tc.expectError {
if err == nil { if err == nil {
......
...@@ -19,7 +19,7 @@ package antiaffinity ...@@ -19,7 +19,7 @@ package antiaffinity
import ( import (
"testing" "testing"
"k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
...@@ -199,7 +199,7 @@ func TestInterPodAffinityAdmission(t *testing.T) { ...@@ -199,7 +199,7 @@ func TestInterPodAffinityAdmission(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
pod.Spec.Affinity = test.affinity pod.Spec.Affinity = test.affinity
err := handler.Validate(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", false, nil), nil) err := handler.Validate(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", nil, false, nil), nil)
if test.errorExpected && err == nil { if test.errorExpected && err == nil {
t.Errorf("Expected error for Anti Affinity %+v but did not get an error", test.affinity) t.Errorf("Expected error for Anti Affinity %+v but did not get an error", test.affinity)
...@@ -267,7 +267,7 @@ func TestOtherResources(t *testing.T) { ...@@ -267,7 +267,7 @@ func TestOtherResources(t *testing.T) {
for _, tc := range tests { for _, tc := range tests {
handler := &Plugin{} handler := &Plugin{}
err := handler.Validate(admission.NewAttributesRecord(tc.object, nil, api.Kind(tc.kind).WithVersion("version"), namespace, name, api.Resource(tc.resource).WithVersion("version"), tc.subresource, admission.Create, false, nil), nil) err := handler.Validate(admission.NewAttributesRecord(tc.object, nil, api.Kind(tc.kind).WithVersion("version"), namespace, name, api.Resource(tc.resource).WithVersion("version"), tc.subresource, admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if tc.expectError { if tc.expectError {
if err == nil { if err == nil {
......
...@@ -263,7 +263,7 @@ func TestForgivenessAdmission(t *testing.T) { ...@@ -263,7 +263,7 @@ func TestForgivenessAdmission(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
err := handler.Admit(admission.NewAttributesRecord(&test.requestedPod, nil, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", false, nil), nil) err := handler.Admit(admission.NewAttributesRecord(&test.requestedPod, nil, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", nil, false, nil), nil)
if err != nil { if err != nil {
t.Errorf("[%s]: unexpected error %v for pod %+v", test.description, err, test.requestedPod) t.Errorf("[%s]: unexpected error %v for pod %+v", test.description, err, test.requestedPod)
} }
......
...@@ -22,6 +22,7 @@ go_test( ...@@ -22,6 +22,7 @@ go_test(
embed = [":go_default_library"], embed = [":go_default_library"],
deps = [ deps = [
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
], ],
) )
......
...@@ -19,13 +19,14 @@ package deny ...@@ -19,13 +19,14 @@ package deny
import ( import (
"testing" "testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
) )
func TestAdmission(t *testing.T) { func TestAdmission(t *testing.T) {
handler := NewAlwaysDeny() handler := NewAlwaysDeny()
err := handler.(*alwaysDeny).Admit(admission.NewAttributesRecord(nil, nil, api.Kind("kind").WithVersion("version"), "namespace", "name", api.Resource("resource").WithVersion("version"), "subresource", admission.Create, false, nil), nil) err := handler.(*alwaysDeny).Admit(admission.NewAttributesRecord(nil, nil, api.Kind("kind").WithVersion("version"), "namespace", "name", api.Resource("resource").WithVersion("version"), "subresource", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if err == nil { if err == nil {
t.Error("Expected error returned from admission handler") t.Error("Expected error returned from admission handler")
} }
......
...@@ -17,6 +17,7 @@ go_test( ...@@ -17,6 +17,7 @@ go_test(
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//plugin/pkg/admission/eventratelimit/apis/eventratelimit:go_default_library", "//plugin/pkg/admission/eventratelimit/apis/eventratelimit:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/clock:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/clock:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
......
...@@ -21,6 +21,7 @@ import ( ...@@ -21,6 +21,7 @@ import (
"time" "time"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/clock" "k8s.io/apimachinery/pkg/util/clock"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
...@@ -46,6 +47,7 @@ func attributesForRequest(rq request) admission.Attributes { ...@@ -46,6 +47,7 @@ func attributesForRequest(rq request) admission.Attributes {
api.Resource("resource").WithVersion("version"), api.Resource("resource").WithVersion("version"),
"", "",
admission.Create, admission.Create,
&metav1.CreateOptions{},
rq.dryRun, rq.dryRun,
&user.DefaultInfo{Name: rq.username}) &user.DefaultInfo{Name: rq.username})
} }
......
...@@ -120,7 +120,7 @@ func testAdmission(t *testing.T, pod *corev1.Pod, handler *DenyExec, shouldAccep ...@@ -120,7 +120,7 @@ func testAdmission(t *testing.T, pod *corev1.Pod, handler *DenyExec, shouldAccep
// pods/exec // pods/exec
{ {
err := handler.Validate(admission.NewAttributesRecord(nil, nil, api.Kind("Pod").WithVersion("version"), "test", pod.Name, api.Resource("pods").WithVersion("version"), "exec", admission.Connect, false, nil), nil) err := handler.Validate(admission.NewAttributesRecord(nil, nil, api.Kind("Pod").WithVersion("version"), "test", pod.Name, api.Resource("pods").WithVersion("version"), "exec", admission.Connect, nil, false, nil), nil)
if shouldAccept && err != nil { if shouldAccept && err != nil {
t.Errorf("Unexpected error returned from admission handler: %v", err) t.Errorf("Unexpected error returned from admission handler: %v", err)
} }
...@@ -131,7 +131,7 @@ func testAdmission(t *testing.T, pod *corev1.Pod, handler *DenyExec, shouldAccep ...@@ -131,7 +131,7 @@ func testAdmission(t *testing.T, pod *corev1.Pod, handler *DenyExec, shouldAccep
// pods/attach // pods/attach
{ {
err := handler.Validate(admission.NewAttributesRecord(nil, nil, api.Kind("Pod").WithVersion("version"), "test", pod.Name, api.Resource("pods").WithVersion("version"), "attach", admission.Connect, false, nil), nil) err := handler.Validate(admission.NewAttributesRecord(nil, nil, api.Kind("Pod").WithVersion("version"), "test", pod.Name, api.Resource("pods").WithVersion("version"), "attach", admission.Connect, nil, false, nil), nil)
if shouldAccept && err != nil { if shouldAccept && err != nil {
t.Errorf("Unexpected error returned from admission handler: %v", err) t.Errorf("Unexpected error returned from admission handler: %v", err)
} }
......
...@@ -354,7 +354,7 @@ func TestAdmit(t *testing.T) { ...@@ -354,7 +354,7 @@ func TestAdmit(t *testing.T) {
}, },
} }
for i, test := range tests { for i, test := range tests {
err := plugin.Admit(admission.NewAttributesRecord(&test.requestedPod, nil, core.Kind("Pod").WithVersion("version"), "foo", "name", core.Resource("pods").WithVersion("version"), "", "ignored", false, nil), nil) err := plugin.Admit(admission.NewAttributesRecord(&test.requestedPod, nil, core.Kind("Pod").WithVersion("version"), "foo", "name", core.Resource("pods").WithVersion("version"), "", "ignored", nil, false, nil), nil)
if err != nil { if err != nil {
t.Errorf("[%d: %s] unexpected error %v for pod %+v", i, test.description, err, test.requestedPod) t.Errorf("[%d: %s] unexpected error %v for pod %+v", i, test.description, err, test.requestedPod)
} }
......
...@@ -302,11 +302,13 @@ func TestGCAdmission(t *testing.T) { ...@@ -302,11 +302,13 @@ func TestGCAdmission(t *testing.T) {
} }
operation := admission.Create operation := admission.Create
var options runtime.Object = &metav1.CreateOptions{}
if tc.oldObj != nil { if tc.oldObj != nil {
operation = admission.Update operation = admission.Update
options = &metav1.UpdateOptions{}
} }
user := &user.DefaultInfo{Name: tc.username} user := &user.DefaultInfo{Name: tc.username}
attributes := admission.NewAttributesRecord(tc.newObj, tc.oldObj, schema.GroupVersionKind{}, metav1.NamespaceDefault, "foo", tc.resource, tc.subresource, operation, false, user) attributes := admission.NewAttributesRecord(tc.newObj, tc.oldObj, schema.GroupVersionKind{}, metav1.NamespaceDefault, "foo", tc.resource, tc.subresource, operation, options, false, user)
err = gcAdmit.Validate(attributes, nil) err = gcAdmit.Validate(attributes, nil)
if !tc.checkError(err) { if !tc.checkError(err) {
...@@ -605,11 +607,13 @@ func TestBlockOwnerDeletionAdmission(t *testing.T) { ...@@ -605,11 +607,13 @@ func TestBlockOwnerDeletionAdmission(t *testing.T) {
for _, tc := range tests { for _, tc := range tests {
operation := admission.Create operation := admission.Create
var options runtime.Object = &metav1.CreateOptions{}
if tc.oldObj != nil { if tc.oldObj != nil {
operation = admission.Update operation = admission.Update
options = &metav1.UpdateOptions{}
} }
user := &user.DefaultInfo{Name: tc.username} user := &user.DefaultInfo{Name: tc.username}
attributes := admission.NewAttributesRecord(tc.newObj, tc.oldObj, schema.GroupVersionKind{}, metav1.NamespaceDefault, "foo", tc.resource, tc.subresource, operation, false, user) attributes := admission.NewAttributesRecord(tc.newObj, tc.oldObj, schema.GroupVersionKind{}, metav1.NamespaceDefault, "foo", tc.resource, tc.subresource, operation, options, false, user)
err := gcAdmit.Validate(attributes, nil) err := gcAdmit.Validate(attributes, nil)
if !tc.checkError(err) { if !tc.checkError(err) {
......
...@@ -42,6 +42,7 @@ go_test( ...@@ -42,6 +42,7 @@ go_test(
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/apis/imagepolicy/install:go_default_library", "//pkg/apis/imagepolicy/install:go_default_library",
"//staging/src/k8s.io/api/imagepolicy/v1alpha1:go_default_library", "//staging/src/k8s.io/api/imagepolicy/v1alpha1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library", "//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//staging/src/k8s.io/client-go/tools/clientcmd/api/v1:go_default_library", "//staging/src/k8s.io/client-go/tools/clientcmd/api/v1:go_default_library",
......
...@@ -29,9 +29,10 @@ import ( ...@@ -29,9 +29,10 @@ import (
"time" "time"
"k8s.io/api/imagepolicy/v1alpha1" "k8s.io/api/imagepolicy/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/authentication/user" "k8s.io/apiserver/pkg/authentication/user"
"k8s.io/client-go/tools/clientcmd/api/v1" v1 "k8s.io/client-go/tools/clientcmd/api/v1"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"fmt" "fmt"
...@@ -482,7 +483,7 @@ func TestTLSConfig(t *testing.T) { ...@@ -482,7 +483,7 @@ func TestTLSConfig(t *testing.T) {
return return
} }
pod := goodPod(strconv.Itoa(rand.Intn(1000))) pod := goodPod(strconv.Itoa(rand.Intn(1000)))
attr := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "namespace", "", api.Resource("pods").WithVersion("version"), "", admission.Create, false, &user.DefaultInfo{}) attr := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "namespace", "", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, &user.DefaultInfo{})
// Allow all and see if we get an error. // Allow all and see if we get an error.
service.Allow() service.Allow()
...@@ -571,7 +572,7 @@ func TestWebhookCache(t *testing.T) { ...@@ -571,7 +572,7 @@ func TestWebhookCache(t *testing.T) {
{statusCode: 500, expectedErr: false, expectedAuthorized: true, expectedCached: true}, {statusCode: 500, expectedErr: false, expectedAuthorized: true, expectedCached: true},
} }
attr := admission.NewAttributesRecord(goodPod("test"), nil, api.Kind("Pod").WithVersion("version"), "namespace", "", api.Resource("pods").WithVersion("version"), "", admission.Create, false, &user.DefaultInfo{}) attr := admission.NewAttributesRecord(goodPod("test"), nil, api.Kind("Pod").WithVersion("version"), "namespace", "", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, &user.DefaultInfo{})
serv.allow = true serv.allow = true
...@@ -583,7 +584,7 @@ func TestWebhookCache(t *testing.T) { ...@@ -583,7 +584,7 @@ func TestWebhookCache(t *testing.T) {
{statusCode: 200, expectedErr: false, expectedAuthorized: true, expectedCached: false}, {statusCode: 200, expectedErr: false, expectedAuthorized: true, expectedCached: false},
{statusCode: 500, expectedErr: false, expectedAuthorized: true, expectedCached: true}, {statusCode: 500, expectedErr: false, expectedAuthorized: true, expectedCached: true},
} }
attr = admission.NewAttributesRecord(goodPod("test2"), nil, api.Kind("Pod").WithVersion("version"), "namespace", "", api.Resource("pods").WithVersion("version"), "", admission.Create, false, &user.DefaultInfo{}) attr = admission.NewAttributesRecord(goodPod("test2"), nil, api.Kind("Pod").WithVersion("version"), "namespace", "", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, &user.DefaultInfo{})
testWebhookCacheCases(t, serv, wh, attr, tests) testWebhookCacheCases(t, serv, wh, attr, tests)
} }
...@@ -757,7 +758,7 @@ func TestContainerCombinations(t *testing.T) { ...@@ -757,7 +758,7 @@ func TestContainerCombinations(t *testing.T) {
return return
} }
attr := admission.NewAttributesRecord(tt.pod, nil, api.Kind("Pod").WithVersion("version"), "namespace", "", api.Resource("pods").WithVersion("version"), "", admission.Create, false, &user.DefaultInfo{}) attr := admission.NewAttributesRecord(tt.pod, nil, api.Kind("Pod").WithVersion("version"), "namespace", "", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, &user.DefaultInfo{})
err = wh.Validate(attr, nil) err = wh.Validate(attr, nil)
if tt.wantAllowed { if tt.wantAllowed {
...@@ -851,7 +852,7 @@ func TestDefaultAllow(t *testing.T) { ...@@ -851,7 +852,7 @@ func TestDefaultAllow(t *testing.T) {
return return
} }
attr := admission.NewAttributesRecord(tt.pod, nil, api.Kind("Pod").WithVersion("version"), "namespace", "", api.Resource("pods").WithVersion("version"), "", admission.Create, false, &user.DefaultInfo{}) attr := admission.NewAttributesRecord(tt.pod, nil, api.Kind("Pod").WithVersion("version"), "namespace", "", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, &user.DefaultInfo{})
annotations := make(map[string]string) annotations := make(map[string]string)
attr = &fakeAttributes{attr, annotations} attr = &fakeAttributes{attr, annotations}
...@@ -961,7 +962,7 @@ func TestAnnotationFiltering(t *testing.T) { ...@@ -961,7 +962,7 @@ func TestAnnotationFiltering(t *testing.T) {
pod := goodPod("test") pod := goodPod("test")
pod.Annotations = tt.annotations pod.Annotations = tt.annotations
attr := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "namespace", "", api.Resource("pods").WithVersion("version"), "", admission.Create, false, &user.DefaultInfo{}) attr := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "namespace", "", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, &user.DefaultInfo{})
err = wh.Validate(attr, nil) err = wh.Validate(attr, nil)
if err != nil { if err != nil {
...@@ -1051,7 +1052,7 @@ func TestReturnedAnnotationAdd(t *testing.T) { ...@@ -1051,7 +1052,7 @@ func TestReturnedAnnotationAdd(t *testing.T) {
pod := tt.pod pod := tt.pod
attr := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "namespace", "", api.Resource("pods").WithVersion("version"), "", admission.Create, false, &user.DefaultInfo{}) attr := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "namespace", "", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, &user.DefaultInfo{})
annotations := make(map[string]string) annotations := make(map[string]string)
attr = &fakeAttributes{attr, annotations} attr = &fakeAttributes{attr, annotations}
......
...@@ -35,7 +35,7 @@ import ( ...@@ -35,7 +35,7 @@ import (
"k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/fake"
core "k8s.io/client-go/testing" core "k8s.io/client-go/testing"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/v1" v1 "k8s.io/kubernetes/pkg/apis/core/v1"
) )
func getComputeResourceList(cpu, memory string) api.ResourceList { func getComputeResourceList(cpu, memory string) api.ResourceList {
...@@ -705,20 +705,20 @@ func TestLimitRangerIgnoresSubresource(t *testing.T) { ...@@ -705,20 +705,20 @@ func TestLimitRangerIgnoresSubresource(t *testing.T) {
informerFactory.Start(wait.NeverStop) informerFactory.Start(wait.NeverStop)
testPod := validPod("testPod", 1, api.ResourceRequirements{}) testPod := validPod("testPod", 1, api.ResourceRequirements{})
err = handler.Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Create, false, nil), nil) err = handler.Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Create, false, nil), nil) err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if err == nil { if err == nil {
t.Errorf("Expected an error since the pod did not specify resource limits in its create call") t.Errorf("Expected an error since the pod did not specify resource limits in its create call")
} }
err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Update, false, nil), nil) err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil), nil)
if err != nil { if err != nil {
t.Errorf("Expected not to call limitranger actions on pod updates") t.Errorf("Expected not to call limitranger actions on pod updates")
} }
err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "status", admission.Update, false, nil), nil) err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "status", admission.Update, &metav1.UpdateOptions{}, false, nil), nil)
if err != nil { if err != nil {
t.Errorf("Should have ignored calls to any subresource of pod %v", err) t.Errorf("Should have ignored calls to any subresource of pod %v", err)
} }
...@@ -735,20 +735,20 @@ func TestLimitRangerAdmitPod(t *testing.T) { ...@@ -735,20 +735,20 @@ func TestLimitRangerAdmitPod(t *testing.T) {
informerFactory.Start(wait.NeverStop) informerFactory.Start(wait.NeverStop)
testPod := validPod("testPod", 1, api.ResourceRequirements{}) testPod := validPod("testPod", 1, api.ResourceRequirements{})
err = handler.Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Create, false, nil), nil) err = handler.Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Create, false, nil), nil) err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if err == nil { if err == nil {
t.Errorf("Expected an error since the pod did not specify resource limits in its create call") t.Errorf("Expected an error since the pod did not specify resource limits in its create call")
} }
err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Update, false, nil), nil) err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil), nil)
if err != nil { if err != nil {
t.Errorf("Expected not to call limitranger actions on pod updates") t.Errorf("Expected not to call limitranger actions on pod updates")
} }
err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "status", admission.Update, false, nil), nil) err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "status", admission.Update, &metav1.UpdateOptions{}, false, nil), nil)
if err != nil { if err != nil {
t.Errorf("Should have ignored calls to any subresource of pod %v", err) t.Errorf("Should have ignored calls to any subresource of pod %v", err)
} }
...@@ -757,7 +757,7 @@ func TestLimitRangerAdmitPod(t *testing.T) { ...@@ -757,7 +757,7 @@ func TestLimitRangerAdmitPod(t *testing.T) {
terminatingPod := validPod("terminatingPod", 1, api.ResourceRequirements{}) terminatingPod := validPod("terminatingPod", 1, api.ResourceRequirements{})
now := metav1.Now() now := metav1.Now()
terminatingPod.DeletionTimestamp = &now terminatingPod.DeletionTimestamp = &now
err = handler.Validate(admission.NewAttributesRecord(&terminatingPod, &terminatingPod, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "terminatingPod", api.Resource("pods").WithVersion("version"), "", admission.Update, false, nil), nil) err = handler.Validate(admission.NewAttributesRecord(&terminatingPod, &terminatingPod, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "terminatingPod", api.Resource("pods").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil), nil)
if err != nil { if err != nil {
t.Errorf("LimitRange should ignore a pod marked for termination") t.Errorf("LimitRange should ignore a pod marked for termination")
} }
......
...@@ -99,7 +99,7 @@ func TestAdmission(t *testing.T) { ...@@ -99,7 +99,7 @@ func TestAdmission(t *testing.T) {
informerFactory.Start(wait.NeverStop) informerFactory.Start(wait.NeverStop)
pod := newPod(namespace) pod := newPod(namespace)
err = handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, false, nil), nil) err = handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if err != nil { if err != nil {
t.Errorf("unexpected error returned from admission handler") t.Errorf("unexpected error returned from admission handler")
} }
...@@ -119,7 +119,7 @@ func TestAdmissionNamespaceExists(t *testing.T) { ...@@ -119,7 +119,7 @@ func TestAdmissionNamespaceExists(t *testing.T) {
informerFactory.Start(wait.NeverStop) informerFactory.Start(wait.NeverStop)
pod := newPod(namespace) pod := newPod(namespace)
err = handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, false, nil), nil) err = handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if err != nil { if err != nil {
t.Errorf("unexpected error returned from admission handler") t.Errorf("unexpected error returned from admission handler")
} }
...@@ -139,7 +139,7 @@ func TestAdmissionDryRun(t *testing.T) { ...@@ -139,7 +139,7 @@ func TestAdmissionDryRun(t *testing.T) {
informerFactory.Start(wait.NeverStop) informerFactory.Start(wait.NeverStop)
pod := newPod(namespace) pod := newPod(namespace)
err = handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, true, nil), nil) err = handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, true, nil), nil)
if err != nil { if err != nil {
t.Errorf("unexpected error returned from admission handler") t.Errorf("unexpected error returned from admission handler")
} }
...@@ -160,7 +160,7 @@ func TestIgnoreAdmission(t *testing.T) { ...@@ -160,7 +160,7 @@ func TestIgnoreAdmission(t *testing.T) {
chainHandler := admission.NewChainHandler(handler) chainHandler := admission.NewChainHandler(handler)
pod := newPod(namespace) pod := newPod(namespace)
err = chainHandler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Update, false, nil), nil) err = chainHandler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil), nil)
if err != nil { if err != nil {
t.Errorf("unexpected error returned from admission handler") t.Errorf("unexpected error returned from admission handler")
} }
...@@ -182,7 +182,7 @@ func TestAdmissionWithLatentCache(t *testing.T) { ...@@ -182,7 +182,7 @@ func TestAdmissionWithLatentCache(t *testing.T) {
informerFactory.Start(wait.NeverStop) informerFactory.Start(wait.NeverStop)
pod := newPod(namespace) pod := newPod(namespace)
err = handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, false, nil), nil) err = handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if err != nil { if err != nil {
t.Errorf("unexpected error returned from admission handler") t.Errorf("unexpected error returned from admission handler")
} }
......
...@@ -88,7 +88,7 @@ func TestAdmissionNamespaceExists(t *testing.T) { ...@@ -88,7 +88,7 @@ func TestAdmissionNamespaceExists(t *testing.T) {
informerFactory.Start(wait.NeverStop) informerFactory.Start(wait.NeverStop)
pod := newPod(namespace) pod := newPod(namespace)
err = handler.Validate(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, false, nil), nil) err = handler.Validate(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if err != nil { if err != nil {
t.Errorf("unexpected error returned from admission handler") t.Errorf("unexpected error returned from admission handler")
} }
...@@ -108,7 +108,7 @@ func TestAdmissionNamespaceDoesNotExist(t *testing.T) { ...@@ -108,7 +108,7 @@ func TestAdmissionNamespaceDoesNotExist(t *testing.T) {
informerFactory.Start(wait.NeverStop) informerFactory.Start(wait.NeverStop)
pod := newPod(namespace) pod := newPod(namespace)
err = handler.Validate(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, false, nil), nil) err = handler.Validate(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if err == nil { if err == nil {
actions := "" actions := ""
for _, action := range mockClient.Actions() { for _, action := range mockClient.Actions() {
......
...@@ -22,6 +22,7 @@ go_test( ...@@ -22,6 +22,7 @@ go_test(
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/features:go_default_library", "//pkg/features:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library", "//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//staging/src/k8s.io/component-base/featuregate:go_default_library", "//staging/src/k8s.io/component-base/featuregate:go_default_library",
......
...@@ -21,6 +21,7 @@ import ( ...@@ -21,6 +21,7 @@ import (
"testing" "testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/authentication/user" "k8s.io/apiserver/pkg/authentication/user"
"k8s.io/component-base/featuregate" "k8s.io/component-base/featuregate"
...@@ -62,6 +63,7 @@ func Test_nodeTaints(t *testing.T) { ...@@ -62,6 +63,7 @@ func Test_nodeTaints(t *testing.T) {
oldNode api.Node oldNode api.Node
features featuregate.FeatureGate features featuregate.FeatureGate
operation admission.Operation operation admission.Operation
options runtime.Object
expectedTaints []api.Taint expectedTaints []api.Taint
}{ }{
{ {
...@@ -69,6 +71,7 @@ func Test_nodeTaints(t *testing.T) { ...@@ -69,6 +71,7 @@ func Test_nodeTaints(t *testing.T) {
node: myNodeObj, node: myNodeObj,
features: enableTaintNodesByCondition, features: enableTaintNodesByCondition,
operation: admission.Create, operation: admission.Create,
options: &metav1.CreateOptions{},
expectedTaints: []api.Taint{notReadyTaint}, expectedTaints: []api.Taint{notReadyTaint},
}, },
{ {
...@@ -76,6 +79,7 @@ func Test_nodeTaints(t *testing.T) { ...@@ -76,6 +79,7 @@ func Test_nodeTaints(t *testing.T) {
node: myNodeObj, node: myNodeObj,
features: disableTaintNodesByCondition, features: disableTaintNodesByCondition,
operation: admission.Create, operation: admission.Create,
options: &metav1.CreateOptions{},
expectedTaints: nil, expectedTaints: nil,
}, },
{ {
...@@ -83,6 +87,7 @@ func Test_nodeTaints(t *testing.T) { ...@@ -83,6 +87,7 @@ func Test_nodeTaints(t *testing.T) {
node: myTaintedNodeObj, node: myTaintedNodeObj,
features: enableTaintNodesByCondition, features: enableTaintNodesByCondition,
operation: admission.Create, operation: admission.Create,
options: &metav1.CreateOptions{},
expectedTaints: []api.Taint{notReadyTaint}, expectedTaints: []api.Taint{notReadyTaint},
}, },
{ {
...@@ -90,12 +95,13 @@ func Test_nodeTaints(t *testing.T) { ...@@ -90,12 +95,13 @@ func Test_nodeTaints(t *testing.T) {
node: myUnreadyNodeObj, node: myUnreadyNodeObj,
features: enableTaintNodesByCondition, features: enableTaintNodesByCondition,
operation: admission.Create, operation: admission.Create,
options: &metav1.CreateOptions{},
expectedTaints: []api.Taint{notReadyTaint}, expectedTaints: []api.Taint{notReadyTaint},
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
attributes := admission.NewAttributesRecord(&tt.node, &tt.oldNode, nodeKind, myNodeObj.Namespace, myNodeObj.Name, resource, "", tt.operation, false, mynode) attributes := admission.NewAttributesRecord(&tt.node, &tt.oldNode, nodeKind, myNodeObj.Namespace, myNodeObj.Name, resource, "", tt.operation, tt.options, false, mynode)
c := NewPlugin() c := NewPlugin()
if tt.features != nil { if tt.features != nil {
c.features = tt.features c.features = tt.features
......
...@@ -161,7 +161,7 @@ func TestPodAdmission(t *testing.T) { ...@@ -161,7 +161,7 @@ func TestPodAdmission(t *testing.T) {
handler.clusterNodeSelectors[namespace.Name] = test.whitelist handler.clusterNodeSelectors[namespace.Name] = test.whitelist
pod.Spec = api.PodSpec{NodeSelector: test.podNodeSelector} pod.Spec = api.PodSpec{NodeSelector: test.podNodeSelector}
err := handler.Admit(admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "testNamespace", namespace.ObjectMeta.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, false, nil), nil) err := handler.Admit(admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "testNamespace", namespace.ObjectMeta.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if test.admit && err != nil { if test.admit && err != nil {
t.Errorf("Test: %s, expected no error but got: %s", test.testName, err) t.Errorf("Test: %s, expected no error but got: %s", test.testName, err)
} else if !test.admit && err == nil { } else if !test.admit && err == nil {
...@@ -170,7 +170,7 @@ func TestPodAdmission(t *testing.T) { ...@@ -170,7 +170,7 @@ func TestPodAdmission(t *testing.T) {
if test.admit && !labels.Equals(test.mergedNodeSelector, labels.Set(pod.Spec.NodeSelector)) { if test.admit && !labels.Equals(test.mergedNodeSelector, labels.Set(pod.Spec.NodeSelector)) {
t.Errorf("Test: %s, expected: %s but got: %s", test.testName, test.mergedNodeSelector, pod.Spec.NodeSelector) t.Errorf("Test: %s, expected: %s but got: %s", test.testName, test.mergedNodeSelector, pod.Spec.NodeSelector)
} }
err = handler.Validate(admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "testNamespace", namespace.ObjectMeta.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, false, nil), nil) err = handler.Validate(admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "testNamespace", namespace.ObjectMeta.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if test.admit && err != nil { if test.admit && err != nil {
t.Errorf("Test: %s, expected no error but got: %s", test.testName, err) t.Errorf("Test: %s, expected no error but got: %s", test.testName, err)
} else if !test.admit && err == nil { } else if !test.admit && err == nil {
......
...@@ -819,6 +819,7 @@ func admitPod(pod *api.Pod, pip *settingsv1alpha1.PodPreset) error { ...@@ -819,6 +819,7 @@ func admitPod(pod *api.Pod, pip *settingsv1alpha1.PodPreset) error {
api.Resource("pods").WithVersion("version"), api.Resource("pods").WithVersion("version"),
"", "",
kadmission.Create, kadmission.Create,
&metav1.CreateOptions{},
false, false,
&user.DefaultInfo{}, &user.DefaultInfo{},
) )
......
...@@ -266,7 +266,7 @@ func TestPodAdmission(t *testing.T) { ...@@ -266,7 +266,7 @@ func TestPodAdmission(t *testing.T) {
pod := test.pod pod := test.pod
pod.Spec.Tolerations = test.podTolerations pod.Spec.Tolerations = test.podTolerations
err = handler.Admit(admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "testNamespace", namespace.ObjectMeta.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, false, nil), nil) err = handler.Admit(admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "testNamespace", namespace.ObjectMeta.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if test.admit && err != nil { if test.admit && err != nil {
t.Errorf("Test: %s, expected no error but got: %s", test.testName, err) t.Errorf("Test: %s, expected no error but got: %s", test.testName, err)
} else if !test.admit && err == nil { } else if !test.admit && err == nil {
...@@ -343,7 +343,7 @@ func TestIgnoreUpdatingInitializedPod(t *testing.T) { ...@@ -343,7 +343,7 @@ func TestIgnoreUpdatingInitializedPod(t *testing.T) {
} }
// if the update of initialized pod is not ignored, an error will be returned because the pod's Tolerations conflicts with namespace's Tolerations. // if the update of initialized pod is not ignored, an error will be returned because the pod's Tolerations conflicts with namespace's Tolerations.
err = handler.Admit(admission.NewAttributesRecord(pod, pod, api.Kind("Pod").WithVersion("version"), "testNamespace", pod.ObjectMeta.Name, api.Resource("pods").WithVersion("version"), "", admission.Update, false, nil), nil) err = handler.Admit(admission.NewAttributesRecord(pod, pod, api.Kind("Pod").WithVersion("version"), "testNamespace", pod.ObjectMeta.Name, api.Resource("pods").WithVersion("version"), "", admission.Update, &metav1.CreateOptions{}, false, nil), nil)
if err != nil { if err != nil {
t.Errorf("expected no error, got: %v", err) t.Errorf("expected no error, got: %v", err)
} }
......
...@@ -30,7 +30,7 @@ import ( ...@@ -30,7 +30,7 @@ import (
featuregatetesting "k8s.io/component-base/featuregate/testing" featuregatetesting "k8s.io/component-base/featuregate/testing"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/scheduling" "k8s.io/kubernetes/pkg/apis/scheduling"
"k8s.io/kubernetes/pkg/apis/scheduling/v1" v1 "k8s.io/kubernetes/pkg/apis/scheduling/v1"
"k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/features"
) )
...@@ -155,6 +155,7 @@ func TestPriorityClassAdmission(t *testing.T) { ...@@ -155,6 +155,7 @@ func TestPriorityClassAdmission(t *testing.T) {
scheduling.Resource("priorityclasses").WithVersion("version"), scheduling.Resource("priorityclasses").WithVersion("version"),
"", "",
admission.Create, admission.Create,
&metav1.CreateOptions{},
false, false,
test.userInfo, test.userInfo,
) )
...@@ -200,7 +201,7 @@ func TestDefaultPriority(t *testing.T) { ...@@ -200,7 +201,7 @@ func TestDefaultPriority(t *testing.T) {
name: "add a default class", name: "add a default class",
classesBefore: []*scheduling.PriorityClass{nondefaultClass1}, classesBefore: []*scheduling.PriorityClass{nondefaultClass1},
classesAfter: []*scheduling.PriorityClass{nondefaultClass1, defaultClass1}, classesAfter: []*scheduling.PriorityClass{nondefaultClass1, defaultClass1},
attributes: admission.NewAttributesRecord(defaultClass1, nil, pcKind, "", defaultClass1.Name, pcResource, "", admission.Create, false, nil), attributes: admission.NewAttributesRecord(defaultClass1, nil, pcKind, "", defaultClass1.Name, pcResource, "", admission.Create, &metav1.CreateOptions{}, false, nil),
expectedDefaultBefore: scheduling.DefaultPriorityWhenNoDefaultClassExists, expectedDefaultBefore: scheduling.DefaultPriorityWhenNoDefaultClassExists,
expectedDefaultNameBefore: "", expectedDefaultNameBefore: "",
expectedDefaultAfter: defaultClass1.Value, expectedDefaultAfter: defaultClass1.Value,
...@@ -210,7 +211,7 @@ func TestDefaultPriority(t *testing.T) { ...@@ -210,7 +211,7 @@ func TestDefaultPriority(t *testing.T) {
name: "multiple default classes resolves to the minimum value among them", name: "multiple default classes resolves to the minimum value among them",
classesBefore: []*scheduling.PriorityClass{defaultClass1, defaultClass2}, classesBefore: []*scheduling.PriorityClass{defaultClass1, defaultClass2},
classesAfter: []*scheduling.PriorityClass{defaultClass2}, classesAfter: []*scheduling.PriorityClass{defaultClass2},
attributes: admission.NewAttributesRecord(nil, nil, pcKind, "", defaultClass1.Name, pcResource, "", admission.Delete, false, nil), attributes: admission.NewAttributesRecord(nil, nil, pcKind, "", defaultClass1.Name, pcResource, "", admission.Delete, &metav1.DeleteOptions{}, false, nil),
expectedDefaultBefore: defaultClass1.Value, expectedDefaultBefore: defaultClass1.Value,
expectedDefaultNameBefore: defaultClass1.Name, expectedDefaultNameBefore: defaultClass1.Name,
expectedDefaultAfter: defaultClass2.Value, expectedDefaultAfter: defaultClass2.Value,
...@@ -220,7 +221,7 @@ func TestDefaultPriority(t *testing.T) { ...@@ -220,7 +221,7 @@ func TestDefaultPriority(t *testing.T) {
name: "delete default priority class", name: "delete default priority class",
classesBefore: []*scheduling.PriorityClass{defaultClass1}, classesBefore: []*scheduling.PriorityClass{defaultClass1},
classesAfter: []*scheduling.PriorityClass{}, classesAfter: []*scheduling.PriorityClass{},
attributes: admission.NewAttributesRecord(nil, nil, pcKind, "", defaultClass1.Name, pcResource, "", admission.Delete, false, nil), attributes: admission.NewAttributesRecord(nil, nil, pcKind, "", defaultClass1.Name, pcResource, "", admission.Delete, &metav1.DeleteOptions{}, false, nil),
expectedDefaultBefore: defaultClass1.Value, expectedDefaultBefore: defaultClass1.Value,
expectedDefaultNameBefore: defaultClass1.Name, expectedDefaultNameBefore: defaultClass1.Name,
expectedDefaultAfter: scheduling.DefaultPriorityWhenNoDefaultClassExists, expectedDefaultAfter: scheduling.DefaultPriorityWhenNoDefaultClassExists,
...@@ -230,7 +231,7 @@ func TestDefaultPriority(t *testing.T) { ...@@ -230,7 +231,7 @@ func TestDefaultPriority(t *testing.T) {
name: "update default class and remove its global default", name: "update default class and remove its global default",
classesBefore: []*scheduling.PriorityClass{defaultClass1}, classesBefore: []*scheduling.PriorityClass{defaultClass1},
classesAfter: []*scheduling.PriorityClass{&updatedDefaultClass1}, classesAfter: []*scheduling.PriorityClass{&updatedDefaultClass1},
attributes: admission.NewAttributesRecord(&updatedDefaultClass1, defaultClass1, pcKind, "", defaultClass1.Name, pcResource, "", admission.Update, false, nil), attributes: admission.NewAttributesRecord(&updatedDefaultClass1, defaultClass1, pcKind, "", defaultClass1.Name, pcResource, "", admission.Update, &metav1.UpdateOptions{}, false, nil),
expectedDefaultBefore: defaultClass1.Value, expectedDefaultBefore: defaultClass1.Value,
expectedDefaultNameBefore: defaultClass1.Name, expectedDefaultNameBefore: defaultClass1.Name,
expectedDefaultAfter: scheduling.DefaultPriorityWhenNoDefaultClassExists, expectedDefaultAfter: scheduling.DefaultPriorityWhenNoDefaultClassExists,
...@@ -600,6 +601,7 @@ func TestPodAdmission(t *testing.T) { ...@@ -600,6 +601,7 @@ func TestPodAdmission(t *testing.T) {
api.Resource("pods").WithVersion("version"), api.Resource("pods").WithVersion("version"),
"", "",
admission.Create, admission.Create,
&metav1.CreateOptions{},
false, false,
nil, nil,
) )
......
...@@ -24,7 +24,7 @@ import ( ...@@ -24,7 +24,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
policy "k8s.io/api/policy/v1beta1" policy "k8s.io/api/policy/v1beta1"
apiequality "k8s.io/apimachinery/pkg/api/equality" apiequality "k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
...@@ -473,7 +473,7 @@ func TestAdmitPreferNonmutating(t *testing.T) { ...@@ -473,7 +473,7 @@ func TestAdmitPreferNonmutating(t *testing.T) {
func TestFailClosedOnInvalidPod(t *testing.T) { func TestFailClosedOnInvalidPod(t *testing.T) {
plugin := NewTestAdmission(nil, nil) plugin := NewTestAdmission(nil, nil)
pod := &v1.Pod{} pod := &v1.Pod{}
attrs := kadmission.NewAttributesRecord(pod, nil, kapi.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, kapi.Resource("pods").WithVersion("version"), "", kadmission.Create, false, &user.DefaultInfo{}) attrs := kadmission.NewAttributesRecord(pod, nil, kapi.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, kapi.Resource("pods").WithVersion("version"), "", kadmission.Create, &metav1.CreateOptions{}, false, &user.DefaultInfo{})
err := plugin.Admit(attrs, nil) err := plugin.Admit(attrs, nil)
if err == nil { if err == nil {
...@@ -1776,7 +1776,7 @@ func testPSPAdmitAdvanced(testCaseName string, op kadmission.Operation, psps []* ...@@ -1776,7 +1776,7 @@ func testPSPAdmitAdvanced(testCaseName string, op kadmission.Operation, psps []*
originalPod := pod.DeepCopy() originalPod := pod.DeepCopy()
plugin := NewTestAdmission(psps, authz) plugin := NewTestAdmission(psps, authz)
attrs := kadmission.NewAttributesRecord(pod, oldPod, kapi.Kind("Pod").WithVersion("version"), pod.Namespace, "", kapi.Resource("pods").WithVersion("version"), "", op, false, userInfo) attrs := kadmission.NewAttributesRecord(pod, oldPod, kapi.Kind("Pod").WithVersion("version"), pod.Namespace, "", kapi.Resource("pods").WithVersion("version"), "", op, nil, false, userInfo)
annotations := make(map[string]string) annotations := make(map[string]string)
attrs = &fakeAttributes{attrs, annotations} attrs = &fakeAttributes{attrs, annotations}
err := plugin.Admit(attrs, nil) err := plugin.Admit(attrs, nil)
...@@ -2240,7 +2240,7 @@ func TestPolicyAuthorizationErrors(t *testing.T) { ...@@ -2240,7 +2240,7 @@ func TestPolicyAuthorizationErrors(t *testing.T) {
pod.Spec.SecurityContext.HostPID = true pod.Spec.SecurityContext.HostPID = true
plugin := NewTestAdmission(tc.inPolicies, authz) plugin := NewTestAdmission(tc.inPolicies, authz)
attrs := kadmission.NewAttributesRecord(pod, nil, kapi.Kind("Pod").WithVersion("version"), ns, "", kapi.Resource("pods").WithVersion("version"), "", kadmission.Create, false, &user.DefaultInfo{Name: userName}) attrs := kadmission.NewAttributesRecord(pod, nil, kapi.Kind("Pod").WithVersion("version"), ns, "", kapi.Resource("pods").WithVersion("version"), "", kadmission.Create, &metav1.CreateOptions{}, false, &user.DefaultInfo{Name: userName})
allowedPod, _, validationErrs, err := plugin.computeSecurityContext(attrs, pod, true, "") allowedPod, _, validationErrs, err := plugin.computeSecurityContext(attrs, pod, true, "")
assert.Nil(t, allowedPod) assert.Nil(t, allowedPod)
...@@ -2333,7 +2333,7 @@ func TestPreferValidatedPSP(t *testing.T) { ...@@ -2333,7 +2333,7 @@ func TestPreferValidatedPSP(t *testing.T) {
pod.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = &allowPrivilegeEscalation pod.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = &allowPrivilegeEscalation
plugin := NewTestAdmission(tc.inPolicies, authz) plugin := NewTestAdmission(tc.inPolicies, authz)
attrs := kadmission.NewAttributesRecord(pod, nil, kapi.Kind("Pod").WithVersion("version"), "ns", "", kapi.Resource("pods").WithVersion("version"), "", kadmission.Update, false, &user.DefaultInfo{Name: "test"}) attrs := kadmission.NewAttributesRecord(pod, nil, kapi.Kind("Pod").WithVersion("version"), "ns", "", kapi.Resource("pods").WithVersion("version"), "", kadmission.Update, &metav1.UpdateOptions{}, false, &user.DefaultInfo{Name: "test"})
_, pspName, validationErrs, err := plugin.computeSecurityContext(attrs, pod, false, tc.validatedPSPHint) _, pspName, validationErrs, err := plugin.computeSecurityContext(attrs, pod, false, tc.validatedPSPHint)
assert.NoError(t, err) assert.NoError(t, err)
......
...@@ -82,7 +82,7 @@ func TestAdmission(t *testing.T) { ...@@ -82,7 +82,7 @@ func TestAdmission(t *testing.T) {
p.Spec.SecurityContext = tc.podSc p.Spec.SecurityContext = tc.podSc
p.Spec.Containers[0].SecurityContext = tc.sc p.Spec.Containers[0].SecurityContext = tc.sc
err := handler.Validate(admission.NewAttributesRecord(p, nil, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", false, nil), nil) err := handler.Validate(admission.NewAttributesRecord(p, nil, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", nil, false, nil), nil)
if err != nil && !tc.expectError { if err != nil && !tc.expectError {
t.Errorf("%v: unexpected error: %v", tc.name, err) t.Errorf("%v: unexpected error: %v", tc.name, err)
} else if err == nil && tc.expectError { } else if err == nil && tc.expectError {
...@@ -96,7 +96,7 @@ func TestAdmission(t *testing.T) { ...@@ -96,7 +96,7 @@ func TestAdmission(t *testing.T) {
p.Spec.InitContainers = p.Spec.Containers p.Spec.InitContainers = p.Spec.Containers
p.Spec.Containers = nil p.Spec.Containers = nil
err = handler.Validate(admission.NewAttributesRecord(p, nil, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", false, nil), nil) err = handler.Validate(admission.NewAttributesRecord(p, nil, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", nil, false, nil), nil)
if err != nil && !tc.expectError { if err != nil && !tc.expectError {
t.Errorf("%v: unexpected error: %v", tc.name, err) t.Errorf("%v: unexpected error: %v", tc.name, err)
} else if err == nil && tc.expectError { } else if err == nil && tc.expectError {
...@@ -140,7 +140,7 @@ func TestPodSecurityContextAdmission(t *testing.T) { ...@@ -140,7 +140,7 @@ func TestPodSecurityContextAdmission(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
pod.Spec.SecurityContext = &test.securityContext pod.Spec.SecurityContext = &test.securityContext
err := handler.Validate(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", false, nil), nil) err := handler.Validate(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", nil, false, nil), nil)
if test.errorExpected && err == nil { if test.errorExpected && err == nil {
t.Errorf("Expected error for security context %+v but did not get an error", test.securityContext) t.Errorf("Expected error for security context %+v but did not get an error", test.securityContext)
......
...@@ -23,7 +23,7 @@ import ( ...@@ -23,7 +23,7 @@ import (
"sort" "sort"
"testing" "testing"
"k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors" 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"
...@@ -756,7 +756,7 @@ func Test_PVLAdmission(t *testing.T) { ...@@ -756,7 +756,7 @@ func Test_PVLAdmission(t *testing.T) {
setPVLabeler(testcase.handler, testcase.pvlabeler) setPVLabeler(testcase.handler, testcase.pvlabeler)
handler := admission.NewChainHandler(testcase.handler) handler := admission.NewChainHandler(testcase.handler)
err := handler.Admit(admission.NewAttributesRecord(testcase.preAdmissionPV, nil, api.Kind("PersistentVolume").WithVersion("version"), testcase.preAdmissionPV.Namespace, testcase.preAdmissionPV.Name, api.Resource("persistentvolumes").WithVersion("version"), "", admission.Create, false, nil), nil) err := handler.Admit(admission.NewAttributesRecord(testcase.preAdmissionPV, nil, api.Kind("PersistentVolume").WithVersion("version"), testcase.preAdmissionPV.Namespace, testcase.preAdmissionPV.Name, api.Resource("persistentvolumes").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if !reflect.DeepEqual(err, testcase.err) { if !reflect.DeepEqual(err, testcase.err) {
t.Logf("expected error: %q", testcase.err) t.Logf("expected error: %q", testcase.err)
t.Logf("actual error: %q", err) t.Logf("actual error: %q", err)
......
...@@ -254,7 +254,8 @@ func TestPVCResizeAdmission(t *testing.T) { ...@@ -254,7 +254,8 @@ func TestPVCResizeAdmission(t *testing.T) {
for _, tc := range tests { for _, tc := range tests {
operation := admission.Update operation := admission.Update
attributes := admission.NewAttributesRecord(tc.newObj, tc.oldObj, schema.GroupVersionKind{}, metav1.NamespaceDefault, "foo", tc.resource, tc.subresource, operation, false, nil) operationOptions := &metav1.CreateOptions{}
attributes := admission.NewAttributesRecord(tc.newObj, tc.oldObj, schema.GroupVersionKind{}, metav1.NamespaceDefault, "foo", tc.resource, tc.subresource, operation, operationOptions, false, nil)
err := ctrl.Validate(attributes, nil) err := ctrl.Validate(attributes, nil)
if !tc.checkError(err) { if !tc.checkError(err) {
......
...@@ -208,6 +208,7 @@ func TestAdmission(t *testing.T) { ...@@ -208,6 +208,7 @@ func TestAdmission(t *testing.T) {
api.Resource("persistentvolumeclaims").WithVersion("version"), api.Resource("persistentvolumeclaims").WithVersion("version"),
"", // subresource "", // subresource
admission.Create, admission.Create,
&metav1.CreateOptions{},
false, // dryRun false, // dryRun
nil, // userInfo nil, // userInfo
) )
......
...@@ -131,6 +131,7 @@ func TestAdmit(t *testing.T) { ...@@ -131,6 +131,7 @@ func TestAdmit(t *testing.T) {
test.resource, test.resource,
"", // subresource "", // subresource
admission.Create, admission.Create,
&metav1.CreateOptions{},
false, // dryRun false, // dryRun
nil, // userInfo nil, // userInfo
) )
......
...@@ -60,7 +60,8 @@ message AdmissionRequest { ...@@ -60,7 +60,8 @@ message AdmissionRequest {
// +optional // +optional
optional string namespace = 6; optional string namespace = 6;
// Operation is the operation being performed // Operation is the operation being performed. This may be different than the operation
// requested. e.g. a patch can result in either a CREATE or UPDATE Operation.
optional string operation = 7; optional string operation = 7;
// UserInfo is information about the requesting user // UserInfo is information about the requesting user
...@@ -78,6 +79,14 @@ message AdmissionRequest { ...@@ -78,6 +79,14 @@ message AdmissionRequest {
// Defaults to false. // Defaults to false.
// +optional // +optional
optional bool dryRun = 11; optional bool dryRun = 11;
// Options is the operation option structure of the operation being performed.
// e.g. `meta.k8s.io/v1.DeleteOptions` or `meta.k8s.io/v1.CreateOptions`. This may be
// different than the options the caller provided. e.g. for a patch request the performed
// Operation might be a CREATE, in which case the Options will a
// `meta.k8s.io/v1.CreateOptions` even though the caller provided `meta.k8s.io/v1.PatchOptions`.
// +optional
optional k8s.io.apimachinery.pkg.runtime.RawExtension options = 12;
} }
// AdmissionResponse describes an admission response. // AdmissionResponse describes an admission response.
......
...@@ -61,7 +61,8 @@ type AdmissionRequest struct { ...@@ -61,7 +61,8 @@ type AdmissionRequest struct {
// Namespace is the namespace associated with the request (if any). // Namespace is the namespace associated with the request (if any).
// +optional // +optional
Namespace string `json:"namespace,omitempty" protobuf:"bytes,6,opt,name=namespace"` Namespace string `json:"namespace,omitempty" protobuf:"bytes,6,opt,name=namespace"`
// Operation is the operation being performed // Operation is the operation being performed. This may be different than the operation
// requested. e.g. a patch can result in either a CREATE or UPDATE Operation.
Operation Operation `json:"operation" protobuf:"bytes,7,opt,name=operation"` Operation Operation `json:"operation" protobuf:"bytes,7,opt,name=operation"`
// UserInfo is information about the requesting user // UserInfo is information about the requesting user
UserInfo authenticationv1.UserInfo `json:"userInfo" protobuf:"bytes,8,opt,name=userInfo"` UserInfo authenticationv1.UserInfo `json:"userInfo" protobuf:"bytes,8,opt,name=userInfo"`
...@@ -75,6 +76,13 @@ type AdmissionRequest struct { ...@@ -75,6 +76,13 @@ type AdmissionRequest struct {
// Defaults to false. // Defaults to false.
// +optional // +optional
DryRun *bool `json:"dryRun,omitempty" protobuf:"varint,11,opt,name=dryRun"` DryRun *bool `json:"dryRun,omitempty" protobuf:"varint,11,opt,name=dryRun"`
// Options is the operation option structure of the operation being performed.
// e.g. `meta.k8s.io/v1.DeleteOptions` or `meta.k8s.io/v1.CreateOptions`. This may be
// different than the options the caller provided. e.g. for a patch request the performed
// Operation might be a CREATE, in which case the Options will a
// `meta.k8s.io/v1.CreateOptions` even though the caller provided `meta.k8s.io/v1.PatchOptions`.
// +optional
Options runtime.RawExtension `json:"options,omitempty" protobuf:"bytes,12,opt,name=options"`
} }
// AdmissionResponse describes an admission response. // AdmissionResponse describes an admission response.
......
...@@ -35,11 +35,12 @@ var map_AdmissionRequest = map[string]string{ ...@@ -35,11 +35,12 @@ var map_AdmissionRequest = map[string]string{
"subResource": "SubResource is the name of the subresource being requested. This is a different resource, scoped to the parent resource, but it may have a different kind. For instance, /pods has the resource \"pods\" and the kind \"Pod\", while /pods/foo/status has the resource \"pods\", the sub resource \"status\", and the kind \"Pod\" (because status operates on pods). The binding resource for a pod though may be /pods/foo/binding, which has resource \"pods\", subresource \"binding\", and kind \"Binding\".", "subResource": "SubResource is the name of the subresource being requested. This is a different resource, scoped to the parent resource, but it may have a different kind. For instance, /pods has the resource \"pods\" and the kind \"Pod\", while /pods/foo/status has the resource \"pods\", the sub resource \"status\", and the kind \"Pod\" (because status operates on pods). The binding resource for a pod though may be /pods/foo/binding, which has resource \"pods\", subresource \"binding\", and kind \"Binding\".",
"name": "Name is the name of the object as presented in the request. On a CREATE operation, the client may omit name and rely on the server to generate the name. If that is the case, this method will return the empty string.", "name": "Name is the name of the object as presented in the request. On a CREATE operation, the client may omit name and rely on the server to generate the name. If that is the case, this method will return the empty string.",
"namespace": "Namespace is the namespace associated with the request (if any).", "namespace": "Namespace is the namespace associated with the request (if any).",
"operation": "Operation is the operation being performed", "operation": "Operation is the operation being performed. This may be different than the operation requested. e.g. a patch can result in either a CREATE or UPDATE Operation.",
"userInfo": "UserInfo is information about the requesting user", "userInfo": "UserInfo is information about the requesting user",
"object": "Object is the object from the incoming request prior to default values being applied", "object": "Object is the object from the incoming request prior to default values being applied",
"oldObject": "OldObject is the existing object. Only populated for UPDATE requests.", "oldObject": "OldObject is the existing object. Only populated for UPDATE requests.",
"dryRun": "DryRun indicates that modifications will definitely not be persisted for this request. Defaults to false.", "dryRun": "DryRun indicates that modifications will definitely not be persisted for this request. Defaults to false.",
"options": "Options is the operation option structure of the operation being performed. e.g. `meta.k8s.io/v1.DeleteOptions` or `meta.k8s.io/v1.CreateOptions`. This may be different than the options the caller provided. e.g. for a patch request the performed Operation might be a CREATE, in which case the Options will a `meta.k8s.io/v1.CreateOptions` even though the caller provided `meta.k8s.io/v1.PatchOptions`.",
} }
func (AdmissionRequest) SwaggerDoc() map[string]string { func (AdmissionRequest) SwaggerDoc() map[string]string {
......
...@@ -38,6 +38,7 @@ func (in *AdmissionRequest) DeepCopyInto(out *AdmissionRequest) { ...@@ -38,6 +38,7 @@ func (in *AdmissionRequest) DeepCopyInto(out *AdmissionRequest) {
*out = new(bool) *out = new(bool)
**out = **in **out = **in
} }
in.Options.DeepCopyInto(&out.Options)
return return
} }
......
...@@ -46,7 +46,7 @@ message ConversionRequest { ...@@ -46,7 +46,7 @@ message ConversionRequest {
// ConversionResponse describes a conversion response. // ConversionResponse describes a conversion response.
message ConversionResponse { message ConversionResponse {
// `uid` is an identifier for the individual request/response. // `uid` is an identifier for the individual request/response.
// This should be copied over from the corresponding AdmissionRequest. // This should be copied over from the corresponding ConversionRequest.
optional string uid = 1; optional string uid = 1;
// `convertedObjects` is the list of converted version of `request.objects` if the `result` is successful otherwise empty. // `convertedObjects` is the list of converted version of `request.objects` if the `result` is successful otherwise empty.
......
...@@ -443,7 +443,7 @@ type ConversionRequest struct { ...@@ -443,7 +443,7 @@ type ConversionRequest struct {
// ConversionResponse describes a conversion response. // ConversionResponse describes a conversion response.
type ConversionResponse struct { type ConversionResponse struct {
// `uid` is an identifier for the individual request/response. // `uid` is an identifier for the individual request/response.
// This should be copied over from the corresponding AdmissionRequest. // This should be copied over from the corresponding ConversionRequest.
UID types.UID `json:"uid" protobuf:"bytes,1,name=uid"` UID types.UID `json:"uid" protobuf:"bytes,1,name=uid"`
// `convertedObjects` is the list of converted version of `request.objects` if the `result` is successful otherwise empty. // `convertedObjects` is the list of converted version of `request.objects` if the `result` is successful otherwise empty.
// The webhook is expected to set apiVersion of these objects to the ConversionRequest.desiredAPIVersion. The list // The webhook is expected to set apiVersion of these objects to the ConversionRequest.desiredAPIVersion. The list
......
...@@ -34,6 +34,7 @@ type attributesRecord struct { ...@@ -34,6 +34,7 @@ type attributesRecord struct {
resource schema.GroupVersionResource resource schema.GroupVersionResource
subresource string subresource string
operation Operation operation Operation
options runtime.Object
dryRun bool dryRun bool
object runtime.Object object runtime.Object
oldObject runtime.Object oldObject runtime.Object
...@@ -45,7 +46,7 @@ type attributesRecord struct { ...@@ -45,7 +46,7 @@ type attributesRecord struct {
annotationsLock sync.RWMutex annotationsLock sync.RWMutex
} }
func NewAttributesRecord(object runtime.Object, oldObject runtime.Object, kind schema.GroupVersionKind, namespace, name string, resource schema.GroupVersionResource, subresource string, operation Operation, dryRun bool, userInfo user.Info) Attributes { func NewAttributesRecord(object runtime.Object, oldObject runtime.Object, kind schema.GroupVersionKind, namespace, name string, resource schema.GroupVersionResource, subresource string, operation Operation, operationOptions runtime.Object, dryRun bool, userInfo user.Info) Attributes {
return &attributesRecord{ return &attributesRecord{
kind: kind, kind: kind,
namespace: namespace, namespace: namespace,
...@@ -53,6 +54,7 @@ func NewAttributesRecord(object runtime.Object, oldObject runtime.Object, kind s ...@@ -53,6 +54,7 @@ func NewAttributesRecord(object runtime.Object, oldObject runtime.Object, kind s
resource: resource, resource: resource,
subresource: subresource, subresource: subresource,
operation: operation, operation: operation,
options: operationOptions,
dryRun: dryRun, dryRun: dryRun,
object: object, object: object,
oldObject: oldObject, oldObject: oldObject,
...@@ -84,6 +86,10 @@ func (record *attributesRecord) GetOperation() Operation { ...@@ -84,6 +86,10 @@ func (record *attributesRecord) GetOperation() Operation {
return record.operation return record.operation
} }
func (record *attributesRecord) GetOperationOptions() runtime.Object {
return record.options
}
func (record *attributesRecord) IsDryRun() bool { func (record *attributesRecord) IsDryRun() bool {
return record.dryRun return record.dryRun
} }
......
...@@ -64,7 +64,7 @@ func (h fakeHandler) Handles(o Operation) bool { ...@@ -64,7 +64,7 @@ func (h fakeHandler) Handles(o Operation) bool {
} }
func attributes() Attributes { func attributes() Attributes {
return NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, "", "", schema.GroupVersionResource{}, "", "", false, nil) return NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, "", "", schema.GroupVersionResource{}, "", "", nil, false, nil)
} }
func TestWithAudit(t *testing.T) { func TestWithAudit(t *testing.T) {
......
...@@ -21,6 +21,7 @@ import ( ...@@ -21,6 +21,7 @@ import (
"testing" "testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
) )
...@@ -63,6 +64,7 @@ func TestAdmitAndValidate(t *testing.T) { ...@@ -63,6 +64,7 @@ func TestAdmitAndValidate(t *testing.T) {
name string name string
ns string ns string
operation Operation operation Operation
options runtime.Object
chain chainAdmissionHandler chain chainAdmissionHandler
accept bool accept bool
calls map[string]bool calls map[string]bool
...@@ -71,6 +73,7 @@ func TestAdmitAndValidate(t *testing.T) { ...@@ -71,6 +73,7 @@ func TestAdmitAndValidate(t *testing.T) {
name: "all accept", name: "all accept",
ns: sysns, ns: sysns,
operation: Create, operation: Create,
options: &metav1.CreateOptions{},
chain: []Interface{ chain: []Interface{
makeHandler("a", true, Update, Delete, Create), makeHandler("a", true, Update, Delete, Create),
makeHandler("b", true, Delete, Create), makeHandler("b", true, Delete, Create),
...@@ -83,6 +86,7 @@ func TestAdmitAndValidate(t *testing.T) { ...@@ -83,6 +86,7 @@ func TestAdmitAndValidate(t *testing.T) {
name: "ignore handler", name: "ignore handler",
ns: otherns, ns: otherns,
operation: Create, operation: Create,
options: &metav1.CreateOptions{},
chain: []Interface{ chain: []Interface{
makeHandler("a", true, Update, Delete, Create), makeHandler("a", true, Update, Delete, Create),
makeHandler("b", false, Delete), makeHandler("b", false, Delete),
...@@ -95,6 +99,7 @@ func TestAdmitAndValidate(t *testing.T) { ...@@ -95,6 +99,7 @@ func TestAdmitAndValidate(t *testing.T) {
name: "ignore all", name: "ignore all",
ns: sysns, ns: sysns,
operation: Connect, operation: Connect,
options: nil,
chain: []Interface{ chain: []Interface{
makeHandler("a", true, Update, Delete, Create), makeHandler("a", true, Update, Delete, Create),
makeHandler("b", false, Delete), makeHandler("b", false, Delete),
...@@ -107,6 +112,7 @@ func TestAdmitAndValidate(t *testing.T) { ...@@ -107,6 +112,7 @@ func TestAdmitAndValidate(t *testing.T) {
name: "reject one", name: "reject one",
ns: otherns, ns: otherns,
operation: Delete, operation: Delete,
options: &metav1.DeleteOptions{},
chain: []Interface{ chain: []Interface{
makeHandler("a", true, Update, Delete, Create), makeHandler("a", true, Update, Delete, Create),
makeHandler("b", false, Delete), makeHandler("b", false, Delete),
...@@ -119,7 +125,7 @@ func TestAdmitAndValidate(t *testing.T) { ...@@ -119,7 +125,7 @@ func TestAdmitAndValidate(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Logf("testcase = %s", test.name) t.Logf("testcase = %s", test.name)
// call admit and check that validate was not called at all // call admit and check that validate was not called at all
err := test.chain.Admit(NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, test.ns, "", schema.GroupVersionResource{}, "", test.operation, false, nil), nil) err := test.chain.Admit(NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, test.ns, "", schema.GroupVersionResource{}, "", test.operation, test.options, false, nil), nil)
accepted := (err == nil) accepted := (err == nil)
if accepted != test.accept { if accepted != test.accept {
t.Errorf("unexpected result of admit call: %v", accepted) t.Errorf("unexpected result of admit call: %v", accepted)
...@@ -140,7 +146,7 @@ func TestAdmitAndValidate(t *testing.T) { ...@@ -140,7 +146,7 @@ func TestAdmitAndValidate(t *testing.T) {
} }
// call validate and check that admit was not called at all // call validate and check that admit was not called at all
err = test.chain.Validate(NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, test.ns, "", schema.GroupVersionResource{}, "", test.operation, false, nil), nil) err = test.chain.Validate(NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, test.ns, "", schema.GroupVersionResource{}, "", test.operation, test.options, false, nil), nil)
accepted = (err == nil) accepted = (err == nil)
if accepted != test.accept { if accepted != test.accept {
t.Errorf("unexpected result of validate call: %v\n", accepted) t.Errorf("unexpected result of validate call: %v\n", accepted)
......
...@@ -36,6 +36,7 @@ func TestNewForbidden(t *testing.T) { ...@@ -36,6 +36,7 @@ func TestNewForbidden(t *testing.T) {
schema.GroupVersionResource{Group: "foo", Version: "bar", Resource: "baz"}, schema.GroupVersionResource{Group: "foo", Version: "bar", Resource: "baz"},
"", "",
Create, Create,
nil,
false, false,
nil) nil)
err := errors.New("some error") err := errors.New("some error")
......
...@@ -41,6 +41,8 @@ type Attributes interface { ...@@ -41,6 +41,8 @@ type Attributes interface {
GetSubresource() string GetSubresource() string
// GetOperation is the operation being performed // GetOperation is the operation being performed
GetOperation() Operation GetOperation() Operation
// GetOperationOptions is the options for the operation being performed
GetOperationOptions() runtime.Object
// IsDryRun indicates that modifications will definitely not be persisted for this request. This is to prevent // IsDryRun indicates that modifications will definitely not be persisted for this request. This is to prevent
// admission controllers with side effects and a method of reconciliation from being overwhelmed. // admission controllers with side effects and a method of reconciliation from being overwhelmed.
// However, a value of false for this does not mean that the modification will be persisted, because it // However, a value of false for this does not mean that the modification will be persisted, because it
......
...@@ -20,6 +20,8 @@ go_test( ...@@ -20,6 +20,8 @@ go_test(
], ],
embed = [":go_default_library"], embed = [":go_default_library"],
deps = [ deps = [
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library", "//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
......
...@@ -21,6 +21,8 @@ import ( ...@@ -21,6 +21,8 @@ import (
"testing" "testing"
"time" "time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
) )
...@@ -28,7 +30,7 @@ import ( ...@@ -28,7 +30,7 @@ import (
var ( var (
kind = schema.GroupVersionKind{Group: "kgroup", Version: "kversion", Kind: "kind"} kind = schema.GroupVersionKind{Group: "kgroup", Version: "kversion", Kind: "kind"}
resource = schema.GroupVersionResource{Group: "rgroup", Version: "rversion", Resource: "resource"} resource = schema.GroupVersionResource{Group: "rgroup", Version: "rversion", Resource: "resource"}
attr = admission.NewAttributesRecord(nil, nil, kind, "ns", "name", resource, "subresource", admission.Create, false, nil) attr = admission.NewAttributesRecord(nil, nil, kind, "ns", "name", resource, "subresource", admission.Create, &metav1.CreateOptions{}, false, nil)
) )
func TestObserveAdmissionStep(t *testing.T) { func TestObserveAdmissionStep(t *testing.T) {
...@@ -85,6 +87,7 @@ func TestWithMetrics(t *testing.T) { ...@@ -85,6 +87,7 @@ func TestWithMetrics(t *testing.T) {
name string name string
ns string ns string
operation admission.Operation operation admission.Operation
options runtime.Object
handler admission.Interface handler admission.Interface
admit, validate bool admit, validate bool
} }
...@@ -93,6 +96,7 @@ func TestWithMetrics(t *testing.T) { ...@@ -93,6 +96,7 @@ func TestWithMetrics(t *testing.T) {
"both-interfaces-admit-and-validate", "both-interfaces-admit-and-validate",
"some-ns", "some-ns",
admission.Create, admission.Create,
&metav1.CreateOptions{},
&mutatingAndValidatingFakeHandler{admission.NewHandler(admission.Create, admission.Update), true, true}, &mutatingAndValidatingFakeHandler{admission.NewHandler(admission.Create, admission.Update), true, true},
true, true, true, true,
}, },
...@@ -100,6 +104,7 @@ func TestWithMetrics(t *testing.T) { ...@@ -100,6 +104,7 @@ func TestWithMetrics(t *testing.T) {
"both-interfaces-dont-admit", "both-interfaces-dont-admit",
"some-ns", "some-ns",
admission.Create, admission.Create,
&metav1.CreateOptions{},
&mutatingAndValidatingFakeHandler{admission.NewHandler(admission.Create, admission.Update), false, true}, &mutatingAndValidatingFakeHandler{admission.NewHandler(admission.Create, admission.Update), false, true},
false, true, false, true,
}, },
...@@ -107,6 +112,7 @@ func TestWithMetrics(t *testing.T) { ...@@ -107,6 +112,7 @@ func TestWithMetrics(t *testing.T) {
"both-interfaces-admit-dont-validate", "both-interfaces-admit-dont-validate",
"some-ns", "some-ns",
admission.Create, admission.Create,
&metav1.CreateOptions{},
&mutatingAndValidatingFakeHandler{admission.NewHandler(admission.Create, admission.Update), true, false}, &mutatingAndValidatingFakeHandler{admission.NewHandler(admission.Create, admission.Update), true, false},
true, false, true, false,
}, },
...@@ -114,6 +120,7 @@ func TestWithMetrics(t *testing.T) { ...@@ -114,6 +120,7 @@ func TestWithMetrics(t *testing.T) {
"validate-interfaces-validate", "validate-interfaces-validate",
"some-ns", "some-ns",
admission.Create, admission.Create,
&metav1.CreateOptions{},
&validatingFakeHandler{admission.NewHandler(admission.Create, admission.Update), true}, &validatingFakeHandler{admission.NewHandler(admission.Create, admission.Update), true},
true, true, true, true,
}, },
...@@ -121,6 +128,7 @@ func TestWithMetrics(t *testing.T) { ...@@ -121,6 +128,7 @@ func TestWithMetrics(t *testing.T) {
"validate-interfaces-dont-validate", "validate-interfaces-dont-validate",
"some-ns", "some-ns",
admission.Create, admission.Create,
&metav1.CreateOptions{},
&validatingFakeHandler{admission.NewHandler(admission.Create, admission.Update), false}, &validatingFakeHandler{admission.NewHandler(admission.Create, admission.Update), false},
true, false, true, false,
}, },
...@@ -128,6 +136,7 @@ func TestWithMetrics(t *testing.T) { ...@@ -128,6 +136,7 @@ func TestWithMetrics(t *testing.T) {
"mutating-interfaces-admit", "mutating-interfaces-admit",
"some-ns", "some-ns",
admission.Create, admission.Create,
&metav1.CreateOptions{},
&mutatingFakeHandler{admission.NewHandler(admission.Create, admission.Update), true}, &mutatingFakeHandler{admission.NewHandler(admission.Create, admission.Update), true},
true, true, true, true,
}, },
...@@ -135,6 +144,7 @@ func TestWithMetrics(t *testing.T) { ...@@ -135,6 +144,7 @@ func TestWithMetrics(t *testing.T) {
"mutating-interfaces-dont-admit", "mutating-interfaces-dont-admit",
"some-ns", "some-ns",
admission.Create, admission.Create,
&metav1.CreateOptions{},
&mutatingFakeHandler{admission.NewHandler(admission.Create, admission.Update), false}, &mutatingFakeHandler{admission.NewHandler(admission.Create, admission.Update), false},
false, true, false, true,
}, },
...@@ -144,7 +154,7 @@ func TestWithMetrics(t *testing.T) { ...@@ -144,7 +154,7 @@ func TestWithMetrics(t *testing.T) {
h := WithMetrics(test.handler, Metrics.ObserveAdmissionController, test.name) h := WithMetrics(test.handler, Metrics.ObserveAdmissionController, test.name)
// test mutation // test mutation
err := h.(admission.MutationInterface).Admit(admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, test.ns, "", schema.GroupVersionResource{}, "", test.operation, false, nil), nil) err := h.(admission.MutationInterface).Admit(admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, test.ns, "", schema.GroupVersionResource{}, "", test.operation, test.options, false, nil), nil)
if test.admit && err != nil { if test.admit && err != nil {
t.Errorf("expected admit to succeed, but failed: %v", err) t.Errorf("expected admit to succeed, but failed: %v", err)
continue continue
...@@ -169,7 +179,7 @@ func TestWithMetrics(t *testing.T) { ...@@ -169,7 +179,7 @@ func TestWithMetrics(t *testing.T) {
} }
// test validation // test validation
err = h.(admission.ValidationInterface).Validate(admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, test.ns, "", schema.GroupVersionResource{}, "", test.operation, false, nil), nil) err = h.(admission.ValidationInterface).Validate(admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, test.ns, "", schema.GroupVersionResource{}, "", test.operation, test.options, false, nil), nil)
if test.validate && err != nil { if test.validate && err != nil {
t.Errorf("expected admit to succeed, but failed: %v", err) t.Errorf("expected admit to succeed, but failed: %v", err)
continue continue
......
...@@ -122,7 +122,7 @@ func TestDispatch(t *testing.T) { ...@@ -122,7 +122,7 @@ func TestDispatch(t *testing.T) {
plugin: &Plugin{}, plugin: &Plugin{},
} }
attr := generic.VersionedAttributes{ attr := generic.VersionedAttributes{
Attributes: admission.NewAttributesRecord(test.out, nil, schema.GroupVersionKind{}, "", "", schema.GroupVersionResource{}, "", admission.Operation(""), false, nil), Attributes: admission.NewAttributesRecord(test.out, nil, schema.GroupVersionKind{}, "", "", schema.GroupVersionResource{}, "", admission.Operation(""), nil, false, nil),
VersionedOldObject: nil, VersionedOldObject: nil,
VersionedObject: test.in, VersionedObject: test.in,
} }
......
...@@ -75,27 +75,27 @@ func TestGetNamespaceLabels(t *testing.T) { ...@@ -75,27 +75,27 @@ func TestGetNamespaceLabels(t *testing.T) {
}{ }{
{ {
name: "request is for creating namespace, the labels should be from the object itself", name: "request is for creating namespace, the labels should be from the object itself",
attr: admission.NewAttributesRecord(&namespace2, nil, schema.GroupVersionKind{}, "", namespace2.Name, schema.GroupVersionResource{Resource: "namespaces"}, "", admission.Create, false, nil), attr: admission.NewAttributesRecord(&namespace2, nil, schema.GroupVersionKind{}, "", namespace2.Name, schema.GroupVersionResource{Resource: "namespaces"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
expectedLabels: namespace2Labels, expectedLabels: namespace2Labels,
}, },
{ {
name: "request is for updating namespace, the labels should be from the new object", name: "request is for updating namespace, the labels should be from the new object",
attr: admission.NewAttributesRecord(&namespace2, nil, schema.GroupVersionKind{}, namespace2.Name, namespace2.Name, schema.GroupVersionResource{Resource: "namespaces"}, "", admission.Update, false, nil), attr: admission.NewAttributesRecord(&namespace2, nil, schema.GroupVersionKind{}, namespace2.Name, namespace2.Name, schema.GroupVersionResource{Resource: "namespaces"}, "", admission.Update, &metav1.UpdateOptions{}, false, nil),
expectedLabels: namespace2Labels, expectedLabels: namespace2Labels,
}, },
{ {
name: "request is for deleting namespace, the labels should be from the cache", name: "request is for deleting namespace, the labels should be from the cache",
attr: admission.NewAttributesRecord(&namespace2, nil, schema.GroupVersionKind{}, namespace1.Name, namespace1.Name, schema.GroupVersionResource{Resource: "namespaces"}, "", admission.Delete, false, nil), attr: admission.NewAttributesRecord(&namespace2, nil, schema.GroupVersionKind{}, namespace1.Name, namespace1.Name, schema.GroupVersionResource{Resource: "namespaces"}, "", admission.Delete, &metav1.DeleteOptions{}, false, nil),
expectedLabels: namespace1Labels, expectedLabels: namespace1Labels,
}, },
{ {
name: "request is for namespace/finalizer", name: "request is for namespace/finalizer",
attr: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, namespace1.Name, "mock-name", schema.GroupVersionResource{Resource: "namespaces"}, "finalizers", admission.Create, false, nil), attr: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, namespace1.Name, "mock-name", schema.GroupVersionResource{Resource: "namespaces"}, "finalizers", admission.Create, &metav1.CreateOptions{}, false, nil),
expectedLabels: namespace1Labels, expectedLabels: namespace1Labels,
}, },
{ {
name: "request is for pod", name: "request is for pod",
attr: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, namespace1.Name, "mock-name", schema.GroupVersionResource{Resource: "pods"}, "", admission.Create, false, nil), attr: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, namespace1.Name, "mock-name", schema.GroupVersionResource{Resource: "pods"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
expectedLabels: namespace1Labels, expectedLabels: namespace1Labels,
}, },
} }
...@@ -117,7 +117,7 @@ func TestNotExemptClusterScopedResource(t *testing.T) { ...@@ -117,7 +117,7 @@ func TestNotExemptClusterScopedResource(t *testing.T) {
hook := &registrationv1beta1.Webhook{ hook := &registrationv1beta1.Webhook{
NamespaceSelector: &metav1.LabelSelector{}, NamespaceSelector: &metav1.LabelSelector{},
} }
attr := admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, "", "mock-name", schema.GroupVersionResource{Version: "v1", Resource: "nodes"}, "", admission.Create, false, nil) attr := admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, "", "mock-name", schema.GroupVersionResource{Version: "v1", Resource: "nodes"}, "", admission.Create, &metav1.CreateOptions{}, false, nil)
matcher := Matcher{} matcher := Matcher{}
matches, err := matcher.MatchNamespaceSelector(hook, attr) matches, err := matcher.MatchNamespaceSelector(hook, attr)
if err != nil { if err != nil {
......
...@@ -68,6 +68,9 @@ func CreateAdmissionReview(attr *generic.VersionedAttributes) admissionv1beta1.A ...@@ -68,6 +68,9 @@ func CreateAdmissionReview(attr *generic.VersionedAttributes) admissionv1beta1.A
Object: attr.VersionedOldObject, Object: attr.VersionedOldObject,
}, },
DryRun: &dryRun, DryRun: &dryRun,
Options: runtime.RawExtension{
Object: attr.GetOperationOptions(),
},
}, },
} }
} }
...@@ -20,6 +20,8 @@ go_test( ...@@ -20,6 +20,8 @@ go_test(
embed = [":go_default_library"], embed = [":go_default_library"],
deps = [ deps = [
"//staging/src/k8s.io/api/admissionregistration/v1beta1:go_default_library", "//staging/src/k8s.io/api/admissionregistration/v1beta1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
......
...@@ -101,9 +101,10 @@ func newAttributesRecord(object metav1.Object, oldObject metav1.Object, kind sch ...@@ -101,9 +101,10 @@ func newAttributesRecord(object metav1.Object, oldObject metav1.Object, kind sch
Name: "webhook-test", Name: "webhook-test",
UID: "webhook-test", UID: "webhook-test",
} }
options := &metav1.UpdateOptions{}
return &FakeAttributes{ return &FakeAttributes{
Attributes: admission.NewAttributesRecord(object.(runtime.Object), oldObject.(runtime.Object), kind, namespace, name, gvr, subResource, admission.Update, dryRun, &userInfo), Attributes: admission.NewAttributesRecord(object.(runtime.Object), oldObject.(runtime.Object), kind, namespace, name, gvr, subResource, admission.Update, options, dryRun, &userInfo),
} }
} }
......
...@@ -27,6 +27,7 @@ go_test( ...@@ -27,6 +27,7 @@ go_test(
"//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/json:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
......
...@@ -106,6 +106,7 @@ func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Int ...@@ -106,6 +106,7 @@ func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Int
scope.err(err, w, req) scope.err(err, w, req)
return return
} }
options.TypeMeta.SetGroupVersionKind(metav1.SchemeGroupVersion.WithKind("CreateOptions"))
defaultGVK := scope.Kind defaultGVK := scope.Kind
original := r.New() original := r.New()
...@@ -128,7 +129,7 @@ func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Int ...@@ -128,7 +129,7 @@ func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Int
audit.LogRequestObject(ae, obj, scope.Resource, scope.Subresource, scope.Serializer) audit.LogRequestObject(ae, obj, scope.Resource, scope.Subresource, scope.Serializer)
userInfo, _ := request.UserFrom(ctx) userInfo, _ := request.UserFrom(ctx)
admissionAttributes := admission.NewAttributesRecord(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, dryrun.IsDryRun(options.DryRun), userInfo) admissionAttributes := admission.NewAttributesRecord(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, options, dryrun.IsDryRun(options.DryRun), userInfo)
if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && mutatingAdmission.Handles(admission.Create) { if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && mutatingAdmission.Handles(admission.Create) {
err = mutatingAdmission.Admit(admissionAttributes, scope) err = mutatingAdmission.Admit(admissionAttributes, scope)
if err != nil { if err != nil {
......
...@@ -113,11 +113,12 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope *RequestSc ...@@ -113,11 +113,12 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope *RequestSc
scope.err(err, w, req) scope.err(err, w, req)
return return
} }
options.TypeMeta.SetGroupVersionKind(metav1.SchemeGroupVersion.WithKind("DeleteOptions"))
trace.Step("About to check admission control") trace.Step("About to check admission control")
if admit != nil && admit.Handles(admission.Delete) { if admit != nil && admit.Handles(admission.Delete) {
userInfo, _ := request.UserFrom(ctx) userInfo, _ := request.UserFrom(ctx)
attrs := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Delete, dryrun.IsDryRun(options.DryRun), userInfo) attrs := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Delete, options, dryrun.IsDryRun(options.DryRun), userInfo)
if mutatingAdmission, ok := admit.(admission.MutationInterface); ok { if mutatingAdmission, ok := admit.(admission.MutationInterface); ok {
if err := mutatingAdmission.Admit(attrs, scope); err != nil { if err := mutatingAdmission.Admit(attrs, scope); err != nil {
scope.err(err, w, req) scope.err(err, w, req)
...@@ -236,6 +237,8 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope *RequestSc ...@@ -236,6 +237,8 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope *RequestSc
scope.err(err, w, req) scope.err(err, w, req)
return return
} }
// For backwards compatibility, we need to allow existing clients to submit per group DeleteOptions
// It is also allowed to pass a body with meta.k8s.io/v1.DeleteOptions
defaultGVK := scope.Kind.GroupVersion().WithKind("DeleteOptions") defaultGVK := scope.Kind.GroupVersion().WithKind("DeleteOptions")
obj, _, err := scope.Serializer.DecoderToVersion(s.Serializer, defaultGVK.GroupVersion()).Decode(body, &defaultGVK, options) obj, _, err := scope.Serializer.DecoderToVersion(s.Serializer, defaultGVK.GroupVersion()).Decode(body, &defaultGVK, options)
if err != nil { if err != nil {
...@@ -262,11 +265,12 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope *RequestSc ...@@ -262,11 +265,12 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope *RequestSc
scope.err(err, w, req) scope.err(err, w, req)
return return
} }
options.TypeMeta.SetGroupVersionKind(metav1.SchemeGroupVersion.WithKind("DeleteOptions"))
admit = admission.WithAudit(admit, ae) admit = admission.WithAudit(admit, ae)
if admit != nil && admit.Handles(admission.Delete) { if admit != nil && admit.Handles(admission.Delete) {
userInfo, _ := request.UserFrom(ctx) userInfo, _ := request.UserFrom(ctx)
attrs := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, "", scope.Resource, scope.Subresource, admission.Delete, dryrun.IsDryRun(options.DryRun), userInfo) attrs := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, "", scope.Resource, scope.Subresource, admission.Delete, options, dryrun.IsDryRun(options.DryRun), userInfo)
if mutatingAdmission, ok := admit.(admission.MutationInterface); ok { if mutatingAdmission, ok := admit.(admission.MutationInterface); ok {
err = mutatingAdmission.Admit(attrs, scope) err = mutatingAdmission.Admit(attrs, scope)
if err != nil { if err != nil {
......
...@@ -23,7 +23,7 @@ import ( ...@@ -23,7 +23,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/evanphx/json-patch" jsonpatch "github.com/evanphx/json-patch"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
...@@ -118,6 +118,7 @@ func PatchResource(r rest.Patcher, scope *RequestScope, admit admission.Interfac ...@@ -118,6 +118,7 @@ func PatchResource(r rest.Patcher, scope *RequestScope, admit admission.Interfac
scope.err(err, w, req) scope.err(err, w, req)
return return
} }
options.TypeMeta.SetGroupVersionKind(metav1.SchemeGroupVersion.WithKind("PatchOptions"))
ae := request.AuditEventFrom(ctx) ae := request.AuditEventFrom(ctx)
admit = admission.WithAudit(admit, ae) admit = admission.WithAudit(admit, ae)
...@@ -151,6 +152,7 @@ func PatchResource(r rest.Patcher, scope *RequestScope, admit admission.Interfac ...@@ -151,6 +152,7 @@ func PatchResource(r rest.Patcher, scope *RequestScope, admit admission.Interfac
scope.Resource, scope.Resource,
scope.Subresource, scope.Subresource,
admission.Create, admission.Create,
patchToCreateOptions(options),
dryrun.IsDryRun(options.DryRun), dryrun.IsDryRun(options.DryRun),
userInfo) userInfo)
staticUpdateAttributes := admission.NewAttributesRecord( staticUpdateAttributes := admission.NewAttributesRecord(
...@@ -162,6 +164,7 @@ func PatchResource(r rest.Patcher, scope *RequestScope, admit admission.Interfac ...@@ -162,6 +164,7 @@ func PatchResource(r rest.Patcher, scope *RequestScope, admit admission.Interfac
scope.Resource, scope.Resource,
scope.Subresource, scope.Subresource,
admission.Update, admission.Update,
patchToUpdateOptions(options),
dryrun.IsDryRun(options.DryRun), dryrun.IsDryRun(options.DryRun),
userInfo, userInfo,
) )
...@@ -489,9 +492,9 @@ func (p *patcher) applyPatch(_ context.Context, _, currentObject runtime.Object) ...@@ -489,9 +492,9 @@ func (p *patcher) applyPatch(_ context.Context, _, currentObject runtime.Object)
return objToUpdate, nil return objToUpdate, nil
} }
func (p *patcher) admissionAttributes(ctx context.Context, updatedObject runtime.Object, currentObject runtime.Object, operation admission.Operation) admission.Attributes { func (p *patcher) admissionAttributes(ctx context.Context, updatedObject runtime.Object, currentObject runtime.Object, operation admission.Operation, operationOptions runtime.Object) admission.Attributes {
userInfo, _ := request.UserFrom(ctx) userInfo, _ := request.UserFrom(ctx)
return admission.NewAttributesRecord(updatedObject, currentObject, p.kind, p.namespace, p.name, p.resource, p.subresource, operation, p.dryRun, userInfo) return admission.NewAttributesRecord(updatedObject, currentObject, p.kind, p.namespace, p.name, p.resource, p.subresource, operation, operationOptions, p.dryRun, userInfo)
} }
// applyAdmission is called every time GuaranteedUpdate asks for the updated object, // applyAdmission is called every time GuaranteedUpdate asks for the updated object,
...@@ -500,16 +503,19 @@ func (p *patcher) admissionAttributes(ctx context.Context, updatedObject runtime ...@@ -500,16 +503,19 @@ func (p *patcher) admissionAttributes(ctx context.Context, updatedObject runtime
func (p *patcher) applyAdmission(ctx context.Context, patchedObject runtime.Object, currentObject runtime.Object) (runtime.Object, error) { func (p *patcher) applyAdmission(ctx context.Context, patchedObject runtime.Object, currentObject runtime.Object) (runtime.Object, error) {
p.trace.Step("About to check admission control") p.trace.Step("About to check admission control")
var operation admission.Operation var operation admission.Operation
var options runtime.Object
if hasUID, err := hasUID(currentObject); err != nil { if hasUID, err := hasUID(currentObject); err != nil {
return nil, err return nil, err
} else if !hasUID { } else if !hasUID {
operation = admission.Create operation = admission.Create
currentObject = nil currentObject = nil
options = patchToCreateOptions(p.options)
} else { } else {
operation = admission.Update operation = admission.Update
options = patchToUpdateOptions(p.options)
} }
if p.admissionCheck != nil && p.admissionCheck.Handles(operation) { if p.admissionCheck != nil && p.admissionCheck.Handles(operation) {
attributes := p.admissionAttributes(ctx, patchedObject, currentObject, operation) attributes := p.admissionAttributes(ctx, patchedObject, currentObject, operation, options)
return patchedObject, p.admissionCheck.Admit(attributes, p.objectInterfaces) return patchedObject, p.admissionCheck.Admit(attributes, p.objectInterfaces)
} }
return patchedObject, nil return patchedObject, nil
...@@ -551,11 +557,8 @@ func (p *patcher) patchResource(ctx context.Context, scope *RequestScope) (runti ...@@ -551,11 +557,8 @@ func (p *patcher) patchResource(ctx context.Context, scope *RequestScope) (runti
wasCreated := false wasCreated := false
p.updatedObjectInfo = rest.DefaultUpdatedObjectInfo(nil, p.applyPatch, p.applyAdmission) p.updatedObjectInfo = rest.DefaultUpdatedObjectInfo(nil, p.applyPatch, p.applyAdmission)
result, err := finishRequest(p.timeout, func() (runtime.Object, error) { result, err := finishRequest(p.timeout, func() (runtime.Object, error) {
// TODO: Pass in UpdateOptions to override UpdateStrategy.AllowUpdateOnCreate // Pass in UpdateOptions to override UpdateStrategy.AllowUpdateOnCreate
options, err := patchToUpdateOptions(p.options) options := patchToUpdateOptions(p.options)
if err != nil {
return nil, err
}
updateObject, created, updateErr := p.restPatcher.Update(ctx, p.name, p.updatedObjectInfo, p.createValidation, p.updateValidation, p.forceAllowCreate, options) updateObject, created, updateErr := p.restPatcher.Update(ctx, p.name, p.updatedObjectInfo, p.createValidation, p.updateValidation, p.forceAllowCreate, options)
wasCreated = created wasCreated = created
return updateObject, updateErr return updateObject, updateErr
...@@ -600,12 +603,28 @@ func interpretStrategicMergePatchError(err error) error { ...@@ -600,12 +603,28 @@ func interpretStrategicMergePatchError(err error) error {
} }
} }
func patchToUpdateOptions(po *metav1.PatchOptions) (*metav1.UpdateOptions, error) { // patchToUpdateOptions creates an UpdateOptions with the same field values as the provided PatchOptions.
b, err := json.Marshal(po) func patchToUpdateOptions(po *metav1.PatchOptions) *metav1.UpdateOptions {
if err != nil { if po == nil {
return nil, err return nil
}
uo := &metav1.UpdateOptions{
DryRun: po.DryRun,
FieldManager: po.FieldManager,
}
uo.TypeMeta.SetGroupVersionKind(metav1.SchemeGroupVersion.WithKind("UpdateOptions"))
return uo
}
// patchToCreateOptions creates an CreateOptions with the same field values as the provided PatchOptions.
func patchToCreateOptions(po *metav1.PatchOptions) *metav1.CreateOptions {
if po == nil {
return nil
}
co := &metav1.CreateOptions{
DryRun: po.DryRun,
FieldManager: po.FieldManager,
} }
uo := metav1.UpdateOptions{} co.TypeMeta.SetGroupVersionKind(metav1.SchemeGroupVersion.WithKind("CreateOptions"))
err = json.Unmarshal(b, &uo) return co
return &uo, err
} }
...@@ -137,14 +137,14 @@ func ConnectResource(connecter rest.Connecter, scope *RequestScope, admit admiss ...@@ -137,14 +137,14 @@ func ConnectResource(connecter rest.Connecter, scope *RequestScope, admit admiss
userInfo, _ := request.UserFrom(ctx) userInfo, _ := request.UserFrom(ctx)
// TODO: remove the mutating admission here as soon as we have ported all plugin that handle CONNECT // TODO: remove the mutating admission here as soon as we have ported all plugin that handle CONNECT
if mutatingAdmission, ok := admit.(admission.MutationInterface); ok { if mutatingAdmission, ok := admit.(admission.MutationInterface); ok {
err = mutatingAdmission.Admit(admission.NewAttributesRecord(opts, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Connect, false, userInfo), scope) err = mutatingAdmission.Admit(admission.NewAttributesRecord(opts, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Connect, nil, false, userInfo), scope)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
} }
} }
if validatingAdmission, ok := admit.(admission.ValidationInterface); ok { if validatingAdmission, ok := admit.(admission.ValidationInterface); ok {
err = validatingAdmission.Validate(admission.NewAttributesRecord(opts, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Connect, false, userInfo), scope) err = validatingAdmission.Validate(admission.NewAttributesRecord(opts, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Connect, nil, false, userInfo), scope)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
......
...@@ -26,7 +26,8 @@ import ( ...@@ -26,7 +26,8 @@ import (
"testing" "testing"
"time" "time"
"github.com/evanphx/json-patch" jsonpatch "github.com/evanphx/json-patch"
fuzz "github.com/google/gofuzz"
apiequality "k8s.io/apimachinery/pkg/api/equality" apiequality "k8s.io/apimachinery/pkg/api/equality"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
...@@ -37,6 +38,7 @@ import ( ...@@ -37,6 +38,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/diff" "k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/util/json"
utilruntime "k8s.io/apimachinery/pkg/util/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/strategicpatch" "k8s.io/apimachinery/pkg/util/strategicpatch"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
...@@ -1000,3 +1002,89 @@ func (alwaysErrorTyper) ObjectKinds(runtime.Object) ([]schema.GroupVersionKind, ...@@ -1000,3 +1002,89 @@ func (alwaysErrorTyper) ObjectKinds(runtime.Object) ([]schema.GroupVersionKind,
func (alwaysErrorTyper) Recognizes(gvk schema.GroupVersionKind) bool { func (alwaysErrorTyper) Recognizes(gvk schema.GroupVersionKind) bool {
return false return false
} }
func TestUpdateToCreateOptions(t *testing.T) {
f := fuzz.New()
for i := 0; i < 100; i++ {
t.Run(fmt.Sprintf("Run %d/100", i), func(t *testing.T) {
update := &metav1.UpdateOptions{}
f.Fuzz(update)
create := updateToCreateOptions(update)
b, err := json.Marshal(create)
if err != nil {
t.Fatalf("failed to marshal CreateOptions (%v): %v", err, create)
}
got := &metav1.UpdateOptions{}
err = json.Unmarshal(b, &got)
if err != nil {
t.Fatalf("failed to unmarshal UpdateOptions: %v", err)
}
got.TypeMeta = metav1.TypeMeta{}
update.TypeMeta = metav1.TypeMeta{}
if !reflect.DeepEqual(*update, *got) {
t.Fatalf(`updateToCreateOptions round-trip failed:
got: %#+v
want: %#+v`, got, update)
}
})
}
}
func TestPatchToUpdateOptions(t *testing.T) {
tests := []struct {
name string
converterFn func(po *metav1.PatchOptions) interface{}
}{
{
name: "patchToUpdateOptions",
converterFn: func(patch *metav1.PatchOptions) interface{} {
return patchToUpdateOptions(patch)
},
},
{
name: "patchToCreateOptions",
converterFn: func(patch *metav1.PatchOptions) interface{} {
return patchToCreateOptions(patch)
},
},
}
f := fuzz.New()
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
for i := 0; i < 100; i++ {
t.Run(fmt.Sprintf("Run %d/100", i), func(t *testing.T) {
patch := &metav1.PatchOptions{}
f.Fuzz(patch)
converted := test.converterFn(patch)
b, err := json.Marshal(converted)
if err != nil {
t.Fatalf("failed to marshal converted object (%v): %v", err, converted)
}
got := &metav1.PatchOptions{}
err = json.Unmarshal(b, &got)
if err != nil {
t.Fatalf("failed to unmarshal converted object: %v", err)
}
// Clear TypeMeta because we expect it to be different between the original and converted type
got.TypeMeta = metav1.TypeMeta{}
patch.TypeMeta = metav1.TypeMeta{}
// clear fields that we know belong in PatchOptions only
patch.Force = nil
if !reflect.DeepEqual(*patch, *got) {
t.Fatalf(`round-trip failed:
got: %#+v
want: %#+v`, got, converted)
}
})
}
})
}
}
...@@ -87,6 +87,7 @@ func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interfa ...@@ -87,6 +87,7 @@ func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interfa
scope.err(err, w, req) scope.err(err, w, req)
return return
} }
options.TypeMeta.SetGroupVersionKind(metav1.SchemeGroupVersion.WithKind("UpdateOptions"))
s, err := negotiation.NegotiateInputSerializer(req, false, scope.Serializer) s, err := negotiation.NegotiateInputSerializer(req, false, scope.Serializer)
if err != nil { if err != nil {
...@@ -138,11 +139,11 @@ func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interfa ...@@ -138,11 +139,11 @@ func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interfa
return nil, fmt.Errorf("unexpected error when extracting UID from oldObj: %v", err.Error()) return nil, fmt.Errorf("unexpected error when extracting UID from oldObj: %v", err.Error())
} else if !isNotZeroObject { } else if !isNotZeroObject {
if mutatingAdmission.Handles(admission.Create) { if mutatingAdmission.Handles(admission.Create) {
return newObj, mutatingAdmission.Admit(admission.NewAttributesRecord(newObj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, dryrun.IsDryRun(options.DryRun), userInfo), scope) return newObj, mutatingAdmission.Admit(admission.NewAttributesRecord(newObj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, updateToCreateOptions(options), dryrun.IsDryRun(options.DryRun), userInfo), scope)
} }
} else { } else {
if mutatingAdmission.Handles(admission.Update) { if mutatingAdmission.Handles(admission.Update) {
return newObj, mutatingAdmission.Admit(admission.NewAttributesRecord(newObj, oldObj, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, dryrun.IsDryRun(options.DryRun), userInfo), scope) return newObj, mutatingAdmission.Admit(admission.NewAttributesRecord(newObj, oldObj, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, options, dryrun.IsDryRun(options.DryRun), userInfo), scope)
} }
} }
return newObj, nil return newObj, nil
...@@ -172,11 +173,11 @@ func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interfa ...@@ -172,11 +173,11 @@ func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interfa
rest.DefaultUpdatedObjectInfo(obj, transformers...), rest.DefaultUpdatedObjectInfo(obj, transformers...),
withAuthorization(rest.AdmissionToValidateObjectFunc( withAuthorization(rest.AdmissionToValidateObjectFunc(
admit, admit,
admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, dryrun.IsDryRun(options.DryRun), userInfo), scope), admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, updateToCreateOptions(options), dryrun.IsDryRun(options.DryRun), userInfo), scope),
scope.Authorizer, createAuthorizerAttributes), scope.Authorizer, createAuthorizerAttributes),
rest.AdmissionToValidateObjectUpdateFunc( rest.AdmissionToValidateObjectUpdateFunc(
admit, admit,
admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, dryrun.IsDryRun(options.DryRun), userInfo), scope), admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, options, dryrun.IsDryRun(options.DryRun), userInfo), scope),
false, false,
options, options,
) )
...@@ -229,3 +230,16 @@ func withAuthorization(validate rest.ValidateObjectFunc, a authorizer.Authorizer ...@@ -229,3 +230,16 @@ func withAuthorization(validate rest.ValidateObjectFunc, a authorizer.Authorizer
return errors.NewForbidden(gr, name, err) return errors.NewForbidden(gr, name, err)
} }
} }
// updateToCreateOptions creates a CreateOptions with the same field values as the provided UpdateOptions.
func updateToCreateOptions(uo *metav1.UpdateOptions) *metav1.CreateOptions {
if uo == nil {
return nil
}
co := &metav1.CreateOptions{
DryRun: uo.DryRun,
FieldManager: uo.FieldManager,
}
co.TypeMeta.SetGroupVersionKind(metav1.SchemeGroupVersion.WithKind("CreateOptions"))
return co
}
...@@ -175,6 +175,7 @@ func AdmissionToValidateObjectFunc(admit admission.Interface, staticAttributes a ...@@ -175,6 +175,7 @@ func AdmissionToValidateObjectFunc(admit admission.Interface, staticAttributes a
staticAttributes.GetResource(), staticAttributes.GetResource(),
staticAttributes.GetSubresource(), staticAttributes.GetSubresource(),
staticAttributes.GetOperation(), staticAttributes.GetOperation(),
staticAttributes.GetOperationOptions(),
staticAttributes.IsDryRun(), staticAttributes.IsDryRun(),
staticAttributes.GetUserInfo(), staticAttributes.GetUserInfo(),
) )
......
...@@ -271,6 +271,7 @@ func AdmissionToValidateObjectUpdateFunc(admit admission.Interface, staticAttrib ...@@ -271,6 +271,7 @@ func AdmissionToValidateObjectUpdateFunc(admit admission.Interface, staticAttrib
staticAttributes.GetResource(), staticAttributes.GetResource(),
staticAttributes.GetSubresource(), staticAttributes.GetSubresource(),
staticAttributes.GetOperation(), staticAttributes.GetOperation(),
staticAttributes.GetOperationOptions(),
staticAttributes.IsDryRun(), staticAttributes.IsDryRun(),
staticAttributes.GetUserInfo(), staticAttributes.GetUserInfo(),
) )
......
...@@ -136,6 +136,7 @@ func TestBanflunderAdmissionPlugin(t *testing.T) { ...@@ -136,6 +136,7 @@ func TestBanflunderAdmissionPlugin(t *testing.T) {
scenario.admissionInputResource, scenario.admissionInputResource,
"", "",
admission.Create, admission.Create,
&metav1.CreateOptions{},
false, false,
nil), nil),
nil, nil,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment