212 lines
6.1 KiB
Go
212 lines
6.1 KiB
Go
|
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||
|
// SPDX-License-Identifier: Apache-2.0
|
||
|
|
||
|
package whoamirequest
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"errors"
|
||
|
"testing"
|
||
|
|
||
|
"github.com/stretchr/testify/require"
|
||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||
|
"k8s.io/apimachinery/pkg/runtime"
|
||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||
|
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||
|
"k8s.io/apiserver/pkg/authentication/user"
|
||
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||
|
"k8s.io/apiserver/pkg/registry/rest"
|
||
|
|
||
|
identityapi "go.pinniped.dev/generated/latest/apis/concierge/identity"
|
||
|
)
|
||
|
|
||
|
func TestNew(t *testing.T) {
|
||
|
r := NewREST(schema.GroupResource{Group: "bears", Resource: "panda"})
|
||
|
require.NotNil(t, r)
|
||
|
require.False(t, r.NamespaceScoped())
|
||
|
require.Equal(t, []string{"pinniped"}, r.Categories())
|
||
|
require.IsType(t, &identityapi.WhoAmIRequest{}, r.New())
|
||
|
require.IsType(t, &identityapi.WhoAmIRequestList{}, r.NewList())
|
||
|
|
||
|
ctx := context.Background()
|
||
|
|
||
|
// check the simple invariants of our no-op list
|
||
|
list, err := r.List(ctx, nil)
|
||
|
require.NoError(t, err)
|
||
|
require.NotNil(t, list)
|
||
|
require.IsType(t, &identityapi.WhoAmIRequestList{}, list)
|
||
|
require.Equal(t, "0", list.(*identityapi.WhoAmIRequestList).ResourceVersion)
|
||
|
require.NotNil(t, list.(*identityapi.WhoAmIRequestList).Items)
|
||
|
require.Len(t, list.(*identityapi.WhoAmIRequestList).Items, 0)
|
||
|
|
||
|
// make sure we can turn lists into tables if needed
|
||
|
table, err := r.ConvertToTable(ctx, list, nil)
|
||
|
require.NoError(t, err)
|
||
|
require.NotNil(t, table)
|
||
|
require.Equal(t, "0", table.ResourceVersion)
|
||
|
require.Nil(t, table.Rows)
|
||
|
|
||
|
// exercise group resource - force error by passing a runtime.Object that does not have an embedded object meta
|
||
|
_, err = r.ConvertToTable(ctx, &metav1.APIGroup{}, nil)
|
||
|
require.Error(t, err, "the resource panda.bears does not support being converted to a Table")
|
||
|
}
|
||
|
|
||
|
func TestCreate(t *testing.T) {
|
||
|
type args struct {
|
||
|
ctx context.Context
|
||
|
obj runtime.Object
|
||
|
createValidation rest.ValidateObjectFunc
|
||
|
options *metav1.CreateOptions
|
||
|
}
|
||
|
tests := []struct {
|
||
|
name string
|
||
|
args args
|
||
|
want runtime.Object
|
||
|
wantErr string
|
||
|
}{
|
||
|
{
|
||
|
name: "wrong type",
|
||
|
args: args{
|
||
|
ctx: genericapirequest.NewContext(),
|
||
|
obj: &metav1.Status{},
|
||
|
createValidation: nil,
|
||
|
options: nil,
|
||
|
},
|
||
|
want: nil,
|
||
|
wantErr: `not a WhoAmIRequest: &v1.Status{TypeMeta:v1.TypeMeta{Kind:"", APIVersion:""}, ListMeta:v1.ListMeta{SelfLink:"", ResourceVersion:"", Continue:"", RemainingItemCount:(*int64)(nil)}, Status:"", Message:"", Reason:"", Details:(*v1.StatusDetails)(nil), Code:0}`,
|
||
|
},
|
||
|
{
|
||
|
name: "bad options",
|
||
|
args: args{
|
||
|
ctx: genericapirequest.NewContext(),
|
||
|
obj: &identityapi.WhoAmIRequest{
|
||
|
TypeMeta: metav1.TypeMeta{
|
||
|
Kind: "SomeKind",
|
||
|
},
|
||
|
ObjectMeta: metav1.ObjectMeta{
|
||
|
Name: "some-name",
|
||
|
},
|
||
|
},
|
||
|
createValidation: nil,
|
||
|
options: &metav1.CreateOptions{DryRun: []string{"stuff"}},
|
||
|
},
|
||
|
want: nil,
|
||
|
wantErr: `SomeKind.identity.concierge.pinniped.dev "some-name" is invalid: dryRun: Unsupported value: []string{"stuff"}`,
|
||
|
},
|
||
|
{
|
||
|
name: "bad namespace",
|
||
|
args: args{
|
||
|
ctx: genericapirequest.WithNamespace(genericapirequest.NewContext(), "some-ns"),
|
||
|
obj: &identityapi.WhoAmIRequest{},
|
||
|
createValidation: nil,
|
||
|
options: nil,
|
||
|
},
|
||
|
want: nil,
|
||
|
wantErr: `namespace is not allowed on WhoAmIRequest: some-ns`,
|
||
|
},
|
||
|
{
|
||
|
// if we add fields to spec, we need additional tests to:
|
||
|
// - make sure admission cannot mutate it
|
||
|
// - the input spec fields are validated correctly
|
||
|
name: "create validation failure",
|
||
|
args: args{
|
||
|
ctx: genericapirequest.NewContext(),
|
||
|
obj: &identityapi.WhoAmIRequest{},
|
||
|
createValidation: func(ctx context.Context, obj runtime.Object) error {
|
||
|
return errors.New("some-error-here")
|
||
|
},
|
||
|
options: nil,
|
||
|
},
|
||
|
want: nil,
|
||
|
wantErr: `some-error-here`,
|
||
|
},
|
||
|
{
|
||
|
name: "no user info",
|
||
|
args: args{
|
||
|
ctx: genericapirequest.NewContext(),
|
||
|
obj: &identityapi.WhoAmIRequest{},
|
||
|
createValidation: nil,
|
||
|
options: nil,
|
||
|
},
|
||
|
want: nil,
|
||
|
wantErr: `Internal error occurred: no user info on request`,
|
||
|
},
|
||
|
{
|
||
|
name: "with user info, no auds",
|
||
|
args: args{
|
||
|
ctx: genericapirequest.WithUser(genericapirequest.NewContext(), &user.DefaultInfo{
|
||
|
Name: "bond",
|
||
|
UID: "007",
|
||
|
Groups: []string{"agents", "ops"},
|
||
|
Extra: map[string][]string{
|
||
|
"fan-of": {"pandas", "twizzlers"},
|
||
|
"needs": {"sleep"},
|
||
|
},
|
||
|
}),
|
||
|
obj: &identityapi.WhoAmIRequest{},
|
||
|
createValidation: nil,
|
||
|
options: nil,
|
||
|
},
|
||
|
want: &identityapi.WhoAmIRequest{
|
||
|
Status: identityapi.WhoAmIRequestStatus{
|
||
|
KubernetesUserInfo: identityapi.KubernetesUserInfo{
|
||
|
User: identityapi.UserInfo{
|
||
|
Username: "bond",
|
||
|
UID: "007",
|
||
|
Groups: []string{"agents", "ops"},
|
||
|
Extra: map[string]identityapi.ExtraValue{
|
||
|
"fan-of": {"pandas", "twizzlers"},
|
||
|
"needs": {"sleep"},
|
||
|
},
|
||
|
},
|
||
|
Audiences: nil,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
wantErr: ``,
|
||
|
},
|
||
|
{
|
||
|
name: "with user info and auds",
|
||
|
args: args{
|
||
|
ctx: authenticator.WithAudiences(
|
||
|
genericapirequest.WithUser(genericapirequest.NewContext(), &user.DefaultInfo{
|
||
|
Name: "panda",
|
||
|
}),
|
||
|
authenticator.Audiences{"gitlab", "aws"},
|
||
|
),
|
||
|
obj: &identityapi.WhoAmIRequest{},
|
||
|
createValidation: nil,
|
||
|
options: nil,
|
||
|
},
|
||
|
want: &identityapi.WhoAmIRequest{
|
||
|
Status: identityapi.WhoAmIRequestStatus{
|
||
|
KubernetesUserInfo: identityapi.KubernetesUserInfo{
|
||
|
User: identityapi.UserInfo{
|
||
|
Username: "panda",
|
||
|
},
|
||
|
Audiences: []string{"gitlab", "aws"},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
wantErr: ``,
|
||
|
},
|
||
|
}
|
||
|
for _, tt := range tests {
|
||
|
tt := tt
|
||
|
t.Run(tt.name, func(t *testing.T) {
|
||
|
r := &REST{}
|
||
|
got, err := r.Create(tt.args.ctx, tt.args.obj, tt.args.createValidation, tt.args.options)
|
||
|
require.Equal(t, tt.wantErr, errString(err))
|
||
|
require.Equal(t, tt.want, got)
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func errString(err error) string {
|
||
|
if err == nil {
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
return err.Error()
|
||
|
}
|