Reuse internal/concierge/scheme

Signed-off-by: Andrew Keesler <akeesler@vmware.com>
This commit is contained in:
Andrew Keesler 2021-03-18 10:40:59 -04:00
parent 05a188d4cd
commit 257d69045d
No known key found for this signature in database
GPG Key ID: 27CE0444346F9413
2 changed files with 4 additions and 230 deletions

View File

@ -15,18 +15,13 @@ import (
"k8s.io/apimachinery/pkg/api/errors" "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" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/runtime/serializer"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
identityapi "go.pinniped.dev/generated/latest/apis/concierge/identity"
identityv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/identity/v1alpha1" identityv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/identity/v1alpha1"
loginapi "go.pinniped.dev/generated/latest/apis/concierge/login" conciergescheme "go.pinniped.dev/internal/concierge/scheme"
loginv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/login/v1alpha1"
"go.pinniped.dev/internal/groupsuffix" "go.pinniped.dev/internal/groupsuffix"
"go.pinniped.dev/internal/here" "go.pinniped.dev/internal/here"
"go.pinniped.dev/internal/plog"
) )
//nolint: gochecknoinits //nolint: gochecknoinits
@ -158,7 +153,7 @@ func writeWhoamiOutputYAML(output io.Writer, apiGroupSuffix string, whoAmI *iden
} }
func serialize(output io.Writer, apiGroupSuffix string, whoAmI *identityv1alpha1.WhoAmIRequest, contentType string) error { func serialize(output io.Writer, apiGroupSuffix string, whoAmI *identityv1alpha1.WhoAmIRequest, contentType string) error {
scheme, _, identityGV := conciergeschemeNew(apiGroupSuffix) scheme, _, identityGV := conciergescheme.New(apiGroupSuffix)
codecs := serializer.NewCodecFactory(scheme) codecs := serializer.NewCodecFactory(scheme)
respInfo, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), contentType) respInfo, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), contentType)
if !ok { if !ok {
@ -189,110 +184,3 @@ func prettyStrings(ss []string) string {
} }
return b.String() return b.String()
} }
// conciergeschemeNew is a temporary private function to stand in place for
// "go.pinniped.dev/internal/concierge/scheme".New until the later function is merged to main.
func conciergeschemeNew(apiGroupSuffix string) (_ *runtime.Scheme, login, identity schema.GroupVersion) {
// standard set up of the server side scheme
scheme := runtime.NewScheme()
// add the options to empty v1
metav1.AddToGroupVersion(scheme, metav1.Unversioned)
// nothing fancy is required if using the standard group suffix
if apiGroupSuffix == groupsuffix.PinnipedDefaultSuffix {
schemeBuilder := runtime.NewSchemeBuilder(
loginv1alpha1.AddToScheme,
loginapi.AddToScheme,
identityv1alpha1.AddToScheme,
identityapi.AddToScheme,
)
utilruntime.Must(schemeBuilder.AddToScheme(scheme))
return scheme, loginv1alpha1.SchemeGroupVersion, identityv1alpha1.SchemeGroupVersion
}
loginConciergeGroupData, identityConciergeGroupData := groupsuffix.ConciergeAggregatedGroups(apiGroupSuffix)
addToSchemeAtNewGroup(scheme, loginv1alpha1.GroupName, loginConciergeGroupData.Group, loginv1alpha1.AddToScheme, loginapi.AddToScheme)
addToSchemeAtNewGroup(scheme, identityv1alpha1.GroupName, identityConciergeGroupData.Group, identityv1alpha1.AddToScheme, identityapi.AddToScheme)
// manually register conversions and defaulting into the correct scheme since we cannot directly call AddToScheme
schemeBuilder := runtime.NewSchemeBuilder(
loginv1alpha1.RegisterConversions,
loginv1alpha1.RegisterDefaults,
identityv1alpha1.RegisterConversions,
identityv1alpha1.RegisterDefaults,
)
utilruntime.Must(schemeBuilder.AddToScheme(scheme))
// we do not want to return errors from the scheme and instead would prefer to defer
// to the REST storage layer for consistency. The simplest way to do this is to force
// a cache miss from the authenticator cache. Kube API groups are validated via the
// IsDNS1123Subdomain func thus we can easily create a group that is guaranteed never
// to be in the authenticator cache. Add a timestamp just to be extra sure.
const authenticatorCacheMissPrefix = "_INVALID_API_GROUP_"
authenticatorCacheMiss := authenticatorCacheMissPrefix + time.Now().UTC().String()
// we do not have any defaulting functions for *loginv1alpha1.TokenCredentialRequest
// today, but we may have some in the future. Calling AddTypeDefaultingFunc overwrites
// any previously registered defaulting function. Thus to make sure that we catch
// a situation where we add a defaulting func, we attempt to call it here with a nil
// *loginv1alpha1.TokenCredentialRequest. This will do nothing when there is no
// defaulting func registered, but it will almost certainly panic if one is added.
scheme.Default((*loginv1alpha1.TokenCredentialRequest)(nil))
// on incoming requests, restore the authenticator API group to the standard group
// note that we are responsible for duplicating this logic for every external API version
scheme.AddTypeDefaultingFunc(&loginv1alpha1.TokenCredentialRequest{}, func(obj interface{}) {
credentialRequest := obj.(*loginv1alpha1.TokenCredentialRequest)
if credentialRequest.Spec.Authenticator.APIGroup == nil {
// force a cache miss because this is an invalid request
plog.Debug("invalid token credential request, nil group", "authenticator", credentialRequest.Spec.Authenticator)
credentialRequest.Spec.Authenticator.APIGroup = &authenticatorCacheMiss
return
}
restoredGroup, ok := groupsuffix.Unreplace(*credentialRequest.Spec.Authenticator.APIGroup, apiGroupSuffix)
if !ok {
// force a cache miss because this is an invalid request
plog.Debug("invalid token credential request, wrong group", "authenticator", credentialRequest.Spec.Authenticator)
credentialRequest.Spec.Authenticator.APIGroup = &authenticatorCacheMiss
return
}
credentialRequest.Spec.Authenticator.APIGroup = &restoredGroup
})
return scheme, schema.GroupVersion(loginConciergeGroupData), schema.GroupVersion(identityConciergeGroupData)
}
func addToSchemeAtNewGroup(scheme *runtime.Scheme, oldGroup, newGroup string, funcs ...func(*runtime.Scheme) error) {
// we need a temporary place to register our types to avoid double registering them
tmpScheme := runtime.NewScheme()
schemeBuilder := runtime.NewSchemeBuilder(funcs...)
utilruntime.Must(schemeBuilder.AddToScheme(tmpScheme))
for gvk := range tmpScheme.AllKnownTypes() {
if gvk.GroupVersion() == metav1.Unversioned {
continue // metav1.AddToGroupVersion registers types outside of our aggregated API group that we need to ignore
}
if gvk.Group != oldGroup {
panic(fmt.Errorf("tmp scheme has type not in the old aggregated API group %s: %s", oldGroup, gvk)) // programmer error
}
obj, err := tmpScheme.New(gvk)
if err != nil {
panic(err) // programmer error, scheme internal code is broken
}
newGVK := schema.GroupVersionKind{
Group: newGroup,
Version: gvk.Version,
Kind: gvk.Kind,
}
// register the existing type but with the new group in the correct scheme
scheme.AddKnownTypeWithName(newGVK, obj)
}
}

