From 616211c1bc2e25ba577da46f762376774e7e25ce Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Tue, 19 Jan 2021 17:23:06 -0500 Subject: [PATCH 1/4] deploy: wire API group suffix through YTT templates I didn't advertise this feature in the deploy README's since (hopefully) not many people will want to use it? Signed-off-by: Andrew Keesler --- deploy/concierge/deployment.yaml | 7 ++++--- deploy/concierge/helpers.lib.yaml | 6 +++++- deploy/concierge/rbac.yaml | 9 ++++++--- deploy/concierge/values.yaml | 6 ++++++ deploy/concierge/z0_crd_overlay.yaml | 14 ++++++++++++-- deploy/supervisor/deployment.yaml | 1 + deploy/supervisor/helpers.lib.yaml | 6 +++++- deploy/supervisor/rbac.yaml | 13 ++++++++----- deploy/supervisor/values.yaml | 6 ++++++ deploy/supervisor/z0_crd_overlay.yaml | 11 +++++++++-- 10 files changed, 62 insertions(+), 17 deletions(-) diff --git a/deploy/concierge/deployment.yaml b/deploy/concierge/deployment.yaml index f5963549..3d7f4243 100644 --- a/deploy/concierge/deployment.yaml +++ b/deploy/concierge/deployment.yaml @@ -3,7 +3,7 @@ #@ load("@ytt:data", "data") #@ load("@ytt:json", "json") -#@ load("helpers.lib.yaml", "defaultLabel", "labels", "namespace", "defaultResourceName", "defaultResourceNameWithSuffix", "getAndValidateLogLevel") +#@ load("helpers.lib.yaml", "defaultLabel", "labels", "namespace", "defaultResourceName", "defaultResourceNameWithSuffix", "getAndValidateLogLevel", "pinnipedDevAPIGroupWithPrefix") #@ if not data.values.into_namespace: --- @@ -37,6 +37,7 @@ data: servingCertificate: durationSeconds: (@= str(data.values.api_serving_certificate_duration_seconds) @) renewBeforeSeconds: (@= str(data.values.api_serving_certificate_renew_before_seconds) @) + apiGroupSuffix: (@= data.values.api_group_suffix @) names: servingCertificateSecret: (@= defaultResourceNameWithSuffix("api-tls-serving-certificate") @) credentialIssuer: (@= defaultResourceNameWithSuffix("config") @) @@ -191,11 +192,11 @@ spec: apiVersion: apiregistration.k8s.io/v1 kind: APIService metadata: - name: v1alpha1.login.concierge.pinniped.dev + name: #@ pinnipedDevAPIGroupWithPrefix("v1alpha1.login.concierge") labels: #@ labels() spec: version: v1alpha1 - group: login.concierge.pinniped.dev + group: #@ pinnipedDevAPIGroupWithPrefix("login.concierge") groupPriorityMinimum: 2500 versionPriority: 10 #! caBundle: Do not include this key here. Starts out null, will be updated/owned by the golang code. diff --git a/deploy/concierge/helpers.lib.yaml b/deploy/concierge/helpers.lib.yaml index 452faa75..6ad07f4b 100644 --- a/deploy/concierge/helpers.lib.yaml +++ b/deploy/concierge/helpers.lib.yaml @@ -1,4 +1,4 @@ -#! Copyright 2020 the Pinniped contributors. All Rights Reserved. +#! Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. #! SPDX-License-Identifier: Apache-2.0 #@ load("@ytt:data", "data") @@ -12,6 +12,10 @@ #@ return data.values.app_name + "-" + suffix #@ end +#@ def pinnipedDevAPIGroupWithPrefix(prefix): +#@ return prefix + "." + data.values.api_group_suffix +#@ end + #@ def namespace(): #@ if data.values.into_namespace: #@ return data.values.into_namespace diff --git a/deploy/concierge/rbac.yaml b/deploy/concierge/rbac.yaml index 088725a7..8df8734b 100644 --- a/deploy/concierge/rbac.yaml +++ b/deploy/concierge/rbac.yaml @@ -2,7 +2,7 @@ #! SPDX-License-Identifier: Apache-2.0 #@ load("@ytt:data", "data") -#@ load("helpers.lib.yaml", "labels", "namespace", "defaultResourceName", "defaultResourceNameWithSuffix") +#@ load("helpers.lib.yaml", "labels", "namespace", "defaultResourceName", "defaultResourceNameWithSuffix", "pinnipedDevAPIGroupWithPrefix") #! Give permission to various cluster-scoped objects --- @@ -66,7 +66,9 @@ rules: - apiGroups: [ "" ] resources: [ pods/exec ] verbs: [ create ] - - apiGroups: [ config.concierge.pinniped.dev, authentication.concierge.pinniped.dev ] + - apiGroups: + - #@ pinnipedDevAPIGroupWithPrefix("config.concierge") + - #@ pinnipedDevAPIGroupWithPrefix("authentication.concierge") resources: [ "*" ] verbs: [ create, get, list, update, watch ] - apiGroups: [apps] @@ -124,7 +126,8 @@ metadata: name: #@ defaultResourceNameWithSuffix("create-token-credential-requests") labels: #@ labels() rules: - - apiGroups: [ login.concierge.pinniped.dev ] + - apiGroups: + - #@ pinnipedDevAPIGroupWithPrefix("login.concierge") resources: [ tokencredentialrequests ] verbs: [ create ] --- diff --git a/deploy/concierge/values.yaml b/deploy/concierge/values.yaml index 5aab13c8..c06aa0b0 100644 --- a/deploy/concierge/values.yaml +++ b/deploy/concierge/values.yaml @@ -57,3 +57,9 @@ log_level: #! By default, when this value is left unset, only warnings and error run_as_user: 1001 #! run_as_user specifies the user ID that will own the local-user-authenticator process run_as_group: 1001 #! run_as_group specifies the group ID that will own the local-user-authenticator process + +#! Specify the API group suffix for all Pinniped API groups. By default, this is set to +#! pinniped.dev, so Pinniped API groups will look like foo.pinniped.dev, +#! authentication.concierge.pinniped.dev, etc. As an example, if this is set to tuna.io, then +#! Pinniped API groups will look like foo.tuna.io. authentication.concierge.tuna.io, etc. +api_group_suffix: pinniped.dev diff --git a/deploy/concierge/z0_crd_overlay.yaml b/deploy/concierge/z0_crd_overlay.yaml index 011d69d6..935d5f8c 100644 --- a/deploy/concierge/z0_crd_overlay.yaml +++ b/deploy/concierge/z0_crd_overlay.yaml @@ -1,23 +1,33 @@ -#! Copyright 2020 the Pinniped contributors. All Rights Reserved. +#! Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. #! SPDX-License-Identifier: Apache-2.0 #@ load("@ytt:overlay", "overlay") -#@ load("helpers.lib.yaml", "labels") +#@ load("helpers.lib.yaml", "labels", "pinnipedDevAPIGroupWithPrefix") +#@ load("@ytt:data", "data") #@overlay/match by=overlay.subset({"kind": "CustomResourceDefinition", "metadata":{"name":"credentialissuers.config.concierge.pinniped.dev"}}), expects=1 --- metadata: #@overlay/match missing_ok=True labels: #@ labels() + name: #@ pinnipedDevAPIGroupWithPrefix("credentialissuers.config.concierge") +spec: + group: #@ pinnipedDevAPIGroupWithPrefix("config.concierge") #@overlay/match by=overlay.subset({"kind": "CustomResourceDefinition", "metadata":{"name":"webhookauthenticators.authentication.concierge.pinniped.dev"}}), expects=1 --- metadata: #@overlay/match missing_ok=True labels: #@ labels() + name: #@ pinnipedDevAPIGroupWithPrefix("webhookauthenticators.authentication.concierge") +spec: + group: #@ pinnipedDevAPIGroupWithPrefix("authentication.concierge") #@overlay/match by=overlay.subset({"kind": "CustomResourceDefinition", "metadata":{"name":"jwtauthenticators.authentication.concierge.pinniped.dev"}}), expects=1 --- metadata: #@overlay/match missing_ok=True labels: #@ labels() + name: #@ pinnipedDevAPIGroupWithPrefix("jwtauthenticators.authentication.concierge") +spec: + group: #@ pinnipedDevAPIGroupWithPrefix("authentication.concierge") diff --git a/deploy/supervisor/deployment.yaml b/deploy/supervisor/deployment.yaml index da9dba99..48607e3f 100644 --- a/deploy/supervisor/deployment.yaml +++ b/deploy/supervisor/deployment.yaml @@ -30,6 +30,7 @@ metadata: data: #@yaml/text-templated-strings pinniped.yaml: | + apiGroupSuffix: (@= data.values.api_group_suffix @) names: defaultTLSCertificateSecret: (@= defaultResourceNameWithSuffix("default-tls-certificate") @) labels: (@= json.encode(labels()).rstrip() @) diff --git a/deploy/supervisor/helpers.lib.yaml b/deploy/supervisor/helpers.lib.yaml index 452faa75..6ad07f4b 100644 --- a/deploy/supervisor/helpers.lib.yaml +++ b/deploy/supervisor/helpers.lib.yaml @@ -1,4 +1,4 @@ -#! Copyright 2020 the Pinniped contributors. All Rights Reserved. +#! Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. #! SPDX-License-Identifier: Apache-2.0 #@ load("@ytt:data", "data") @@ -12,6 +12,10 @@ #@ return data.values.app_name + "-" + suffix #@ end +#@ def pinnipedDevAPIGroupWithPrefix(prefix): +#@ return prefix + "." + data.values.api_group_suffix +#@ end + #@ def namespace(): #@ if data.values.into_namespace: #@ return data.values.into_namespace diff --git a/deploy/supervisor/rbac.yaml b/deploy/supervisor/rbac.yaml index 44b7b93a..ea7f4e65 100644 --- a/deploy/supervisor/rbac.yaml +++ b/deploy/supervisor/rbac.yaml @@ -1,8 +1,8 @@ -#! Copyright 2020 the Pinniped contributors. All Rights Reserved. +#! Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. #! SPDX-License-Identifier: Apache-2.0 #@ load("@ytt:data", "data") -#@ load("helpers.lib.yaml", "labels", "namespace", "defaultResourceName", "defaultResourceNameWithSuffix") +#@ load("helpers.lib.yaml", "labels", "namespace", "defaultResourceName", "defaultResourceNameWithSuffix", "pinnipedDevAPIGroupWithPrefix") #! Give permission to various objects within the app's own namespace --- @@ -16,13 +16,16 @@ rules: - apiGroups: [""] resources: [secrets] verbs: [create, get, list, patch, update, watch, delete] - - apiGroups: [config.supervisor.pinniped.dev] + - apiGroups: + - #@ pinnipedDevAPIGroupWithPrefix("config.supervisor") resources: [federationdomains] verbs: [update, get, list, watch] - - apiGroups: [idp.supervisor.pinniped.dev] + - apiGroups: + - #@ pinnipedDevAPIGroupWithPrefix("idp.supervisor") resources: [oidcidentityproviders] verbs: [get, list, watch] - - apiGroups: [idp.supervisor.pinniped.dev] + - apiGroups: + - #@ pinnipedDevAPIGroupWithPrefix("idp.supervisor") resources: [oidcidentityproviders/status] verbs: [get, patch, update] #! We want to be able to read pods/replicasets/deployment so we can learn who our deployment is to set diff --git a/deploy/supervisor/values.yaml b/deploy/supervisor/values.yaml index e522155f..fd27b336 100644 --- a/deploy/supervisor/values.yaml +++ b/deploy/supervisor/values.yaml @@ -59,3 +59,9 @@ log_level: #! By default, when this value is left unset, only warnings and error run_as_user: 1001 #! run_as_user specifies the user ID that will own the local-user-authenticator process run_as_group: 1001 #! run_as_group specifies the group ID that will own the local-user-authenticator process + +#! Specify the API group suffix for all Pinniped API groups. By default, this is set to +#! pinniped.dev, so Pinniped API groups will look like foo.pinniped.dev, +#! authentication.concierge.pinniped.dev, etc. As an example, if this is set to tuna.io, then +#! Pinniped API groups will look like foo.tuna.io. authentication.concierge.tuna.io, etc. +api_group_suffix: pinniped.dev diff --git a/deploy/supervisor/z0_crd_overlay.yaml b/deploy/supervisor/z0_crd_overlay.yaml index e7888f5b..c3bb8173 100644 --- a/deploy/supervisor/z0_crd_overlay.yaml +++ b/deploy/supervisor/z0_crd_overlay.yaml @@ -1,17 +1,24 @@ -#! Copyright 2020 the Pinniped contributors. All Rights Reserved. +#! Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. #! SPDX-License-Identifier: Apache-2.0 #@ load("@ytt:overlay", "overlay") -#@ load("helpers.lib.yaml", "labels") +#@ load("helpers.lib.yaml", "labels", "pinnipedDevAPIGroupWithPrefix") +#@ load("@ytt:data", "data") #@overlay/match by=overlay.subset({"kind": "CustomResourceDefinition", "metadata":{"name":"federationdomains.config.supervisor.pinniped.dev"}}), expects=1 --- metadata: #@overlay/match missing_ok=True labels: #@ labels() + name: #@ pinnipedDevAPIGroupWithPrefix("federationdomains.config.supervisor") +spec: + group: #@ pinnipedDevAPIGroupWithPrefix("config.supervisor") #@overlay/match by=overlay.subset({"kind": "CustomResourceDefinition", "metadata":{"name":"oidcidentityproviders.idp.supervisor.pinniped.dev"}}), expects=1 --- metadata: #@overlay/match missing_ok=True labels: #@ labels() + name: #@ pinnipedDevAPIGroupWithPrefix("oidcidentityproviders.idp.supervisor") +spec: + group: #@ pinnipedDevAPIGroupWithPrefix("idp.supervisor") From 88fd9e5c5eae3807fcd60df1188ba2db7a70c95b Mon Sep 17 00:00:00 2001 From: Andrew Keesler Date: Tue, 19 Jan 2021 10:52:12 -0500 Subject: [PATCH 2/4] internal/config: wire API group suffix through to server components Signed-off-by: Andrew Keesler --- cmd/pinniped-supervisor/main.go | 1 + internal/apigroup/apigroup.go | 29 +++++++++++++++ internal/apigroup/apigroup_test.go | 36 +++++++++++++++++++ internal/concierge/server/server.go | 16 ++++++--- internal/config/concierge/config.go | 9 ++++- internal/config/concierge/config_test.go | 5 ++- internal/config/concierge/types.go | 3 +- internal/config/supervisor/config.go | 14 +++++++- internal/config/supervisor/config_test.go | 7 ++-- internal/config/supervisor/types.go | 9 ++--- .../controllermanager/prepare_controllers.go | 13 ++++++- 11 files changed, 127 insertions(+), 15 deletions(-) create mode 100644 internal/apigroup/apigroup.go create mode 100644 internal/apigroup/apigroup_test.go diff --git a/cmd/pinniped-supervisor/main.go b/cmd/pinniped-supervisor/main.go index 98c9f4fe..950e07d7 100644 --- a/cmd/pinniped-supervisor/main.go +++ b/cmd/pinniped-supervisor/main.go @@ -264,6 +264,7 @@ func run(podInfo *downward.PodInfo, cfg *supervisor.Config) error { return fmt.Errorf("cannot create deployment ref: %w", err) } + _ = *cfg.APIGroupSuffix // TODO: wire API group into kubeclient. client, err := kubeclient.New(dref) if err != nil { return fmt.Errorf("cannot create k8s client: %w", err) diff --git a/internal/apigroup/apigroup.go b/internal/apigroup/apigroup.go new file mode 100644 index 00000000..065812a5 --- /dev/null +++ b/internal/apigroup/apigroup.go @@ -0,0 +1,29 @@ +// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Package apigroup provides centralized logic around Pinniped's API group parameterization. +package apigroup + +import ( + "fmt" + "strings" +) + +// defaultAPIGroupSuffix is the default suffix of the Concierge API group. Our generated code uses +// this suffix, so we know that we can replace this suffix with the configured API group suffix. +const defaultAPIGroupSuffix = "pinniped.dev" + +// Make constructs an API group from a baseAPIGroup and a parameterized apiGroupSuffix. +// +// We assume that all apiGroup's will end in "pinniped.dev", and therefore we can safely replace the +// reference to "pinniped.dev" with the provided apiGroupSuffix. If the provided baseAPIGroup does +// not end in "pinniped.dev", then this function will return an empty string and false. +// +// See Example_loginv1alpha1 and Example_string for more information on input/output pairs. +func Make(baseAPIGroup, apiGroupSuffix string) (string, bool) { + if !strings.HasSuffix(baseAPIGroup, defaultAPIGroupSuffix) { + return "", false + } + i := strings.LastIndex(baseAPIGroup, defaultAPIGroupSuffix) + return fmt.Sprintf("%s%s", baseAPIGroup[:i], apiGroupSuffix), true +} diff --git a/internal/apigroup/apigroup_test.go b/internal/apigroup/apigroup_test.go new file mode 100644 index 00000000..1f68c6d9 --- /dev/null +++ b/internal/apigroup/apigroup_test.go @@ -0,0 +1,36 @@ +// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package apigroup + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + loginv1alpha1 "go.pinniped.dev/generated/1.20/apis/concierge/login/v1alpha1" +) + +func TestMakeError(t *testing.T) { + _, ok := Make("bad-suffix", "shouldnt-matter.com") + require.False(t, ok) +} + +func TestMakeSuffix(t *testing.T) { + s, ok := Make("something.pinniped.dev.something-else.pinniped.dev", "tuna.io") + require.Equal(t, "something.pinniped.dev.something-else.tuna.io", s) + require.True(t, ok) +} + +func Example_loginv1alpha1() { + s, _ := Make(loginv1alpha1.GroupName, "tuna.fish.io") + fmt.Println(s) + // Output: login.concierge.tuna.fish.io +} + +func Example_string() { + s, _ := Make("idp.supervisor.pinniped.dev", "marlin.io") + fmt.Println(s) + // Output: idp.supervisor.marlin.io +} diff --git a/internal/concierge/server/server.go b/internal/concierge/server/server.go index a2beccd6..fcbaa138 100644 --- a/internal/concierge/server/server.go +++ b/internal/concierge/server/server.go @@ -15,6 +15,7 @@ import ( genericoptions "k8s.io/apiserver/pkg/server/options" loginv1alpha1 "go.pinniped.dev/generated/1.20/apis/concierge/login/v1alpha1" + "go.pinniped.dev/internal/apigroup" "go.pinniped.dev/internal/certauthority/dynamiccertauthority" "go.pinniped.dev/internal/concierge/apiserver" "go.pinniped.dev/internal/config/concierge" @@ -36,10 +37,6 @@ type App struct { downwardAPIPath string } -// This is ignored for now because we turn off etcd storage below, but this is -// the right prefix in case we turn it back on. -const defaultEtcdPathPrefix = "/registry/" + loginv1alpha1.GroupName - // New constructs a new App with command line args, stdout and stderr. func New(ctx context.Context, args []string, stdout, stderr io.Writer) *App { app := &App{} @@ -125,6 +122,7 @@ func (a *App) runServer(ctx context.Context) error { startControllersFunc, err := controllermanager.PrepareControllers( &controllermanager.Config{ ServerInstallationInfo: podInfo, + APIGroupSuffix: *cfg.APIGroupSuffix, NamesConfig: &cfg.NamesConfig, Labels: cfg.Labels, KubeCertAgentConfig: &cfg.KubeCertAgentConfig, @@ -146,6 +144,7 @@ func (a *App) runServer(ctx context.Context) error { authenticators, dynamiccertauthority.New(dynamicSigningCertProvider), startControllersFunc, + *cfg.APIGroupSuffix, ) if err != nil { return fmt.Errorf("could not configure aggregated API server: %w", err) @@ -167,7 +166,16 @@ func getAggregatedAPIServerConfig( authenticator credentialrequest.TokenCredentialRequestAuthenticator, issuer credentialrequest.CertIssuer, startControllersPostStartHook func(context.Context), + apiGroupSuffix string, ) (*apiserver.Config, error) { + // This is ignored for now because we turn off etcd storage below, but this is + // the right prefix in case we turn it back on. + apiGroup, ok := apigroup.Make(loginv1alpha1.GroupName, apiGroupSuffix) + if !ok { + return nil, fmt.Errorf("cannot make api group from %s/%s", loginv1alpha1.GroupName, apiGroupSuffix) + } + defaultEtcdPathPrefix := fmt.Sprintf("/registry/%s", apiGroup) + recommendedOptions := genericoptions.NewRecommendedOptions( defaultEtcdPathPrefix, apiserver.Codecs.LegacyCodec(loginv1alpha1.SchemeGroupVersion), diff --git a/internal/config/concierge/config.go b/internal/config/concierge/config.go index 20555a7d..ead188de 100644 --- a/internal/config/concierge/config.go +++ b/internal/config/concierge/config.go @@ -1,4 +1,4 @@ -// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // Package concierge contains functionality to load/store Config's from/to @@ -40,6 +40,7 @@ func FromPath(path string) (*Config, error) { } maybeSetAPIDefaults(&config.APIConfig) + maybeSetAPIGroupSuffixDefault(&config.APIGroupSuffix) maybeSetKubeCertAgentDefaults(&config.KubeCertAgentConfig) if err := validateAPI(&config.APIConfig); err != nil { @@ -71,6 +72,12 @@ func maybeSetAPIDefaults(apiConfig *APIConfigSpec) { } } +func maybeSetAPIGroupSuffixDefault(apiGroupSuffix **string) { + if *apiGroupSuffix == nil { + *apiGroupSuffix = stringPtr("pinniped.dev") + } +} + func maybeSetKubeCertAgentDefaults(cfg *KubeCertAgentSpec) { if cfg.NamePrefix == nil { cfg.NamePrefix = stringPtr("pinniped-kube-cert-agent-") diff --git a/internal/config/concierge/config_test.go b/internal/config/concierge/config_test.go index 38315d74..bbba174d 100644 --- a/internal/config/concierge/config_test.go +++ b/internal/config/concierge/config_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package concierge @@ -30,6 +30,7 @@ func TestFromPath(t *testing.T) { servingCertificate: durationSeconds: 3600 renewBeforeSeconds: 2400 + apiGroupSuffix: some.suffix.com names: servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate credentialIssuer: pinniped-config @@ -53,6 +54,7 @@ func TestFromPath(t *testing.T) { RenewBeforeSeconds: int64Ptr(2400), }, }, + APIGroupSuffix: stringPtr("some.suffix.com"), NamesConfig: NamesConfigSpec{ ServingCertificateSecret: "pinniped-concierge-api-tls-serving-certificate", CredentialIssuer: "pinniped-config", @@ -82,6 +84,7 @@ func TestFromPath(t *testing.T) { DiscoveryInfo: DiscoveryInfoSpec{ URL: nil, }, + APIGroupSuffix: stringPtr("pinniped.dev"), APIConfig: APIConfigSpec{ ServingCertificateConfig: ServingCertificateConfigSpec{ DurationSeconds: int64Ptr(60 * 60 * 24 * 365), // about a year diff --git a/internal/config/concierge/types.go b/internal/config/concierge/types.go index fc8517b5..1f402a34 100644 --- a/internal/config/concierge/types.go +++ b/internal/config/concierge/types.go @@ -1,4 +1,4 @@ -// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package concierge @@ -9,6 +9,7 @@ import "go.pinniped.dev/internal/plog" type Config struct { DiscoveryInfo DiscoveryInfoSpec `json:"discovery"` APIConfig APIConfigSpec `json:"api"` + APIGroupSuffix *string `json:"apiGroupSuffix,omitempty"` NamesConfig NamesConfigSpec `json:"names"` KubeCertAgentConfig KubeCertAgentSpec `json:"kubeCertAgent"` Labels map[string]string `json:"labels"` diff --git a/internal/config/supervisor/config.go b/internal/config/supervisor/config.go index 5c7308c5..6e20b21f 100644 --- a/internal/config/supervisor/config.go +++ b/internal/config/supervisor/config.go @@ -1,4 +1,4 @@ -// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // Package supervisor contains functionality to load/store Config's from/to @@ -34,6 +34,8 @@ func FromPath(path string) (*Config, error) { config.Labels = make(map[string]string) } + maybeSetAPIGroupSuffixDefault(&config.APIGroupSuffix) + if err := validateNames(&config.NamesConfig); err != nil { return nil, fmt.Errorf("validate names: %w", err) } @@ -45,6 +47,12 @@ func FromPath(path string) (*Config, error) { return &config, nil } +func maybeSetAPIGroupSuffixDefault(apiGroupSuffix **string) { + if *apiGroupSuffix == nil { + *apiGroupSuffix = stringPtr("pinniped.dev") + } +} + func validateNames(names *NamesConfigSpec) error { missingNames := []string{} if names.DefaultTLSCertificateSecret == "" { @@ -55,3 +63,7 @@ func validateNames(names *NamesConfigSpec) error { } return nil } + +func stringPtr(s string) *string { + return &s +} diff --git a/internal/config/supervisor/config_test.go b/internal/config/supervisor/config_test.go index 8b77c6f2..a2e5d46f 100644 --- a/internal/config/supervisor/config_test.go +++ b/internal/config/supervisor/config_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package supervisor @@ -24,6 +24,7 @@ func TestFromPath(t *testing.T) { name: "Happy", yaml: here.Doc(` --- + apiGroupSuffix: some.suffix.com labels: myLabelKey1: myLabelValue1 myLabelKey2: myLabelValue2 @@ -31,6 +32,7 @@ func TestFromPath(t *testing.T) { defaultTLSCertificateSecret: my-secret-name `), wantConfig: &Config{ + APIGroupSuffix: stringPtr("some.suffix.com"), Labels: map[string]string{ "myLabelKey1": "myLabelValue1", "myLabelKey2": "myLabelValue2", @@ -48,7 +50,8 @@ func TestFromPath(t *testing.T) { defaultTLSCertificateSecret: my-secret-name `), wantConfig: &Config{ - Labels: map[string]string{}, + APIGroupSuffix: stringPtr("pinniped.dev"), + Labels: map[string]string{}, NamesConfig: NamesConfigSpec{ DefaultTLSCertificateSecret: "my-secret-name", }, diff --git a/internal/config/supervisor/types.go b/internal/config/supervisor/types.go index 8a487d77..f6f17696 100644 --- a/internal/config/supervisor/types.go +++ b/internal/config/supervisor/types.go @@ -1,4 +1,4 @@ -// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package supervisor @@ -7,9 +7,10 @@ import "go.pinniped.dev/internal/plog" // Config contains knobs to setup an instance of the Pinniped Supervisor. type Config struct { - Labels map[string]string `json:"labels"` - NamesConfig NamesConfigSpec `json:"names"` - LogLevel plog.LogLevel `json:"logLevel"` + APIGroupSuffix *string `json:"apiGroupSuffix,omitempty"` + Labels map[string]string `json:"labels"` + NamesConfig NamesConfigSpec `json:"names"` + LogLevel plog.LogLevel `json:"logLevel"` } // NamesConfigSpec configures the names of some Kubernetes resources for the Supervisor. diff --git a/internal/controllermanager/prepare_controllers.go b/internal/controllermanager/prepare_controllers.go index c70fd4cf..a7f87d6a 100644 --- a/internal/controllermanager/prepare_controllers.go +++ b/internal/controllermanager/prepare_controllers.go @@ -18,6 +18,7 @@ import ( loginv1alpha1 "go.pinniped.dev/generated/1.20/apis/concierge/login/v1alpha1" pinnipedclientset "go.pinniped.dev/generated/1.20/client/concierge/clientset/versioned" pinnipedinformers "go.pinniped.dev/generated/1.20/client/concierge/informers/externalversions" + "go.pinniped.dev/internal/apigroup" "go.pinniped.dev/internal/config/concierge" "go.pinniped.dev/internal/controller/apicerts" "go.pinniped.dev/internal/controller/authenticator/authncache" @@ -45,6 +46,9 @@ type Config struct { // ServerInstallationInfo provides the name of the pod in which Pinniped is running and the namespace in which Pinniped is deployed. ServerInstallationInfo *downward.PodInfo + // APIGroupSuffix is the suffix of the Pinniped API that should be targeted by these controllers. + APIGroupSuffix string + // NamesConfig comes from the Pinniped config API (see api.Config). It specifies how Kubernetes // objects should be named. NamesConfig *concierge.NamesConfigSpec @@ -85,6 +89,7 @@ func PrepareControllers(c *Config) (func(ctx context.Context), error) { return nil, fmt.Errorf("cannot create deployment ref: %w", err) } + _ = c.APIGroupSuffix // TODO: wire API group into kubeclient. client, err := kubeclient.New(dref) if err != nil { return nil, fmt.Errorf("could not create clients for the controllers: %w", err) @@ -106,6 +111,12 @@ func PrepareControllers(c *Config) (func(ctx context.Context), error) { Name: c.NamesConfig.CredentialIssuer, } + groupName, ok := apigroup.Make(loginv1alpha1.GroupName, c.APIGroupSuffix) + if !ok { + return nil, fmt.Errorf("cannot make api group from %s/%s", loginv1alpha1.GroupName, c.APIGroupSuffix) + } + apiServiceName := loginv1alpha1.SchemeGroupVersion.Version + "." + groupName + // Create controller manager. controllerManager := controllerlib. NewManager(). @@ -145,7 +156,7 @@ func PrepareControllers(c *Config) (func(ctx context.Context), error) { apicerts.NewAPIServiceUpdaterController( c.ServerInstallationInfo.Namespace, c.NamesConfig.ServingCertificateSecret, - loginv1alpha1.SchemeGroupVersion.Version+"."+loginv1alpha1.GroupName, + apiServiceName, client.Aggregation, informers.installationNamespaceK8s.Core().V1().Secrets(), controllerlib.WithInformer, From 1c3518e18a7a8a39688364f0e45870cfc1f3fd5a Mon Sep 17 00:00:00 2001 From: Andrew Keesler Date: Tue, 19 Jan 2021 11:29:15 -0500 Subject: [PATCH 3/4] cmd/pinniped: wire API group suffix through to client components Signed-off-by: Andrew Keesler --- cmd/pinniped/cmd/deprecated.go | 3 ++- cmd/pinniped/cmd/kubeconfig.go | 3 +++ cmd/pinniped/cmd/kubeconfig_test.go | 6 ++++++ cmd/pinniped/cmd/login_oidc.go | 5 ++++- cmd/pinniped/cmd/login_oidc_test.go | 4 +++- cmd/pinniped/cmd/login_static.go | 5 ++++- cmd/pinniped/cmd/login_static_test.go | 3 ++- pkg/conciergeclient/conciergeclient.go | 23 ++++++++++++++++----- pkg/conciergeclient/conciergeclient_test.go | 10 +++++++++ 9 files changed, 52 insertions(+), 10 deletions(-) diff --git a/cmd/pinniped/cmd/deprecated.go b/cmd/pinniped/cmd/deprecated.go index 8894eef4..aa229d4e 100644 --- a/cmd/pinniped/cmd/deprecated.go +++ b/cmd/pinniped/cmd/deprecated.go @@ -1,4 +1,4 @@ -// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package cmd @@ -70,6 +70,7 @@ func legacyGetKubeconfigCommand(deps kubeconfigDeps) *cobra.Command { namespace: namespace, authenticatorName: authenticatorName, authenticatorType: authenticatorType, + apiGroupSuffix: "pinniped.dev", }, }) } diff --git a/cmd/pinniped/cmd/kubeconfig.go b/cmd/pinniped/cmd/kubeconfig.go index 8ca7f64d..5bf0b342 100644 --- a/cmd/pinniped/cmd/kubeconfig.go +++ b/cmd/pinniped/cmd/kubeconfig.go @@ -73,6 +73,7 @@ type getKubeconfigConciergeParams struct { namespace string authenticatorName string authenticatorType string + apiGroupSuffix string } type getKubeconfigParams struct { @@ -103,6 +104,7 @@ func kubeconfigCommand(deps kubeconfigDeps) *cobra.Command { f.StringVar(&flags.concierge.namespace, "concierge-namespace", "pinniped-concierge", "Namespace in which the concierge was installed") f.StringVar(&flags.concierge.authenticatorType, "concierge-authenticator-type", "", "Concierge authenticator type (e.g., 'webhook', 'jwt') (default: autodiscover)") f.StringVar(&flags.concierge.authenticatorName, "concierge-authenticator-name", "", "Concierge authenticator name (default: autodiscover)") + f.StringVar(&flags.concierge.apiGroupSuffix, "concierge-api-group-suffix", "pinniped.dev", "Concierge API group suffix") f.StringVar(&flags.oidc.issuer, "oidc-issuer", "", "OpenID Connect issuer URL (default: autodiscover)") f.StringVar(&flags.oidc.clientID, "oidc-client-id", "pinniped-cli", "OpenID Connect client ID (default: autodiscover)") @@ -258,6 +260,7 @@ func configureConcierge(authenticator metav1.Object, flags *getKubeconfigParams, // Append the flags to configure the Concierge credential exchange at runtime. execConfig.Args = append(execConfig.Args, "--enable-concierge", + "--concierge-api-group-suffix="+flags.concierge.apiGroupSuffix, "--concierge-namespace="+flags.concierge.namespace, "--concierge-authenticator-name="+flags.concierge.authenticatorName, "--concierge-authenticator-type="+flags.concierge.authenticatorType, diff --git a/cmd/pinniped/cmd/kubeconfig_test.go b/cmd/pinniped/cmd/kubeconfig_test.go index c5b2e1c6..90d4635d 100644 --- a/cmd/pinniped/cmd/kubeconfig_test.go +++ b/cmd/pinniped/cmd/kubeconfig_test.go @@ -57,6 +57,7 @@ func TestGetKubeconfig(t *testing.T) { kubeconfig [flags] Flags: + --concierge-api-group-suffix string Concierge API group suffix (default "pinniped.dev") --concierge-authenticator-name string Concierge authenticator name (default: autodiscover) --concierge-authenticator-type string Concierge authenticator type (e.g., 'webhook', 'jwt') (default: autodiscover) --concierge-namespace string Namespace in which the concierge was installed (default "pinniped-concierge") @@ -313,6 +314,7 @@ func TestGetKubeconfig(t *testing.T) { - login - static - --enable-concierge + - --concierge-api-group-suffix=pinniped.dev - --concierge-namespace=test-namespace - --concierge-authenticator-name=test-authenticator - --concierge-authenticator-type=webhook @@ -358,6 +360,7 @@ func TestGetKubeconfig(t *testing.T) { - login - static - --enable-concierge + - --concierge-api-group-suffix=pinniped.dev - --concierge-namespace=test-namespace - --concierge-authenticator-name=test-authenticator - --concierge-authenticator-type=webhook @@ -410,6 +413,7 @@ func TestGetKubeconfig(t *testing.T) { - login - oidc - --enable-concierge + - --concierge-api-group-suffix=pinniped.dev - --concierge-namespace=pinniped-concierge - --concierge-authenticator-name=test-authenticator - --concierge-authenticator-type=jwt @@ -429,6 +433,7 @@ func TestGetKubeconfig(t *testing.T) { name: "autodetect nothing, set a bunch of options", args: []string{ "--kubeconfig", "./testdata/kubeconfig.yaml", + "--concierge-api-group-suffix", "tuna.io", "--concierge-authenticator-type", "webhook", "--concierge-authenticator-name", "test-authenticator", "--oidc-issuer", "https://example.com/issuer", @@ -468,6 +473,7 @@ func TestGetKubeconfig(t *testing.T) { - login - oidc - --enable-concierge + - --concierge-api-group-suffix=tuna.io - --concierge-namespace=pinniped-concierge - --concierge-authenticator-name=test-authenticator - --concierge-authenticator-type=webhook diff --git a/cmd/pinniped/cmd/login_oidc.go b/cmd/pinniped/cmd/login_oidc.go index 39718105..7802eeea 100644 --- a/cmd/pinniped/cmd/login_oidc.go +++ b/cmd/pinniped/cmd/login_oidc.go @@ -1,4 +1,4 @@ -// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package cmd @@ -64,6 +64,7 @@ type oidcLoginFlags struct { conciergeAuthenticatorName string conciergeEndpoint string conciergeCABundle string + conciergeAPIGroupSuffix string } func oidcLoginCommand(deps oidcLoginCommandDeps) *cobra.Command { @@ -92,6 +93,7 @@ func oidcLoginCommand(deps oidcLoginCommandDeps) *cobra.Command { cmd.Flags().StringVar(&flags.conciergeAuthenticatorName, "concierge-authenticator-name", "", "Concierge authenticator name") cmd.Flags().StringVar(&flags.conciergeEndpoint, "concierge-endpoint", "", "API base for the Pinniped concierge endpoint") cmd.Flags().StringVar(&flags.conciergeCABundle, "concierge-ca-bundle-data", "", "CA bundle to use when connecting to the concierge") + cmd.Flags().StringVar(&flags.conciergeAPIGroupSuffix, "concierge-api-group-suffix", "pinniped.dev", "Concierge API group suffix") mustMarkHidden(&cmd, "debug-session-cache") mustMarkRequired(&cmd, "issuer") @@ -135,6 +137,7 @@ func runOIDCLogin(cmd *cobra.Command, deps oidcLoginCommandDeps, flags oidcLogin conciergeclient.WithEndpoint(flags.conciergeEndpoint), conciergeclient.WithBase64CABundle(flags.conciergeCABundle), conciergeclient.WithAuthenticator(flags.conciergeAuthenticatorType, flags.conciergeAuthenticatorName), + conciergeclient.WithAPIGroupSuffix(flags.conciergeAPIGroupSuffix), ) if err != nil { return fmt.Errorf("invalid concierge parameters: %w", err) diff --git a/cmd/pinniped/cmd/login_oidc_test.go b/cmd/pinniped/cmd/login_oidc_test.go index da7515a0..a12050f0 100644 --- a/cmd/pinniped/cmd/login_oidc_test.go +++ b/cmd/pinniped/cmd/login_oidc_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package cmd @@ -60,6 +60,7 @@ func TestLoginOIDCCommand(t *testing.T) { --ca-bundle strings Path to TLS certificate authority bundle (PEM format, optional, can be repeated) --ca-bundle-data strings Base64 endcoded TLS certificate authority bundle (base64 encoded PEM format, optional, can be repeated) --client-id string OpenID Connect client ID (default "pinniped-cli") + --concierge-api-group-suffix string Concierge API group suffix (default "pinniped.dev") --concierge-authenticator-name string Concierge authenticator name --concierge-authenticator-type string Concierge authenticator type (e.g., 'webhook', 'jwt') --concierge-ca-bundle-data string CA bundle to use when connecting to the concierge @@ -175,6 +176,7 @@ func TestLoginOIDCCommand(t *testing.T) { "--concierge-authenticator-name", "test-authenticator", "--concierge-endpoint", "https://127.0.0.1:1234/", "--concierge-ca-bundle-data", base64.StdEncoding.EncodeToString(testCA.Bundle()), + "--concierge-api-group-suffix", "some.suffix.com", }, wantOptionsCount: 7, wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{},"status":{"token":"exchanged-token"}}` + "\n", diff --git a/cmd/pinniped/cmd/login_static.go b/cmd/pinniped/cmd/login_static.go index a051e4fa..52fcf095 100644 --- a/cmd/pinniped/cmd/login_static.go +++ b/cmd/pinniped/cmd/login_static.go @@ -1,4 +1,4 @@ -// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package cmd @@ -46,6 +46,7 @@ type staticLoginParams struct { conciergeAuthenticatorName string conciergeEndpoint string conciergeCABundle string + conciergeAPIGroupSuffix string } func staticLoginCommand(deps staticLoginDeps) *cobra.Command { @@ -66,6 +67,7 @@ func staticLoginCommand(deps staticLoginDeps) *cobra.Command { cmd.Flags().StringVar(&flags.conciergeAuthenticatorName, "concierge-authenticator-name", "", "Concierge authenticator name") cmd.Flags().StringVar(&flags.conciergeEndpoint, "concierge-endpoint", "", "API base for the Pinniped concierge endpoint") cmd.Flags().StringVar(&flags.conciergeCABundle, "concierge-ca-bundle-data", "", "CA bundle to use when connecting to the concierge") + cmd.Flags().StringVar(&flags.conciergeAPIGroupSuffix, "concierge-api-group-suffix", "pinniped.dev", "Concierge API group suffix") cmd.RunE = func(cmd *cobra.Command, args []string) error { return runStaticLogin(cmd.OutOrStdout(), deps, flags) } return &cmd } @@ -83,6 +85,7 @@ func runStaticLogin(out io.Writer, deps staticLoginDeps, flags staticLoginParams conciergeclient.WithEndpoint(flags.conciergeEndpoint), conciergeclient.WithBase64CABundle(flags.conciergeCABundle), conciergeclient.WithAuthenticator(flags.conciergeAuthenticatorType, flags.conciergeAuthenticatorName), + conciergeclient.WithAPIGroupSuffix(flags.conciergeAPIGroupSuffix), ) if err != nil { return fmt.Errorf("invalid concierge parameters: %w", err) diff --git a/cmd/pinniped/cmd/login_static_test.go b/cmd/pinniped/cmd/login_static_test.go index ef523020..f1c30da3 100644 --- a/cmd/pinniped/cmd/login_static_test.go +++ b/cmd/pinniped/cmd/login_static_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package cmd @@ -51,6 +51,7 @@ func TestLoginStaticCommand(t *testing.T) { static [--token TOKEN] [--token-env TOKEN_NAME] [flags] Flags: + --concierge-api-group-suffix string Concierge API group suffix (default "pinniped.dev") --concierge-authenticator-name string Concierge authenticator name --concierge-authenticator-type string Concierge authenticator type (e.g., 'webhook', 'jwt') --concierge-ca-bundle-data string CA bundle to use when connecting to the concierge diff --git a/pkg/conciergeclient/conciergeclient.go b/pkg/conciergeclient/conciergeclient.go index 11d40879..b8163437 100644 --- a/pkg/conciergeclient/conciergeclient.go +++ b/pkg/conciergeclient/conciergeclient.go @@ -33,10 +33,11 @@ type Option func(*Client) error // Client is a configuration for talking to the Pinniped concierge. type Client struct { - namespace string - authenticator *corev1.TypedLocalObjectReference - caBundle string - endpoint *url.URL + namespace string + authenticator *corev1.TypedLocalObjectReference + caBundle string + endpoint *url.URL + apiGroupSuffix string } // WithNamespace configures the namespace where the TokenCredentialRequest is to be sent. @@ -112,9 +113,20 @@ func WithEndpoint(endpoint string) Option { } } +// WithAPIGroupSuffix configures the concierge's API group suffix (e.g., "pinniped.dev"). +func WithAPIGroupSuffix(apiGroupSuffix string) Option { + return func(c *Client) error { + if apiGroupSuffix == "" { + return fmt.Errorf("api group suffix must not be empty") + } + c.apiGroupSuffix = apiGroupSuffix + return nil + } +} + // New validates the specified options and returns a newly initialized *Client. func New(opts ...Option) (*Client, error) { - c := Client{namespace: "pinniped-concierge"} + c := Client{namespace: "pinniped-concierge", apiGroupSuffix: "pinniped.dev"} for _, opt := range opts { if err := opt(&c); err != nil { return nil, err @@ -151,6 +163,7 @@ func (c *Client) clientset() (conciergeclientset.Interface, error) { if err != nil { return nil, err } + _ = c.apiGroupSuffix // TODO: wire API group into kubeclient. client, err := kubeclient.New(kubeclient.WithConfig(cfg)) if err != nil { return nil, err diff --git a/pkg/conciergeclient/conciergeclient_test.go b/pkg/conciergeclient/conciergeclient_test.go index ce19c704..79560c71 100644 --- a/pkg/conciergeclient/conciergeclient_test.go +++ b/pkg/conciergeclient/conciergeclient_test.go @@ -104,6 +104,15 @@ func TestNew(t *testing.T) { }, wantErr: "WithEndpoint must be specified", }, + { + name: "empty api group suffix", + opts: []Option{ + WithAuthenticator("jwt", "test-authenticator"), + WithEndpoint("https://example.com"), + WithAPIGroupSuffix(""), + }, + wantErr: "api group suffix must not be empty", + }, { name: "valid", opts: []Option{ @@ -114,6 +123,7 @@ func TestNew(t *testing.T) { WithBase64CABundle(base64.StdEncoding.EncodeToString(testCA.Bundle())), WithAuthenticator("jwt", "test-authenticator"), WithAuthenticator("webhook", "test-authenticator"), + WithAPIGroupSuffix("suffix.com"), }, }, } From 906bfa023c5c61d56aaab4b15eb33a3129911852 Mon Sep 17 00:00:00 2001 From: Andrew Keesler Date: Tue, 19 Jan 2021 13:50:22 -0500 Subject: [PATCH 4/4] test: wire API group suffix through to tests Signed-off-by: Andrew Keesler --- test/integration/cli_test.go | 3 +- test/integration/client_test.go | 14 +++- .../concierge_api_serving_certs_test.go | 2 +- test/integration/e2e_test.go | 1 + test/integration/kube_api_discovery_test.go | 84 +++++++++++-------- test/integration/kubeclient_test.go | 1 + test/library/client.go | 15 +++- test/library/env.go | 10 +++ 8 files changed, 87 insertions(+), 43 deletions(-) diff --git a/test/integration/cli_test.go b/test/integration/cli_test.go index a859dcac..cc8a4ac0 100644 --- a/test/integration/cli_test.go +++ b/test/integration/cli_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package integration @@ -65,6 +65,7 @@ func TestCLIGetKubeconfigStaticToken(t *testing.T) { args: []string{ "get", "kubeconfig", "--static-token", env.TestUser.Token, + "--concierge-api-group-suffix", env.APIGroupSuffix, "--concierge-namespace", env.ConciergeNamespace, "--concierge-authenticator-type", "webhook", "--concierge-authenticator-name", authenticator.Name, diff --git a/test/integration/client_test.go b/test/integration/client_test.go index 64dd1aba..67f23083 100644 --- a/test/integration/client_test.go +++ b/test/integration/client_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package integration @@ -13,8 +13,8 @@ import ( "github.com/stretchr/testify/require" clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1" - "go.pinniped.dev/internal/client" "go.pinniped.dev/internal/here" + "go.pinniped.dev/pkg/conciergeclient" "go.pinniped.dev/test/library" ) @@ -69,10 +69,18 @@ func TestClient(t *testing.T) { // Using the CA bundle and host from the current (admin) kubeconfig, do the token exchange. clientConfig := library.NewClientConfig(t) + client, err := conciergeclient.New( + conciergeclient.WithNamespace(env.ConciergeNamespace), + conciergeclient.WithCABundle(string(clientConfig.CAData)), + conciergeclient.WithEndpoint(clientConfig.Host), + conciergeclient.WithAuthenticator("webhook", webhook.Name), + conciergeclient.WithAPIGroupSuffix(env.APIGroupSuffix), + ) + require.NoError(t, err) var resp *clientauthenticationv1beta1.ExecCredential assert.Eventually(t, func() bool { - resp, err = client.ExchangeToken(ctx, env.ConciergeNamespace, webhook, env.TestUser.Token, string(clientConfig.CAData), clientConfig.Host) + resp, err = client.ExchangeToken(ctx, env.TestUser.Token) return err == nil }, 10*time.Second, 500*time.Millisecond) require.NoError(t, err) diff --git a/test/integration/concierge_api_serving_certs_test.go b/test/integration/concierge_api_serving_certs_test.go index 2225bd96..e2138457 100644 --- a/test/integration/concierge_api_serving_certs_test.go +++ b/test/integration/concierge_api_serving_certs_test.go @@ -79,7 +79,7 @@ func TestAPIServingCertificateAutoCreationAndRotation(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) defer cancel() - const apiServiceName = "v1alpha1.login.concierge.pinniped.dev" + apiServiceName := "v1alpha1.login.concierge." + env.APIGroupSuffix // Get the initial auto-generated version of the Secret. secret, err := kubeClient.CoreV1().Secrets(env.ConciergeNamespace).Get(ctx, defaultServingCertResourceName, metav1.GetOptions{}) diff --git a/test/integration/e2e_test.go b/test/integration/e2e_test.go index 5afa4c9d..2c9ce325 100644 --- a/test/integration/e2e_test.go +++ b/test/integration/e2e_test.go @@ -138,6 +138,7 @@ func TestE2EFullIntegration(t *testing.T) { // Run "pinniped get kubeconfig" to get a kubeconfig YAML. kubeconfigYAML, stderr := runPinnipedCLI(t, pinnipedExe, "get", "kubeconfig", + "--concierge-api-group-suffix", env.APIGroupSuffix, "--concierge-namespace", env.ConciergeNamespace, "--concierge-authenticator-type", "jwt", "--concierge-authenticator-name", authenticator.Name, diff --git a/test/integration/kube_api_discovery_test.go b/test/integration/kube_api_discovery_test.go index fd58b065..ed82226d 100644 --- a/test/integration/kube_api_discovery_test.go +++ b/test/integration/kube_api_discovery_test.go @@ -1,46 +1,60 @@ -// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package integration import ( + "fmt" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" "go.pinniped.dev/test/library" ) func TestGetAPIResourceList(t *testing.T) { - library.SkipUnlessIntegration(t) + env := library.IntegrationEnv(t) client := library.NewClientset(t) groups, resources, err := client.Discovery().ServerGroupsAndResources() require.NoError(t, err) + makeGV := func(firstSegment, secondSegment string) schema.GroupVersion { + return schema.GroupVersion{ + Group: fmt.Sprintf("%s.%s.%s", firstSegment, secondSegment, env.APIGroupSuffix), + Version: "v1alpha1", + } + } + loginConciergeGV := makeGV("login", "concierge") + authenticationConciergeGV := makeGV("authentication", "concierge") + configConciergeGV := makeGV("config", "concierge") + idpSupervisorGV := makeGV("idp", "supervisor") + configSupervisorGV := makeGV("config", "supervisor") + tests := []struct { group metav1.APIGroup resourceByVersion map[string][]metav1.APIResource }{ { group: metav1.APIGroup{ - Name: "login.concierge.pinniped.dev", + Name: loginConciergeGV.Group, Versions: []metav1.GroupVersionForDiscovery{ { - GroupVersion: "login.concierge.pinniped.dev/v1alpha1", - Version: "v1alpha1", + GroupVersion: loginConciergeGV.String(), + Version: loginConciergeGV.Version, }, }, PreferredVersion: metav1.GroupVersionForDiscovery{ - GroupVersion: "login.concierge.pinniped.dev/v1alpha1", - Version: "v1alpha1", + GroupVersion: loginConciergeGV.String(), + Version: loginConciergeGV.Version, }, }, resourceByVersion: map[string][]metav1.APIResource{ - "login.concierge.pinniped.dev/v1alpha1": { + loginConciergeGV.String(): { { Name: "tokencredentialrequests", Kind: "TokenCredentialRequest", @@ -53,20 +67,20 @@ func TestGetAPIResourceList(t *testing.T) { }, { group: metav1.APIGroup{ - Name: "config.supervisor.pinniped.dev", + Name: configSupervisorGV.Group, Versions: []metav1.GroupVersionForDiscovery{ { - GroupVersion: "config.supervisor.pinniped.dev/v1alpha1", - Version: "v1alpha1", + GroupVersion: configSupervisorGV.String(), + Version: configSupervisorGV.Version, }, }, PreferredVersion: metav1.GroupVersionForDiscovery{ - GroupVersion: "config.supervisor.pinniped.dev/v1alpha1", - Version: "v1alpha1", + GroupVersion: configSupervisorGV.String(), + Version: configSupervisorGV.Version, }, }, resourceByVersion: map[string][]metav1.APIResource{ - "config.supervisor.pinniped.dev/v1alpha1": { + configSupervisorGV.String(): { { Name: "federationdomains", SingularName: "federationdomain", @@ -80,20 +94,20 @@ func TestGetAPIResourceList(t *testing.T) { }, { group: metav1.APIGroup{ - Name: "idp.supervisor.pinniped.dev", + Name: idpSupervisorGV.Group, Versions: []metav1.GroupVersionForDiscovery{ { - GroupVersion: "idp.supervisor.pinniped.dev/v1alpha1", - Version: "v1alpha1", + GroupVersion: idpSupervisorGV.String(), + Version: idpSupervisorGV.Version, }, }, PreferredVersion: metav1.GroupVersionForDiscovery{ - GroupVersion: "idp.supervisor.pinniped.dev/v1alpha1", - Version: "v1alpha1", + GroupVersion: idpSupervisorGV.String(), + Version: idpSupervisorGV.Version, }, }, resourceByVersion: map[string][]metav1.APIResource{ - "idp.supervisor.pinniped.dev/v1alpha1": { + idpSupervisorGV.String(): { { Name: "oidcidentityproviders", SingularName: "oidcidentityprovider", @@ -113,20 +127,20 @@ func TestGetAPIResourceList(t *testing.T) { }, { group: metav1.APIGroup{ - Name: "config.concierge.pinniped.dev", + Name: configConciergeGV.Group, Versions: []metav1.GroupVersionForDiscovery{ { - GroupVersion: "config.concierge.pinniped.dev/v1alpha1", - Version: "v1alpha1", + GroupVersion: configConciergeGV.String(), + Version: configConciergeGV.Version, }, }, PreferredVersion: metav1.GroupVersionForDiscovery{ - GroupVersion: "config.concierge.pinniped.dev/v1alpha1", - Version: "v1alpha1", + GroupVersion: configConciergeGV.String(), + Version: configConciergeGV.Version, }, }, resourceByVersion: map[string][]metav1.APIResource{ - "config.concierge.pinniped.dev/v1alpha1": { + configConciergeGV.String(): { { Name: "credentialissuers", SingularName: "credentialissuer", @@ -140,20 +154,20 @@ func TestGetAPIResourceList(t *testing.T) { }, { group: metav1.APIGroup{ - Name: "authentication.concierge.pinniped.dev", + Name: authenticationConciergeGV.Group, Versions: []metav1.GroupVersionForDiscovery{ { - GroupVersion: "authentication.concierge.pinniped.dev/v1alpha1", - Version: "v1alpha1", + GroupVersion: authenticationConciergeGV.String(), + Version: authenticationConciergeGV.Version, }, }, PreferredVersion: metav1.GroupVersionForDiscovery{ - GroupVersion: "authentication.concierge.pinniped.dev/v1alpha1", - Version: "v1alpha1", + GroupVersion: authenticationConciergeGV.String(), + Version: authenticationConciergeGV.Version, }, }, resourceByVersion: map[string][]metav1.APIResource{ - "authentication.concierge.pinniped.dev/v1alpha1": { + authenticationConciergeGV.String(): { { Name: "webhookauthenticators", SingularName: "webhookauthenticator", @@ -182,7 +196,7 @@ func TestGetAPIResourceList(t *testing.T) { testedGroups[tt.group.Name] = true } for _, g := range groups { - if !strings.Contains(g.Name, "pinniped.dev") { + if !strings.Contains(g.Name, env.APIGroupSuffix) { continue } assert.Truef(t, testedGroups[g.Name], "expected group %q to have assertions defined", g.Name) @@ -192,7 +206,7 @@ func TestGetAPIResourceList(t *testing.T) { t.Run("every API categorized appropriately", func(t *testing.T) { t.Parallel() for _, r := range resources { - if !strings.Contains(r.GroupVersion, "pinniped.dev") { + if !strings.Contains(r.GroupVersion, env.APIGroupSuffix) { continue } for _, a := range r.APIResources { @@ -208,7 +222,7 @@ func TestGetAPIResourceList(t *testing.T) { t.Run("Pinniped resources do not have short names", func(t *testing.T) { t.Parallel() for _, r := range resources { - if !strings.Contains(r.GroupVersion, "pinniped.dev") { + if !strings.Contains(r.GroupVersion, env.APIGroupSuffix) { continue } for _, a := range r.APIResources { diff --git a/test/integration/kubeclient_test.go b/test/integration/kubeclient_test.go index f65026ba..f225df36 100644 --- a/test/integration/kubeclient_test.go +++ b/test/integration/kubeclient_test.go @@ -69,6 +69,7 @@ func TestKubeClientOwnerRef(t *testing.T) { Name: parentSecret.Name, UID: parentSecret.UID, } + _ = env.APIGroupSuffix // TODO: wire API group into kubeclient. ownerRefClient, err := kubeclient.New( kubeclient.WithMiddleware(ownerref.New(ref)), kubeclient.WithConfig(library.NewClientConfig(t)), diff --git a/test/library/client.go b/test/library/client.go index 430d48e1..d432f919 100644 --- a/test/library/client.go +++ b/test/library/client.go @@ -31,6 +31,7 @@ import ( idpv1alpha1 "go.pinniped.dev/generated/1.20/apis/supervisor/idp/v1alpha1" conciergeclientset "go.pinniped.dev/generated/1.20/client/concierge/clientset/versioned" supervisorclientset "go.pinniped.dev/generated/1.20/client/supervisor/clientset/versioned" + "go.pinniped.dev/internal/kubeclient" // Import to initialize client auth plugins - the kubeconfig that we use for // testing may use gcloud, az, oidc, etc. @@ -76,19 +77,19 @@ func NewClientsetWithCertAndKey(t *testing.T, clientCertificateData, clientKeyDa func NewSupervisorClientset(t *testing.T) supervisorclientset.Interface { t.Helper() - return supervisorclientset.NewForConfigOrDie(NewClientConfig(t)) + return newKubeclient(t, NewClientConfig(t)).PinnipedSupervisor } func NewConciergeClientset(t *testing.T) conciergeclientset.Interface { t.Helper() - return conciergeclientset.NewForConfigOrDie(NewClientConfig(t)) + return newKubeclient(t, NewClientConfig(t)).PinnipedConcierge } func NewAnonymousConciergeClientset(t *testing.T) conciergeclientset.Interface { t.Helper() - return conciergeclientset.NewForConfigOrDie(newAnonymousClientRestConfig(t)) + return newKubeclient(t, newAnonymousClientRestConfig(t)).PinnipedConcierge } func NewAggregatedClientset(t *testing.T) aggregatorclient.Interface { @@ -132,6 +133,14 @@ func newAnonymousClientRestConfigWithCertAndKeyAdded(t *testing.T, clientCertifi return config } +func newKubeclient(t *testing.T, config *rest.Config) *kubeclient.Client { + t.Helper() + _ = IntegrationEnv(t).APIGroupSuffix // TODO: wire API group into kubeclient. + client, err := kubeclient.New(kubeclient.WithConfig(config)) + require.NoError(t, err) + return client +} + // CreateTestWebhookAuthenticator creates and returns a test WebhookAuthenticator in $PINNIPED_TEST_CONCIERGE_NAMESPACE, which will be // automatically deleted at the end of the current test's lifetime. It returns a corev1.TypedLocalObjectReference which // describes the test webhook authenticator within the test namespace. diff --git a/test/library/env.go b/test/library/env.go index 0fd61789..8fa53a93 100644 --- a/test/library/env.go +++ b/test/library/env.go @@ -38,6 +38,7 @@ type TestEnv struct { SupervisorHTTPSIngressAddress string `json:"supervisorHttpsIngressAddress"` SupervisorHTTPSIngressCABundle string `json:"supervisorHttpsIngressCABundle"` Proxy string `json:"proxy"` + APIGroupSuffix string `json:"apiGroupSuffix"` TestUser struct { Token string `json:"token"` @@ -106,6 +107,14 @@ func needEnv(t *testing.T, key string) string { return value } +func wantEnv(key, dephault string) string { + value, ok := os.LookupEnv(key) + if !ok { + return dephault + } + return value +} + func filterEmpty(ss []string) []string { filtered := []string{} for _, s := range ss { @@ -154,6 +163,7 @@ func loadEnvVars(t *testing.T, result *TestEnv) { result.SupervisorCustomLabels = supervisorCustomLabels require.NotEmpty(t, result.SupervisorCustomLabels, "PINNIPED_TEST_SUPERVISOR_CUSTOM_LABELS cannot be empty") result.Proxy = os.Getenv("PINNIPED_TEST_PROXY") + result.APIGroupSuffix = wantEnv("PINNIPED_TEST_API_GROUP_SUFFIX", "pinniped.dev") result.CLITestUpstream = TestOIDCUpstream{ Issuer: needEnv(t, "PINNIPED_TEST_CLI_OIDC_ISSUER"),