mirror of https://github.com/knative/pkg.git
Fix invalid creator or lastModifier annotations on core resources (#2409)
When the admission request is for a resource with an empty string as group, which happens on core resources, the `creator` or `lastModifier` annotations are invalid since they become `/creator` or `/lastModifier`. This patch removes the `/` when group = `""`. Signed-off-by: Pierangelo Di Pilato <pierdipi@redhat.com>
This commit is contained in:
parent
21b467ba3f
commit
f4b57aef00
|
@ -24,6 +24,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
||||||
|
@ -31,6 +32,7 @@ import (
|
||||||
_ "knative.dev/pkg/client/injection/kube/client/fake"
|
_ "knative.dev/pkg/client/injection/kube/client/fake"
|
||||||
_ "knative.dev/pkg/client/injection/kube/informers/admissionregistration/v1/mutatingwebhookconfiguration/fake"
|
_ "knative.dev/pkg/client/injection/kube/informers/admissionregistration/v1/mutatingwebhookconfiguration/fake"
|
||||||
_ "knative.dev/pkg/injection/clients/namespacedkube/informers/core/v1/secret/fake"
|
_ "knative.dev/pkg/injection/clients/namespacedkube/informers/core/v1/secret/fake"
|
||||||
|
"knative.dev/pkg/ptr"
|
||||||
|
|
||||||
"gomodules.xyz/jsonpatch/v2"
|
"gomodules.xyz/jsonpatch/v2"
|
||||||
admissionv1 "k8s.io/api/admission/v1"
|
admissionv1 "k8s.io/api/admission/v1"
|
||||||
|
@ -105,6 +107,7 @@ var (
|
||||||
Version: "v1beta1",
|
Version: "v1beta1",
|
||||||
Kind: "ResourceCallbackDefaultCreate",
|
Kind: "ResourceCallbackDefaultCreate",
|
||||||
}: NewCallback(resourceCallback, webhook.Create),
|
}: NewCallback(resourceCallback, webhook.Create),
|
||||||
|
corev1.SchemeGroupVersion.WithKind("Pod"): NewCallback(podCallback, webhook.Create, webhook.Update),
|
||||||
}
|
}
|
||||||
|
|
||||||
initialResourceWebhook = &admissionregistrationv1.MutatingWebhookConfiguration{
|
initialResourceWebhook = &admissionregistrationv1.MutatingWebhookConfiguration{
|
||||||
|
@ -738,6 +741,137 @@ func TestAdmitUpdatesCallback(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAdmitCoreUserInfo(t *testing.T) {
|
||||||
|
gvk := corev1.SchemeGroupVersion.WithKind("Pod")
|
||||||
|
|
||||||
|
ctx, _ := SetupFakeContext(t)
|
||||||
|
ctx = webhook.WithOptions(ctx, webhook.Options{SecretName: "webhook-secret"})
|
||||||
|
r := newTestResourceAdmissionController(t)
|
||||||
|
|
||||||
|
mGvk := metav1.GroupVersionKind{
|
||||||
|
Group: gvk.Group,
|
||||||
|
Version: gvk.Version,
|
||||||
|
Kind: gvk.Kind,
|
||||||
|
}
|
||||||
|
mGvr := metav1.GroupVersionResource{
|
||||||
|
Group: gvk.Group,
|
||||||
|
Version: gvk.Version,
|
||||||
|
Resource: "pods",
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &admissionv1.AdmissionRequest{
|
||||||
|
UID: "58e22c80-9675-4fa4-801c-cb6bf348c799",
|
||||||
|
Kind: mGvk,
|
||||||
|
Resource: metav1.GroupVersionResource{},
|
||||||
|
RequestKind: &mGvk,
|
||||||
|
RequestResource: &mGvr,
|
||||||
|
Name: "p-1",
|
||||||
|
Namespace: "ns",
|
||||||
|
Operation: webhook.Create,
|
||||||
|
UserInfo: authenticationv1.UserInfo{
|
||||||
|
Username: "username",
|
||||||
|
UID: "e4a45e22-c352-4353-a7f1-bcbdcbf7af21",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
tt := []struct {
|
||||||
|
name string
|
||||||
|
allowed bool
|
||||||
|
object *corev1.Pod
|
||||||
|
oldObject *corev1.Pod
|
||||||
|
expected *corev1.Pod
|
||||||
|
patches []jsonpatch.JsonPatchOperation
|
||||||
|
}{{
|
||||||
|
name: "create",
|
||||||
|
allowed: true,
|
||||||
|
object: &corev1.Pod{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: gvk.Kind,
|
||||||
|
APIVersion: gvk.GroupVersion().String(),
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Namespace: req.Namespace,
|
||||||
|
Name: req.Name,
|
||||||
|
},
|
||||||
|
Spec: corev1.PodSpec{},
|
||||||
|
},
|
||||||
|
patches: []jsonpatch.JsonPatchOperation{{
|
||||||
|
Operation: "add",
|
||||||
|
Path: "/metadata/annotations",
|
||||||
|
Value: map[string]interface{}{
|
||||||
|
"creator": req.UserInfo.Username,
|
||||||
|
"lastModifier": req.UserInfo.Username,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
Operation: "add",
|
||||||
|
Path: "/spec/automountServiceAccountToken",
|
||||||
|
Value: true,
|
||||||
|
}},
|
||||||
|
}, {
|
||||||
|
name: "update",
|
||||||
|
allowed: true,
|
||||||
|
object: &corev1.Pod{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: gvk.Kind,
|
||||||
|
APIVersion: gvk.GroupVersion().String(),
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Namespace: req.Namespace,
|
||||||
|
Name: req.Name,
|
||||||
|
},
|
||||||
|
Spec: corev1.PodSpec{},
|
||||||
|
},
|
||||||
|
oldObject: &corev1.Pod{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: gvk.Kind,
|
||||||
|
APIVersion: gvk.GroupVersion().String(),
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Namespace: req.Namespace,
|
||||||
|
Name: req.Name,
|
||||||
|
},
|
||||||
|
Spec: corev1.PodSpec{},
|
||||||
|
},
|
||||||
|
patches: []jsonpatch.JsonPatchOperation{{
|
||||||
|
Operation: "add",
|
||||||
|
Path: "/metadata/annotations",
|
||||||
|
Value: map[string]interface{}{
|
||||||
|
"lastModifier": req.UserInfo.Username,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
Operation: "add",
|
||||||
|
Path: "/spec/automountServiceAccountToken",
|
||||||
|
Value: true,
|
||||||
|
}},
|
||||||
|
}}
|
||||||
|
|
||||||
|
for _, tc := range tt {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
req := req.DeepCopy()
|
||||||
|
|
||||||
|
if tc.object != nil {
|
||||||
|
objRaw, _ := json.Marshal(tc.object)
|
||||||
|
req.Object = runtime.RawExtension{Raw: objRaw, Object: tc.object}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.oldObject != nil {
|
||||||
|
oldObjRaw, _ := json.Marshal(tc.oldObject)
|
||||||
|
req.OldObject = runtime.RawExtension{Raw: oldObjRaw, Object: tc.oldObject}
|
||||||
|
}
|
||||||
|
|
||||||
|
res := r.Admit(ctx, req)
|
||||||
|
|
||||||
|
if tc.allowed != res.Allowed {
|
||||||
|
t.Fatal("Request must be allowed", res.Result)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.allowed {
|
||||||
|
ExpectPatches(t, res.Patch, tc.patches)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func createUpdateResource(ctx context.Context, t *testing.T, old, new *Resource) *admissionv1.AdmissionRequest {
|
func createUpdateResource(ctx context.Context, t *testing.T, old, new *Resource) *admissionv1.AdmissionRequest {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
req := &admissionv1.AdmissionRequest{
|
req := &admissionv1.AdmissionRequest{
|
||||||
|
@ -863,3 +997,21 @@ func resourceCallback(ctx context.Context, uns *unstructured.Unstructured) error
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func podCallback(ctx context.Context, u *unstructured.Unstructured) error {
|
||||||
|
pod := &corev1.Pod{}
|
||||||
|
err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), pod)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pod.Spec.AutomountServiceAccountToken = ptr.Bool(true)
|
||||||
|
|
||||||
|
r, err := runtime.DefaultUnstructuredConverter.ToUnstructured(pod)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
u.Object = r
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -79,6 +79,13 @@ func TestReconcile(t *testing.T) {
|
||||||
|
|
||||||
// These are the rules we expect given the context of "handlers".
|
// These are the rules we expect given the context of "handlers".
|
||||||
expectedRules := []admissionregistrationv1.RuleWithOperations{{
|
expectedRules := []admissionregistrationv1.RuleWithOperations{{
|
||||||
|
Operations: []admissionregistrationv1.OperationType{"CREATE", "UPDATE"},
|
||||||
|
Rule: admissionregistrationv1.Rule{
|
||||||
|
APIGroups: []string{""},
|
||||||
|
APIVersions: []string{"v1"},
|
||||||
|
Resources: []string{"pods", "pods/status"},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
Operations: []admissionregistrationv1.OperationType{"CREATE", "UPDATE"},
|
Operations: []admissionregistrationv1.OperationType{"CREATE", "UPDATE"},
|
||||||
Rule: admissionregistrationv1.Rule{
|
Rule: admissionregistrationv1.Rule{
|
||||||
APIGroups: []string{"pkg.knative.dev"},
|
APIGroups: []string{"pkg.knative.dev"},
|
||||||
|
|
|
@ -27,6 +27,11 @@ import (
|
||||||
"knative.dev/pkg/apis"
|
"knative.dev/pkg/apis"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
emptyGroupUpdaterAnnotation = apis.UpdaterAnnotationSuffix[1:]
|
||||||
|
emptyGroupCreatorAnnotation = apis.CreatorAnnotationSuffix[1:]
|
||||||
|
)
|
||||||
|
|
||||||
// setUserInfoAnnotations sets creator and updater annotations on a resource.
|
// setUserInfoAnnotations sets creator and updater annotations on a resource.
|
||||||
func setUserInfoAnnotations(ctx context.Context, resource apis.HasSpec, groupName string) {
|
func setUserInfoAnnotations(ctx context.Context, resource apis.HasSpec, groupName string) {
|
||||||
if ui := apis.GetUserInfo(ctx); ui != nil {
|
if ui := apis.GetUserInfo(ctx); ui != nil {
|
||||||
|
@ -41,15 +46,22 @@ func setUserInfoAnnotations(ctx context.Context, resource apis.HasSpec, groupNam
|
||||||
objectMetaAccessor.GetObjectMeta().SetAnnotations(annotations)
|
objectMetaAccessor.GetObjectMeta().SetAnnotations(annotations)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updaterAnnotation := emptyGroupUpdaterAnnotation
|
||||||
|
creatorAnnotation := emptyGroupCreatorAnnotation
|
||||||
|
if groupName != "" {
|
||||||
|
updaterAnnotation = groupName + apis.UpdaterAnnotationSuffix
|
||||||
|
creatorAnnotation = groupName + apis.CreatorAnnotationSuffix
|
||||||
|
}
|
||||||
|
|
||||||
if apis.IsInUpdate(ctx) {
|
if apis.IsInUpdate(ctx) {
|
||||||
old := apis.GetBaseline(ctx).(apis.HasSpec)
|
old := apis.GetBaseline(ctx).(apis.HasSpec)
|
||||||
if equality.Semantic.DeepEqual(old.GetUntypedSpec(), resource.GetUntypedSpec()) {
|
if equality.Semantic.DeepEqual(old.GetUntypedSpec(), resource.GetUntypedSpec()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
annotations[groupName+apis.UpdaterAnnotationSuffix] = ui.Username
|
annotations[updaterAnnotation] = ui.Username
|
||||||
} else {
|
} else {
|
||||||
annotations[groupName+apis.CreatorAnnotationSuffix] = ui.Username
|
annotations[creatorAnnotation] = ui.Username
|
||||||
annotations[groupName+apis.UpdaterAnnotationSuffix] = ui.Username
|
annotations[updaterAnnotation] = ui.Username
|
||||||
}
|
}
|
||||||
objectMetaAccessor.GetObjectMeta().SetAnnotations(annotations)
|
objectMetaAccessor.GetObjectMeta().SetAnnotations(annotations)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue