knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/resolver/authenticatable_resolver_test.go (about) 1 /* 2 Copyright 2024 The Knative Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package resolver_test 18 19 import ( 20 "context" 21 "fmt" 22 "testing" 23 24 "github.com/google/go-cmp/cmp" 25 corev1 "k8s.io/api/core/v1" 26 "k8s.io/apimachinery/pkg/runtime" 27 "k8s.io/apimachinery/pkg/types" 28 duckv1 "knative.dev/pkg/apis/duck/v1" 29 fakedynamicclient "knative.dev/pkg/injection/clients/dynamicclient/fake" 30 "knative.dev/pkg/ptr" 31 "knative.dev/pkg/resolver" 32 33 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 34 "k8s.io/apimachinery/pkg/runtime/schema" 35 "k8s.io/client-go/kubernetes/scheme" 36 duckv1alpha1 "knative.dev/pkg/apis/duck/v1alpha1" 37 duckv1beta1 "knative.dev/pkg/apis/duck/v1beta1" 38 "knative.dev/pkg/client/injection/ducks/duck/v1/authstatus" 39 "knative.dev/pkg/tracker" 40 ) 41 42 const ( 43 authenticatableName = "testsource" 44 authenticatableKind = "Source" 45 authenticatableAPIVersion = "duck.knative.dev/v1" 46 authenticatableResource = "sources.duck.knative.dev" 47 48 authenticatable2Name = "test2-source" 49 authenticatable2Kind = "AnotherSource" 50 authenticatable2APIVersion = "duck.knative.dev/v1" 51 authenticatable2Resource = "anothersources.duck.knative.dev" 52 53 unauthenticatableName = "testunauthenticatable" 54 unauthenticatableKind = "KResource" 55 unauthenticatableAPIVersion = "duck.knative.dev/v1alpha1" 56 57 authenticatableServiceAccountName = "my-service-account" 58 authenticatableServiceAccountName1 = "my-service-account-1" 59 authenticatableServiceAccountName2 = "my-service-account-2" 60 authenticatable2ServiceAccountName = "service-account-of-2nd-authenticatable" 61 ) 62 63 func init() { 64 // Add types to scheme 65 duckv1alpha1.AddToScheme(scheme.Scheme) 66 duckv1beta1.AddToScheme(scheme.Scheme) 67 68 scheme.Scheme.AddKnownTypeWithName( 69 schema.FromAPIVersionAndKind(unauthenticatableAPIVersion, unauthenticatableKind), 70 &unstructured.Unstructured{}, 71 ) 72 scheme.Scheme.AddKnownTypeWithName( 73 schema.FromAPIVersionAndKind(unauthenticatableAPIVersion, unauthenticatableKind+"List"), 74 &unstructured.UnstructuredList{}, 75 ) 76 scheme.Scheme.AddKnownTypeWithName( 77 schema.FromAPIVersionAndKind(authenticatableAPIVersion, authenticatableKind), 78 &unstructured.Unstructured{}, 79 ) 80 scheme.Scheme.AddKnownTypeWithName( 81 schema.FromAPIVersionAndKind(authenticatableAPIVersion, authenticatableKind+"List"), 82 &unstructured.UnstructuredList{}, 83 ) 84 } 85 86 func TestAuthenticatableResolver_AuthStatusFromObjectReference(t *testing.T) { 87 tests := []struct { 88 name string 89 objects []runtime.Object 90 objectRef *corev1.ObjectReference 91 want *duckv1.AuthStatus 92 wantErr string 93 }{ 94 { 95 name: "nil everything", 96 wantErr: "ref is nil", 97 }, { 98 name: "Valid authenticatable", 99 objects: []runtime.Object{ 100 getAuthenticatable(), 101 }, 102 objectRef: authenticatableRef(), 103 want: &duckv1.AuthStatus{ 104 ServiceAccountName: ptr.String(authenticatableServiceAccountName), 105 }, 106 }, { 107 name: "Valid authenticatable in multiple objects", 108 objects: []runtime.Object{ 109 getUnauthenticatable(), 110 getAuthenticatable(), 111 getAuthenticatable2(), 112 }, 113 objectRef: authenticatable2Ref(), 114 want: &duckv1.AuthStatus{ 115 ServiceAccountName: ptr.String(authenticatable2ServiceAccountName), 116 }, 117 }, { 118 name: "Valid authenticatable multiple SAs", 119 objects: []runtime.Object{ 120 getAuthenticatableWithMultipleSAs(), 121 }, 122 objectRef: authenticatableRef(), 123 want: &duckv1.AuthStatus{ 124 ServiceAccountNames: []string{ 125 authenticatableServiceAccountName1, 126 authenticatableServiceAccountName2, 127 }, 128 }, 129 }, { 130 name: "Unauthenticatable", 131 objects: []runtime.Object{ 132 getUnauthenticatable(), 133 }, 134 objectRef: unauthenticatableRef(), 135 wantErr: fmt.Sprintf(".status.auth is missing in object %s/%s", testNS, unauthenticatableName), 136 }, { 137 name: "Authenticatable not found", 138 objects: []runtime.Object{ 139 getUnauthenticatable(), 140 }, 141 objectRef: authenticatableRef(), 142 wantErr: fmt.Sprintf("failed to get authenticatable %s/%s: failed to get object %s/%s: %s %q not found", testNS, authenticatableName, testNS, authenticatableName, authenticatableResource, authenticatableName), 143 }, 144 } 145 for _, tt := range tests { 146 t.Run(tt.name, func(t *testing.T) { 147 ctx, _ := fakedynamicclient.With(context.Background(), scheme.Scheme, tt.objects...) 148 ctx = authstatus.WithDuck(ctx) 149 r := resolver.NewAuthenticatableResolverFromTracker(ctx, tracker.New(func(types.NamespacedName) {}, 0)) 150 151 // Run it twice since this should be idempotent. AuthenticatableResolver should 152 // not modify the cache's copy. 153 _, _ = r.AuthStatusFromObjectReference(tt.objectRef, getAuthenticatable()) 154 authStatus, gotErr := r.AuthStatusFromObjectReference(tt.objectRef, getAuthenticatable()) 155 156 if gotErr != nil { 157 if tt.wantErr != "" { 158 if got, want := gotErr.Error(), tt.wantErr; got != want { 159 t.Errorf("Unexpected error (-want, +got) =\n%s", cmp.Diff(want, got)) 160 } 161 } else { 162 t.Error("Unexpected error:", gotErr) 163 } 164 return 165 } 166 167 if got, want := authStatus, tt.want; !cmp.Equal(got, want) { 168 t.Errorf("Unexpected object (-want, +got) =\n%s", cmp.Diff(got, want)) 169 } 170 }) 171 } 172 } 173 174 func getAuthenticatable() *unstructured.Unstructured { 175 return &unstructured.Unstructured{ 176 Object: map[string]interface{}{ 177 "apiVersion": authenticatableAPIVersion, 178 "kind": authenticatableKind, 179 "metadata": map[string]interface{}{ 180 "namespace": testNS, 181 "name": authenticatableName, 182 }, 183 "status": map[string]interface{}{ 184 "auth": map[string]interface{}{ 185 "serviceAccountName": authenticatableServiceAccountName, 186 }, 187 }, 188 }, 189 } 190 } 191 192 func getAuthenticatable2() *unstructured.Unstructured { 193 return &unstructured.Unstructured{ 194 Object: map[string]interface{}{ 195 "apiVersion": authenticatable2APIVersion, 196 "kind": authenticatable2Kind, 197 "metadata": map[string]interface{}{ 198 "namespace": testNS, 199 "name": authenticatable2Name, 200 }, 201 "status": map[string]interface{}{ 202 "auth": map[string]interface{}{ 203 "serviceAccountName": authenticatable2ServiceAccountName, 204 }, 205 }, 206 }, 207 } 208 } 209 210 func getAuthenticatableWithMultipleSAs() *unstructured.Unstructured { 211 return &unstructured.Unstructured{ 212 Object: map[string]interface{}{ 213 "apiVersion": authenticatableAPIVersion, 214 "kind": authenticatableKind, 215 "metadata": map[string]interface{}{ 216 "namespace": testNS, 217 "name": authenticatableName, 218 }, 219 "status": map[string]interface{}{ 220 "auth": map[string]interface{}{ 221 "serviceAccountNames": []interface{}{ 222 authenticatableServiceAccountName1, 223 authenticatableServiceAccountName2, 224 }, 225 }, 226 }, 227 }, 228 } 229 } 230 231 func getUnauthenticatable() *unstructured.Unstructured { 232 return &unstructured.Unstructured{ 233 Object: map[string]interface{}{ 234 "apiVersion": unauthenticatableAPIVersion, 235 "kind": unauthenticatableKind, 236 "metadata": map[string]interface{}{ 237 "namespace": testNS, 238 "name": unauthenticatableName, 239 }, 240 "status": map[string]interface{}{ 241 "something": map[string]interface{}{ 242 "foo": "bar", 243 }, 244 }, 245 }, 246 } 247 } 248 249 func authenticatableRef() *corev1.ObjectReference { 250 return &corev1.ObjectReference{ 251 Kind: authenticatableKind, 252 Name: authenticatableName, 253 APIVersion: authenticatableAPIVersion, 254 Namespace: testNS, 255 } 256 } 257 258 func authenticatable2Ref() *corev1.ObjectReference { 259 return &corev1.ObjectReference{ 260 Kind: authenticatable2Kind, 261 Name: authenticatable2Name, 262 APIVersion: authenticatable2APIVersion, 263 Namespace: testNS, 264 } 265 } 266 267 func unauthenticatableRef() *corev1.ObjectReference { 268 return &corev1.ObjectReference{ 269 Kind: unauthenticatableKind, 270 Name: unauthenticatableName, 271 APIVersion: unauthenticatableAPIVersion, 272 Namespace: testNS, 273 } 274 }