Skip to content

Commit

Permalink
Update webhook status integration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
benjaminapetersen committed Mar 13, 2024
1 parent b90c54c commit 8148985
Show file tree
Hide file tree
Showing 10 changed files with 445 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,24 @@ import (
)

const (
controllerName = "webhookcachefiller-controller"
typeReady = "Ready"
typeTLSBundleValid = "TLSBundleValid"
typeTLSConnetionNegotiationValid = "TLSConnetionNegotiationValid"
typeEndpointURLValid = "EndpointURLValid"
typeAuthenticatorValid = "AuthenticatorValid"
reasonSuccess = "Success"
reasonNotReady = "NotReady"
reasonUnableToValidate = "UnableToValidate"
reasonUnableToCreateTempFile = "UnableToCreateTempFile"
reasonUnableToMarshallKubeconfig = "UnableToMarshallKubeconfig"
reasonUnableToLoadKubeconfig = "UnableToLoadKubeconfig"
reasonUnableToInstantiateWebhook = "UnableToInstantiateWebhook"
reasonInvalidTLSConfiguration = "InvalidTLSConfiguration"
reasonInvalidEndpointURL = "InvalidEndpointURL"
reasonInvalidEndpointURLScheme = "InvalidEndpointURLScheme"
reasonUnableToDialServer = "UnableToDialServer"
msgUnableToValidate = "unable to validate; see other conditions for details"
controllerName = "webhookcachefiller-controller"
typeReady = "Ready"
typeTLSConfigurationValid = "TLSConfigurationValid"
typeTLSConnectionNegotiationValid = "TLSConnectionNegotiationValid"
typeEndpointURLValid = "EndpointURLValid"
typeAuthenticatorValid = "AuthenticatorValid"
reasonSuccess = "Success"
reasonNotReady = "NotReady"
reasonUnableToValidate = "UnableToValidate"
reasonUnableToCreateTempFile = "UnableToCreateTempFile"
reasonUnableToMarshallKubeconfig = "UnableToMarshallKubeconfig"
reasonUnableToLoadKubeconfig = "UnableToLoadKubeconfig"
reasonUnableToInstantiateWebhook = "UnableToInstantiateWebhook"
reasonInvalidTLSConfiguration = "InvalidTLSConfiguration"
reasonInvalidEndpointURL = "InvalidEndpointURL"
reasonInvalidEndpointURLScheme = "InvalidEndpointURLScheme"
reasonUnableToDialServer = "UnableToDialServer"
msgUnableToValidate = "unable to validate; see other conditions for details"
)

// New instantiates a new controllerlib.Controller which will populate the provided authncache.Cache.
Expand Down Expand Up @@ -281,7 +281,7 @@ func newWebhookAuthenticator(
func (c *webhookCacheFillerController) validateTLSNegotiation(certPool *x509.CertPool, endpointURL *url.URL, conditions []*metav1.Condition, prereqOk bool) ([]*metav1.Condition, error) {
if !prereqOk {
conditions = append(conditions, &metav1.Condition{
Type: typeTLSConnetionNegotiationValid,
Type: typeTLSConnectionNegotiationValid,
Status: metav1.ConditionUnknown,
Reason: reasonUnableToValidate,
Message: msgUnableToValidate,
Expand All @@ -307,7 +307,7 @@ func (c *webhookCacheFillerController) validateTLSNegotiation(certPool *x509.Cer
errText := "cannot dial server"
msg := fmt.Sprintf("%s: %s", errText, dialErr.Error())
conditions = append(conditions, &metav1.Condition{
Type: typeTLSConnetionNegotiationValid,
Type: typeTLSConnectionNegotiationValid,
Status: metav1.ConditionFalse,
Reason: reasonUnableToDialServer,
Message: msg,
Expand All @@ -322,7 +322,7 @@ func (c *webhookCacheFillerController) validateTLSNegotiation(certPool *x509.Cer
}

Check warning on line 322 in internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go

View check run for this annotation

Codecov / codecov/patch

internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go#L321-L322

Added lines #L321 - L322 were not covered by tests

conditions = append(conditions, &metav1.Condition{
Type: typeTLSConnetionNegotiationValid,
Type: typeTLSConnectionNegotiationValid,
Status: metav1.ConditionTrue,
Reason: reasonSuccess,
Message: "tls verified",
Expand All @@ -335,7 +335,7 @@ func (c *webhookCacheFillerController) validateTLSBundle(tlsSpec *auth1alpha1.TL
if err != nil {
msg := fmt.Sprintf("%s: %s", "invalid TLS configuration", err.Error())
conditions = append(conditions, &metav1.Condition{
Type: typeTLSBundleValid,
Type: typeTLSConfigurationValid,
Status: metav1.ConditionFalse,
Reason: reasonInvalidTLSConfiguration,
Message: msg,
Expand All @@ -347,7 +347,7 @@ func (c *webhookCacheFillerController) validateTLSBundle(tlsSpec *auth1alpha1.TL
msg = "no CA bundle specified"
}
conditions = append(conditions, &metav1.Condition{
Type: typeTLSBundleValid,
Type: typeTLSConfigurationValid,
Status: metav1.ConditionTrue,
Reason: reasonSuccess,
Message: msg,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,29 +240,29 @@ func TestController(t *testing.T) {
}
}

happyTLSBundleValidCAParsed := func(time metav1.Time, observedGeneration int64) metav1.Condition {
happyTLSConfigurationValidCAParsed := func(time metav1.Time, observedGeneration int64) metav1.Condition {
return metav1.Condition{
Type: "TLSBundleValid",
Type: "TLSConfigurationValid",
Status: "True",
ObservedGeneration: observedGeneration,
LastTransitionTime: time,
Reason: "Success",
Message: "successfully parsed specified CA bundle",
}
}
happyTLSBundleValidNoCA := func(time metav1.Time, observedGeneration int64) metav1.Condition {
happyTLSConfigurationValidNoCA := func(time metav1.Time, observedGeneration int64) metav1.Condition {
return metav1.Condition{
Type: "TLSBundleValid",
Type: "TLSConfigurationValid",
Status: "True",
ObservedGeneration: observedGeneration,
LastTransitionTime: time,
Reason: "Success",
Message: "no CA bundle specified",
}
}
sadTLSBundleValid := func(time metav1.Time, observedGeneration int64) metav1.Condition {
sadTLSConfigurationValid := func(time metav1.Time, observedGeneration int64) metav1.Condition {
return metav1.Condition{
Type: "TLSBundleValid",
Type: "TLSConfigurationValid",
Status: "False",
ObservedGeneration: observedGeneration,
LastTransitionTime: time,
Expand All @@ -271,39 +271,39 @@ func TestController(t *testing.T) {
}
}

happyTLSConnetionNegotiationValid := func(time metav1.Time, observedGeneration int64) metav1.Condition {
happyTLSConnectionNegotiationValid := func(time metav1.Time, observedGeneration int64) metav1.Condition {
return metav1.Condition{
Type: "TLSConnetionNegotiationValid",
Type: "TLSConnectionNegotiationValid",
Status: "True",
ObservedGeneration: observedGeneration,
LastTransitionTime: time,
Reason: "Success",
Message: "tls verified",
}
}
unknownTLSConnetionNegotiationValid := func(time metav1.Time, observedGeneration int64) metav1.Condition {
unknownTLSConnectionNegotiationValid := func(time metav1.Time, observedGeneration int64) metav1.Condition {
return metav1.Condition{
Type: "TLSConnetionNegotiationValid",
Type: "TLSConnectionNegotiationValid",
Status: "Unknown",
ObservedGeneration: observedGeneration,
LastTransitionTime: time,
Reason: "UnableToValidate",
Message: "unable to validate; see other conditions for details",
}
}
sadTLSConnetionNegotiationValid := func(time metav1.Time, observedGeneration int64) metav1.Condition {
sadTLSConnectionNegotiationValid := func(time metav1.Time, observedGeneration int64) metav1.Condition {
return metav1.Condition{
Type: "TLSConnetionNegotiationValid",
Type: "TLSConnectionNegotiationValid",
Status: "False",
ObservedGeneration: observedGeneration,
LastTransitionTime: time,
Reason: "UnableToDialServer",
Message: "cannot dial server: tls: failed to verify certificate: x509: certificate signed by unknown authority",
}
}
sadTLSConnetionNegotiationNoIPSANs := func(time metav1.Time, observedGeneration int64) metav1.Condition {
sadTLSConnectionNegotiationNoIPSANs := func(time metav1.Time, observedGeneration int64) metav1.Condition {
return metav1.Condition{
Type: "TLSConnetionNegotiationValid",
Type: "TLSConnectionNegotiationValid",
Status: "False",
ObservedGeneration: observedGeneration,
LastTransitionTime: time,
Expand Down Expand Up @@ -345,9 +345,9 @@ func TestController(t *testing.T) {

allHappyConditionsSuccess := func(endpoint string, someTime metav1.Time, observedGeneration int64) []metav1.Condition {
return conditionstestutil.SortByType([]metav1.Condition{
happyTLSBundleValidCAParsed(someTime, observedGeneration),
happyTLSConfigurationValidCAParsed(someTime, observedGeneration),
happyEndpointURLValid(someTime, observedGeneration),
happyTLSConnetionNegotiationValid(someTime, observedGeneration),
happyTLSConnectionNegotiationValid(someTime, observedGeneration),
happyAuthenticatorValid(someTime, observedGeneration),
happyReadyCondition(someTime, observedGeneration),
})
Expand Down Expand Up @@ -551,8 +551,8 @@ func TestController(t *testing.T) {
Conditions: conditionstestutil.Replace(
allHappyConditionsSuccess(goodEndpoint, frozenMetav1Now, 0),
[]metav1.Condition{
happyTLSBundleValidNoCA(frozenMetav1Now, 0),
sadTLSConnetionNegotiationValid(frozenMetav1Now, 0),
happyTLSConfigurationValidNoCA(frozenMetav1Now, 0),
sadTLSConnectionNegotiationValid(frozenMetav1Now, 0),
sadReadyCondition(frozenMetav1Now, 0),
unknownAuthenticatorValid(frozenMetav1Now, 0),
},
Expand Down Expand Up @@ -591,8 +591,8 @@ func TestController(t *testing.T) {
Conditions: conditionstestutil.Replace(
allHappyConditionsSuccess(goodEndpoint, frozenMetav1Now, 0),
[]metav1.Condition{
sadTLSBundleValid(frozenMetav1Now, 0),
unknownTLSConnetionNegotiationValid(frozenMetav1Now, 0),
sadTLSConfigurationValid(frozenMetav1Now, 0),
unknownTLSConnectionNegotiationValid(frozenMetav1Now, 0),
unknownAuthenticatorValid(frozenMetav1Now, 0),
sadReadyCondition(frozenMetav1Now, 0),
},
Expand Down Expand Up @@ -634,9 +634,9 @@ func TestController(t *testing.T) {
Conditions: conditionstestutil.Replace(
allHappyConditionsSuccess(goodEndpoint, frozenMetav1Now, 0),
[]metav1.Condition{
happyTLSBundleValidNoCA(frozenMetav1Now, 0),
happyTLSConfigurationValidNoCA(frozenMetav1Now, 0),
sadEndpointURLValid("https://.café .com/café/café/café/coffee", frozenMetav1Now, 0),
unknownTLSConnetionNegotiationValid(frozenMetav1Now, 0),
unknownTLSConnectionNegotiationValid(frozenMetav1Now, 0),
unknownAuthenticatorValid(frozenMetav1Now, 0),
sadReadyCondition(frozenMetav1Now, 0),
},
Expand Down Expand Up @@ -677,9 +677,9 @@ func TestController(t *testing.T) {
Conditions: conditionstestutil.Replace(
allHappyConditionsSuccess(goodEndpoint, frozenMetav1Now, 0),
[]metav1.Condition{
happyTLSBundleValidNoCA(frozenMetav1Now, 0),
happyTLSConfigurationValidNoCA(frozenMetav1Now, 0),
sadEndpointURLValidHTTPS("http://localhost", frozenMetav1Now, 0),
unknownTLSConnetionNegotiationValid(frozenMetav1Now, 0),
unknownTLSConnectionNegotiationValid(frozenMetav1Now, 0),
unknownAuthenticatorValid(frozenMetav1Now, 0),
sadReadyCondition(frozenMetav1Now, 0),
},
Expand Down Expand Up @@ -720,7 +720,7 @@ func TestController(t *testing.T) {
[]metav1.Condition{
unknownAuthenticatorValid(frozenMetav1Now, 0),
sadReadyCondition(frozenMetav1Now, 0),
sadTLSConnetionNegotiationValid(frozenMetav1Now, 0),
sadTLSConnectionNegotiationValid(frozenMetav1Now, 0),
},
),
Phase: "Error",
Expand Down Expand Up @@ -878,7 +878,7 @@ func TestController(t *testing.T) {
Conditions: conditionstestutil.Replace(
allHappyConditionsSuccess(localWithExampleDotComCertServer.URL, frozenMetav1Now, 0),
[]metav1.Condition{
sadTLSConnetionNegotiationNoIPSANs(frozenMetav1Now, 0),
sadTLSConnectionNegotiationNoIPSANs(frozenMetav1Now, 0),
unknownAuthenticatorValid(frozenMetav1Now, 0),
sadReadyCondition(frozenMetav1Now, 0),
},
Expand Down
28 changes: 28 additions & 0 deletions internal/testutil/conciergetestutil/tlstestutil.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package conciergetestutil

import (
"crypto/tls"
"encoding/base64"
"encoding/pem"

auth1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
)

func TlsSpecFromTLSConfig(tls *tls.Config) *auth1alpha1.TLSSpec {
pemData := make([]byte, 0)
for _, certificate := range tls.Certificates {
// this is the public part of the certificate, the private is the certificate.PrivateKey
for _, reallyCertificate := range certificate.Certificate {
pemData = append(pemData, pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: reallyCertificate,
})...)
}
}
return &auth1alpha1.TLSSpec{
CertificateAuthorityData: base64.StdEncoding.EncodeToString(pemData),
}
}
5 changes: 3 additions & 2 deletions test/integration/cli_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package integration

Expand Down Expand Up @@ -26,6 +26,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/serializer"
clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"

"go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
identityv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/identity/v1alpha1"
conciergescheme "go.pinniped.dev/internal/concierge/scheme"
"go.pinniped.dev/pkg/oidcclient"
Expand All @@ -42,7 +43,7 @@ func TestCLIGetKubeconfigStaticToken_Parallel(t *testing.T) {
ctx, cancelFunc := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancelFunc()

authenticator := testlib.CreateTestWebhookAuthenticator(ctx, t)
authenticator := testlib.CreateTestWebhookAuthenticator(ctx, t, nil, v1alpha1.WebhookAuthenticatorPhaseReady)

// Build pinniped CLI.
pinnipedExe := testlib.PinnipedCLIPath(t)
Expand Down
5 changes: 3 additions & 2 deletions test/integration/concierge_api_serving_certs_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

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

"go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
loginv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/login/v1alpha1"
"go.pinniped.dev/internal/testutil"
"go.pinniped.dev/test/testlib"
Expand Down Expand Up @@ -83,7 +84,7 @@ func TestAPIServingCertificateAutoCreationAndRotation_Disruptive(t *testing.T) {

// Create a testWebhook so we have a legitimate authenticator to pass to the
// TokenCredentialRequest API.
testWebhook := testlib.CreateTestWebhookAuthenticator(ctx, t)
testWebhook := testlib.CreateTestWebhookAuthenticator(ctx, t, nil, v1alpha1.WebhookAuthenticatorPhaseReady)

// Get the initial auto-generated version of the Secret.
secret, err := kubeClient.CoreV1().Secrets(env.ConciergeNamespace).Get(ctx, defaultServingCertResourceName, metav1.GetOptions{})
Expand Down
5 changes: 3 additions & 2 deletions test/integration/concierge_client_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package integration
Expand All @@ -11,6 +11,7 @@ import (

"github.com/stretchr/testify/require"

"go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
"go.pinniped.dev/internal/here"
"go.pinniped.dev/pkg/conciergeclient"
"go.pinniped.dev/test/testlib"
Expand Down Expand Up @@ -58,7 +59,7 @@ func TestClient(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()

webhook := testlib.CreateTestWebhookAuthenticator(ctx, t)
webhook := testlib.CreateTestWebhookAuthenticator(ctx, t, nil, v1alpha1.WebhookAuthenticatorPhaseReady)

// Use an invalid certificate/key to validate that the ServerVersion API fails like we assume.
invalidClient := testlib.NewClientsetWithCertAndKey(t, testCert, testKey)
Expand Down
15 changes: 11 additions & 4 deletions test/integration/concierge_credentialrequest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,15 @@ func TestSuccessfulCredentialRequest_Browser(t *testing.T) {
token func(t *testing.T) (token string, username string, groups []string)
}{
{
name: "webhook",
authenticator: testlib.CreateTestWebhookAuthenticator,
name: "webhook",
authenticator: func(ctx context.Context, t *testing.T) corev1.TypedLocalObjectReference {
authenticator := testlib.CreateTestWebhookAuthenticator(ctx, t, nil, auth1alpha1.WebhookAuthenticatorPhaseReady)
return corev1.TypedLocalObjectReference{
APIGroup: &auth1alpha1.SchemeGroupVersion.Group,
Kind: "WebhookAuthenticator",
Name: authenticator.Name,
}
},
token: func(t *testing.T) (string, string, []string) {
return testlib.IntegrationEnv(t).TestUser.Token, env.TestUser.ExpectedUsername, env.TestUser.ExpectedGroups
},
Expand Down Expand Up @@ -148,7 +155,7 @@ func TestFailedCredentialRequestWhenTheRequestIsValidButTheTokenDoesNotAuthentic
// TokenCredentialRequest API.
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
testWebhook := testlib.CreateTestWebhookAuthenticator(ctx, t)
testWebhook := testlib.CreateTestWebhookAuthenticator(ctx, t, nil, auth1alpha1.WebhookAuthenticatorPhaseReady)

response, err := testlib.CreateTokenCredentialRequest(context.Background(), t,
loginv1alpha1.TokenCredentialRequestSpec{Token: "not a good token", Authenticator: testWebhook},
Expand All @@ -169,7 +176,7 @@ func TestCredentialRequest_ShouldFailWhenRequestDoesNotIncludeToken_Parallel(t *
// TokenCredentialRequest API.
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
testWebhook := testlib.CreateTestWebhookAuthenticator(ctx, t)
testWebhook := testlib.CreateTestWebhookAuthenticator(ctx, t, nil, auth1alpha1.WebhookAuthenticatorPhaseReady)

response, err := testlib.CreateTokenCredentialRequest(context.Background(), t,
loginv1alpha1.TokenCredentialRequestSpec{Token: "", Authenticator: testWebhook},
Expand Down
3 changes: 2 additions & 1 deletion test/integration/concierge_impersonation_proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import (
"k8s.io/client-go/util/retry"
"k8s.io/utils/ptr"

"go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
conciergev1alpha "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
identityv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/identity/v1alpha1"
loginv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/login/v1alpha1"
Expand Down Expand Up @@ -120,7 +121,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl
// Create a WebhookAuthenticator and prepare a TokenCredentialRequestSpec using the authenticator for use later.
credentialRequestSpecWithWorkingCredentials := loginv1alpha1.TokenCredentialRequestSpec{
Token: env.TestUser.Token,
Authenticator: testlib.CreateTestWebhookAuthenticator(ctx, t),
Authenticator: testlib.CreateTestWebhookAuthenticator(ctx, t, nil, v1alpha1.WebhookAuthenticatorPhaseReady),
}

// The address of the ClusterIP service that points at the impersonation proxy's port (used when there is no load balancer).
Expand Down
Loading

0 comments on commit 8148985

Please sign in to comment.