mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-01-04 20:24:26 +00:00
- WebhookAuthenticator will now detect the proxy setting and skip dialing the connection probe if it should go through a proxy - GitHubIdentityProvider will avoid using tls.Dial altogether by instead making a real request to the GitHub API as its connection probe, because this will respect the proxy settings
580 lines
21 KiB
Go
580 lines
21 KiB
Go
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Package webhookcachefiller implements a controller for filling an authncache.Cache with each added/updated WebhookAuthenticator.
|
|
package webhookcachefiller
|
|
|
|
import (
|
|
"context"
|
|
"crypto/x509"
|
|
"fmt"
|
|
"net/url"
|
|
"reflect"
|
|
"slices"
|
|
"strings"
|
|
"time"
|
|
|
|
k8sauthv1beta1 "k8s.io/api/authentication/v1beta1"
|
|
corev1 "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/api/equality"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/labels"
|
|
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
|
k8snetutil "k8s.io/apimachinery/pkg/util/net"
|
|
"k8s.io/apiserver/pkg/authentication/authenticator"
|
|
"k8s.io/apiserver/plugin/pkg/authenticator/token/webhook"
|
|
corev1informers "k8s.io/client-go/informers/core/v1"
|
|
"k8s.io/client-go/rest"
|
|
"k8s.io/utils/clock"
|
|
|
|
authenticationv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
|
|
conciergeclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned"
|
|
authinformers "go.pinniped.dev/generated/latest/client/concierge/informers/externalversions/authentication/v1alpha1"
|
|
pinnipedcontroller "go.pinniped.dev/internal/controller"
|
|
"go.pinniped.dev/internal/controller/authenticator/authncache"
|
|
"go.pinniped.dev/internal/controller/conditionsutil"
|
|
"go.pinniped.dev/internal/controller/tlsconfigutil"
|
|
"go.pinniped.dev/internal/controllerlib"
|
|
"go.pinniped.dev/internal/crypto/ptls"
|
|
"go.pinniped.dev/internal/endpointaddr"
|
|
"go.pinniped.dev/internal/kubeclient"
|
|
"go.pinniped.dev/internal/plog"
|
|
"go.pinniped.dev/internal/proxydetect"
|
|
)
|
|
|
|
const (
|
|
controllerName = "webhookcachefiller-controller"
|
|
|
|
typeReady = "Ready"
|
|
typeWebhookConnectionValid = "WebhookConnectionValid"
|
|
typeEndpointURLValid = "EndpointURLValid"
|
|
typeAuthenticatorValid = "AuthenticatorValid"
|
|
|
|
reasonUnableToCreateClient = "UnableToCreateClient"
|
|
reasonUnableToInstantiateWebhook = "UnableToInstantiateWebhook"
|
|
reasonInvalidEndpointURL = "InvalidEndpointURL"
|
|
reasonInvalidEndpointURLScheme = "InvalidEndpointURLScheme"
|
|
reasonInvalidEndpointCannotDetermineProxy = "InvalidEndpointCannotDetermineProxy"
|
|
reasonUnableToDialServer = "UnableToDialServer"
|
|
)
|
|
|
|
type cachedWebhookAuthenticator struct {
|
|
authenticator.Token
|
|
endpoint string
|
|
caBundleHash tlsconfigutil.CABundleHash
|
|
}
|
|
|
|
func (*cachedWebhookAuthenticator) Close() {
|
|
// no-op, because no cleanup is needed on webhook authenticators
|
|
}
|
|
|
|
// New instantiates a new controllerlib.Controller which will populate the provided authncache.Cache.
|
|
func New(
|
|
namespace string,
|
|
cache *authncache.Cache,
|
|
client conciergeclientset.Interface,
|
|
webhookInformer authinformers.WebhookAuthenticatorInformer,
|
|
secretInformer corev1informers.SecretInformer,
|
|
configMapInformer corev1informers.ConfigMapInformer,
|
|
withInformer pinnipedcontroller.WithInformerOptionFunc,
|
|
clock clock.Clock,
|
|
log plog.Logger,
|
|
dialer ptls.Dialer,
|
|
proxyDetector proxydetect.ProxyDetect,
|
|
) controllerlib.Controller {
|
|
return controllerlib.New(
|
|
controllerlib.Config{
|
|
Name: controllerName,
|
|
Syncer: &webhookCacheFillerController{
|
|
namespace: namespace,
|
|
cache: cache,
|
|
client: client,
|
|
webhookInformer: webhookInformer,
|
|
secretInformer: secretInformer,
|
|
configMapInformer: configMapInformer,
|
|
clock: clock,
|
|
log: log.WithName(controllerName),
|
|
dialer: dialer,
|
|
proxyDetector: proxyDetector,
|
|
},
|
|
},
|
|
withInformer(
|
|
webhookInformer,
|
|
pinnipedcontroller.MatchAnythingFilter(pinnipedcontroller.SingletonQueue()),
|
|
controllerlib.InformerOption{},
|
|
),
|
|
withInformer(
|
|
secretInformer,
|
|
pinnipedcontroller.MatchAnySecretOfTypesFilter(
|
|
[]corev1.SecretType{
|
|
corev1.SecretTypeOpaque,
|
|
corev1.SecretTypeTLS,
|
|
},
|
|
pinnipedcontroller.SingletonQueue(),
|
|
),
|
|
controllerlib.InformerOption{},
|
|
),
|
|
withInformer(
|
|
configMapInformer,
|
|
pinnipedcontroller.MatchAnythingFilter(pinnipedcontroller.SingletonQueue()),
|
|
controllerlib.InformerOption{},
|
|
),
|
|
)
|
|
}
|
|
|
|
type webhookCacheFillerController struct {
|
|
namespace string
|
|
cache *authncache.Cache
|
|
webhookInformer authinformers.WebhookAuthenticatorInformer
|
|
secretInformer corev1informers.SecretInformer
|
|
configMapInformer corev1informers.ConfigMapInformer
|
|
client conciergeclientset.Interface
|
|
clock clock.Clock
|
|
log plog.Logger
|
|
dialer ptls.Dialer
|
|
proxyDetector proxydetect.ProxyDetect
|
|
}
|
|
|
|
// Sync implements controllerlib.Syncer.
|
|
func (c *webhookCacheFillerController) Sync(ctx controllerlib.Context) error {
|
|
webhookAuthenticators, err := c.webhookInformer.Lister().List(labels.Everything())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(webhookAuthenticators) == 0 {
|
|
c.log.Info("No WebhookAuthenticators found")
|
|
return nil
|
|
}
|
|
|
|
// Sort them by name so that order is predictable and therefore output is consistent for tests and logs.
|
|
slices.SortStableFunc(webhookAuthenticators, func(a, b *authenticationv1alpha1.WebhookAuthenticator) int {
|
|
return strings.Compare(a.Name, b.Name)
|
|
})
|
|
|
|
var errs []error
|
|
for _, webhookAuthenticator := range webhookAuthenticators {
|
|
err = c.syncIndividualWebhookAuthenticator(ctx.Context, webhookAuthenticator)
|
|
if err != nil {
|
|
errs = append(errs, fmt.Errorf("error for WebhookAuthenticator %s: %w", webhookAuthenticator.Name, err))
|
|
}
|
|
}
|
|
return utilerrors.NewAggregate(errs)
|
|
}
|
|
|
|
func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx context.Context, webhookAuthenticator *authenticationv1alpha1.WebhookAuthenticator) error {
|
|
cacheKey := authncache.Key{
|
|
APIGroup: authenticationv1alpha1.GroupName,
|
|
Kind: "WebhookAuthenticator",
|
|
Name: webhookAuthenticator.Name,
|
|
}
|
|
|
|
logger := c.log.WithValues(
|
|
"webhookAuthenticator", webhookAuthenticator.Name,
|
|
"endpoint", webhookAuthenticator.Spec.Endpoint)
|
|
|
|
var errs []error
|
|
conditions := make([]*metav1.Condition, 0)
|
|
var newWebhookAuthenticatorForCache *cachedWebhookAuthenticator
|
|
|
|
caBundle, conditions, tlsBundleOk := c.validateTLSBundle(webhookAuthenticator.Spec.TLS, conditions)
|
|
|
|
endpointHostPort, conditions, usingProxyForHost, endpointOk := c.validateEndpoint(webhookAuthenticator.Spec.Endpoint, conditions)
|
|
okSoFar := tlsBundleOk && endpointOk
|
|
|
|
// Only revalidate and update the cache if the cached authenticator is different from the desired authenticator.
|
|
// There is no need to repeat connection probe validations for a URL and CA bundle combination that was already
|
|
// successfully validated. We are making a design decision to avoid repeating the validation which dials the server,
|
|
// even though the server's TLS configuration could have changed, because it is also possible that the network
|
|
// could be flaky. We are choosing to prefer to keep the authenticator cached (available for end-user auth attempts)
|
|
// during times of network flakes rather than trying to show the most up-to-date status possible. These validations
|
|
// are for administrator convenience at the time of a configuration change, to catch typos and blatant
|
|
// misconfigurations, rather than to constantly monitor for external issues.
|
|
foundAuthenticatorInCache, previouslyValidatedWithSameEndpointAndBundle := c.havePreviouslyValidated(
|
|
cacheKey, webhookAuthenticator.Spec.Endpoint, tlsBundleOk, caBundle.Hash(), logger)
|
|
if previouslyValidatedWithSameEndpointAndBundle {
|
|
// Because the authenticator was previously cached, that implies that the following conditions were
|
|
// previously validated. These are the expensive validations to repeat, so skip them this time.
|
|
// However, the status may be lagging behind due to the informer cache being slow to catch up
|
|
// after previous status updates, so always calculate the new status conditions again and check
|
|
// if they need to be updated.
|
|
logger.Info("cached webhook authenticator and desired webhook authenticator are the same: already cached, so skipping validations")
|
|
conditions = append(conditions,
|
|
successfulWebhookConnectionValidCondition(usingProxyForHost),
|
|
successfulAuthenticatorValidCondition(),
|
|
)
|
|
} else {
|
|
// Run all remaining validations.
|
|
a, moreConditions, moreErrs := c.doExpensiveValidations(
|
|
ctx, webhookAuthenticator, endpointHostPort, caBundle, okSoFar, usingProxyForHost, logger,
|
|
)
|
|
newWebhookAuthenticatorForCache = a
|
|
conditions = append(conditions, moreConditions...)
|
|
errs = append(errs, moreErrs...)
|
|
}
|
|
|
|
authenticatorValid := !conditionsutil.HadErrorCondition(conditions)
|
|
|
|
// If we calculated a failed status condition, then remove it from the cache even before we try to write
|
|
// the status, because writing the status can fail for various reasons.
|
|
if !authenticatorValid {
|
|
// The authenticator was determined to be invalid. Remove it from the cache, in case it was previously
|
|
// validated and cached. Do not allow an old, previously validated spec of the authenticator to continue
|
|
// being used for authentication.
|
|
c.cache.Delete(cacheKey)
|
|
logger.Info("invalid webhook authenticator",
|
|
"removedFromCache", foundAuthenticatorInCache)
|
|
}
|
|
|
|
// Always try to update the status, even when we found it in the authenticator cache.
|
|
updateErr := c.updateStatus(ctx, webhookAuthenticator, conditions, logger)
|
|
errs = append(errs, updateErr)
|
|
|
|
// Only add/update this authenticator to the cache when we have a new one and the status update succeeded.
|
|
if newWebhookAuthenticatorForCache != nil && authenticatorValid && updateErr == nil {
|
|
c.cache.Store(cacheKey, newWebhookAuthenticatorForCache)
|
|
logger.Info("added or updated webhook authenticator in cache",
|
|
"isOverwrite", foundAuthenticatorInCache)
|
|
}
|
|
|
|
// Sync loop errors:
|
|
// - Should not be configuration errors. Config errors a user must correct belong on the .Status
|
|
// object. The controller simply must wait for a user to correct before running again.
|
|
// - Other errors, such as networking errors, etc. are the types of errors that should return here
|
|
// and signal the controller to retry the sync loop. These may be corrected by machines.
|
|
return utilerrors.NewAggregate(errs)
|
|
}
|
|
|
|
func (c *webhookCacheFillerController) doExpensiveValidations(
|
|
ctx context.Context,
|
|
webhookAuthenticator *authenticationv1alpha1.WebhookAuthenticator,
|
|
endpointHostPort *endpointaddr.HostPort,
|
|
caBundle *tlsconfigutil.CABundle,
|
|
okSoFar bool,
|
|
usingProxyForHost bool,
|
|
logger plog.Logger,
|
|
) (*cachedWebhookAuthenticator, []*metav1.Condition, []error) {
|
|
var newWebhookAuthenticatorForCache *cachedWebhookAuthenticator
|
|
var conditions []*metav1.Condition
|
|
var errs []error
|
|
|
|
conditions, tlsNegotiateErr := c.validateConnection(ctx, caBundle.CertPool(), endpointHostPort, conditions, okSoFar, usingProxyForHost, logger)
|
|
errs = append(errs, tlsNegotiateErr)
|
|
okSoFar = okSoFar && tlsNegotiateErr == nil
|
|
|
|
newAuthenticator, conditions, err := newWebhookAuthenticator(
|
|
// Note that we use the whole URL when constructing the webhook client,
|
|
// not just the host and port that we validated above. We need the path, etc.
|
|
webhookAuthenticator.Spec.Endpoint,
|
|
caBundle.PEMBytes(),
|
|
conditions,
|
|
okSoFar,
|
|
)
|
|
errs = append(errs, err)
|
|
|
|
if newAuthenticator != nil {
|
|
newWebhookAuthenticatorForCache = &cachedWebhookAuthenticator{
|
|
Token: newAuthenticator,
|
|
endpoint: webhookAuthenticator.Spec.Endpoint,
|
|
caBundleHash: caBundle.Hash(),
|
|
}
|
|
}
|
|
return newWebhookAuthenticatorForCache, conditions, errs
|
|
}
|
|
|
|
func (c *webhookCacheFillerController) havePreviouslyValidated(
|
|
cacheKey authncache.Key,
|
|
endpoint string,
|
|
tlsBundleOk bool,
|
|
caBundleHash tlsconfigutil.CABundleHash,
|
|
logger plog.Logger,
|
|
) (bool, bool) {
|
|
var authenticatorFromCache *cachedWebhookAuthenticator
|
|
valueFromCache := c.cache.Get(cacheKey)
|
|
if valueFromCache == nil {
|
|
return false, false
|
|
}
|
|
authenticatorFromCache = c.cacheValueAsWebhookAuthenticator(valueFromCache, logger)
|
|
if authenticatorFromCache == nil {
|
|
return false, false
|
|
}
|
|
if authenticatorFromCache.endpoint == endpoint &&
|
|
tlsBundleOk && // if there was any error while validating the latest CA bundle, then do not consider it previously validated
|
|
authenticatorFromCache.caBundleHash.Equal(caBundleHash) {
|
|
return true, true
|
|
}
|
|
return true, false // found the authenticator, but it had not been previously validated with these same settings
|
|
}
|
|
|
|
func (c *webhookCacheFillerController) cacheValueAsWebhookAuthenticator(value authncache.Value, log plog.Logger) *cachedWebhookAuthenticator {
|
|
webhookAuthenticator, ok := value.(*cachedWebhookAuthenticator)
|
|
if !ok {
|
|
actualType := "<nil>"
|
|
if t := reflect.TypeOf(value); t != nil {
|
|
actualType = t.String()
|
|
}
|
|
log.Info("wrong webhook authenticator type in cache",
|
|
"actualType", actualType)
|
|
return nil
|
|
}
|
|
return webhookAuthenticator
|
|
}
|
|
|
|
func (c *webhookCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1alpha1.TLSSpec, conditions []*metav1.Condition) (*tlsconfigutil.CABundle, []*metav1.Condition, bool) {
|
|
condition, caBundle := tlsconfigutil.ValidateTLSConfig(
|
|
tlsconfigutil.TLSSpecForConcierge(tlsSpec),
|
|
"spec.tls",
|
|
c.namespace,
|
|
c.secretInformer,
|
|
c.configMapInformer)
|
|
|
|
conditions = append(conditions, condition)
|
|
return caBundle, conditions, condition.Status == metav1.ConditionTrue
|
|
}
|
|
|
|
func successfulAuthenticatorValidCondition() *metav1.Condition {
|
|
return &metav1.Condition{
|
|
Type: typeAuthenticatorValid,
|
|
Status: metav1.ConditionTrue,
|
|
Reason: conditionsutil.ReasonSuccess,
|
|
Message: "authenticator initialized",
|
|
}
|
|
}
|
|
|
|
// newWebhookAuthenticator creates a webhook from the provided API server url and caBundle
|
|
// used to validate TLS connections.
|
|
func newWebhookAuthenticator(
|
|
endpointURL string,
|
|
pemBytes []byte,
|
|
conditions []*metav1.Condition,
|
|
prereqOk bool,
|
|
) (*webhook.WebhookTokenAuthenticator, []*metav1.Condition, error) {
|
|
if !prereqOk {
|
|
conditions = append(conditions, &metav1.Condition{
|
|
Type: typeAuthenticatorValid,
|
|
Status: metav1.ConditionUnknown,
|
|
Reason: conditionsutil.ReasonUnableToValidate,
|
|
Message: conditionsutil.MessageUnableToValidate,
|
|
})
|
|
return nil, conditions, nil
|
|
}
|
|
|
|
// We use v1beta1 instead of v1 since v1beta1 is more prevalent in our desired
|
|
// integration points.
|
|
version := k8sauthv1beta1.SchemeGroupVersion.Version
|
|
|
|
// At the current time, we don't provide any audiences because we simply don't
|
|
// have any requirements to do so. This can be changed in the future as
|
|
// requirements change.
|
|
var implicitAuds authenticator.Audiences
|
|
|
|
// We set this to nil because we would only need this to support some of the
|
|
// custom proxy stuff used by the API server.
|
|
var customDial k8snetutil.DialFunc
|
|
|
|
restConfig := &rest.Config{
|
|
Host: endpointURL,
|
|
TLSClientConfig: rest.TLSClientConfig{CAData: pemBytes},
|
|
|
|
// The remainder of these settings are copied from webhookutil.LoadKubeconfig in k8s.io/apiserver/pkg/util/webhook.
|
|
Dial: customDial,
|
|
Timeout: 30 * time.Second,
|
|
QPS: -1,
|
|
}
|
|
|
|
client, err := kubeclient.New(kubeclient.WithConfig(restConfig), kubeclient.WithTLSConfigFunc(ptls.Default))
|
|
if err != nil {
|
|
errText := "unable to create client for this webhook"
|
|
msg := fmt.Sprintf("%s: %s", errText, err.Error())
|
|
conditions = append(conditions, &metav1.Condition{
|
|
Type: typeAuthenticatorValid,
|
|
Status: metav1.ConditionFalse,
|
|
Reason: reasonUnableToCreateClient,
|
|
Message: msg,
|
|
})
|
|
return nil, conditions, fmt.Errorf("%s: %w", errText, err)
|
|
}
|
|
|
|
webhookAuthenticator, err := webhook.New(client.JSONConfig, version, implicitAuds, *webhook.DefaultRetryBackoff())
|
|
if err != nil {
|
|
// no unit test for this failure.
|
|
errText := "unable to instantiate webhook"
|
|
msg := fmt.Sprintf("%s: %s", errText, err.Error())
|
|
conditions = append(conditions, &metav1.Condition{
|
|
Type: typeAuthenticatorValid,
|
|
Status: metav1.ConditionFalse,
|
|
Reason: reasonUnableToInstantiateWebhook,
|
|
Message: msg,
|
|
})
|
|
return nil, conditions, fmt.Errorf("%s: %w", errText, err)
|
|
}
|
|
|
|
conditions = append(conditions, successfulAuthenticatorValidCondition())
|
|
|
|
return webhookAuthenticator, conditions, nil
|
|
}
|
|
|
|
func successfulWebhookConnectionValidCondition(usingProxyForHost bool) *metav1.Condition {
|
|
msg := "successfully dialed webhook server"
|
|
if usingProxyForHost {
|
|
msg = "skipped dialing connection probe because HTTPS_PROXY is configured for use with the specified host"
|
|
}
|
|
return &metav1.Condition{
|
|
Type: typeWebhookConnectionValid,
|
|
Status: metav1.ConditionTrue,
|
|
Reason: conditionsutil.ReasonSuccess,
|
|
Message: msg,
|
|
}
|
|
}
|
|
|
|
func (c *webhookCacheFillerController) validateConnection(
|
|
ctx context.Context,
|
|
certPool *x509.CertPool,
|
|
endpointHostPort *endpointaddr.HostPort,
|
|
conditions []*metav1.Condition,
|
|
prereqOk bool,
|
|
usingProxyForHost bool,
|
|
logger plog.Logger,
|
|
) ([]*metav1.Condition, error) {
|
|
if !prereqOk {
|
|
conditions = append(conditions, &metav1.Condition{
|
|
Type: typeWebhookConnectionValid,
|
|
Status: metav1.ConditionUnknown,
|
|
Reason: conditionsutil.ReasonUnableToValidate,
|
|
Message: conditionsutil.MessageUnableToValidate,
|
|
})
|
|
return conditions, nil
|
|
}
|
|
|
|
if usingProxyForHost {
|
|
// We cannot assume that we can directly dial the host in the case where we should
|
|
// be using a web proxy to reach the host, so skip the dial probe in that case.
|
|
conditions = append(conditions, successfulWebhookConnectionValidCondition(usingProxyForHost))
|
|
return conditions, nil
|
|
}
|
|
|
|
dialCtx, dialCancel := context.WithTimeout(ctx, 30*time.Second)
|
|
defer dialCancel()
|
|
err := c.dialer.IsReachableAndTLSValidationSucceeds(dialCtx, endpointHostPort.Endpoint(), certPool, logger)
|
|
|
|
if err != nil {
|
|
errText := "cannot dial server"
|
|
msg := fmt.Sprintf("%s: %s", errText, err)
|
|
conditions = append(conditions, &metav1.Condition{
|
|
Type: typeWebhookConnectionValid,
|
|
Status: metav1.ConditionFalse,
|
|
Reason: reasonUnableToDialServer,
|
|
Message: msg,
|
|
})
|
|
return conditions, fmt.Errorf("%s: %w", errText, err)
|
|
}
|
|
|
|
conditions = append(conditions, successfulWebhookConnectionValidCondition(usingProxyForHost))
|
|
return conditions, nil
|
|
}
|
|
|
|
func (c *webhookCacheFillerController) validateEndpoint(endpoint string, conditions []*metav1.Condition) (*endpointaddr.HostPort, []*metav1.Condition, bool, bool) {
|
|
endpointURL, err := url.Parse(endpoint)
|
|
if err != nil {
|
|
msg := fmt.Sprintf("%s: %s", "spec.endpoint URL cannot be parsed", err.Error())
|
|
conditions = append(conditions, &metav1.Condition{
|
|
Type: typeEndpointURLValid,
|
|
Status: metav1.ConditionFalse,
|
|
Reason: reasonInvalidEndpointURL,
|
|
Message: msg,
|
|
})
|
|
return nil, conditions, false, false
|
|
}
|
|
|
|
// handles empty string and other issues as well.
|
|
if endpointURL.Scheme != "https" {
|
|
msg := fmt.Sprintf("spec.endpoint URL %s has invalid scheme, require 'https'", endpoint)
|
|
conditions = append(conditions, &metav1.Condition{
|
|
Type: typeEndpointURLValid,
|
|
Status: metav1.ConditionFalse,
|
|
Reason: reasonInvalidEndpointURLScheme,
|
|
Message: msg,
|
|
})
|
|
return nil, conditions, false, false
|
|
}
|
|
|
|
endpointHostPort, err := endpointaddr.ParseFromURL(endpointURL, 443)
|
|
if err != nil {
|
|
msg := fmt.Sprintf("%s: %s", "spec.endpoint URL is not valid", err.Error())
|
|
conditions = append(conditions, &metav1.Condition{
|
|
Type: typeEndpointURLValid,
|
|
Status: metav1.ConditionFalse,
|
|
Reason: reasonInvalidEndpointURL,
|
|
Message: msg,
|
|
})
|
|
return nil, conditions, false, false
|
|
}
|
|
|
|
usingProxyForHost, err := c.proxyDetector.UsingProxyForHost(endpointHostPort.Host)
|
|
if err != nil {
|
|
msg := fmt.Sprintf("%s: %s", "spec.endpoint URL error", err.Error())
|
|
conditions = append(conditions, &metav1.Condition{
|
|
Type: typeEndpointURLValid,
|
|
Status: metav1.ConditionFalse,
|
|
Reason: reasonInvalidEndpointCannotDetermineProxy,
|
|
Message: msg,
|
|
})
|
|
return nil, conditions, false, false
|
|
}
|
|
|
|
conditions = append(conditions, &metav1.Condition{
|
|
Type: typeEndpointURLValid,
|
|
Status: metav1.ConditionTrue,
|
|
Reason: conditionsutil.ReasonSuccess,
|
|
Message: "spec.endpoint is a valid URL",
|
|
})
|
|
return &endpointHostPort, conditions, usingProxyForHost, true
|
|
}
|
|
|
|
func (c *webhookCacheFillerController) updateStatus(
|
|
ctx context.Context,
|
|
original *authenticationv1alpha1.WebhookAuthenticator,
|
|
conditions []*metav1.Condition,
|
|
logger plog.Logger,
|
|
) error {
|
|
updated := original.DeepCopy()
|
|
|
|
if conditionsutil.HadErrorCondition(conditions) {
|
|
updated.Status.Phase = authenticationv1alpha1.WebhookAuthenticatorPhaseError
|
|
conditions = append(conditions, &metav1.Condition{
|
|
Type: typeReady,
|
|
Status: metav1.ConditionFalse,
|
|
Reason: conditionsutil.ReasonNotReady,
|
|
Message: "the WebhookAuthenticator is not ready: see other conditions for details",
|
|
})
|
|
} else {
|
|
updated.Status.Phase = authenticationv1alpha1.WebhookAuthenticatorPhaseReady
|
|
conditions = append(conditions, &metav1.Condition{
|
|
Type: typeReady,
|
|
Status: metav1.ConditionTrue,
|
|
Reason: conditionsutil.ReasonSuccess,
|
|
Message: "the WebhookAuthenticator is ready",
|
|
})
|
|
}
|
|
|
|
_ = conditionsutil.MergeConditions(
|
|
conditions,
|
|
&updated.Status.Conditions,
|
|
original.Generation,
|
|
metav1.NewTime(c.clock.Now()),
|
|
logger,
|
|
)
|
|
|
|
if equality.Semantic.DeepEqual(original, updated) {
|
|
logger.Debug("choosing to not update the webhookauthenticator status since there is no update to make",
|
|
"phase", updated.Status.Phase)
|
|
return nil
|
|
}
|
|
_, err := c.client.AuthenticationV1alpha1().WebhookAuthenticators().UpdateStatus(ctx, updated, metav1.UpdateOptions{})
|
|
if err == nil {
|
|
logger.Debug("webhookauthenticator status successfully updated",
|
|
"phase", updated.Status.Phase)
|
|
}
|
|
return err
|
|
}
|