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"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/clock"
"k8s.io/apimachinery/pkg/util/intstr"
corev1informers "k8s.io/client-go/informers/core/v1"
"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/clusterhost"
"go.pinniped.dev/internal/concierge/impersonator"
pinnipedcontroller "go.pinniped.dev/internal/controller"
"go.pinniped.dev/internal/controller/issuerconfig"
"go.pinniped.dev/internal/controllerlib"
"go.pinniped.dev/internal/plog"
)
@ -40,19 +44,29 @@ const (
caCrtKey = "ca.crt"
caKeyKey = "ca.key"
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 {
namespace string
configMapResourceName string
k8sClient kubernetes.Interface
configMapsInformer corev1informers.ConfigMapInformer
servicesInformer corev1informers.ServiceInformer
secretsInformer corev1informers.SecretInformer
credentialIssuerResourceName string
generatedLoadBalancerServiceName string
tlsSecretName string
caSecretName string
k8sClient kubernetes.Interface
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)
@ -67,7 +81,9 @@ type StartTLSListenerFunc func(network, listenAddress string, config *tls.Config
func NewImpersonatorConfigController(
namespace string,
configMapResourceName string,
credentialIssuerResourceName string,
k8sClient kubernetes.Interface,
pinnipedAPIClient pinnipedclientset.Interface,
configMapsInformer corev1informers.ConfigMapInformer,
servicesInformer corev1informers.ServiceInformer,
secretsInformer corev1informers.SecretInformer,
@ -77,6 +93,7 @@ func NewImpersonatorConfigController(
tlsSecretName string,
caSecretName string,
labels map[string]string,
clock clock.Clock,
startTLSListenerFunc StartTLSListenerFunc,
httpHandlerFactory func() (http.Handler, error),
) controllerlib.Controller {
@ -86,14 +103,17 @@ func NewImpersonatorConfigController(
Syncer: &impersonatorConfigController{
namespace: namespace,
configMapResourceName: configMapResourceName,
k8sClient: k8sClient,
configMapsInformer: configMapsInformer,
servicesInformer: servicesInformer,
secretsInformer: secretsInformer,
credentialIssuerResourceName: credentialIssuerResourceName,
generatedLoadBalancerServiceName: generatedLoadBalancerServiceName,
tlsSecretName: tlsSecretName,
caSecretName: caSecretName,
k8sClient: k8sClient,
pinnipedAPIClient: pinnipedAPIClient,
configMapsInformer: configMapsInformer,
servicesInformer: servicesInformer,
secretsInformer: secretsInformer,
labels: labels,
clock: clock,
startTLSListenerFunc: startTLSListenerFunc,
httpHandlerFactory: httpHandlerFactory,
},
@ -126,11 +146,37 @@ func NewImpersonatorConfigController(
func (c *impersonatorConfigController) Sync(syncCtx controllerlib.Context) error {
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()
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,
@ -140,7 +186,7 @@ func (c *impersonatorConfigController) Sync(syncCtx controllerlib.Context) error
if c.hasControlPlaneNodes == nil {
hasControlPlaneNodes, err := clusterhost.New(c.k8sClient).HasControlPlaneNodes(ctx)
if err != nil {
return err
return nil, err
}
c.hasControlPlaneNodes = &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 err = c.ensureImpersonatorIsStarted(); err != nil {
return err
return nil, err
}
} else {
if err = c.ensureImpersonatorIsStopped(); err != nil {
return err
return nil, err
}
}
if c.shouldHaveLoadBalancer(config) {
if err = c.ensureLoadBalancerIsStarted(ctx); err != nil {
return err
return nil, err
}
} else {
if err = c.ensureLoadBalancerIsStopped(ctx); err != nil {
return err
return nil, err
}
}
waitingForLoadBalancer := false
if c.shouldHaveTLSSecret(config) {
var impersonationCA *certauthority.CA
if impersonationCA, err = c.ensureCASecretIsCreated(ctx); err != nil {
return err
return nil, err
}
if err = c.ensureTLSSecret(ctx, config, impersonationCA); err != nil {
return err
if waitingForLoadBalancer, err = c.ensureTLSSecret(ctx, config, impersonationCA); err != nil {
return nil, err
}
} else if err = c.ensureTLSSecretIsRemoved(ctx); err != nil {
return err
return nil, err
}
plog.Debug("Successfully finished impersonatorConfigController Sync")
return nil
return c.doSyncResult(waitingForLoadBalancer, config), nil
}
func (c *impersonatorConfigController) loadImpersonationProxyConfiguration() (*impersonator.Config, error) {
@ -212,7 +257,19 @@ func (c *impersonatorConfigController) loadImpersonationProxyConfiguration() (*i
}
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 {
@ -223,6 +280,10 @@ func (c *impersonatorConfigController) shouldHaveTLSSecret(config *impersonator.
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) {
_, err := c.servicesInformer.Lister().Services(c.namespace).Get(c.generatedLoadBalancerServiceName)
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{})
}
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)
notFound := k8serrors.IsNotFound(err)
if !notFound && err != nil {
return err
return false, err
}
if !notFound {
secretWasDeleted, err := c.deleteTLSSecretWhenCertificateDoesNotMatchDesiredState(ctx, config, ca, secretFromInformer)
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
// 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
}
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 {
err := c.loadTLSCertFromSecret(secret)
if err != nil {
return err
return false, err
}
return nil
return false, nil
}
ip, hostname, nameIsReady, err := c.findDesiredTLSCertificateName(config)
if err != nil {
return err
return false, err
}
if !nameIsReady {
// 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)
if err != nil {
return err
return false, err
}
err = c.loadTLSCertFromSecret(newTLSSecret)
if err != nil {
return err
return false, err
}
return nil
return false, nil
}
func (c *impersonatorConfigController) ensureCASecretIsCreated(ctx context.Context) (*certauthority.CA, error) {
@ -672,6 +734,43 @@ func (c *impersonatorConfigController) ensureTLSSecretIsRemoved(ctx context.Cont
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) {
c.tlsCertMutex.Lock()
defer c.tlsCertMutex.Unlock()

View File

@ -26,12 +26,15 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/clock"
kubeinformers "k8s.io/client-go/informers"
corev1informers "k8s.io/client-go/informers/core/v1"
kubernetesfake "k8s.io/client-go/kubernetes/fake"
coretesting "k8s.io/client-go/testing"
"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/controllerlib"
"go.pinniped.dev/internal/testutil"
@ -86,6 +89,8 @@ func TestImpersonatorConfigControllerOptions(t *testing.T) {
_ = NewImpersonatorConfigController(
installedInNamespace,
configMapResourceName,
"",
nil,
nil,
configMapsInformer,
servicesInformer,
@ -98,6 +103,7 @@ func TestImpersonatorConfigControllerOptions(t *testing.T) {
nil,
nil,
nil,
nil,
)
configMapsInformerFilter = observableWithInformerOption.GetFilterForInformer(configMapsInformer)
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) {
const installedInNamespace = "some-namespace"
const configMapResourceName = "some-configmap-resource-name"
const credentialIssuerResourceName = "some-credential-issuer-resource-name"
const loadBalancerServiceName = "some-service-resource-name"
const tlsSecretName = "some-tls-secret-name" //nolint:gosec // this is not a credential
const caSecretName = "some-ca-secret-name"
@ -284,6 +291,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
var subject controllerlib.Controller
var kubeAPIClient *kubernetesfake.Clientset
var pinnipedAPIClient *pinnipedfake.Clientset
var kubeInformerClient *kubernetesfake.Clientset
var kubeInformers kubeinformers.SharedInformerFactory
var timeoutContext context.Context
@ -294,6 +302,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
var startTLSListenerUponCloseError error
var httpHandlerFactoryFuncError error
var startedTLSListener net.Listener
var frozenNow time.Time
var startTLSListenerFunc = func(network, listenAddress string, config *tls.Config) (net.Listener, error) {
startTLSListenerFuncWasCalled++
@ -423,7 +432,9 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
subject = NewImpersonatorConfigController(
installedInNamespace,
configMapResourceName,
credentialIssuerResourceName,
kubeAPIClient,
pinnipedAPIClient,
kubeInformers.Core().V1().ConfigMaps(),
kubeInformers.Core().V1().Services(),
kubeInformers.Core().V1().Secrets(),
@ -433,6 +444,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
tlsSecretName,
caSecretName,
labels,
clock.NewFakeClock(frozenNow),
startTLSListenerFunc,
func() (http.Handler, error) {
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) {
createAction, ok := action.(coretesting.CreateAction)
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),
)
kubeAPIClient = kubernetesfake.NewSimpleClientset()
pinnipedAPIClient = pinnipedfake.NewSimpleClientset()
frozenNow = time.Date(2021, time.March, 2, 7, 42, 0, 0, time.Local)
})
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() {
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() {
@ -761,6 +843,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSServerWasNeverStarted()
r.Len(kubeAPIClient.Actions(), 1)
requireNodesListed(kubeAPIClient.Actions()[0])
requireCredentialIssuer(newAutoDisabledStrategy())
})
})
@ -780,6 +863,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireNodesListed(kubeAPIClient.Actions()[0])
requireLoadBalancerWasDeleted(kubeAPIClient.Actions()[1])
requireTLSSecretWasDeleted(kubeAPIClient.Actions()[2])
requireCredentialIssuer(newAutoDisabledStrategy())
})
})
@ -796,10 +880,11 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireNodesListed(kubeAPIClient.Actions()[0])
requireLoadBalancerWasCreated(kubeAPIClient.Actions()[1])
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() {
addNodeWithRoleToTracker("worker", kubeAPIClient)
addLoadBalancerServiceToTracker(loadBalancerServiceName, kubeInformerClient)
@ -813,6 +898,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.Len(kubeAPIClient.Actions(), 2)
requireNodesListed(kubeAPIClient.Actions()[0])
requireCASecretWasCreated(kubeAPIClient.Actions()[1])
requireCredentialIssuer(newPendingStrategy())
})
})
@ -830,6 +916,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.Len(kubeAPIClient.Actions(), 2)
requireNodesListed(kubeAPIClient.Actions()[0])
requireCASecretWasCreated(kubeAPIClient.Actions()[1])
requireCredentialIssuer(newPendingStrategy())
})
})
@ -847,6 +934,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.Len(kubeAPIClient.Actions(), 2)
requireNodesListed(kubeAPIClient.Actions()[0])
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])
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
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() {
@ -876,6 +965,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.NoError(runControllerSync())
r.Len(kubeAPIClient.Actions(), 3) // nothing changed
requireCredentialIssuer(newSuccessStrategy())
})
})
@ -895,6 +985,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
ca := requireCASecretWasCreated(kubeAPIClient.Actions()[1])
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
requireTLSServerIsRunning(ca, firstHostname, map[string]string{firstHostname + httpsPort: testServerAddr()})
requireCredentialIssuer(newSuccessStrategy())
})
it("keeps the secret around after resync", func() {
@ -906,6 +997,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.NoError(runControllerSync())
r.Len(kubeAPIClient.Actions(), 3) // nothing changed
requireCredentialIssuer(newSuccessStrategy())
})
})
@ -925,6 +1017,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
ca := requireCASecretWasCreated(kubeAPIClient.Actions()[1])
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
requireTLSServerIsRunning(ca, firstHostname, map[string]string{firstHostname + httpsPort: testServerAddr()})
requireCredentialIssuer(newSuccessStrategy())
})
it("keeps the secret around after resync", func() {
@ -936,6 +1029,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.NoError(runControllerSync())
r.Len(kubeAPIClient.Actions(), 3) // nothing changed
requireCredentialIssuer(newSuccessStrategy())
})
})
@ -960,6 +1054,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasDeleted(kubeAPIClient.Actions()[1])
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], caCrt)
requireTLSServerIsRunning(caCrt, testServerAddr(), nil)
requireCredentialIssuer(newSuccessStrategy())
})
})
@ -983,6 +1078,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireNodesListed(kubeAPIClient.Actions()[0])
requireTLSSecretWasDeleted(kubeAPIClient.Actions()[1])
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")
waitForInformerCacheToSeeResourceVersion(kubeInformers.Core().V1().Services().Informer(), "1")
r.EqualError(runControllerSync(),
"could not find valid IP addresses or hostnames from load balancer some-namespace/some-service-resource-name")
errString := "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
requireTLSServerIsRunning(caCrt, testServerAddr(), nil)
requireCredentialIssuer(newErrorStrategy(errString))
})
})
})
@ -1036,6 +1133,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSServerWasNeverStarted()
requireNodesListed(kubeAPIClient.Actions()[0])
r.Len(kubeAPIClient.Actions(), 1)
requireCredentialIssuer(newAutoDisabledStrategy())
})
})
@ -1052,6 +1150,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
ca := requireCASecretWasCreated(kubeAPIClient.Actions()[1])
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
requireTLSServerIsRunning(ca, testServerAddr(), nil)
requireCredentialIssuer(newSuccessStrategy())
})
})
})
@ -1068,6 +1167,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSServerWasNeverStarted()
requireNodesListed(kubeAPIClient.Actions()[0])
r.Len(kubeAPIClient.Actions(), 1)
requireCredentialIssuer(newManuallyDisabledStrategy())
})
})
@ -1078,25 +1178,22 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
addNodeWithRoleToTracker("control-plane", kubeAPIClient)
})
it("starts the impersonator", 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() {
it("starts the impersonator and creates a load balancer", func() {
startInformersAndController()
r.NoError(runControllerSync())
r.Len(kubeAPIClient.Actions(), 3)
requireNodesListed(kubeAPIClient.Actions()[0])
requireLoadBalancerWasCreated(kubeAPIClient.Actions()[1])
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)
})
it("starts the impersonator", func() {
it("starts the impersonator without creating a load balancer", func() {
startInformersAndController()
r.NoError(runControllerSync())
r.Len(kubeAPIClient.Actions(), 2)
requireNodesListed(kubeAPIClient.Actions()[0])
requireCASecretWasCreated(kubeAPIClient.Actions()[1])
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")
})
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])
requireCredentialIssuer(newErrorStrategy("tls error"))
})
})
@ -1150,10 +1244,11 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.Len(kubeAPIClient.Actions(), 1)
requireNodesListed(kubeAPIClient.Actions()[0])
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"
it.Before(func() {
configMapYAML := fmt.Sprintf("{mode: enabled, endpoint: %s}", fakeHostname)
@ -1161,7 +1256,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
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()
r.NoError(runControllerSync())
r.Len(kubeAPIClient.Actions(), 3)
@ -1170,10 +1265,11 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
// 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()})
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"
it.Before(func() {
configMapYAML := fmt.Sprintf("{mode: enabled, endpoint: %s}", fakeIPWithPort)
@ -1181,7 +1277,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
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()
r.NoError(runControllerSync())
r.Len(kubeAPIClient.Actions(), 3)
@ -1190,10 +1286,11 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
// 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()})
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"
it.Before(func() {
configMapYAML := fmt.Sprintf("{mode: enabled, endpoint: %s}", fakeHostnameWithPort)
@ -1201,7 +1298,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
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()
r.NoError(runControllerSync())
r.Len(kubeAPIClient.Actions(), 3)
@ -1210,10 +1307,11 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
// 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()})
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 fakeIP = "127.0.0.42"
var hostnameYAML = fmt.Sprintf("{mode: enabled, endpoint: %s}", fakeHostname)
@ -1232,6 +1330,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
// 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()})
requireCredentialIssuer(newSuccessStrategy())
// Simulate the informer cache's background update from its watch.
addSecretFromCreateActionToTracker(kubeAPIClient.Actions()[1], kubeInformerClient, "1")
@ -1249,6 +1348,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
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.
requireTLSServerIsRunning(ca, fakeHostname, map[string]string{fakeHostname + httpsPort: testServerAddr()})
requireCredentialIssuer(newSuccessStrategy())
// Simulate the informer cache's background update from its watch.
deleteSecretFromTracker(tlsSecretName, kubeInformerClient)
@ -1265,6 +1365,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
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.
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)
// 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()})
requireCredentialIssuer(newSuccessStrategy())
// Simulate the informer cache's background update from its watch for the CA Secret.
addSecretFromCreateActionToTracker(kubeAPIClient.Actions()[1], kubeInformerClient, "1")
@ -1300,6 +1402,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasCreated(kubeAPIClient.Actions()[3], ca)
// 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()})
requireCredentialIssuer(newSuccessStrategy())
})
})
@ -1320,6 +1423,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
// 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()})
requireCredentialIssuer(newSuccessStrategy())
// Simulate the informer cache's background update from its watch for the CA Secret.
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
// 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()})
requireCredentialIssuer(newSuccessStrategy())
})
})
@ -1355,6 +1460,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
// 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()})
requireCredentialIssuer(newSuccessStrategy())
// Simulate the informer cache's background update from its watch for the CA Secret.
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
// 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()})
requireCredentialIssuer(newSuccessStrategy())
})
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.Len(kubeAPIClient.Actions(), 4)
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])
requireLoadBalancerWasCreated(kubeAPIClient.Actions()[1])
requireCASecretWasCreated(kubeAPIClient.Actions()[2])
requireCredentialIssuer(newPendingStrategy())
// Simulate the informer cache's background update from its watch.
addServiceFromCreateActionToTracker(kubeAPIClient.Actions()[1], kubeInformerClient, "1")
@ -1431,6 +1540,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSServerIsNoLongerRunning()
r.Len(kubeAPIClient.Actions(), 4)
requireLoadBalancerWasDeleted(kubeAPIClient.Actions()[3])
requireCredentialIssuer(newManuallyDisabledStrategy())
deleteServiceFromTracker(loadBalancerServiceName, kubeInformerClient)
waitForServiceToBeDeleted(kubeInformers.Core().V1().Services(), loadBalancerServiceName)
@ -1442,6 +1552,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSServerIsRunningWithoutCerts()
r.Len(kubeAPIClient.Actions(), 5)
requireLoadBalancerWasCreated(kubeAPIClient.Actions()[4])
requireCredentialIssuer(newPendingStrategy())
})
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")
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
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
requireTLSServerIsRunning(ca, testServerAddr(), nil)
requireCredentialIssuer(newSuccessStrategy())
// Simulate the informer cache's background update from its watch.
addSecretFromCreateActionToTracker(kubeAPIClient.Actions()[1], kubeInformerClient, "1")
@ -1496,6 +1609,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireLoadBalancerWasCreated(kubeAPIClient.Actions()[3])
requireTLSSecretWasDeleted(kubeAPIClient.Actions()[4]) // the Secret was deleted because it contained a cert with the wrong IP
requireTLSServerIsRunningWithoutCerts()
requireCredentialIssuer(newPendingStrategy())
// Simulate the informer cache's background update from its watch.
addServiceFromCreateActionToTracker(kubeAPIClient.Actions()[3], kubeInformerClient, "1")
@ -1507,6 +1621,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.NoError(runControllerSync())
r.Len(kubeAPIClient.Actions(), 5) // no new actions while it is waiting for the load balancer's ingress
requireTLSServerIsRunningWithoutCerts()
requireCredentialIssuer(newPendingStrategy())
// Update the ingress of the LB in the informer's client and run Sync again.
fakeIP := "127.0.0.123"
@ -1517,6 +1632,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
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.
requireTLSServerIsRunning(ca, fakeIP, map[string]string{fakeIP + httpsPort: testServerAddr()})
requireCredentialIssuer(newSuccessStrategy())
// Simulate the informer cache's background update from its watch.
addSecretFromCreateActionToTracker(kubeAPIClient.Actions()[5], kubeInformerClient, "3")
@ -1532,6 +1648,8 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireLoadBalancerWasDeleted(kubeAPIClient.Actions()[6])
requireTLSSecretWasDeleted(kubeAPIClient.Actions()[7])
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])
requireCASecretWasCreated(kubeAPIClient.Actions()[2])
requireTLSServerIsRunningWithoutCerts()
requireCredentialIssuer(newPendingStrategy())
// Simulate the informer cache's background update from its watch.
addServiceFromCreateActionToTracker(kubeAPIClient.Actions()[1], kubeInformerClient, "1")
@ -1559,6 +1678,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.NoError(runControllerSync())
r.Equal(1, startTLSListenerFuncWasCalled) // wasn't started a second time
requireTLSServerIsRunningWithoutCerts() // still running
requireCredentialIssuer(newPendingStrategy())
r.Len(kubeAPIClient.Actions(), 3) // no new API calls
})
@ -1570,6 +1690,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireLoadBalancerWasCreated(kubeAPIClient.Actions()[1])
ca := requireCASecretWasCreated(kubeAPIClient.Actions()[2])
requireTLSServerIsRunningWithoutCerts()
requireCredentialIssuer(newPendingStrategy())
// Simulate the informer cache's background update from its watch.
addServiceFromCreateActionToTracker(kubeAPIClient.Actions()[1], kubeInformerClient, "0")
@ -1585,6 +1706,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.Len(kubeAPIClient.Actions(), 4)
requireTLSSecretWasCreated(kubeAPIClient.Actions()[3], ca) // uses the ca from last time
requireTLSServerIsRunning(ca, testServerAddr(), nil) // running with certs now
requireCredentialIssuer(newSuccessStrategy())
// Simulate the informer cache's background update from its watch.
addSecretFromCreateActionToTracker(kubeAPIClient.Actions()[3], kubeInformerClient, "2")
@ -1594,6 +1716,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.Equal(1, startTLSListenerFuncWasCalled) // wasn't started again
r.Len(kubeAPIClient.Actions(), 4) // no more actions
requireTLSServerIsRunning(ca, testServerAddr(), nil) // still running
requireCredentialIssuer(newSuccessStrategy())
})
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])
ca := requireCASecretWasCreated(kubeAPIClient.Actions()[2])
requireTLSServerIsRunningWithoutCerts()
requireCredentialIssuer(newPendingStrategy())
// Simulate the informer cache's background update from its watch.
addServiceFromCreateActionToTracker(kubeAPIClient.Actions()[1], kubeInformerClient, "0")
@ -1620,6 +1744,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.Len(kubeAPIClient.Actions(), 4)
requireTLSSecretWasCreated(kubeAPIClient.Actions()[3], ca) // uses the ca from last time
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.
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.Len(kubeAPIClient.Actions(), 4) // no more actions
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() {
startInformersAndController()
r.EqualError(runControllerSync(), "no nodes found")
requireCredentialIssuer(newErrorStrategy("no nodes found"))
requireTLSServerWasNeverStarted()
})
})
@ -1649,6 +1776,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
it("returns an error", func() {
startInformersAndController()
r.EqualError(runControllerSync(), "some factory error")
requireCredentialIssuer(newErrorStrategy("some factory error"))
requireTLSServerWasNeverStarted()
})
})
@ -1660,7 +1788,9 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
it("returns an error", func() {
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()
})
})
@ -1668,14 +1798,16 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
when("there is an error creating the load balancer", func() {
it.Before(func() {
addNodeWithRoleToTracker("worker", kubeAPIClient)
startInformersAndController()
kubeAPIClient.PrependReactor("create", "services", func(action coretesting.Action) (handled bool, ret runtime.Object, err error) {
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")
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() {
startInformersAndController()
r.EqualError(runControllerSync(), "error on tls secret create")
requireCredentialIssuer(newErrorStrategy("error on tls secret create"))
requireTLSServerIsRunningWithoutCerts()
r.Len(kubeAPIClient.Actions(), 3)
requireNodesListed(kubeAPIClient.Actions()[0])
@ -1719,6 +1852,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
it("starts the impersonator without certs and returns an error", func() {
startInformersAndController()
r.EqualError(runControllerSync(), "error on ca secret create")
requireCredentialIssuer(newErrorStrategy("error on ca secret create"))
requireTLSServerIsRunningWithoutCerts()
r.Len(kubeAPIClient.Actions(), 2)
requireNodesListed(kubeAPIClient.Actions()[0])
@ -1735,7 +1869,9 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
it("starts the impersonator without certs and returns an error", func() {
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()
r.Len(kubeAPIClient.Actions(), 1)
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() {
r.EqualError(runControllerSync(), "error on delete")
requireCredentialIssuer(newErrorStrategy("error on delete"))
requireTLSServerWasNeverStarted()
r.Len(kubeAPIClient.Actions(), 3)
requireNodesListed(kubeAPIClient.Actions()[0])
@ -1791,6 +1928,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasDeleted(kubeAPIClient.Actions()[2]) // deleted the bad cert
requireTLSSecretWasCreated(kubeAPIClient.Actions()[3], ca)
requireTLSServerIsRunning(ca, testServerAddr(), nil)
requireCredentialIssuer(newSuccessStrategy())
})
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() {
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()
r.Len(kubeAPIClient.Actions(), 3)
requireNodesListed(kubeAPIClient.Actions()[0])
@ -1835,6 +1975,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasDeleted(kubeAPIClient.Actions()[1]) // deleted the bad cert
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], caCrt)
requireTLSServerIsRunning(caCrt, testServerAddr(), nil)
requireCredentialIssuer(newSuccessStrategy())
})
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() {
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()
r.Len(kubeAPIClient.Actions(), 2)
requireNodesListed(kubeAPIClient.Actions()[0])
@ -1880,6 +2023,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireTLSSecretWasDeleted(kubeAPIClient.Actions()[1]) // deleted the bad cert
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], caCrt)
requireTLSServerIsRunning(caCrt, testServerAddr(), nil)
requireCredentialIssuer(newSuccessStrategy())
})
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() {
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()
r.Len(kubeAPIClient.Actions(), 2)
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{}))
}

View File

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