mirror of https://github.com/knative/pkg.git
Add AuthenticatableType duck type (#3056)
* Add AuthenticatableType * Add Resolver for AuthenticatableType * Run gofmt and goimports * Fix linter issues
This commit is contained in:
parent
15e6cdf2f3
commit
339c22b821
|
@ -16,6 +16,21 @@ limitations under the License.
|
||||||
|
|
||||||
package v1
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"knative.dev/pkg/apis"
|
||||||
|
"knative.dev/pkg/apis/duck/ducktypes"
|
||||||
|
"knative.dev/pkg/kmeta"
|
||||||
|
"knative.dev/pkg/ptr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// +genduck
|
||||||
|
|
||||||
// AuthStatus is meant to provide the generated service account name
|
// AuthStatus is meant to provide the generated service account name
|
||||||
// in the resource status.
|
// in the resource status.
|
||||||
type AuthStatus struct {
|
type AuthStatus struct {
|
||||||
|
@ -28,3 +43,81 @@ type AuthStatus struct {
|
||||||
// when the component uses multiple identities (e.g. in case of a Parallel).
|
// when the component uses multiple identities (e.g. in case of a Parallel).
|
||||||
ServiceAccountNames []string `json:"serviceAccountNames,omitempty"`
|
ServiceAccountNames []string `json:"serviceAccountNames,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|
||||||
|
// AuthenticatableType is a skeleton type wrapping AuthStatus in the manner we expect
|
||||||
|
// resource writers defining compatible resources to embed it. We will
|
||||||
|
// typically use this type to deserialize AuthenticatableType ObjectReferences and
|
||||||
|
// access the AuthenticatableType data. This is not a real resource.
|
||||||
|
type AuthenticatableType struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
|
||||||
|
Status AuthenticatableStatus `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthenticatableStatus struct {
|
||||||
|
// Auth contains the service account name for the subscription
|
||||||
|
// +optional
|
||||||
|
Auth *AuthStatus `json:"auth,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// AuthStatus is a Convertible type.
|
||||||
|
_ apis.Convertible = (*AuthStatus)(nil)
|
||||||
|
|
||||||
|
// Verify AuthenticatableType resources meet duck contracts.
|
||||||
|
_ apis.Listable = (*AuthenticatableType)(nil)
|
||||||
|
_ ducktypes.Populatable = (*AuthenticatableType)(nil)
|
||||||
|
_ kmeta.OwnerRefable = (*AuthenticatableType)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetFullType implements duck.Implementable
|
||||||
|
func (*AuthStatus) GetFullType() ducktypes.Populatable {
|
||||||
|
return &AuthenticatableType{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertTo implements apis.Convertible
|
||||||
|
func (a *AuthStatus) ConvertTo(_ context.Context, to apis.Convertible) error {
|
||||||
|
return fmt.Errorf("v1 is the highest known version, got: %T", to)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertFrom implements apis.Convertible
|
||||||
|
func (a *AuthStatus) ConvertFrom(_ context.Context, from apis.Convertible) error {
|
||||||
|
return fmt.Errorf("v1 is the highest known version, got: %T", from)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate implements duck.Populatable
|
||||||
|
func (t *AuthenticatableType) Populate() {
|
||||||
|
t.Status = AuthenticatableStatus{
|
||||||
|
Auth: &AuthStatus{
|
||||||
|
// Populate ALL fields
|
||||||
|
ServiceAccountName: ptr.String("foo"),
|
||||||
|
ServiceAccountNames: []string{
|
||||||
|
"bar",
|
||||||
|
"baz",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGroupVersionKind implements kmeta.OwnerRefable
|
||||||
|
func (t *AuthenticatableType) GetGroupVersionKind() schema.GroupVersionKind {
|
||||||
|
return t.GroupVersionKind()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetListType implements apis.Listable
|
||||||
|
func (*AuthenticatableType) GetListType() runtime.Object {
|
||||||
|
return &AuthenticatableTypeList{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|
||||||
|
// AuthenticatableTypeList is a list of AuthenticatableType resources
|
||||||
|
type AuthenticatableTypeList struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ListMeta `json:"metadata"`
|
||||||
|
|
||||||
|
Items []AuthenticatableType `json:"items"`
|
||||||
|
}
|
||||||
|
|
|
@ -176,6 +176,87 @@ func (in *AuthStatus) DeepCopy() *AuthStatus {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *AuthenticatableStatus) DeepCopyInto(out *AuthenticatableStatus) {
|
||||||
|
*out = *in
|
||||||
|
if in.Auth != nil {
|
||||||
|
in, out := &in.Auth, &out.Auth
|
||||||
|
*out = new(AuthStatus)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthenticatableStatus.
|
||||||
|
func (in *AuthenticatableStatus) DeepCopy() *AuthenticatableStatus {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(AuthenticatableStatus)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *AuthenticatableType) DeepCopyInto(out *AuthenticatableType) {
|
||||||
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
|
in.Status.DeepCopyInto(&out.Status)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthenticatableType.
|
||||||
|
func (in *AuthenticatableType) DeepCopy() *AuthenticatableType {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(AuthenticatableType)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *AuthenticatableType) DeepCopyObject() runtime.Object {
|
||||||
|
if c := in.DeepCopy(); c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *AuthenticatableTypeList) DeepCopyInto(out *AuthenticatableTypeList) {
|
||||||
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||||
|
if in.Items != nil {
|
||||||
|
in, out := &in.Items, &out.Items
|
||||||
|
*out = make([]AuthenticatableType, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthenticatableTypeList.
|
||||||
|
func (in *AuthenticatableTypeList) DeepCopy() *AuthenticatableTypeList {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(AuthenticatableTypeList)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *AuthenticatableTypeList) DeepCopyObject() runtime.Object {
|
||||||
|
if c := in.DeepCopy(); c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *Binding) DeepCopyInto(out *Binding) {
|
func (in *Binding) DeepCopyInto(out *Binding) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
Copyright 2022 The Knative Authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by injection-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package authstatus
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
|
||||||
|
duck "knative.dev/pkg/apis/duck"
|
||||||
|
v1 "knative.dev/pkg/apis/duck/v1"
|
||||||
|
controller "knative.dev/pkg/controller"
|
||||||
|
injection "knative.dev/pkg/injection"
|
||||||
|
dynamicclient "knative.dev/pkg/injection/clients/dynamicclient"
|
||||||
|
logging "knative.dev/pkg/logging"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
injection.Default.RegisterDuck(WithDuck)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key is used for associating the Informer inside the context.Context.
|
||||||
|
type Key struct{}
|
||||||
|
|
||||||
|
func WithDuck(ctx context.Context) context.Context {
|
||||||
|
dc := dynamicclient.Get(ctx)
|
||||||
|
dif := &duck.CachedInformerFactory{
|
||||||
|
Delegate: &duck.TypedInformerFactory{
|
||||||
|
Client: dc,
|
||||||
|
Type: (&v1.AuthStatus{}).GetFullType(),
|
||||||
|
ResyncPeriod: controller.GetResyncPeriod(ctx),
|
||||||
|
StopChannel: ctx.Done(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return context.WithValue(ctx, Key{}, dif)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get extracts the typed informer from the context.
|
||||||
|
func Get(ctx context.Context) duck.InformerFactory {
|
||||||
|
untyped := ctx.Value(Key{})
|
||||||
|
if untyped == nil {
|
||||||
|
logging.FromContext(ctx).Panic(
|
||||||
|
"Unable to fetch knative.dev/pkg/apis/duck.InformerFactory from context.")
|
||||||
|
}
|
||||||
|
return untyped.(duck.InformerFactory)
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
Copyright 2022 The Knative Authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by injection-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package fake
|
||||||
|
|
||||||
|
import (
|
||||||
|
authstatus "knative.dev/pkg/client/injection/ducks/duck/v1/authstatus"
|
||||||
|
injection "knative.dev/pkg/injection"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Get = authstatus.Get
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
injection.Fake.RegisterDuck(authstatus.WithDuck)
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
Copyright 2024 The Knative Authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package resolver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/client-go/tools/cache"
|
||||||
|
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
|
"knative.dev/pkg/client/injection/ducks/duck/v1/authstatus"
|
||||||
|
"knative.dev/pkg/controller"
|
||||||
|
|
||||||
|
pkgapisduck "knative.dev/pkg/apis/duck"
|
||||||
|
duckv1 "knative.dev/pkg/apis/duck/v1"
|
||||||
|
"knative.dev/pkg/tracker"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AuthenticatableResolver resolves ObjectReferences into a AuthenticatableType.
|
||||||
|
type AuthenticatableResolver struct {
|
||||||
|
tracker tracker.Interface
|
||||||
|
listerFactory func(schema.GroupVersionResource) (cache.GenericLister, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAuthenticatableResolverFromTracker constructs a new AuthenticatableResolver with context and a tracker.
|
||||||
|
func NewAuthenticatableResolverFromTracker(ctx context.Context, t tracker.Interface) *AuthenticatableResolver {
|
||||||
|
ret := &AuthenticatableResolver{
|
||||||
|
tracker: t,
|
||||||
|
}
|
||||||
|
|
||||||
|
informerFactory := &pkgapisduck.CachedInformerFactory{
|
||||||
|
Delegate: &pkgapisduck.EnqueueInformerFactory{
|
||||||
|
Delegate: authstatus.Get(ctx),
|
||||||
|
EventHandler: controller.HandleAll(ret.tracker.OnChanged),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.listerFactory = func(gvr schema.GroupVersionResource) (cache.GenericLister, error) {
|
||||||
|
_, l, err := informerFactory.Get(ctx, gvr)
|
||||||
|
return l, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthStatusFromObjectReference returns the AuthStatus from an object
|
||||||
|
func (r *AuthenticatableResolver) AuthStatusFromObjectReference(ref *corev1.ObjectReference, parent interface{}) (*duckv1.AuthStatus, error) {
|
||||||
|
if ref == nil {
|
||||||
|
return nil, apierrs.NewBadRequest("ref is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticatable, err := r.authenticatableFromObjectReference(ref, parent)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get authenticatable %s/%s: %w", ref.Namespace, ref.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if authenticatable.Status.Auth == nil {
|
||||||
|
return nil, fmt.Errorf(".status.auth is missing in object %s/%s", ref.Namespace, ref.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return authenticatable.Status.Auth, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// authenticatableFromObjectReference resolves an object reference into an AuthenticatableType
|
||||||
|
func (r *AuthenticatableResolver) authenticatableFromObjectReference(ref *corev1.ObjectReference, parent interface{}) (*duckv1.AuthenticatableType, error) {
|
||||||
|
if ref == nil {
|
||||||
|
return nil, apierrs.NewBadRequest("ref is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
gvr, _ := meta.UnsafeGuessKindToResource(ref.GroupVersionKind())
|
||||||
|
if err := r.tracker.TrackReference(tracker.Reference{
|
||||||
|
APIVersion: ref.APIVersion,
|
||||||
|
Kind: ref.Kind,
|
||||||
|
Namespace: ref.Namespace,
|
||||||
|
Name: ref.Name,
|
||||||
|
}, parent); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to track reference %s %s/%s: %w", gvr.String(), ref.Namespace, ref.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
lister, err := r.listerFactory(gvr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get lister for %s: %w", gvr.String(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
obj, err := lister.ByNamespace(ref.Namespace).Get(ref.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get object %s/%s: %w", ref.Namespace, ref.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticatable, ok := obj.(*duckv1.AuthenticatableType)
|
||||||
|
if !ok {
|
||||||
|
return nil, apierrs.NewBadRequest(fmt.Sprintf("%s(%T) is not an AuthenticatableType", ref, ref))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not modify informer copy.
|
||||||
|
authenticatable = authenticatable.DeepCopy()
|
||||||
|
|
||||||
|
return authenticatable, nil
|
||||||
|
}
|
|
@ -0,0 +1,274 @@
|
||||||
|
/*
|
||||||
|
Copyright 2024 The Knative Authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package resolver_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
duckv1 "knative.dev/pkg/apis/duck/v1"
|
||||||
|
fakedynamicclient "knative.dev/pkg/injection/clients/dynamicclient/fake"
|
||||||
|
"knative.dev/pkg/ptr"
|
||||||
|
"knative.dev/pkg/resolver"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/client-go/kubernetes/scheme"
|
||||||
|
duckv1alpha1 "knative.dev/pkg/apis/duck/v1alpha1"
|
||||||
|
duckv1beta1 "knative.dev/pkg/apis/duck/v1beta1"
|
||||||
|
"knative.dev/pkg/client/injection/ducks/duck/v1/authstatus"
|
||||||
|
"knative.dev/pkg/tracker"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
authenticatableName = "testsource"
|
||||||
|
authenticatableKind = "Source"
|
||||||
|
authenticatableAPIVersion = "duck.knative.dev/v1"
|
||||||
|
authenticatableResource = "sources.duck.knative.dev"
|
||||||
|
|
||||||
|
authenticatable2Name = "test2-source"
|
||||||
|
authenticatable2Kind = "AnotherSource"
|
||||||
|
authenticatable2APIVersion = "duck.knative.dev/v1"
|
||||||
|
authenticatable2Resource = "anothersources.duck.knative.dev"
|
||||||
|
|
||||||
|
unauthenticatableName = "testunauthenticatable"
|
||||||
|
unauthenticatableKind = "KResource"
|
||||||
|
unauthenticatableAPIVersion = "duck.knative.dev/v1alpha1"
|
||||||
|
|
||||||
|
authenticatableServiceAccountName = "my-service-account"
|
||||||
|
authenticatableServiceAccountName1 = "my-service-account-1"
|
||||||
|
authenticatableServiceAccountName2 = "my-service-account-2"
|
||||||
|
authenticatable2ServiceAccountName = "service-account-of-2nd-authenticatable"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Add types to scheme
|
||||||
|
duckv1alpha1.AddToScheme(scheme.Scheme)
|
||||||
|
duckv1beta1.AddToScheme(scheme.Scheme)
|
||||||
|
|
||||||
|
scheme.Scheme.AddKnownTypeWithName(
|
||||||
|
schema.FromAPIVersionAndKind(unauthenticatableAPIVersion, unauthenticatableKind),
|
||||||
|
&unstructured.Unstructured{},
|
||||||
|
)
|
||||||
|
scheme.Scheme.AddKnownTypeWithName(
|
||||||
|
schema.FromAPIVersionAndKind(unauthenticatableAPIVersion, unauthenticatableKind+"List"),
|
||||||
|
&unstructured.UnstructuredList{},
|
||||||
|
)
|
||||||
|
scheme.Scheme.AddKnownTypeWithName(
|
||||||
|
schema.FromAPIVersionAndKind(authenticatableAPIVersion, authenticatableKind),
|
||||||
|
&unstructured.Unstructured{},
|
||||||
|
)
|
||||||
|
scheme.Scheme.AddKnownTypeWithName(
|
||||||
|
schema.FromAPIVersionAndKind(authenticatableAPIVersion, authenticatableKind+"List"),
|
||||||
|
&unstructured.UnstructuredList{},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAuthenticatableResolver_AuthStatusFromObjectReference(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
objects []runtime.Object
|
||||||
|
objectRef *corev1.ObjectReference
|
||||||
|
want *duckv1.AuthStatus
|
||||||
|
wantErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nil everything",
|
||||||
|
wantErr: "ref is nil",
|
||||||
|
}, {
|
||||||
|
name: "Valid authenticatable",
|
||||||
|
objects: []runtime.Object{
|
||||||
|
getAuthenticatable(),
|
||||||
|
},
|
||||||
|
objectRef: authenticatableRef(),
|
||||||
|
want: &duckv1.AuthStatus{
|
||||||
|
ServiceAccountName: ptr.String(authenticatableServiceAccountName),
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
name: "Valid authenticatable in multiple objects",
|
||||||
|
objects: []runtime.Object{
|
||||||
|
getUnauthenticatable(),
|
||||||
|
getAuthenticatable(),
|
||||||
|
getAuthenticatable2(),
|
||||||
|
},
|
||||||
|
objectRef: authenticatable2Ref(),
|
||||||
|
want: &duckv1.AuthStatus{
|
||||||
|
ServiceAccountName: ptr.String(authenticatable2ServiceAccountName),
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
name: "Valid authenticatable multiple SAs",
|
||||||
|
objects: []runtime.Object{
|
||||||
|
getAuthenticatableWithMultipleSAs(),
|
||||||
|
},
|
||||||
|
objectRef: authenticatableRef(),
|
||||||
|
want: &duckv1.AuthStatus{
|
||||||
|
ServiceAccountNames: []string{
|
||||||
|
authenticatableServiceAccountName1,
|
||||||
|
authenticatableServiceAccountName2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
name: "Unauthenticatable",
|
||||||
|
objects: []runtime.Object{
|
||||||
|
getUnauthenticatable(),
|
||||||
|
},
|
||||||
|
objectRef: unauthenticatableRef(),
|
||||||
|
wantErr: fmt.Sprintf(".status.auth is missing in object %s/%s", testNS, unauthenticatableName),
|
||||||
|
}, {
|
||||||
|
name: "Authenticatable not found",
|
||||||
|
objects: []runtime.Object{
|
||||||
|
getUnauthenticatable(),
|
||||||
|
},
|
||||||
|
objectRef: authenticatableRef(),
|
||||||
|
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),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
ctx, _ := fakedynamicclient.With(context.Background(), scheme.Scheme, tt.objects...)
|
||||||
|
ctx = authstatus.WithDuck(ctx)
|
||||||
|
r := resolver.NewAuthenticatableResolverFromTracker(ctx, tracker.New(func(types.NamespacedName) {}, 0))
|
||||||
|
|
||||||
|
// Run it twice since this should be idempotent. AuthenticatableResolver should
|
||||||
|
// not modify the cache's copy.
|
||||||
|
_, _ = r.AuthStatusFromObjectReference(tt.objectRef, getAuthenticatable())
|
||||||
|
authStatus, gotErr := r.AuthStatusFromObjectReference(tt.objectRef, getAuthenticatable())
|
||||||
|
|
||||||
|
if gotErr != nil {
|
||||||
|
if tt.wantErr != "" {
|
||||||
|
if got, want := gotErr.Error(), tt.wantErr; got != want {
|
||||||
|
t.Errorf("Unexpected error (-want, +got) =\n%s", cmp.Diff(want, got))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Error("Unexpected error:", gotErr)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if got, want := authStatus, tt.want; !cmp.Equal(got, want) {
|
||||||
|
t.Errorf("Unexpected object (-want, +got) =\n%s", cmp.Diff(got, want))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAuthenticatable() *unstructured.Unstructured {
|
||||||
|
return &unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"apiVersion": authenticatableAPIVersion,
|
||||||
|
"kind": authenticatableKind,
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"namespace": testNS,
|
||||||
|
"name": authenticatableName,
|
||||||
|
},
|
||||||
|
"status": map[string]interface{}{
|
||||||
|
"auth": map[string]interface{}{
|
||||||
|
"serviceAccountName": authenticatableServiceAccountName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAuthenticatable2() *unstructured.Unstructured {
|
||||||
|
return &unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"apiVersion": authenticatable2APIVersion,
|
||||||
|
"kind": authenticatable2Kind,
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"namespace": testNS,
|
||||||
|
"name": authenticatable2Name,
|
||||||
|
},
|
||||||
|
"status": map[string]interface{}{
|
||||||
|
"auth": map[string]interface{}{
|
||||||
|
"serviceAccountName": authenticatable2ServiceAccountName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAuthenticatableWithMultipleSAs() *unstructured.Unstructured {
|
||||||
|
return &unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"apiVersion": authenticatableAPIVersion,
|
||||||
|
"kind": authenticatableKind,
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"namespace": testNS,
|
||||||
|
"name": authenticatableName,
|
||||||
|
},
|
||||||
|
"status": map[string]interface{}{
|
||||||
|
"auth": map[string]interface{}{
|
||||||
|
"serviceAccountNames": []interface{}{
|
||||||
|
authenticatableServiceAccountName1,
|
||||||
|
authenticatableServiceAccountName2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUnauthenticatable() *unstructured.Unstructured {
|
||||||
|
return &unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"apiVersion": unauthenticatableAPIVersion,
|
||||||
|
"kind": unauthenticatableKind,
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"namespace": testNS,
|
||||||
|
"name": unauthenticatableName,
|
||||||
|
},
|
||||||
|
"status": map[string]interface{}{
|
||||||
|
"something": map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func authenticatableRef() *corev1.ObjectReference {
|
||||||
|
return &corev1.ObjectReference{
|
||||||
|
Kind: authenticatableKind,
|
||||||
|
Name: authenticatableName,
|
||||||
|
APIVersion: authenticatableAPIVersion,
|
||||||
|
Namespace: testNS,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func authenticatable2Ref() *corev1.ObjectReference {
|
||||||
|
return &corev1.ObjectReference{
|
||||||
|
Kind: authenticatable2Kind,
|
||||||
|
Name: authenticatable2Name,
|
||||||
|
APIVersion: authenticatable2APIVersion,
|
||||||
|
Namespace: testNS,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unauthenticatableRef() *corev1.ObjectReference {
|
||||||
|
return &corev1.ObjectReference{
|
||||||
|
Kind: unauthenticatableKind,
|
||||||
|
Name: unauthenticatableName,
|
||||||
|
APIVersion: unauthenticatableAPIVersion,
|
||||||
|
Namespace: testNS,
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue