Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# SPDX-License-Identifier: Apache-2.0

ARG BUILD_IMAGE=golang:1.27.3@sha256:f20d83a83e18dbd2a4714b8ffd4b30b287b6243962dd75608e86abcc63ba4efc
ARG BASE_IMAGE=gcr.io/distroless/static:nonroot@sha256:d62067879de890a1def9da0e7766b7d20810a5e5555cef8506b752437d7a3e88
ARG BASE_IMAGE=gcr.io/distroless/static:nonroot@sha256:18f4cc26c9ffa7f20a359298be7deadac5c607c8e03a02b8119f1a5fc2f82c99

# Prepare to cross-compile by always running the build stage in the build platform, not the target platform.
FROM --platform=$BUILDPLATFORM $BUILD_IMAGE AS build-env
Expand Down
2 changes: 1 addition & 1 deletion hack/Dockerfile_fips
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
# and https://kupczynski.info/posts/fips-golang/ for details.

ARG BUILD_IMAGE=golang:1.27.3@sha256:f20d83a83e18dbd2a4714b8ffd4b30b287b6243962dd75608e86abcc63ba4efc
ARG BASE_IMAGE=gcr.io/distroless/static:nonroot@sha256:d62067879de890a1def9da0e7766b7d20810a5e5555cef8506b752437d7a3e88
ARG BASE_IMAGE=gcr.io/distroless/static:nonroot@sha256:18f4cc26c9ffa7f20a359298be7deadac5c607c8e03a02b8119f1a5fc2f82c99

# This is not currently using --platform to prepare to cross-compile because we use gcc below to build
# platform-specific GCO code. This makes multi-arch builds slow due to target platform emulation.
Expand Down
18 changes: 14 additions & 4 deletions test/integration/concierge_jwtauthenticator_status_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2024-2025 the Pinniped contributors. All Rights Reserved.
// Copyright 2024-2026 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package integration
Expand All @@ -16,6 +16,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

authenticationv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
"go.pinniped.dev/internal/testutil"
"go.pinniped.dev/test/testlib"
)