View File

@ -24,19 +24,12 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
"gopkg.in/square/go-jose.v2" "gopkg.in/square/go-jose.v2"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/runtime/serializer"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1" clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
identityapi "go.pinniped.dev/generated/latest/apis/concierge/identity"
identityv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/identity/v1alpha1" identityv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/identity/v1alpha1"
loginapi "go.pinniped.dev/generated/latest/apis/concierge/login" conciergescheme "go.pinniped.dev/internal/concierge/scheme"
loginv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/login/v1alpha1"
"go.pinniped.dev/internal/groupsuffix"
"go.pinniped.dev/internal/plog"
"go.pinniped.dev/internal/testutil" "go.pinniped.dev/internal/testutil"
"go.pinniped.dev/pkg/oidcclient" "go.pinniped.dev/pkg/oidcclient"
"go.pinniped.dev/pkg/oidcclient/filesession" "go.pinniped.dev/pkg/oidcclient/filesession"
@ -173,7 +166,7 @@ func assertWhoami(ctx context.Context, t *testing.T, useProxy bool, pinnipedExe,
func deserializeWhoAmIRequest(t *testing.T, data string, apiGroupSuffix string) *identityv1alpha1.WhoAmIRequest { func deserializeWhoAmIRequest(t *testing.T, data string, apiGroupSuffix string) *identityv1alpha1.WhoAmIRequest {
t.Helper() t.Helper()
scheme, _, _ := conciergeschemeNew(apiGroupSuffix) scheme, _, _ := conciergescheme.New(apiGroupSuffix)
codecs := serializer.NewCodecFactory(scheme) codecs := serializer.NewCodecFactory(scheme)
respInfo, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), runtime.ContentTypeYAML) respInfo, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), runtime.ContentTypeYAML)
require.True(t, ok) require.True(t, ok)
@ -420,110 +413,3 @@ func oidcLoginCommand(ctx context.Context, t *testing.T, pinnipedExe string, ses
cmd.Env = append(os.Environ(), env.ProxyEnv()...) cmd.Env = append(os.Environ(), env.ProxyEnv()...)
return cmd return cmd
} }
// conciergeschemeNew is a temporary private function to stand in place for
// "go.pinniped.dev/internal/concierge/scheme".New until the later function is merged to main.
func conciergeschemeNew(apiGroupSuffix string) (_ *runtime.Scheme, login, identity schema.GroupVersion) {
// standard set up of the server side scheme
scheme := runtime.NewScheme()
// add the options to empty v1
metav1.AddToGroupVersion(scheme, metav1.Unversioned)
// nothing fancy is required if using the standard group suffix
if apiGroupSuffix == groupsuffix.PinnipedDefaultSuffix {
schemeBuilder := runtime.NewSchemeBuilder(
loginv1alpha1.AddToScheme,
loginapi.AddToScheme,
identityv1alpha1.AddToScheme,
identityapi.AddToScheme,
)
utilruntime.Must(schemeBuilder.AddToScheme(scheme))
return scheme, loginv1alpha1.SchemeGroupVersion, identityv1alpha1.SchemeGroupVersion
}
loginConciergeGroupData, identityConciergeGroupData := groupsuffix.ConciergeAggregatedGroups(apiGroupSuffix)
addToSchemeAtNewGroup(scheme, loginv1alpha1.GroupName, loginConciergeGroupData.Group, loginv1alpha1.AddToScheme, loginapi.AddToScheme)
addToSchemeAtNewGroup(scheme, identityv1alpha1.GroupName, identityConciergeGroupData.Group, identityv1alpha1.AddToScheme, identityapi.AddToScheme)
// manually register conversions and defaulting into the correct scheme since we cannot directly call AddToScheme
schemeBuilder := runtime.NewSchemeBuilder(
loginv1alpha1.RegisterConversions,
loginv1alpha1.RegisterDefaults,
identityv1alpha1.RegisterConversions,
identityv1alpha1.RegisterDefaults,
)
utilruntime.Must(schemeBuilder.AddToScheme(scheme))
// we do not want to return errors from the scheme and instead would prefer to defer
// to the REST storage layer for consistency. The simplest way to do this is to force
// a cache miss from the authenticator cache. Kube API groups are validated via the
// IsDNS1123Subdomain func thus we can easily create a group that is guaranteed never
// to be in the authenticator cache. Add a timestamp just to be extra sure.
const authenticatorCacheMissPrefix = "_INVALID_API_GROUP_"
authenticatorCacheMiss := authenticatorCacheMissPrefix + time.Now().UTC().String()
// we do not have any defaulting functions for *loginv1alpha1.TokenCredentialRequest
// today, but we may have some in the future. Calling AddTypeDefaultingFunc overwrites
// any previously registered defaulting function. Thus to make sure that we catch
// a situation where we add a defaulting func, we attempt to call it here with a nil
// *loginv1alpha1.TokenCredentialRequest. This will do nothing when there is no
// defaulting func registered, but it will almost certainly panic if one is added.
scheme.Default((*loginv1alpha1.TokenCredentialRequest)(nil))
// on incoming requests, restore the authenticator API group to the standard group
// note that we are responsible for duplicating this logic for every external API version
scheme.AddTypeDefaultingFunc(&loginv1alpha1.TokenCredentialRequest{}, func(obj interface{}) {
credentialRequest := obj.(*loginv1alpha1.TokenCredentialRequest)
if credentialRequest.Spec.Authenticator.APIGroup == nil {
// force a cache miss because this is an invalid request
plog.Debug("invalid token credential request, nil group", "authenticator", credentialRequest.Spec.Authenticator)
credentialRequest.Spec.Authenticator.APIGroup = &authenticatorCacheMiss
return
}
restoredGroup, ok := groupsuffix.Unreplace(*credentialRequest.Spec.Authenticator.APIGroup, apiGroupSuffix)
if !ok {
// force a cache miss because this is an invalid request
plog.Debug("invalid token credential request, wrong group", "authenticator", credentialRequest.Spec.Authenticator)
credentialRequest.Spec.Authenticator.APIGroup = &authenticatorCacheMiss
return
}
credentialRequest.Spec.Authenticator.APIGroup = &restoredGroup
})
return scheme, schema.GroupVersion(loginConciergeGroupData), schema.GroupVersion(identityConciergeGroupData)
}
func addToSchemeAtNewGroup(scheme *runtime.Scheme, oldGroup, newGroup string, funcs ...func(*runtime.Scheme) error) {
// we need a temporary place to register our types to avoid double registering them
tmpScheme := runtime.NewScheme()
schemeBuilder := runtime.NewSchemeBuilder(funcs...)
utilruntime.Must(schemeBuilder.AddToScheme(tmpScheme))
for gvk := range tmpScheme.AllKnownTypes() {
if gvk.GroupVersion() == metav1.Unversioned {
continue // metav1.AddToGroupVersion registers types outside of our aggregated API group that we need to ignore
}
if gvk.Group != oldGroup {
panic(fmt.Errorf("tmp scheme has type not in the old aggregated API group %s: %s", oldGroup, gvk)) // programmer error
}
obj, err := tmpScheme.New(gvk)
if err != nil {
panic(err) // programmer error, scheme internal code is broken
}
newGVK := schema.GroupVersionKind{
Group: newGroup,
Version: gvk.Version,
Kind: gvk.Kind,
}
// register the existing type but with the new group in the correct scheme
scheme.AddKnownTypeWithName(newGVK, obj)
}
}