Impersonation controller updates CredentialIssuer on every call to Sync

- This commit does not include the updates that we plan to make to
  the `status.strategies[].frontend` field of the CredentialIssuer.
  That will come in a future commit.
This commit is contained in:
Ryan Richard 2021-03-02 14:48:58 -08:00
parent 84cc42b2ca
commit 1ad2c38509
3 changed files with 354 additions and 79 deletions

View File

@ -20,14 +20,18 @@ import (
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors" k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/clock"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
corev1informers "k8s.io/client-go/informers/core/v1" corev1informers "k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
pinnipedclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned"
"go.pinniped.dev/internal/certauthority" "go.pinniped.dev/internal/certauthority"
"go.pinniped.dev/internal/clusterhost" "go.pinniped.dev/internal/clusterhost"
"go.pinniped.dev/internal/concierge/impersonator" "go.pinniped.dev/internal/concierge/impersonator"
pinnipedcontroller "go.pinniped.dev/internal/controller" pinnipedcontroller "go.pinniped.dev/internal/controller"
"go.pinniped.dev/internal/controller/issuerconfig"
"go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/controllerlib"
"go.pinniped.dev/internal/plog" "go.pinniped.dev/internal/plog"
) )
@ -40,21 +44,31 @@ const (
caCrtKey = "ca.crt" caCrtKey = "ca.crt"
caKeyKey = "ca.key" caKeyKey = "ca.key"
appLabelKey = "app" appLabelKey = "app"
// TODO move these to the api package after resolving an upcoming merge.
PendingStrategyReason = v1alpha1.StrategyReason("Pending")
ErrorDuringSetupStrategyReason = v1alpha1.StrategyReason("ErrorDuringSetup")
) )
type impersonatorConfigController struct { type impersonatorConfigController struct {
namespace string namespace string
configMapResourceName string configMapResourceName string
k8sClient kubernetes.Interface credentialIssuerResourceName string
configMapsInformer corev1informers.ConfigMapInformer
servicesInformer corev1informers.ServiceInformer
secretsInformer corev1informers.SecretInformer
generatedLoadBalancerServiceName string generatedLoadBalancerServiceName string
tlsSecretName string tlsSecretName string
caSecretName string caSecretName string
labels map[string]string
startTLSListenerFunc StartTLSListenerFunc k8sClient kubernetes.Interface
httpHandlerFactory func() (http.Handler, error) pinnipedAPIClient pinnipedclientset.Interface
configMapsInformer corev1informers.ConfigMapInformer
servicesInformer corev1informers.ServiceInformer
secretsInformer corev1informers.SecretInformer
labels map[string]string
clock clock.Clock
startTLSListenerFunc StartTLSListenerFunc
httpHandlerFactory func() (http.Handler, error)
server *http.Server server *http.Server
hasControlPlaneNodes *bool hasControlPlaneNodes *bool
@ -67,7 +81,9 @@ type StartTLSListenerFunc func(network, listenAddress string, config *tls.Config
func NewImpersonatorConfigController( func NewImpersonatorConfigController(
namespace string, namespace string,
configMapResourceName string, configMapResourceName string,
credentialIssuerResourceName string,
k8sClient kubernetes.Interface, k8sClient kubernetes.Interface,
pinnipedAPIClient pinnipedclientset.Interface,
configMapsInformer corev1informers.ConfigMapInformer, configMapsInformer corev1informers.ConfigMapInformer,
servicesInformer corev1informers.ServiceInformer, servicesInformer corev1informers.ServiceInformer,
secretsInformer corev1informers.SecretInformer, secretsInformer corev1informers.SecretInformer,
@ -77,6 +93,7 @@ func NewImpersonatorConfigController(
tlsSecretName string, tlsSecretName string,
caSecretName string, caSecretName string,
labels map[string]string, labels map[string]string,
clock clock.Clock,
startTLSListenerFunc StartTLSListenerFunc, startTLSListenerFunc StartTLSListenerFunc,
httpHandlerFactory func() (http.Handler, error), httpHandlerFactory func() (http.Handler, error),
) controllerlib.Controller { ) controllerlib.Controller {
@ -86,14 +103,17 @@ func NewImpersonatorConfigController(
Syncer: &impersonatorConfigController{ Syncer: &impersonatorConfigController{
namespace: namespace, namespace: namespace,
configMapResourceName: configMapResourceName, configMapResourceName: configMapResourceName,
k8sClient: k8sClient, credentialIssuerResourceName: credentialIssuerResourceName,
configMapsInformer: configMapsInformer,
servicesInformer: servicesInformer,
secretsInformer: secretsInformer,
generatedLoadBalancerServiceName: generatedLoadBalancerServiceName, generatedLoadBalancerServiceName: generatedLoadBalancerServiceName,
tlsSecretName: tlsSecretName, tlsSecretName: tlsSecretName,
caSecretName: caSecretName, caSecretName: caSecretName,
k8sClient: k8sClient,
pinnipedAPIClient: pinnipedAPIClient,
configMapsInformer: configMapsInformer,
servicesInformer: servicesInformer,
secretsInformer: secretsInformer,
labels: labels, labels: labels,
clock: clock,
startTLSListenerFunc: startTLSListenerFunc, startTLSListenerFunc: startTLSListenerFunc,
httpHandlerFactory: httpHandlerFactory, httpHandlerFactory: httpHandlerFactory,
}, },
@ -126,11 +146,37 @@ func NewImpersonatorConfigController(
func (c *impersonatorConfigController) Sync(syncCtx controllerlib.Context) error { func (c *impersonatorConfigController) Sync(syncCtx controllerlib.Context) error {
plog.Debug("Starting impersonatorConfigController Sync") plog.Debug("Starting impersonatorConfigController Sync")
ctx := syncCtx.Context
strategy, err := c.doSync(syncCtx.Context)
if err != nil {
strategy = &v1alpha1.CredentialIssuerStrategy{
Type: v1alpha1.ImpersonationProxyStrategyType,
Status: v1alpha1.ErrorStrategyStatus,
Reason: ErrorDuringSetupStrategyReason,
Message: err.Error(),
LastUpdateTime: metav1.NewTime(c.clock.Now()),
}
}
updateStrategyErr := c.updateStrategy(syncCtx.Context, strategy)
if updateStrategyErr != nil {
plog.Error("error while updating the CredentialIssuer status", err)
if err == nil {
err = updateStrategyErr
}
}
if err == nil {
plog.Debug("Successfully finished impersonatorConfigController Sync")
}
return err
}
func (c *impersonatorConfigController) doSync(ctx context.Context) (*v1alpha1.CredentialIssuerStrategy, error) {
config, err := c.loadImpersonationProxyConfiguration() config, err := c.loadImpersonationProxyConfiguration()
if err != nil { if err != nil {
return err return nil, err
} }
// Make a live API call to avoid the cost of having an informer watch all node changes on the cluster, // Make a live API call to avoid the cost of having an informer watch all node changes on the cluster,
@ -140,7 +186,7 @@ func (c *impersonatorConfigController) Sync(syncCtx controllerlib.Context) error
if c.hasControlPlaneNodes == nil { if c.hasControlPlaneNodes == nil {
hasControlPlaneNodes, err := clusterhost.New(c.k8sClient).HasControlPlaneNodes(ctx) hasControlPlaneNodes, err := clusterhost.New(c.k8sClient).HasControlPlaneNodes(ctx)
if err != nil { if err != nil {
return err return nil, err
} }
c.hasControlPlaneNodes = &hasControlPlaneNodes c.hasControlPlaneNodes = &hasControlPlaneNodes
plog.Debug("Queried for control plane nodes", "foundControlPlaneNodes", hasControlPlaneNodes) plog.Debug("Queried for control plane nodes", "foundControlPlaneNodes", hasControlPlaneNodes)
@ -148,39 +194,38 @@ func (c *impersonatorConfigController) Sync(syncCtx controllerlib.Context) error
if c.shouldHaveImpersonator(config) { if c.shouldHaveImpersonator(config) {
if err = c.ensureImpersonatorIsStarted(); err != nil { if err = c.ensureImpersonatorIsStarted(); err != nil {
return err return nil, err
} }
} else { } else {
if err = c.ensureImpersonatorIsStopped(); err != nil { if err = c.ensureImpersonatorIsStopped(); err != nil {
return err return nil, err
} }
} }
if c.shouldHaveLoadBalancer(config) { if c.shouldHaveLoadBalancer(config) {
if err = c.ensureLoadBalancerIsStarted(ctx); err != nil { if err = c.ensureLoadBalancerIsStarted(ctx); err != nil {
return err return nil, err
} }
} else { } else {
if err = c.ensureLoadBalancerIsStopped(ctx); err != nil { if err = c.ensureLoadBalancerIsStopped(ctx); err != nil {
return err return nil, err
} }
} }
waitingForLoadBalancer := false
if c.shouldHaveTLSSecret(config) { if c.shouldHaveTLSSecret(config) {
var impersonationCA *certauthority.CA var impersonationCA *certauthority.CA
if impersonationCA, err = c.ensureCASecretIsCreated(ctx); err != nil { if impersonationCA, err = c.ensureCASecretIsCreated(ctx); err != nil {
return err return nil, err
} }
if err = c.ensureTLSSecret(ctx, config, impersonationCA); err != nil { if waitingForLoadBalancer, err = c.ensureTLSSecret(ctx, config, impersonationCA); err != nil {
return err return nil, err
} }
} else if err = c.ensureTLSSecretIsRemoved(ctx); err != nil { } else if err = c.ensureTLSSecretIsRemoved(ctx); err != nil {
return err return nil, err
} }
plog.Debug("Successfully finished impersonatorConfigController Sync") return c.doSyncResult(waitingForLoadBalancer, config), nil
return nil
} }
func (c *impersonatorConfigController) loadImpersonationProxyConfiguration() (*impersonator.Config, error) { func (c *impersonatorConfigController) loadImpersonationProxyConfiguration() (*impersonator.Config, error) {
@ -212,7 +257,19 @@ func (c *impersonatorConfigController) loadImpersonationProxyConfiguration() (*i
} }
func (c *impersonatorConfigController) shouldHaveImpersonator(config *impersonator.Config) bool { func (c *impersonatorConfigController) shouldHaveImpersonator(config *impersonator.Config) bool {
return (config.Mode == impersonator.ModeAuto && !*c.hasControlPlaneNodes) || config.Mode == impersonator.ModeEnabled return c.enabledByAutoMode(config) || config.Mode == impersonator.ModeEnabled
}
func (c *impersonatorConfigController) enabledByAutoMode(config *impersonator.Config) bool {
return config.Mode == impersonator.ModeAuto && !*c.hasControlPlaneNodes
}
func (c *impersonatorConfigController) disabledByAutoMode(config *impersonator.Config) bool {
return config.Mode == impersonator.ModeAuto && *c.hasControlPlaneNodes
}
func (c *impersonatorConfigController) disabledExplicitly(config *impersonator.Config) bool {
return config.Mode == impersonator.ModeDisabled
} }
func (c *impersonatorConfigController) shouldHaveLoadBalancer(config *impersonator.Config) bool { func (c *impersonatorConfigController) shouldHaveLoadBalancer(config *impersonator.Config) bool {
@ -223,6 +280,10 @@ func (c *impersonatorConfigController) shouldHaveTLSSecret(config *impersonator.
return c.shouldHaveImpersonator(config) return c.shouldHaveImpersonator(config)
} }
func (c *impersonatorConfigController) updateStrategy(ctx context.Context, strategy *v1alpha1.CredentialIssuerStrategy) error {
return issuerconfig.UpdateStrategy(ctx, c.credentialIssuerResourceName, c.labels, c.pinnipedAPIClient, *strategy)
}
func (c *impersonatorConfigController) loadBalancerExists() (bool, error) { func (c *impersonatorConfigController) loadBalancerExists() (bool, error) {
_, err := c.servicesInformer.Lister().Services(c.namespace).Get(c.generatedLoadBalancerServiceName) _, err := c.servicesInformer.Lister().Services(c.namespace).Get(c.generatedLoadBalancerServiceName)
notFound := k8serrors.IsNotFound(err) notFound := k8serrors.IsNotFound(err)
@ -342,17 +403,17 @@ func (c *impersonatorConfigController) ensureLoadBalancerIsStopped(ctx context.C
return c.k8sClient.CoreV1().Services(c.namespace).Delete(ctx, c.generatedLoadBalancerServiceName, metav1.DeleteOptions{}) return c.k8sClient.CoreV1().Services(c.namespace).Delete(ctx, c.generatedLoadBalancerServiceName, metav1.DeleteOptions{})
} }
func (c *impersonatorConfigController) ensureTLSSecret(ctx context.Context, config *impersonator.Config, ca *certauthority.CA) error { func (c *impersonatorConfigController) ensureTLSSecret(ctx context.Context, config *impersonator.Config, ca *certauthority.CA) (bool, error) {
secretFromInformer, err := c.secretsInformer.Lister().Secrets(c.namespace).Get(c.tlsSecretName) secretFromInformer, err := c.secretsInformer.Lister().Secrets(c.namespace).Get(c.tlsSecretName)
notFound := k8serrors.IsNotFound(err) notFound := k8serrors.IsNotFound(err)
if !notFound && err != nil { if !notFound && err != nil {
return err return false, err
} }
if !notFound { if !notFound {
secretWasDeleted, err := c.deleteTLSSecretWhenCertificateDoesNotMatchDesiredState(ctx, config, ca, secretFromInformer) secretWasDeleted, err := c.deleteTLSSecretWhenCertificateDoesNotMatchDesiredState(ctx, config, ca, secretFromInformer)
if err != nil { if err != nil {
return err return false, err
} }
// If it was deleted by the above call, then set it to nil. This allows us to avoid waiting // If it was deleted by the above call, then set it to nil. This allows us to avoid waiting
// for the informer cache to update before deciding to proceed to create the new Secret below. // for the informer cache to update before deciding to proceed to create the new Secret below.
@ -457,35 +518,36 @@ func certHostnameAndIPMatchDesiredState(desiredIP net.IP, actualIPs []net.IP, de
return false return false
} }
func (c *impersonatorConfigController) ensureTLSSecretIsCreatedAndLoaded(ctx context.Context, config *impersonator.Config, secret *v1.Secret, ca *certauthority.CA) error { func (c *impersonatorConfigController) ensureTLSSecretIsCreatedAndLoaded(ctx context.Context, config *impersonator.Config, secret *v1.Secret, ca *certauthority.CA) (bool, error) {
if secret != nil { if secret != nil {
err := c.loadTLSCertFromSecret(secret) err := c.loadTLSCertFromSecret(secret)
if err != nil { if err != nil {
return err return false, err
} }
return nil return false, nil
} }
ip, hostname, nameIsReady, err := c.findDesiredTLSCertificateName(config) ip, hostname, nameIsReady, err := c.findDesiredTLSCertificateName(config)
if err != nil { if err != nil {
return err return false, err
} }
if !nameIsReady { if !nameIsReady {
// Sync will get called again when the load balancer is updated with its ingress info, so this is not an error. // Sync will get called again when the load balancer is updated with its ingress info, so this is not an error.
return nil // Return "true" meaning that we are waiting for the load balancer.
return true, nil
} }
newTLSSecret, err := c.createNewTLSSecret(ctx, ca, ip, hostname) newTLSSecret, err := c.createNewTLSSecret(ctx, ca, ip, hostname)
if err != nil { if err != nil {
return err return false, err
} }
err = c.loadTLSCertFromSecret(newTLSSecret) err = c.loadTLSCertFromSecret(newTLSSecret)
if err != nil { if err != nil {
return err return false, err
} }
return nil return false, nil
} }
func (c *impersonatorConfigController) ensureCASecretIsCreated(ctx context.Context) (*certauthority.CA, error) { func (c *impersonatorConfigController) ensureCASecretIsCreated(ctx context.Context) (*certauthority.CA, error) {
@ -672,6 +734,43 @@ func (c *impersonatorConfigController) ensureTLSSecretIsRemoved(ctx context.Cont
return nil return nil
} }
func (c *impersonatorConfigController) doSyncResult(waitingForLoadBalancer bool, config *impersonator.Config) *v1alpha1.CredentialIssuerStrategy {
switch {
case waitingForLoadBalancer:
return &v1alpha1.CredentialIssuerStrategy{
Type: v1alpha1.ImpersonationProxyStrategyType,
Status: v1alpha1.ErrorStrategyStatus,
Reason: PendingStrategyReason,
Message: "waiting for load balancer Service to be assigned IP or hostname",
LastUpdateTime: metav1.NewTime(c.clock.Now()),
}
case c.disabledExplicitly(config):
return &v1alpha1.CredentialIssuerStrategy{
Type: v1alpha1.ImpersonationProxyStrategyType,
Status: v1alpha1.ErrorStrategyStatus,
Reason: v1alpha1.DisabledStrategyReason,
Message: "impersonation proxy was explicitly disabled by configuration",
LastUpdateTime: metav1.NewTime(c.clock.Now()),
}
case c.disabledByAutoMode(config):
return &v1alpha1.CredentialIssuerStrategy{
Type: v1alpha1.ImpersonationProxyStrategyType,
Status: v1alpha1.ErrorStrategyStatus,
Reason: v1alpha1.DisabledStrategyReason,
Message: "automatically determined that impersonation proxy should be disabled",
LastUpdateTime: metav1.NewTime(c.clock.Now()),
}
default:
return &v1alpha1.CredentialIssuerStrategy{
Type: v1alpha1.ImpersonationProxyStrategyType,
Status: v1alpha1.SuccessStrategyStatus,
Reason: v1alpha1.ListeningStrategyReason,
Message: "impersonation proxy is ready to accept client connections",
LastUpdateTime: metav1.NewTime(c.clock.Now()),
}
}
}
func (c *impersonatorConfigController) setTLSCert(cert *tls.Certificate) { func (c *impersonatorConfigController) setTLSCert(cert *tls.Certificate) {
c.tlsCertMutex.Lock() c.tlsCertMutex.Lock()
defer c.tlsCertMutex.Unlock() defer c.tlsCertMutex.Unlock()

View File

@ -26,12 +26,15 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/clock"
kubeinformers "k8s.io/client-go/informers" kubeinformers "k8s.io/client-go/informers"
corev1informers "k8s.io/client-go/informers/core/v1" corev1informers "k8s.io/client-go/informers/core/v1"
kubernetesfake "k8s.io/client-go/kubernetes/fake" kubernetesfake "k8s.io/client-go/kubernetes/fake"
coretesting "k8s.io/client-go/testing" coretesting "k8s.io/client-go/testing"
"k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/cache"
"go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
pinnipedfake "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned/fake"
"go.pinniped.dev/internal/certauthority" "go.pinniped.dev/internal/certauthority"
"go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/controllerlib"
"go.pinniped.dev/internal/testutil" "go.pinniped.dev/internal/testutil"
@ -86,6 +89,8 @@ func TestImpersonatorConfigControllerOptions(t *testing.T) {
_ = NewImpersonatorConfigController( _ = NewImpersonatorConfigController(
installedInNamespace, installedInNamespace,
configMapResourceName, configMapResourceName,
"",
nil,
nil, nil,
configMapsInformer, configMapsInformer,
servicesInformer, servicesInformer,
@ -98,6 +103,7 @@ func TestImpersonatorConfigControllerOptions(t *testing.T) {
nil, nil,
nil, nil,
nil, nil,
nil,
) )
configMapsInformerFilter = observableWithInformerOption.GetFilterForInformer(configMapsInformer) configMapsInformerFilter = observableWithInformerOption.GetFilterForInformer(configMapsInformer)
servicesInformerFilter = observableWithInformerOption.GetFilterForInformer(servicesInformer) servicesInformerFilter = observableWithInformerOption.GetFilterForInformer(servicesInformer)
@ -273,6 +279,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
spec.Run(t, "Sync", func(t *testing.T, when spec.G, it spec.S) { spec.Run(t, "Sync", func(t *testing.T, when spec.G, it spec.S) {
const installedInNamespace = "some-namespace" const installedInNamespace = "some-namespace"
const configMapResourceName = "some-configmap-resource-name" const configMapResourceName = "some-configmap-resource-name"
const credentialIssuerResourceName = "some-credential-issuer-resource-name"
const loadBalancerServiceName = "some-service-resource-name" const loadBalancerServiceName = "some-service-resource-name"
const tlsSecretName = "some-tls-secret-name" //nolint:gosec // this is not a credential const tlsSecretName = "some-tls-secret-name" //nolint:gosec // this is not a credential
const caSecretName = "some-ca-secret-name" const caSecretName = "some-ca-secret-name"
@ -284,6 +291,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
var subject controllerlib.Controller var subject controllerlib.Controller
var kubeAPIClient *kubernetesfake.Clientset var kubeAPIClient *kubernetesfake.Clientset
var pinnipedAPIClient *pinnipedfake.Clientset
var kubeInformerClient *kubernetesfake.Clientset var kubeInformerClient *kubernetesfake.Clientset
var kubeInformers kubeinformers.SharedInformerFactory var kubeInformers kubeinformers.SharedInformerFactory
var timeoutContext context.Context var timeoutContext context.Context
@ -294,6 +302,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
var startTLSListenerUponCloseError error var startTLSListenerUponCloseError error
var httpHandlerFactoryFuncError error var httpHandlerFactoryFuncError error
var startedTLSListener net.Listener var startedTLSListener net.Listener
var frozenNow time.Time
var startTLSListenerFunc = func(network, listenAddress string, config *tls.Config) (net.Listener, error) { var startTLSListenerFunc = func(network, listenAddress string, config *tls.Config) (net.Listener, error) {
startTLSListenerFuncWasCalled++ startTLSListenerFuncWasCalled++
@ -423,7 +432,9 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
subject = NewImpersonatorConfigController( subject = NewImpersonatorConfigController(
installedInNamespace, installedInNamespace,
configMapResourceName, configMapResourceName,
credentialIssuerResourceName,
kubeAPIClient, kubeAPIClient,
pinnipedAPIClient,
kubeInformers.Core().V1().ConfigMaps(), kubeInformers.Core().V1().ConfigMaps(),
kubeInformers.Core().V1().Services(), kubeInformers.Core().V1().Services(),
kubeInformers.Core().V1().Secrets(), kubeInformers.Core().V1().Secrets(),
@ -433,6 +444,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
tlsSecretName, tlsSecretName,
caSecretName, caSecretName,
labels, labels,
clock.NewFakeClock(frozenNow),
startTLSListenerFunc, startTLSListenerFunc,
func() (http.Handler, error) { func() (http.Handler, error) {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
@ -652,6 +664,74 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
) )
} }
var newSuccessStrategy = func() v1alpha1.CredentialIssuerStrategy {
return v1alpha1.CredentialIssuerStrategy{
Type: v1alpha1.ImpersonationProxyStrategyType,
Status: v1alpha1.SuccessStrategyStatus,
Reason: v1alpha1.ListeningStrategyReason,
Message: "impersonation proxy is ready to accept client connections",
LastUpdateTime: metav1.NewTime(frozenNow),
}
}
var newAutoDisabledStrategy = func() v1alpha1.CredentialIssuerStrategy {
return v1alpha1.CredentialIssuerStrategy{
Type: v1alpha1.ImpersonationProxyStrategyType,
Status: v1alpha1.ErrorStrategyStatus,
Reason: v1alpha1.DisabledStrategyReason,
Message: "automatically determined that impersonation proxy should be disabled",
LastUpdateTime: metav1.NewTime(frozenNow),
}
}
var newManuallyDisabledStrategy = func() v1alpha1.CredentialIssuerStrategy {
s := newAutoDisabledStrategy()
s.Message = "impersonation proxy was explicitly disabled by configuration"
return s
}
var newPendingStrategy = func() v1alpha1.CredentialIssuerStrategy {
return v1alpha1.CredentialIssuerStrategy{
Type: v1alpha1.ImpersonationProxyStrategyType,
Status: v1alpha1.ErrorStrategyStatus,
Reason: PendingStrategyReason,
Message: "waiting for load balancer Service to be assigned IP or hostname",
LastUpdateTime: metav1.NewTime(frozenNow),
}
}
var newErrorStrategy = func(msg string) v1alpha1.CredentialIssuerStrategy {
return v1alpha1.CredentialIssuerStrategy{
Type: v1alpha1.ImpersonationProxyStrategyType,
Status: v1alpha1.ErrorStrategyStatus,
Reason: ErrorDuringSetupStrategyReason,
Message: msg,
LastUpdateTime: metav1.NewTime(frozenNow),
}
}
var requireCredentialIssuer = func(expectedStrategy v1alpha1.CredentialIssuerStrategy) {
// Rather than looking at the specific API actions on pinnipedAPIClient, we just look
// at the final result here.
// This is because the implementation is using a helper from another package to create
// and update the CredentialIssuer, and the specific API actions performed by that
// implementation are pretty complex and are already tested by its own unit tests.
// As long as we get the final result that we wanted then we are happy for the purposes
// of this test.
credentialIssuerObj, err := pinnipedAPIClient.Tracker().Get(
schema.GroupVersionResource{
Group: v1alpha1.SchemeGroupVersion.Group,
Version: v1alpha1.SchemeGroupVersion.Version,
Resource: "credentialissuers",
}, "", credentialIssuerResourceName,
)
r.NoError(err)
credentialIssuer, ok := credentialIssuerObj.(*v1alpha1.CredentialIssuer)
r.True(ok, "should have been able to cast this obj to CredentialIssuer: %v", credentialIssuerObj)
r.Equal(labels, credentialIssuer.Labels)
r.Equal([]v1alpha1.CredentialIssuerStrategy{expectedStrategy}, credentialIssuer.Status.Strategies)
}
var requireLoadBalancerWasCreated = func(action coretesting.Action) { var requireLoadBalancerWasCreated = func(action coretesting.Action) {
createAction, ok := action.(coretesting.CreateAction) createAction, ok := action.(coretesting.CreateAction)
r.True(ok, "should have been able to cast this action to CreateAction: %v", action) r.True(ok, "should have been able to cast this action to CreateAction: %v", action)
@ -738,6 +818,8 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
kubeinformers.WithNamespace(installedInNamespace), kubeinformers.WithNamespace(installedInNamespace),
) )
kubeAPIClient = kubernetesfake.NewSimpleClientset() kubeAPIClient = kubernetesfake.NewSimpleClientset()
pinnipedAPIClient = pinnipedfake.NewSimpleClientset()
frozenNow = time.Date(2021, time.March, 2, 7, 42, 0, 0, time.Local)
}) })
it.After(func() { it.After(func() {
@ -747,7 +829,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
when("the ConfigMap does not yet exist in the installation namespace or it was deleted (defaults to auto mode)", func() { when("the ConfigMap does not yet exist in the installation namespace or it was deleted (defaults to auto mode)", func() {
it.Before(func() { it.Before(func() {
addImpersonatorConfigMapToTracker("some-other-configmap", "foo: bar", kubeInformerClient) addImpersonatorConfigMapToTracker("some-other-unrelated-configmap", "foo: bar", kubeInformerClient)
}) })
when("there are visible control plane nodes", func() { when("there are visible control plane nodes", func() {
@ -761,6 +843,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSServerWasNeverStarted() requireTLSServerWasNeverStarted()
r.Len(kubeAPIClient.Actions(), 1) r.Len(kubeAPIClient.Actions(), 1)
requireNodesListed(kubeAPIClient.Actions()[0]) requireNodesListed(kubeAPIClient.Actions()[0])
requireCredentialIssuer(newAutoDisabledStrategy())
}) })
}) })
@ -780,6 +863,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireNodesListed(kubeAPIClient.Actions()[0]) requireNodesListed(kubeAPIClient.Actions()[0])
requireLoadBalancerWasDeleted(kubeAPIClient.Actions()[1]) requireLoadBalancerWasDeleted(kubeAPIClient.Actions()[1])
requireTLSSecretWasDeleted(kubeAPIClient.Actions()[2]) requireTLSSecretWasDeleted(kubeAPIClient.Actions()[2])
requireCredentialIssuer(newAutoDisabledStrategy())
}) })
}) })
@ -796,10 +880,11 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireNodesListed(kubeAPIClient.Actions()[0]) requireNodesListed(kubeAPIClient.Actions()[0])
requireLoadBalancerWasCreated(kubeAPIClient.Actions()[1]) requireLoadBalancerWasCreated(kubeAPIClient.Actions()[1])
requireCASecretWasCreated(kubeAPIClient.Actions()[2]) requireCASecretWasCreated(kubeAPIClient.Actions()[2])
requireCredentialIssuer(newPendingStrategy())
}) })
}) })
when("there are not visible control plane nodes and a load balancer already exists without an IP", func() { when("there are not visible control plane nodes and a load balancer already exists without an IP/hostname", func() {
it.Before(func() { it.Before(func() {
addNodeWithRoleToTracker("worker", kubeAPIClient) addNodeWithRoleToTracker("worker", kubeAPIClient)
addLoadBalancerServiceToTracker(loadBalancerServiceName, kubeInformerClient) addLoadBalancerServiceToTracker(loadBalancerServiceName, kubeInformerClient)
@ -813,6 +898,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.Len(kubeAPIClient.Actions(), 2) r.Len(kubeAPIClient.Actions(), 2)
requireNodesListed(kubeAPIClient.Actions()[0]) requireNodesListed(kubeAPIClient.Actions()[0])
requireCASecretWasCreated(kubeAPIClient.Actions()[1]) requireCASecretWasCreated(kubeAPIClient.Actions()[1])
requireCredentialIssuer(newPendingStrategy())
}) })
}) })
@ -830,6 +916,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.Len(kubeAPIClient.Actions(), 2) r.Len(kubeAPIClient.Actions(), 2)
requireNodesListed(kubeAPIClient.Actions()[0]) requireNodesListed(kubeAPIClient.Actions()[0])
requireCASecretWasCreated(kubeAPIClient.Actions()[1]) requireCASecretWasCreated(kubeAPIClient.Actions()[1])
requireCredentialIssuer(newPendingStrategy())
}) })
}) })
@ -847,6 +934,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.Len(kubeAPIClient.Actions(), 2) r.Len(kubeAPIClient.Actions(), 2)
requireNodesListed(kubeAPIClient.Actions()[0]) requireNodesListed(kubeAPIClient.Actions()[0])
requireCASecretWasCreated(kubeAPIClient.Actions()[1]) requireCASecretWasCreated(kubeAPIClient.Actions()[1])
requireCredentialIssuer(newErrorStrategy("could not find valid IP addresses or hostnames from load balancer some-namespace/some-service-resource-name"))
}) })
}) })
@ -865,6 +953,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
ca := requireCASecretWasCreated(kubeAPIClient.Actions()[1]) ca := requireCASecretWasCreated(kubeAPIClient.Actions()[1])
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca) requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
requireTLSServerIsRunning(ca, "127.0.0.123", map[string]string{"127.0.0.123:443": testServerAddr()}) requireTLSServerIsRunning(ca, "127.0.0.123", map[string]string{"127.0.0.123:443": testServerAddr()})
requireCredentialIssuer(newSuccessStrategy())
}) })
it("keeps the secret around after resync", func() { it("keeps the secret around after resync", func() {
@ -876,6 +965,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.NoError(runControllerSync()) r.NoError(runControllerSync())
r.Len(kubeAPIClient.Actions(), 3) // nothing changed r.Len(kubeAPIClient.Actions(), 3) // nothing changed
requireCredentialIssuer(newSuccessStrategy())
}) })
}) })
@ -895,6 +985,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
ca := requireCASecretWasCreated(kubeAPIClient.Actions()[1]) ca := requireCASecretWasCreated(kubeAPIClient.Actions()[1])
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca) requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
requireTLSServerIsRunning(ca, firstHostname, map[string]string{firstHostname + httpsPort: testServerAddr()}) requireTLSServerIsRunning(ca, firstHostname, map[string]string{firstHostname + httpsPort: testServerAddr()})
requireCredentialIssuer(newSuccessStrategy())
}) })
it("keeps the secret around after resync", func() { it("keeps the secret around after resync", func() {
@ -906,6 +997,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.NoError(runControllerSync()) r.NoError(runControllerSync())
r.Len(kubeAPIClient.Actions(), 3) // nothing changed r.Len(kubeAPIClient.Actions(), 3) // nothing changed
requireCredentialIssuer(newSuccessStrategy())
}) })
}) })
@ -925,6 +1017,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
ca := requireCASecretWasCreated(kubeAPIClient.Actions()[1]) ca := requireCASecretWasCreated(kubeAPIClient.Actions()[1])
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca) requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
requireTLSServerIsRunning(ca, firstHostname, map[string]string{firstHostname + httpsPort: testServerAddr()}) requireTLSServerIsRunning(ca, firstHostname, map[string]string{firstHostname + httpsPort: testServerAddr()})
requireCredentialIssuer(newSuccessStrategy())
}) })
it("keeps the secret around after resync", func() { it("keeps the secret around after resync", func() {
@ -936,6 +1029,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.NoError(runControllerSync()) r.NoError(runControllerSync())
r.Len(kubeAPIClient.Actions(), 3) // nothing changed r.Len(kubeAPIClient.Actions(), 3) // nothing changed
requireCredentialIssuer(newSuccessStrategy())
}) })
}) })
@ -960,6 +1054,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasDeleted(kubeAPIClient.Actions()[1]) requireTLSSecretWasDeleted(kubeAPIClient.Actions()[1])
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], caCrt) requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], caCrt)
requireTLSServerIsRunning(caCrt, testServerAddr(), nil) requireTLSServerIsRunning(caCrt, testServerAddr(), nil)
requireCredentialIssuer(newSuccessStrategy())
}) })
}) })
@ -983,6 +1078,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireNodesListed(kubeAPIClient.Actions()[0]) requireNodesListed(kubeAPIClient.Actions()[0])
requireTLSSecretWasDeleted(kubeAPIClient.Actions()[1]) requireTLSSecretWasDeleted(kubeAPIClient.Actions()[1])
requireTLSServerIsRunningWithoutCerts() requireTLSServerIsRunningWithoutCerts()
requireCredentialIssuer(newErrorStrategy("error on delete"))
}) })
}) })
@ -1010,10 +1106,11 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
updateLoadBalancerServiceInTracker(loadBalancerServiceName, []corev1.LoadBalancerIngress{{IP: "not-an-ip"}}, kubeInformerClient, "1") updateLoadBalancerServiceInTracker(loadBalancerServiceName, []corev1.LoadBalancerIngress{{IP: "not-an-ip"}}, kubeInformerClient, "1")
waitForInformerCacheToSeeResourceVersion(kubeInformers.Core().V1().Services().Informer(), "1") waitForInformerCacheToSeeResourceVersion(kubeInformers.Core().V1().Services().Informer(), "1")
r.EqualError(runControllerSync(), errString := "could not find valid IP addresses or hostnames from load balancer some-namespace/some-service-resource-name"
"could not find valid IP addresses or hostnames from load balancer some-namespace/some-service-resource-name") r.EqualError(runControllerSync(), errString)
r.Len(kubeAPIClient.Actions(), 1) // no new actions r.Len(kubeAPIClient.Actions(), 1) // no new actions
requireTLSServerIsRunning(caCrt, testServerAddr(), nil) requireTLSServerIsRunning(caCrt, testServerAddr(), nil)
requireCredentialIssuer(newErrorStrategy(errString))
}) })
}) })
}) })
@ -1036,6 +1133,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSServerWasNeverStarted() requireTLSServerWasNeverStarted()
requireNodesListed(kubeAPIClient.Actions()[0]) requireNodesListed(kubeAPIClient.Actions()[0])
r.Len(kubeAPIClient.Actions(), 1) r.Len(kubeAPIClient.Actions(), 1)
requireCredentialIssuer(newAutoDisabledStrategy())
}) })
}) })
@ -1052,6 +1150,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
ca := requireCASecretWasCreated(kubeAPIClient.Actions()[1]) ca := requireCASecretWasCreated(kubeAPIClient.Actions()[1])
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca) requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
requireTLSServerIsRunning(ca, testServerAddr(), nil) requireTLSServerIsRunning(ca, testServerAddr(), nil)
requireCredentialIssuer(newSuccessStrategy())
}) })
}) })
}) })
@ -1068,6 +1167,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSServerWasNeverStarted() requireTLSServerWasNeverStarted()
requireNodesListed(kubeAPIClient.Actions()[0]) requireNodesListed(kubeAPIClient.Actions()[0])
r.Len(kubeAPIClient.Actions(), 1) r.Len(kubeAPIClient.Actions(), 1)
requireCredentialIssuer(newManuallyDisabledStrategy())
}) })
}) })
@ -1078,25 +1178,22 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
addNodeWithRoleToTracker("control-plane", kubeAPIClient) addNodeWithRoleToTracker("control-plane", kubeAPIClient)
}) })
it("starts the impersonator", func() { it("starts the impersonator and creates a load balancer", func() {
startInformersAndController()
r.NoError(runControllerSync())
requireTLSServerIsRunningWithoutCerts()
})
it("returns an error when the tls listener fails to start", func() {
startTLSListenerFuncError = errors.New("tls error")
startInformersAndController()
r.EqualError(runControllerSync(), "tls error")
})
it("starts the load balancer", func() {
startInformersAndController() startInformersAndController()
r.NoError(runControllerSync()) r.NoError(runControllerSync())
r.Len(kubeAPIClient.Actions(), 3) r.Len(kubeAPIClient.Actions(), 3)
requireNodesListed(kubeAPIClient.Actions()[0]) requireNodesListed(kubeAPIClient.Actions()[0])
requireLoadBalancerWasCreated(kubeAPIClient.Actions()[1]) requireLoadBalancerWasCreated(kubeAPIClient.Actions()[1])
requireCASecretWasCreated(kubeAPIClient.Actions()[2]) requireCASecretWasCreated(kubeAPIClient.Actions()[2])
requireTLSServerIsRunningWithoutCerts()
requireCredentialIssuer(newPendingStrategy())
})
it("returns an error when the tls listener fails to start", func() {
startTLSListenerFuncError = errors.New("tls error")
startInformersAndController()
r.EqualError(runControllerSync(), "tls error")
requireCredentialIssuer(newErrorStrategy("tls error"))
}) })
}) })
@ -1108,24 +1205,21 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
addLoadBalancerServiceToTracker(loadBalancerServiceName, kubeAPIClient) addLoadBalancerServiceToTracker(loadBalancerServiceName, kubeAPIClient)
}) })
it("starts the impersonator", func() { it("starts the impersonator without creating a load balancer", func() {
startInformersAndController() startInformersAndController()
r.NoError(runControllerSync()) r.NoError(runControllerSync())
r.Len(kubeAPIClient.Actions(), 2)
requireNodesListed(kubeAPIClient.Actions()[0])
requireCASecretWasCreated(kubeAPIClient.Actions()[1])
requireTLSServerIsRunningWithoutCerts() requireTLSServerIsRunningWithoutCerts()
requireCredentialIssuer(newPendingStrategy())
}) })
it("returns an error when the tls listener fails to start", func() { it("returns an error when the tls listener fails to start", func() {
startTLSListenerFuncError = errors.New("tls error") startTLSListenerFuncError = errors.New("tls error")
startInformersAndController() startInformersAndController()
r.EqualError(runControllerSync(), "tls error") r.EqualError(runControllerSync(), "tls error")
}) requireCredentialIssuer(newErrorStrategy("tls error"))
it("does not start the load balancer", func() {
startInformersAndController()
r.NoError(runControllerSync())
r.Len(kubeAPIClient.Actions(), 2)
requireNodesListed(kubeAPIClient.Actions()[0])
requireCASecretWasCreated(kubeAPIClient.Actions()[1])
}) })
}) })
@ -1150,10 +1244,11 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.Len(kubeAPIClient.Actions(), 1) r.Len(kubeAPIClient.Actions(), 1)
requireNodesListed(kubeAPIClient.Actions()[0]) requireNodesListed(kubeAPIClient.Actions()[0])
requireTLSServerIsRunning(caCrt, testServerAddr(), nil) requireTLSServerIsRunning(caCrt, testServerAddr(), nil)
requireCredentialIssuer(newSuccessStrategy())
}) })
}) })
when("we have a hostname specified for the endpoint", func() { when("the configmap has a hostname specified for the endpoint", func() {
const fakeHostname = "fake.example.com" const fakeHostname = "fake.example.com"
it.Before(func() { it.Before(func() {
configMapYAML := fmt.Sprintf("{mode: enabled, endpoint: %s}", fakeHostname) configMapYAML := fmt.Sprintf("{mode: enabled, endpoint: %s}", fakeHostname)
@ -1161,7 +1256,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
addNodeWithRoleToTracker("worker", kubeAPIClient) addNodeWithRoleToTracker("worker", kubeAPIClient)
}) })
it("starts the impersonator, generates a valid cert for the hostname", func() { it("starts the impersonator, generates a valid cert for the specified hostname", func() {
startInformersAndController() startInformersAndController()
r.NoError(runControllerSync()) r.NoError(runControllerSync())
r.Len(kubeAPIClient.Actions(), 3) r.Len(kubeAPIClient.Actions(), 3)
@ -1170,10 +1265,11 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca) requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
// Check that the server is running and that TLS certs that are being served are are for fakeHostname. // Check that the server is running and that TLS certs that are being served are are for fakeHostname.
requireTLSServerIsRunning(ca, fakeHostname, map[string]string{fakeHostname + httpsPort: testServerAddr()}) requireTLSServerIsRunning(ca, fakeHostname, map[string]string{fakeHostname + httpsPort: testServerAddr()})
requireCredentialIssuer(newSuccessStrategy())
}) })
}) })
when("endpoint is IP address with a port", func() { when("the configmap has a endpoint which is an IP address with a port", func() {
const fakeIPWithPort = "127.0.0.1:3000" const fakeIPWithPort = "127.0.0.1:3000"
it.Before(func() { it.Before(func() {
configMapYAML := fmt.Sprintf("{mode: enabled, endpoint: %s}", fakeIPWithPort) configMapYAML := fmt.Sprintf("{mode: enabled, endpoint: %s}", fakeIPWithPort)
@ -1181,7 +1277,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
addNodeWithRoleToTracker("worker", kubeAPIClient) addNodeWithRoleToTracker("worker", kubeAPIClient)
}) })
it("starts the impersonator, generates a valid cert for the hostname", func() { it("starts the impersonator, generates a valid cert for the specified IP address", func() {
startInformersAndController() startInformersAndController()
r.NoError(runControllerSync()) r.NoError(runControllerSync())
r.Len(kubeAPIClient.Actions(), 3) r.Len(kubeAPIClient.Actions(), 3)
@ -1190,10 +1286,11 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca) requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
// Check that the server is running and that TLS certs that are being served are are for fakeIPWithPort. // Check that the server is running and that TLS certs that are being served are are for fakeIPWithPort.
requireTLSServerIsRunning(ca, fakeIPWithPort, map[string]string{fakeIPWithPort: testServerAddr()}) requireTLSServerIsRunning(ca, fakeIPWithPort, map[string]string{fakeIPWithPort: testServerAddr()})
requireCredentialIssuer(newSuccessStrategy())
}) })
}) })
when("endpoint is hostname with a port", func() { when("the configmap has a endpoint which is a hostname with a port", func() {
const fakeHostnameWithPort = "fake.example.com:3000" const fakeHostnameWithPort = "fake.example.com:3000"
it.Before(func() { it.Before(func() {
configMapYAML := fmt.Sprintf("{mode: enabled, endpoint: %s}", fakeHostnameWithPort) configMapYAML := fmt.Sprintf("{mode: enabled, endpoint: %s}", fakeHostnameWithPort)
@ -1201,7 +1298,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
addNodeWithRoleToTracker("worker", kubeAPIClient) addNodeWithRoleToTracker("worker", kubeAPIClient)
}) })
it("starts the impersonator, generates a valid cert for the hostname", func() { it("starts the impersonator, generates a valid cert for the specified hostname", func() {
startInformersAndController() startInformersAndController()
r.NoError(runControllerSync()) r.NoError(runControllerSync())
r.Len(kubeAPIClient.Actions(), 3) r.Len(kubeAPIClient.Actions(), 3)
@ -1210,10 +1307,11 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca) requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
// Check that the server is running and that TLS certs that are being served are are for fakeHostnameWithPort. // Check that the server is running and that TLS certs that are being served are are for fakeHostnameWithPort.
requireTLSServerIsRunning(ca, fakeHostnameWithPort, map[string]string{fakeHostnameWithPort: testServerAddr()}) requireTLSServerIsRunning(ca, fakeHostnameWithPort, map[string]string{fakeHostnameWithPort: testServerAddr()})
requireCredentialIssuer(newSuccessStrategy())
}) })
}) })
when("switching from ip address endpoint to hostname endpoint and back to ip address", func() { when("switching the configmap from ip address endpoint to hostname endpoint and back to ip address", func() {
const fakeHostname = "fake.example.com" const fakeHostname = "fake.example.com"
const fakeIP = "127.0.0.42" const fakeIP = "127.0.0.42"
var hostnameYAML = fmt.Sprintf("{mode: enabled, endpoint: %s}", fakeHostname) var hostnameYAML = fmt.Sprintf("{mode: enabled, endpoint: %s}", fakeHostname)
@ -1232,6 +1330,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca) requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
// Check that the server is running and that TLS certs that are being served are are for fakeIP. // Check that the server is running and that TLS certs that are being served are are for fakeIP.
requireTLSServerIsRunning(ca, fakeIP, map[string]string{fakeIP + httpsPort: testServerAddr()}) requireTLSServerIsRunning(ca, fakeIP, map[string]string{fakeIP + httpsPort: testServerAddr()})
requireCredentialIssuer(newSuccessStrategy())
// Simulate the informer cache's background update from its watch. // Simulate the informer cache's background update from its watch.
addSecretFromCreateActionToTracker(kubeAPIClient.Actions()[1], kubeInformerClient, "1") addSecretFromCreateActionToTracker(kubeAPIClient.Actions()[1], kubeInformerClient, "1")
@ -1249,6 +1348,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasCreated(kubeAPIClient.Actions()[4], ca) // reuses the old CA requireTLSSecretWasCreated(kubeAPIClient.Actions()[4], ca) // reuses the old CA
// Check that the server is running and that TLS certs that are being served are are for fakeHostname. // Check that the server is running and that TLS certs that are being served are are for fakeHostname.
requireTLSServerIsRunning(ca, fakeHostname, map[string]string{fakeHostname + httpsPort: testServerAddr()}) requireTLSServerIsRunning(ca, fakeHostname, map[string]string{fakeHostname + httpsPort: testServerAddr()})
requireCredentialIssuer(newSuccessStrategy())
// Simulate the informer cache's background update from its watch. // Simulate the informer cache's background update from its watch.
deleteSecretFromTracker(tlsSecretName, kubeInformerClient) deleteSecretFromTracker(tlsSecretName, kubeInformerClient)
@ -1265,6 +1365,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasCreated(kubeAPIClient.Actions()[6], ca) // reuses the old CA again requireTLSSecretWasCreated(kubeAPIClient.Actions()[6], ca) // reuses the old CA again
// Check that the server is running and that TLS certs that are being served are are for fakeIP. // Check that the server is running and that TLS certs that are being served are are for fakeIP.
requireTLSServerIsRunning(ca, fakeIP, map[string]string{fakeIP + httpsPort: testServerAddr()}) requireTLSServerIsRunning(ca, fakeIP, map[string]string{fakeIP + httpsPort: testServerAddr()})
requireCredentialIssuer(newSuccessStrategy())
}) })
}) })
@ -1285,6 +1386,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca) requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
// Check that the server is running and that TLS certs that are being served are are for fakeHostname. // Check that the server is running and that TLS certs that are being served are are for fakeHostname.
requireTLSServerIsRunning(ca, fakeHostname, map[string]string{fakeHostname + httpsPort: testServerAddr()}) requireTLSServerIsRunning(ca, fakeHostname, map[string]string{fakeHostname + httpsPort: testServerAddr()})
requireCredentialIssuer(newSuccessStrategy())
// Simulate the informer cache's background update from its watch for the CA Secret. // Simulate the informer cache's background update from its watch for the CA Secret.
addSecretFromCreateActionToTracker(kubeAPIClient.Actions()[1], kubeInformerClient, "1") addSecretFromCreateActionToTracker(kubeAPIClient.Actions()[1], kubeInformerClient, "1")
@ -1300,6 +1402,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasCreated(kubeAPIClient.Actions()[3], ca) requireTLSSecretWasCreated(kubeAPIClient.Actions()[3], ca)
// Check that the server is running and that TLS certs that are being served are are for fakeHostname. // Check that the server is running and that TLS certs that are being served are are for fakeHostname.
requireTLSServerIsRunning(ca, fakeHostname, map[string]string{fakeHostname + httpsPort: testServerAddr()}) requireTLSServerIsRunning(ca, fakeHostname, map[string]string{fakeHostname + httpsPort: testServerAddr()})
requireCredentialIssuer(newSuccessStrategy())
}) })
}) })
@ -1320,6 +1423,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca) requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
// Check that the server is running and that TLS certs that are being served are are for fakeHostname. // Check that the server is running and that TLS certs that are being served are are for fakeHostname.
requireTLSServerIsRunning(ca, fakeHostname, map[string]string{fakeHostname + httpsPort: testServerAddr()}) requireTLSServerIsRunning(ca, fakeHostname, map[string]string{fakeHostname + httpsPort: testServerAddr()})
requireCredentialIssuer(newSuccessStrategy())
// Simulate the informer cache's background update from its watch for the CA Secret. // Simulate the informer cache's background update from its watch for the CA Secret.
addSecretFromCreateActionToTracker(kubeAPIClient.Actions()[2], kubeInformerClient, "1") addSecretFromCreateActionToTracker(kubeAPIClient.Actions()[2], kubeInformerClient, "1")
@ -1337,6 +1441,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasCreated(kubeAPIClient.Actions()[5], ca) // created using the new CA requireTLSSecretWasCreated(kubeAPIClient.Actions()[5], ca) // created using the new CA
// Check that the server is running and that TLS certs that are being served are are for fakeHostname. // Check that the server is running and that TLS certs that are being served are are for fakeHostname.
requireTLSServerIsRunning(ca, fakeHostname, map[string]string{fakeHostname + httpsPort: testServerAddr()}) requireTLSServerIsRunning(ca, fakeHostname, map[string]string{fakeHostname + httpsPort: testServerAddr()})
requireCredentialIssuer(newSuccessStrategy())
}) })
}) })
@ -1355,6 +1460,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca) requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
// Check that the server is running and that TLS certs that are being served are are for fakeHostname. // Check that the server is running and that TLS certs that are being served are are for fakeHostname.
requireTLSServerIsRunning(ca, fakeHostname, map[string]string{fakeHostname + httpsPort: testServerAddr()}) requireTLSServerIsRunning(ca, fakeHostname, map[string]string{fakeHostname + httpsPort: testServerAddr()})
requireCredentialIssuer(newSuccessStrategy())
// Simulate the informer cache's background update from its watch for the CA Secret. // Simulate the informer cache's background update from its watch for the CA Secret.
addSecretFromCreateActionToTracker(kubeAPIClient.Actions()[2], kubeInformerClient, "1") addSecretFromCreateActionToTracker(kubeAPIClient.Actions()[2], kubeInformerClient, "1")
@ -1381,6 +1487,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasCreated(kubeAPIClient.Actions()[4], caCrt) // created using the updated CA requireTLSSecretWasCreated(kubeAPIClient.Actions()[4], caCrt) // created using the updated CA
// Check that the server is running and that TLS certs that are being served are are for fakeHostname. // Check that the server is running and that TLS certs that are being served are are for fakeHostname.
requireTLSServerIsRunning(caCrt, fakeHostname, map[string]string{fakeHostname + httpsPort: testServerAddr()}) requireTLSServerIsRunning(caCrt, fakeHostname, map[string]string{fakeHostname + httpsPort: testServerAddr()})
requireCredentialIssuer(newSuccessStrategy())
}) })
when("deleting the TLS cert due to mismatched CA results in an error", func() { when("deleting the TLS cert due to mismatched CA results in an error", func() {
@ -1397,6 +1504,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.Error(runControllerSync(), "error on tls secret delete") r.Error(runControllerSync(), "error on tls secret delete")
r.Len(kubeAPIClient.Actions(), 4) r.Len(kubeAPIClient.Actions(), 4)
requireTLSSecretWasDeleted(kubeAPIClient.Actions()[3]) // tried to delete cert but failed requireTLSSecretWasDeleted(kubeAPIClient.Actions()[3]) // tried to delete cert but failed
requireCredentialIssuer(newErrorStrategy("error on tls secret delete"))
}) })
}) })
}) })
@ -1417,6 +1525,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireNodesListed(kubeAPIClient.Actions()[0]) requireNodesListed(kubeAPIClient.Actions()[0])
requireLoadBalancerWasCreated(kubeAPIClient.Actions()[1]) requireLoadBalancerWasCreated(kubeAPIClient.Actions()[1])
requireCASecretWasCreated(kubeAPIClient.Actions()[2]) requireCASecretWasCreated(kubeAPIClient.Actions()[2])
requireCredentialIssuer(newPendingStrategy())
// Simulate the informer cache's background update from its watch. // Simulate the informer cache's background update from its watch.
addServiceFromCreateActionToTracker(kubeAPIClient.Actions()[1], kubeInformerClient, "1") addServiceFromCreateActionToTracker(kubeAPIClient.Actions()[1], kubeInformerClient, "1")
@ -1431,6 +1540,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSServerIsNoLongerRunning() requireTLSServerIsNoLongerRunning()
r.Len(kubeAPIClient.Actions(), 4) r.Len(kubeAPIClient.Actions(), 4)
requireLoadBalancerWasDeleted(kubeAPIClient.Actions()[3]) requireLoadBalancerWasDeleted(kubeAPIClient.Actions()[3])
requireCredentialIssuer(newManuallyDisabledStrategy())
deleteServiceFromTracker(loadBalancerServiceName, kubeInformerClient) deleteServiceFromTracker(loadBalancerServiceName, kubeInformerClient)
waitForServiceToBeDeleted(kubeInformers.Core().V1().Services(), loadBalancerServiceName) waitForServiceToBeDeleted(kubeInformers.Core().V1().Services(), loadBalancerServiceName)
@ -1442,6 +1552,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSServerIsRunningWithoutCerts() requireTLSServerIsRunningWithoutCerts()
r.Len(kubeAPIClient.Actions(), 5) r.Len(kubeAPIClient.Actions(), 5)
requireLoadBalancerWasCreated(kubeAPIClient.Actions()[4]) requireLoadBalancerWasCreated(kubeAPIClient.Actions()[4])
requireCredentialIssuer(newPendingStrategy())
}) })
when("there is an error while shutting down the server", func() { when("there is an error while shutting down the server", func() {
@ -1459,6 +1570,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.EqualError(runControllerSync(), "fake server close error") r.EqualError(runControllerSync(), "fake server close error")
requireTLSServerIsNoLongerRunning() requireTLSServerIsNoLongerRunning()
requireCredentialIssuer(newErrorStrategy("fake server close error"))
}) })
}) })
}) })
@ -1480,6 +1592,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
ca := requireCASecretWasCreated(kubeAPIClient.Actions()[1]) // created immediately because "endpoint" was specified ca := requireCASecretWasCreated(kubeAPIClient.Actions()[1]) // created immediately because "endpoint" was specified
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca) requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
requireTLSServerIsRunning(ca, testServerAddr(), nil) requireTLSServerIsRunning(ca, testServerAddr(), nil)
requireCredentialIssuer(newSuccessStrategy())
// Simulate the informer cache's background update from its watch. // Simulate the informer cache's background update from its watch.
addSecretFromCreateActionToTracker(kubeAPIClient.Actions()[1], kubeInformerClient, "1") addSecretFromCreateActionToTracker(kubeAPIClient.Actions()[1], kubeInformerClient, "1")
@ -1496,6 +1609,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireLoadBalancerWasCreated(kubeAPIClient.Actions()[3]) requireLoadBalancerWasCreated(kubeAPIClient.Actions()[3])
requireTLSSecretWasDeleted(kubeAPIClient.Actions()[4]) // the Secret was deleted because it contained a cert with the wrong IP requireTLSSecretWasDeleted(kubeAPIClient.Actions()[4]) // the Secret was deleted because it contained a cert with the wrong IP
requireTLSServerIsRunningWithoutCerts() requireTLSServerIsRunningWithoutCerts()
requireCredentialIssuer(newPendingStrategy())
// Simulate the informer cache's background update from its watch. // Simulate the informer cache's background update from its watch.
addServiceFromCreateActionToTracker(kubeAPIClient.Actions()[3], kubeInformerClient, "1") addServiceFromCreateActionToTracker(kubeAPIClient.Actions()[3], kubeInformerClient, "1")
@ -1507,6 +1621,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.NoError(runControllerSync()) r.NoError(runControllerSync())
r.Len(kubeAPIClient.Actions(), 5) // no new actions while it is waiting for the load balancer's ingress r.Len(kubeAPIClient.Actions(), 5) // no new actions while it is waiting for the load balancer's ingress
requireTLSServerIsRunningWithoutCerts() requireTLSServerIsRunningWithoutCerts()
requireCredentialIssuer(newPendingStrategy())
// Update the ingress of the LB in the informer's client and run Sync again. // Update the ingress of the LB in the informer's client and run Sync again.
fakeIP := "127.0.0.123" fakeIP := "127.0.0.123"
@ -1517,6 +1632,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasCreated(kubeAPIClient.Actions()[5], ca) // reuses the existing CA requireTLSSecretWasCreated(kubeAPIClient.Actions()[5], ca) // reuses the existing CA
// Check that the server is running and that TLS certs that are being served are are for fakeIP. // Check that the server is running and that TLS certs that are being served are are for fakeIP.
requireTLSServerIsRunning(ca, fakeIP, map[string]string{fakeIP + httpsPort: testServerAddr()}) requireTLSServerIsRunning(ca, fakeIP, map[string]string{fakeIP + httpsPort: testServerAddr()})
requireCredentialIssuer(newSuccessStrategy())
// Simulate the informer cache's background update from its watch. // Simulate the informer cache's background update from its watch.
addSecretFromCreateActionToTracker(kubeAPIClient.Actions()[5], kubeInformerClient, "3") addSecretFromCreateActionToTracker(kubeAPIClient.Actions()[5], kubeInformerClient, "3")
@ -1532,6 +1648,8 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireLoadBalancerWasDeleted(kubeAPIClient.Actions()[6]) requireLoadBalancerWasDeleted(kubeAPIClient.Actions()[6])
requireTLSSecretWasDeleted(kubeAPIClient.Actions()[7]) requireTLSSecretWasDeleted(kubeAPIClient.Actions()[7])
requireTLSSecretWasCreated(kubeAPIClient.Actions()[8], ca) // recreated because the endpoint was updated, reused the old CA requireTLSSecretWasCreated(kubeAPIClient.Actions()[8], ca) // recreated because the endpoint was updated, reused the old CA
requireTLSServerIsRunning(ca, testServerAddr(), nil)
requireCredentialIssuer(newSuccessStrategy())
}) })
}) })
}) })
@ -1549,6 +1667,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireLoadBalancerWasCreated(kubeAPIClient.Actions()[1]) requireLoadBalancerWasCreated(kubeAPIClient.Actions()[1])
requireCASecretWasCreated(kubeAPIClient.Actions()[2]) requireCASecretWasCreated(kubeAPIClient.Actions()[2])
requireTLSServerIsRunningWithoutCerts() requireTLSServerIsRunningWithoutCerts()
requireCredentialIssuer(newPendingStrategy())
// Simulate the informer cache's background update from its watch. // Simulate the informer cache's background update from its watch.
addServiceFromCreateActionToTracker(kubeAPIClient.Actions()[1], kubeInformerClient, "1") addServiceFromCreateActionToTracker(kubeAPIClient.Actions()[1], kubeInformerClient, "1")
@ -1559,7 +1678,8 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.NoError(runControllerSync()) r.NoError(runControllerSync())
r.Equal(1, startTLSListenerFuncWasCalled) // wasn't started a second time r.Equal(1, startTLSListenerFuncWasCalled) // wasn't started a second time
requireTLSServerIsRunningWithoutCerts() // still running requireTLSServerIsRunningWithoutCerts() // still running
r.Len(kubeAPIClient.Actions(), 3) // no new API calls requireCredentialIssuer(newPendingStrategy())
r.Len(kubeAPIClient.Actions(), 3) // no new API calls
}) })
it("creates certs from the ip address listed on the load balancer", func() { it("creates certs from the ip address listed on the load balancer", func() {
@ -1570,6 +1690,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireLoadBalancerWasCreated(kubeAPIClient.Actions()[1]) requireLoadBalancerWasCreated(kubeAPIClient.Actions()[1])
ca := requireCASecretWasCreated(kubeAPIClient.Actions()[2]) ca := requireCASecretWasCreated(kubeAPIClient.Actions()[2])
requireTLSServerIsRunningWithoutCerts() requireTLSServerIsRunningWithoutCerts()
requireCredentialIssuer(newPendingStrategy())
// Simulate the informer cache's background update from its watch. // Simulate the informer cache's background update from its watch.
addServiceFromCreateActionToTracker(kubeAPIClient.Actions()[1], kubeInformerClient, "0") addServiceFromCreateActionToTracker(kubeAPIClient.Actions()[1], kubeInformerClient, "0")
@ -1585,6 +1706,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.Len(kubeAPIClient.Actions(), 4) r.Len(kubeAPIClient.Actions(), 4)
requireTLSSecretWasCreated(kubeAPIClient.Actions()[3], ca) // uses the ca from last time requireTLSSecretWasCreated(kubeAPIClient.Actions()[3], ca) // uses the ca from last time
requireTLSServerIsRunning(ca, testServerAddr(), nil) // running with certs now requireTLSServerIsRunning(ca, testServerAddr(), nil) // running with certs now
requireCredentialIssuer(newSuccessStrategy())
// Simulate the informer cache's background update from its watch. // Simulate the informer cache's background update from its watch.
addSecretFromCreateActionToTracker(kubeAPIClient.Actions()[3], kubeInformerClient, "2") addSecretFromCreateActionToTracker(kubeAPIClient.Actions()[3], kubeInformerClient, "2")
@ -1594,6 +1716,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.Equal(1, startTLSListenerFuncWasCalled) // wasn't started again r.Equal(1, startTLSListenerFuncWasCalled) // wasn't started again
r.Len(kubeAPIClient.Actions(), 4) // no more actions r.Len(kubeAPIClient.Actions(), 4) // no more actions
requireTLSServerIsRunning(ca, testServerAddr(), nil) // still running requireTLSServerIsRunning(ca, testServerAddr(), nil) // still running
requireCredentialIssuer(newSuccessStrategy())
}) })
it("creates certs from the hostname listed on the load balancer", func() { it("creates certs from the hostname listed on the load balancer", func() {
@ -1605,6 +1728,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireLoadBalancerWasCreated(kubeAPIClient.Actions()[1]) requireLoadBalancerWasCreated(kubeAPIClient.Actions()[1])
ca := requireCASecretWasCreated(kubeAPIClient.Actions()[2]) ca := requireCASecretWasCreated(kubeAPIClient.Actions()[2])
requireTLSServerIsRunningWithoutCerts() requireTLSServerIsRunningWithoutCerts()
requireCredentialIssuer(newPendingStrategy())
// Simulate the informer cache's background update from its watch. // Simulate the informer cache's background update from its watch.
addServiceFromCreateActionToTracker(kubeAPIClient.Actions()[1], kubeInformerClient, "0") addServiceFromCreateActionToTracker(kubeAPIClient.Actions()[1], kubeInformerClient, "0")
@ -1620,6 +1744,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.Len(kubeAPIClient.Actions(), 4) r.Len(kubeAPIClient.Actions(), 4)
requireTLSSecretWasCreated(kubeAPIClient.Actions()[3], ca) // uses the ca from last time requireTLSSecretWasCreated(kubeAPIClient.Actions()[3], ca) // uses the ca from last time
requireTLSServerIsRunning(ca, hostname, map[string]string{hostname + httpsPort: testServerAddr()}) // running with certs now requireTLSServerIsRunning(ca, hostname, map[string]string{hostname + httpsPort: testServerAddr()}) // running with certs now
requireCredentialIssuer(newSuccessStrategy())
// Simulate the informer cache's background update from its watch. // Simulate the informer cache's background update from its watch.
addSecretFromCreateActionToTracker(kubeAPIClient.Actions()[3], kubeInformerClient, "1") addSecretFromCreateActionToTracker(kubeAPIClient.Actions()[3], kubeInformerClient, "1")
@ -1629,6 +1754,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.Equal(1, startTLSListenerFuncWasCalled) // wasn't started a third time r.Equal(1, startTLSListenerFuncWasCalled) // wasn't started a third time
r.Len(kubeAPIClient.Actions(), 4) // no more actions r.Len(kubeAPIClient.Actions(), 4) // no more actions
requireTLSServerIsRunning(ca, hostname, map[string]string{hostname + httpsPort: testServerAddr()}) // still running requireTLSServerIsRunning(ca, hostname, map[string]string{hostname + httpsPort: testServerAddr()}) // still running
requireCredentialIssuer(newSuccessStrategy())
}) })
}) })
@ -1636,6 +1762,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
it("returns an error", func() { it("returns an error", func() {
startInformersAndController() startInformersAndController()
r.EqualError(runControllerSync(), "no nodes found") r.EqualError(runControllerSync(), "no nodes found")
requireCredentialIssuer(newErrorStrategy("no nodes found"))
requireTLSServerWasNeverStarted() requireTLSServerWasNeverStarted()
}) })
}) })
@ -1649,6 +1776,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
it("returns an error", func() { it("returns an error", func() {
startInformersAndController() startInformersAndController()
r.EqualError(runControllerSync(), "some factory error") r.EqualError(runControllerSync(), "some factory error")
requireCredentialIssuer(newErrorStrategy("some factory error"))
requireTLSServerWasNeverStarted() requireTLSServerWasNeverStarted()
}) })
}) })
@ -1660,7 +1788,9 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
it("returns an error", func() { it("returns an error", func() {
startInformersAndController() startInformersAndController()
r.EqualError(runControllerSync(), "invalid impersonator configuration: decode yaml: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type impersonator.Config") errString := "invalid impersonator configuration: decode yaml: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type impersonator.Config"
r.EqualError(runControllerSync(), errString)
requireCredentialIssuer(newErrorStrategy(errString))
requireTLSServerWasNeverStarted() requireTLSServerWasNeverStarted()
}) })
}) })
@ -1668,14 +1798,16 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
when("there is an error creating the load balancer", func() { when("there is an error creating the load balancer", func() {
it.Before(func() { it.Before(func() {
addNodeWithRoleToTracker("worker", kubeAPIClient) addNodeWithRoleToTracker("worker", kubeAPIClient)
startInformersAndController()
kubeAPIClient.PrependReactor("create", "services", func(action coretesting.Action) (handled bool, ret runtime.Object, err error) { kubeAPIClient.PrependReactor("create", "services", func(action coretesting.Action) (handled bool, ret runtime.Object, err error) {
return true, nil, fmt.Errorf("error on create") return true, nil, fmt.Errorf("error on create")
}) })
}) })
it("exits with an error", func() { it("returns an error", func() {
startInformersAndController()
r.EqualError(runControllerSync(), "error on create") r.EqualError(runControllerSync(), "error on create")
requireCredentialIssuer(newErrorStrategy("error on create"))
requireTLSServerIsRunningWithoutCerts()
}) })
}) })
@ -1695,6 +1827,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
it("starts the impersonator without certs and returns an error", func() { it("starts the impersonator without certs and returns an error", func() {
startInformersAndController() startInformersAndController()
r.EqualError(runControllerSync(), "error on tls secret create") r.EqualError(runControllerSync(), "error on tls secret create")
requireCredentialIssuer(newErrorStrategy("error on tls secret create"))
requireTLSServerIsRunningWithoutCerts() requireTLSServerIsRunningWithoutCerts()
r.Len(kubeAPIClient.Actions(), 3) r.Len(kubeAPIClient.Actions(), 3)
requireNodesListed(kubeAPIClient.Actions()[0]) requireNodesListed(kubeAPIClient.Actions()[0])
@ -1719,6 +1852,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
it("starts the impersonator without certs and returns an error", func() { it("starts the impersonator without certs and returns an error", func() {
startInformersAndController() startInformersAndController()
r.EqualError(runControllerSync(), "error on ca secret create") r.EqualError(runControllerSync(), "error on ca secret create")
requireCredentialIssuer(newErrorStrategy("error on ca secret create"))
requireTLSServerIsRunningWithoutCerts() requireTLSServerIsRunningWithoutCerts()
r.Len(kubeAPIClient.Actions(), 2) r.Len(kubeAPIClient.Actions(), 2)
requireNodesListed(kubeAPIClient.Actions()[0]) requireNodesListed(kubeAPIClient.Actions()[0])
@ -1735,7 +1869,9 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
it("starts the impersonator without certs and returns an error", func() { it("starts the impersonator without certs and returns an error", func() {
startInformersAndController() startInformersAndController()
r.EqualError(runControllerSync(), "could not load CA: tls: failed to find any PEM data in certificate input") errString := "could not load CA: tls: failed to find any PEM data in certificate input"
r.EqualError(runControllerSync(), errString)
requireCredentialIssuer(newErrorStrategy(errString))
requireTLSServerIsRunningWithoutCerts() requireTLSServerIsRunningWithoutCerts()
r.Len(kubeAPIClient.Actions(), 1) r.Len(kubeAPIClient.Actions(), 1)
requireNodesListed(kubeAPIClient.Actions()[0]) requireNodesListed(kubeAPIClient.Actions()[0])
@ -1756,6 +1892,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
it("does not start the impersonator, deletes the loadbalancer, returns an error", func() { it("does not start the impersonator, deletes the loadbalancer, returns an error", func() {
r.EqualError(runControllerSync(), "error on delete") r.EqualError(runControllerSync(), "error on delete")
requireCredentialIssuer(newErrorStrategy("error on delete"))
requireTLSServerWasNeverStarted() requireTLSServerWasNeverStarted()
r.Len(kubeAPIClient.Actions(), 3) r.Len(kubeAPIClient.Actions(), 3)
requireNodesListed(kubeAPIClient.Actions()[0]) requireNodesListed(kubeAPIClient.Actions()[0])
@ -1791,6 +1928,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasDeleted(kubeAPIClient.Actions()[2]) // deleted the bad cert requireTLSSecretWasDeleted(kubeAPIClient.Actions()[2]) // deleted the bad cert
requireTLSSecretWasCreated(kubeAPIClient.Actions()[3], ca) requireTLSSecretWasCreated(kubeAPIClient.Actions()[3], ca)
requireTLSServerIsRunning(ca, testServerAddr(), nil) requireTLSServerIsRunning(ca, testServerAddr(), nil)
requireCredentialIssuer(newSuccessStrategy())
}) })
when("there is an error while the invalid cert is being deleted", func() { when("there is an error while the invalid cert is being deleted", func() {
@ -1802,7 +1940,9 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
it("tries to delete the invalid cert, starts the impersonator without certs, and returns an error", func() { it("tries to delete the invalid cert, starts the impersonator without certs, and returns an error", func() {
startInformersAndController() startInformersAndController()
r.EqualError(runControllerSync(), "PEM data represented an invalid cert, but got error while deleting it: error on delete") errString := "PEM data represented an invalid cert, but got error while deleting it: error on delete"
r.EqualError(runControllerSync(), errString)
requireCredentialIssuer(newErrorStrategy(errString))
requireTLSServerIsRunningWithoutCerts() requireTLSServerIsRunningWithoutCerts()
r.Len(kubeAPIClient.Actions(), 3) r.Len(kubeAPIClient.Actions(), 3)
requireNodesListed(kubeAPIClient.Actions()[0]) requireNodesListed(kubeAPIClient.Actions()[0])
@ -1835,6 +1975,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasDeleted(kubeAPIClient.Actions()[1]) // deleted the bad cert requireTLSSecretWasDeleted(kubeAPIClient.Actions()[1]) // deleted the bad cert
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], caCrt) requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], caCrt)
requireTLSServerIsRunning(caCrt, testServerAddr(), nil) requireTLSServerIsRunning(caCrt, testServerAddr(), nil)
requireCredentialIssuer(newSuccessStrategy())
}) })
when("there is an error while the invalid cert is being deleted", func() { when("there is an error while the invalid cert is being deleted", func() {
@ -1846,7 +1987,9 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
it("tries to delete the invalid cert, starts the impersonator without certs, and returns an error", func() { it("tries to delete the invalid cert, starts the impersonator without certs, and returns an error", func() {
startInformersAndController() startInformersAndController()
r.EqualError(runControllerSync(), "found missing or not PEM-encoded data in TLS Secret, but got error while deleting it: error on delete") errString := "found missing or not PEM-encoded data in TLS Secret, but got error while deleting it: error on delete"
r.EqualError(runControllerSync(), errString)
requireCredentialIssuer(newErrorStrategy(errString))
requireTLSServerIsRunningWithoutCerts() requireTLSServerIsRunningWithoutCerts()
r.Len(kubeAPIClient.Actions(), 2) r.Len(kubeAPIClient.Actions(), 2)
requireNodesListed(kubeAPIClient.Actions()[0]) requireNodesListed(kubeAPIClient.Actions()[0])
@ -1880,6 +2023,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasDeleted(kubeAPIClient.Actions()[1]) // deleted the bad cert requireTLSSecretWasDeleted(kubeAPIClient.Actions()[1]) // deleted the bad cert
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], caCrt) requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], caCrt)
requireTLSServerIsRunning(caCrt, testServerAddr(), nil) requireTLSServerIsRunning(caCrt, testServerAddr(), nil)
requireCredentialIssuer(newSuccessStrategy())
}) })
when("there is an error while the invalid cert is being deleted", func() { when("there is an error while the invalid cert is being deleted", func() {
@ -1891,7 +2035,9 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
it("tries to delete the invalid cert, starts the impersonator without certs, and returns an error", func() { it("tries to delete the invalid cert, starts the impersonator without certs, and returns an error", func() {
startInformersAndController() startInformersAndController()
r.EqualError(runControllerSync(), "cert had an invalid private key, but got error while deleting it: error on delete") errString := "cert had an invalid private key, but got error while deleting it: error on delete"
r.EqualError(runControllerSync(), errString)
requireCredentialIssuer(newErrorStrategy(errString))
requireTLSServerIsRunningWithoutCerts() requireTLSServerIsRunningWithoutCerts()
r.Len(kubeAPIClient.Actions(), 2) r.Len(kubeAPIClient.Actions(), 2)
requireNodesListed(kubeAPIClient.Actions()[0]) requireNodesListed(kubeAPIClient.Actions()[0])
@ -1900,5 +2046,32 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
}) })
}) })
}) })
when("there is an error while creating or updating the CredentialIssuer status", func() {
it.Before(func() {
addNodeWithRoleToTracker("worker", kubeAPIClient)
pinnipedAPIClient.PrependReactor("create", "credentialissuers", func(action coretesting.Action) (handled bool, ret runtime.Object, err error) {
return true, nil, fmt.Errorf("error on create")
})
})
it("returns the error", func() {
startInformersAndController()
r.EqualError(runControllerSync(), "could not create or update credentialissuer: create failed: error on create")
})
when("there is also a more fundamental error while starting the impersonator", func() {
it.Before(func() {
kubeAPIClient.PrependReactor("create", "services", func(action coretesting.Action) (handled bool, ret runtime.Object, err error) {
return true, nil, fmt.Errorf("error on service creation")
})
})
it("returns the more fundamental error instead of the CredentialIssuer error", func() {
startInformersAndController()
r.EqualError(runControllerSync(), "error on service creation")
})
})
})
}, spec.Parallel(), spec.Report(report.Terminal{})) }, spec.Parallel(), spec.Report(report.Terminal{}))
} }

View File

@ -277,12 +277,14 @@ func PrepareControllers(c *Config) (func(ctx context.Context), error) {
singletonWorker, singletonWorker,
). ).
// The impersonation proxy configuration controllers dynamically configure the impersonation proxy feature. // The impersonator configuration controller dynamically configures the impersonation proxy feature.
WithController( WithController(
impersonatorconfig.NewImpersonatorConfigController( impersonatorconfig.NewImpersonatorConfigController(
c.ServerInstallationInfo.Namespace, c.ServerInstallationInfo.Namespace,
c.NamesConfig.ImpersonationConfigMap, c.NamesConfig.ImpersonationConfigMap,
c.NamesConfig.CredentialIssuer,
client.Kubernetes, client.Kubernetes,
client.PinnipedConcierge,
informers.installationNamespaceK8s.Core().V1().ConfigMaps(), informers.installationNamespaceK8s.Core().V1().ConfigMaps(),
informers.installationNamespaceK8s.Core().V1().Services(), informers.installationNamespaceK8s.Core().V1().Services(),
informers.installationNamespaceK8s.Core().V1().Secrets(), informers.installationNamespaceK8s.Core().V1().Secrets(),
@ -292,6 +294,7 @@ func PrepareControllers(c *Config) (func(ctx context.Context), error) {
c.NamesConfig.ImpersonationTLSCertificateSecret, c.NamesConfig.ImpersonationTLSCertificateSecret,
c.NamesConfig.ImpersonationCACertificateSecret, c.NamesConfig.ImpersonationCACertificateSecret,
c.Labels, c.Labels,
clock.RealClock{},
tls.Listen, tls.Listen,
func() (http.Handler, error) { func() (http.Handler, error) {
impersonationProxyHandler, err := impersonator.New( impersonationProxyHandler, err := impersonator.New(