Expand Down Expand Up @@ -637,16 +638,18 @@ func TestConciergeJWTAuthenticatorStatus_Parallel(t *testing.T) {

func TestConciergeJWTAuthenticatorCRDValidations_Parallel(t *testing.T) {
env := testlib.IntegrationEnv(t)
kubeIs38OrNewer := testutil.KubeServerMinorVersionAtLeastInclusive(t, testlib.NewKubernetesClientset(t).Discovery(), 38)
jwtAuthenticatorClient := testlib.NewConciergeClientset(t).AuthenticationV1alpha1().JWTAuthenticators()

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
t.Cleanup(cancel)

objectMeta := testlib.ObjectMetaWithRandomName(t, "jwt-authenticator")
tests := []struct {
name string
jwtAuthenticator *authenticationv1alpha1.JWTAuthenticator
wantErr string
name string
jwtAuthenticator *authenticationv1alpha1.JWTAuthenticator
wantErr string
wantKube38OrNewerErr string
}{
{
name: "issuer can not be empty string",
Expand All @@ -659,6 +662,8 @@ func TestConciergeJWTAuthenticatorCRDValidations_Parallel(t *testing.T) {
},
wantErr: `JWTAuthenticator.authentication.concierge.` + env.APIGroupSuffix + ` "` + objectMeta.Name + `" is invalid: ` +
`spec.issuer: Invalid value: "": spec.issuer in body should be at least 1 chars long`,
wantKube38OrNewerErr: `JWTAuthenticator.authentication.concierge.` + env.APIGroupSuffix + ` "` + objectMeta.Name + `" is invalid: ` +
`spec.issuer: Too short: must be at least 1 character`,
},
{
name: "audience can not be empty string",
Expand All @@ -671,6 +676,8 @@ func TestConciergeJWTAuthenticatorCRDValidations_Parallel(t *testing.T) {
},
wantErr: `JWTAuthenticator.authentication.concierge.` + env.APIGroupSuffix + ` "` + objectMeta.Name + `" is invalid: ` +
`spec.audience: Invalid value: "": spec.audience in body should be at least 1 chars long`,
wantKube38OrNewerErr: `JWTAuthenticator.authentication.concierge.` + env.APIGroupSuffix + ` "` + objectMeta.Name + `" is invalid: ` +

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a merge-blocker, just nit/observation:
I would have preferred a string formatter, interpolator instead.
but since this is a test, that's fine I think.

`spec.audience: Too short: must be at least 1 character`,
},
{
name: "issuer must be https",
Expand Down Expand Up @@ -768,6 +775,9 @@ func TestConciergeJWTAuthenticatorCRDValidations_Parallel(t *testing.T) {

if tt.wantErr != "" {
wantErr := tt.wantErr
if kubeIs38OrNewer && tt.wantKube38OrNewerErr != "" {
wantErr = tt.wantKube38OrNewerErr
}
require.EqualError(t, createErr, wantErr)
} else {
require.NoError(t, createErr)
Expand Down
29 changes: 20 additions & 9 deletions test/integration/concierge_tls_spec_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2024-2025 the Pinniped contributors. All Rights Reserved.
// Copyright 2024-2026 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package integration

Expand All @@ -21,6 +21,7 @@ import (

"go.pinniped.dev/internal/certauthority"
"go.pinniped.dev/internal/here"
"go.pinniped.dev/internal/testutil"
"go.pinniped.dev/test/testlib"
)

Expand All @@ -29,6 +30,8 @@ import (
func TestTLSSpecValidationConcierge_Parallel(t *testing.T) {
env := testlib.IntegrationEnv(t)

kubeIs38OrNewer := testutil.KubeServerMinorVersionAtLeastInclusive(t, testlib.NewKubernetesClientset(t).Discovery(), 38)

ca, err := certauthority.New("pinniped-test", 24*time.Hour)
require.NoError(t, err)
indentedCAPEM := indentForHeredoc(string(ca.Bundle()))
Expand Down Expand Up @@ -63,8 +66,9 @@ func TestTLSSpecValidationConcierge_Parallel(t *testing.T) {
secretType string
secretOrConfigmapDataYAML string

wantErrorSnippets []string
wantTLSValidConditionMessage func(namespace string, secretOrConfigmapName string) string
wantErrorSnippets []string
wantKube38OrNewerErrorSnippets []string
wantTLSValidConditionMessage func(namespace string, secretOrConfigmapName string) string
}{
{
name: "should disallow certificate authority data source with missing name",
Expand All @@ -89,7 +93,8 @@ func TestTLSSpecValidationConcierge_Parallel(t *testing.T) {
key: bar
`)
},
wantErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Invalid value: "": spec.tls.certificateAuthorityDataSource.name in body should be at least 1 chars long`},
wantErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Invalid value: "": spec.tls.certificateAuthorityDataSource.name in body should be at least 1 chars long`},
wantKube38OrNewerErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Too short: must be at least 1 character`},
},
{
name: "should disallow certificate authority data source with missing key",
Expand All @@ -114,7 +119,8 @@ func TestTLSSpecValidationConcierge_Parallel(t *testing.T) {
key: ""
`)
},
wantErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Invalid value: "": spec.tls.certificateAuthorityDataSource.key in body should be at least 1 chars long`},
wantErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Invalid value: "": spec.tls.certificateAuthorityDataSource.key in body should be at least 1 chars long`},
wantKube38OrNewerErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Too short: must be at least 1 character`},
},
{
name: "should disallow certificate authority data source with missing kind",
Expand Down Expand Up @@ -472,6 +478,11 @@ func TestTLSSpecValidationConcierge_Parallel(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

wantErrorSnippets := tc.wantErrorSnippets
if kubeIs38OrNewer && len(tc.wantKube38OrNewerErrorSnippets) > 0 {
wantErrorSnippets = tc.wantKube38OrNewerErrorSnippets
}

t.Run("apply webhook authenticator", func(t *testing.T) {
resourceName := "test-webhook-authenticator-" + testlib.RandHex(t, 7)

Expand All @@ -490,12 +501,12 @@ func TestTLSSpecValidationConcierge_Parallel(t *testing.T) {
stdOut, stdErr, err := performKubectlApply(t, resourceName, yamlBytes)
requireKubectlApplyResult(t, stdOut, stdErr, err,
fmt.Sprintf(`webhookauthenticator.authentication.concierge.%s`, env.APIGroupSuffix),
tc.wantErrorSnippets,
wantErrorSnippets,
"WebhookAuthenticator",
resourceName,
)

if tc.wantErrorSnippets == nil {
if wantErrorSnippets == nil {
requireTLSValidConditionMessageOnResource(t,
resourceName,
env.ConciergeNamespace,
Expand Down Expand Up @@ -525,12 +536,12 @@ func TestTLSSpecValidationConcierge_Parallel(t *testing.T) {
stdOut, stdErr, err := performKubectlApply(t, resourceName, yamlBytes)
requireKubectlApplyResult(t, stdOut, stdErr, err,
fmt.Sprintf(`jwtauthenticator.authentication.concierge.%s`, env.APIGroupSuffix),
tc.wantErrorSnippets,
wantErrorSnippets,
"JWTAuthenticator",
resourceName,
)

if tc.wantErrorSnippets == nil {
if wantErrorSnippets == nil {
requireTLSValidConditionMessageOnResource(t,
resourceName,
env.ConciergeNamespace,
Expand Down
10 changes: 9 additions & 1 deletion test/integration/concierge_webhookauthenticator_status_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2024-2026 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package integration
Expand All @@ -16,6 +16,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

authenticationv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
"go.pinniped.dev/internal/testutil"
"go.pinniped.dev/test/testlib"
)

Expand Down Expand Up @@ -313,6 +314,7 @@ func TestConciergeWebhookAuthenticatorStatus_Parallel(t *testing.T) {

func TestConciergeWebhookAuthenticatorCRDValidations_Parallel(t *testing.T) {
env := testlib.IntegrationEnv(t)
kubeIs38OrNewer := testutil.KubeServerMinorVersionAtLeastInclusive(t, testlib.NewKubernetesClientset(t).Discovery(), 38)
webhookAuthenticatorClient := testlib.NewConciergeClientset(t).AuthenticationV1alpha1().WebhookAuthenticators()

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
Expand All @@ -323,6 +325,7 @@ func TestConciergeWebhookAuthenticatorCRDValidations_Parallel(t *testing.T) {
name string
webhookAuthenticator *authenticationv1alpha1.WebhookAuthenticator
wantErr string
wantKube38OrNewerErr string
}{
{
name: "endpoint can not be empty string",
Expand All @@ -334,6 +337,8 @@ func TestConciergeWebhookAuthenticatorCRDValidations_Parallel(t *testing.T) {
},
wantErr: `WebhookAuthenticator.authentication.concierge.` + env.APIGroupSuffix + ` "` + objectMeta.Name + `" is invalid: ` +
`spec.endpoint: Invalid value: "": spec.endpoint in body should be at least 1 chars long`,
wantKube38OrNewerErr: `WebhookAuthenticator.authentication.concierge.` + env.APIGroupSuffix + ` "` + objectMeta.Name + `" is invalid: ` +
`spec.endpoint: Too short: must be at least 1 character`,
},
{
name: "endpoint must be https",
Expand Down Expand Up @@ -407,6 +412,9 @@ func TestConciergeWebhookAuthenticatorCRDValidations_Parallel(t *testing.T) {

if tt.wantErr != "" {
wantErr := tt.wantErr
if kubeIs38OrNewer && tt.wantKube38OrNewerErr != "" {
wantErr = tt.wantKube38OrNewerErr
}
require.EqualError(t, createErr, wantErr)
} else {
require.NoError(t, createErr)
Expand Down
20 changes: 15 additions & 5 deletions test/integration/supervisor_federationdomain_status_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023-2025 the Pinniped contributors. All Rights Reserved.
// Copyright 2023-2026 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package integration
Expand Down Expand Up @@ -582,6 +582,7 @@ func TestSupervisorFederationDomainCRDValidations_Parallel(t *testing.T) {
wantKube24Through31InclusiveErrs []string
wantKube32Through33InclusiveErrs []string
wantKube34OrNewerErrs []string
wantKube38OrNewerErrs []string

// These errors are appended to any other wanted errors when k8sAPIServerSupportsCEL is true
wantCELErrorsForKube25Through28Inclusive []string
Expand All @@ -596,6 +597,7 @@ func TestSupervisorFederationDomainCRDValidations_Parallel(t *testing.T) {
Issuer: "",
},
wantErrs: []string{`spec.issuer: Invalid value: "": spec.issuer in body should be at least 1 chars long`},
wantKube38OrNewerErrs: []string{`spec.issuer: Too short: must be at least 1 character`},
wantCELErrorsForKube25Through28Inclusive: []string{`spec.issuer: Invalid value: "string": issuer must be an HTTPS URL`},
wantCELErrorsForKube29Through34Inclusive: []string{`spec.issuer: Invalid value: "string": issuer must be an HTTPS URL`},
wantCELErrorsForKube35OrNewer: []string{`spec.issuer: Invalid value: "": issuer must be an HTTPS URL`},
Expand Down Expand Up @@ -631,7 +633,8 @@ func TestSupervisorFederationDomainCRDValidations_Parallel(t *testing.T) {
},
},
},
wantErrs: []string{`spec.identityProviders[0].displayName: Invalid value: "": spec.identityProviders[0].displayName in body should be at least 1 chars long`},
wantErrs: []string{`spec.identityProviders[0].displayName: Invalid value: "": spec.identityProviders[0].displayName in body should be at least 1 chars long`},
wantKube38OrNewerErrs: []string{`spec.identityProviders[0].displayName: Too short: must be at least 1 character`},
},
{
name: "IDP transform constants must have unique names",
Expand Down Expand Up @@ -675,7 +678,8 @@ func TestSupervisorFederationDomainCRDValidations_Parallel(t *testing.T) {
},
},
},
wantErrs: []string{`spec.identityProviders[0].transforms.constants[0].name: Invalid value: "": spec.identityProviders[0].transforms.constants[0].name in body should be at least 1 chars long`},
wantErrs: []string{`spec.identityProviders[0].transforms.constants[0].name: Invalid value: "": spec.identityProviders[0].transforms.constants[0].name in body should be at least 1 chars long`},
wantKube38OrNewerErrs: []string{`spec.identityProviders[0].transforms.constants[0].name: Too short: must be at least 1 character`},
},
{
name: "IDP transform constant names cannot be more than 64 characters",
Expand Down Expand Up @@ -797,7 +801,8 @@ func TestSupervisorFederationDomainCRDValidations_Parallel(t *testing.T) {
},
},
},
wantErrs: []string{`spec.identityProviders[0].transforms.expressions[0].expression: Invalid value: "": spec.identityProviders[0].transforms.expressions[0].expression in body should be at least 1 chars long`},
wantErrs: []string{`spec.identityProviders[0].transforms.expressions[0].expression: Invalid value: "": spec.identityProviders[0].transforms.expressions[0].expression in body should be at least 1 chars long`},
wantKube38OrNewerErrs: []string{`spec.identityProviders[0].transforms.expressions[0].expression: Too short: must be at least 1 character`},
},
{
name: "IDP transform example usernames cannot be empty",
Expand All @@ -818,7 +823,8 @@ func TestSupervisorFederationDomainCRDValidations_Parallel(t *testing.T) {
},
},
},
wantErrs: []string{`spec.identityProviders[0].transforms.examples[0].username: Invalid value: "": spec.identityProviders[0].transforms.examples[0].username in body should be at least 1 chars long`},
wantErrs: []string{`spec.identityProviders[0].transforms.examples[0].username: Invalid value: "": spec.identityProviders[0].transforms.examples[0].username in body should be at least 1 chars long`},
wantKube38OrNewerErrs: []string{`spec.identityProviders[0].transforms.examples[0].username: Too short: must be at least 1 character`},
},
{
name: "minimum valid",
Expand Down Expand Up @@ -923,6 +929,10 @@ func TestSupervisorFederationDomainCRDValidations_Parallel(t *testing.T) {
// Also allow overriding with an exact expected error for these Kube versions.
wantErr = tt.wantKube34OrNewerErrs
}
if minor >= 38 && len(tt.wantKube38OrNewerErrs) > 0 {
// Also allow overriding with an exact expected error for these Kube versions.
wantErr = tt.wantKube38OrNewerErrs
}

if minor >= 25 && minor <= 28 && len(tt.wantCELErrorsForKube25Through28Inclusive) > 0 {
wantErr = append(wantErr, tt.wantCELErrorsForKube25Through28Inclusive...)
Expand Down
33 changes: 21 additions & 12 deletions test/integration/supervisor_github_idp_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2024-2026 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package integration
Expand Down Expand Up @@ -34,6 +34,7 @@ func TestGitHubIDPStaticValidationOnCreate_Parallel(t *testing.T) {

namespaceClient := adminClient.CoreV1().Namespaces()
skipCELTests := !testutil.KubeServerMinorVersionAtLeastInclusive(t, adminClient.Discovery(), 26)
kubeIs38OrNewer := testutil.KubeServerMinorVersionAtLeastInclusive(t, adminClient.Discovery(), 38)

ns, err := namespaceClient.Create(ctx, &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Expand All @@ -49,11 +50,12 @@ func TestGitHubIDPStaticValidationOnCreate_Parallel(t *testing.T) {
gitHubIDPClient := testlib.NewSupervisorClientset(t).IDPV1alpha1().GitHubIdentityProviders(ns.Name)

tests := []struct {
name string
usesCELValidation bool
inputSpec idpv1alpha1.GitHubIdentityProviderSpec
wantSpec idpv1alpha1.GitHubIdentityProviderSpec
wantErr string
name string
usesCELValidation bool
inputSpec idpv1alpha1.GitHubIdentityProviderSpec
wantSpec idpv1alpha1.GitHubIdentityProviderSpec
wantErr string
wantKube38OrNewerErr string
}{
{
name: "all fields set",
Expand Down Expand Up @@ -190,9 +192,10 @@ func TestGitHubIDPStaticValidationOnCreate_Parallel(t *testing.T) {
wantErr: "spec.allowAuthentication.organizations.policy must be 'AllGitHubUsers' when spec.allowAuthentication.organizations.allowed is empty",
},
{
name: "spec.client.secretName in body should be at least 1 chars long",
inputSpec: idpv1alpha1.GitHubIdentityProviderSpec{},
wantErr: "spec.client.secretName in body should be at least 1 chars long",
name: "spec.client.secretName in body should be at least 1 chars long",
inputSpec: idpv1alpha1.GitHubIdentityProviderSpec{},
wantErr: "spec.client.secretName in body should be at least 1 chars long",
wantKube38OrNewerErr: "spec.client.secretName: Too short: must be at least 1 character",
},
{
name: "spec.githubAPI.host in body should be at least 1 chars long",
Expand All @@ -209,7 +212,8 @@ func TestGitHubIDPStaticValidationOnCreate_Parallel(t *testing.T) {
SecretName: "name-of-a-secret",
},
},
wantErr: "spec.githubAPI.host in body should be at least 1 chars long",
wantErr: "spec.githubAPI.host in body should be at least 1 chars long",
wantKube38OrNewerErr: "spec.githubAPI.host: Too short: must be at least 1 character",
},
{
name: "duplicates not permitted in spec.allowAuthentication.organizations.allowed",
Expand Down Expand Up @@ -245,12 +249,17 @@ func TestGitHubIDPStaticValidationOnCreate_Parallel(t *testing.T) {
Spec: tt.inputSpec,
}

wantErr := tt.wantErr
if kubeIs38OrNewer && tt.wantKube38OrNewerErr != "" {
wantErr = tt.wantKube38OrNewerErr
}

outputGitHubIDP, err := gitHubIDPClient.Create(ctx, input, metav1.CreateOptions{})
if tt.wantErr == "" {
if wantErr == "" {
require.NoError(t, err)
require.Equal(t, tt.wantSpec, outputGitHubIDP.Spec)
} else {
require.ErrorContains(t, err, tt.wantErr)
require.ErrorContains(t, err, wantErr)
}
})
}
Expand Down
Loading
Loading