WIP: not using impersonator.config just credentialissuer directly

Signed-off-by: Matt Moyer <moyerm@vmware.com>
This commit is contained in:
Margo Crawford 2021-05-18 12:16:27 -07:00 committed by Matt Moyer
parent 9af3cb1115
commit 51f1a0ec13
4 changed files with 709 additions and 819 deletions

View File

@ -1,83 +0,0 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package impersonator
import (
"fmt"
v1 "k8s.io/api/core/v1"
"sigs.k8s.io/yaml"
"go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
)
type Mode string
const (
// Explicitly enable the impersonation proxy.
ModeEnabled Mode = "enabled"
// Explicitly disable the impersonation proxy.
ModeDisabled Mode = "disabled"
// Allow the proxy to decide if it should be enabled or disabled based upon the cluster in which it is running.
ModeAuto Mode = "auto"
)
const (
ConfigMapDataKey = "config.yaml"
)
type Config struct {
// Enable or disable the impersonation proxy. Optional. Defaults to ModeAuto.
Mode Mode `json:"mode,omitempty"`
// Used when creating TLS certificates and for clients to discover the endpoint. Optional. When not specified, if the
// impersonation proxy is started, then it will automatically create a LoadBalancer Service and use its ingress as the
// endpoint.
//
// When specified, it may be a hostname or IP address, optionally with a port number, of the impersonation proxy
// for clients to use from outside the cluster. E.g. myhost.mycompany.com:8443. Clients should assume that they should
// connect via HTTPS to this service.
Endpoint string `json:"endpoint,omitempty"`
}
func (c *Config) HasEndpoint() bool {
return c.Endpoint != ""
}
func NewConfig() *Config {
return &Config{Mode: ModeAuto}
}
func ConfigFromConfigMap(configMap *v1.ConfigMap) (*Config, error) {
stringConfig, ok := configMap.Data[ConfigMapDataKey]
if !ok {
return nil, fmt.Errorf(`ConfigMap is missing expected key "%s"`, ConfigMapDataKey)
}
config := NewConfig()
if err := yaml.Unmarshal([]byte(stringConfig), config); err != nil {
return nil, fmt.Errorf("decode yaml: %w", err)
}
if config.Mode != ModeAuto && config.Mode != ModeEnabled && config.Mode != ModeDisabled {
return nil, fmt.Errorf(`illegal value for "mode": %s`, config.Mode)
}
return config, nil
}
func ConfigFromCredentialIssuer(credIssuer *v1alpha1.CredentialIssuer) (*Config, error) {
config := NewConfig()
switch mode := credIssuer.Spec.ImpersonationProxy.Mode; mode {
case v1alpha1.ImpersonationProxyModeAuto:
config.Mode = ModeAuto
case v1alpha1.ImpersonationProxyModeDisabled:
config.Mode = ModeDisabled
case v1alpha1.ImpersonationProxyModeEnabled:
config.Mode = ModeEnabled
default:
return nil, fmt.Errorf("invalid impersonation proxy mode %q, valid values are auto, disabled, or enabled", mode)
}
config.Endpoint = credIssuer.Spec.ImpersonationProxy.ExternalEndpoint
return config, nil
}

View File

@ -1,155 +0,0 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package impersonator
import (
"testing"
"github.com/stretchr/testify/require"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"go.pinniped.dev/internal/here"
)
func TestNewConfig(t *testing.T) {
// It defaults the mode.
require.Equal(t, &Config{Mode: ModeAuto}, NewConfig())
}
func TestHasEndpoint(t *testing.T) {
configWithoutEndpoint := Config{}
configWithEndpoint := Config{Endpoint: "something"}
require.False(t, configWithoutEndpoint.HasEndpoint())
require.True(t, configWithEndpoint.HasEndpoint())
}
func TestConfigFromConfigMap(t *testing.T) {
tests := []struct {
name string
configMap *v1.ConfigMap
wantConfig *Config
wantError string
}{
{
name: "fully configured, valid config",
configMap: &v1.ConfigMap{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{},
Data: map[string]string{
"config.yaml": here.Doc(`
mode: enabled
endpoint: proxy.example.com:8443
`),
},
},
wantConfig: &Config{
Mode: "enabled",
Endpoint: "proxy.example.com:8443",
},
},
{
name: "empty, valid config",
configMap: &v1.ConfigMap{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{},
Data: map[string]string{
"config.yaml": "",
},
},
wantConfig: &Config{
Mode: "auto",
Endpoint: "",
},
},
{
name: "valid config with mode enabled",
configMap: &v1.ConfigMap{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{},
Data: map[string]string{
"config.yaml": "mode: enabled",
},
},
wantConfig: &Config{
Mode: "enabled",
Endpoint: "",
},
},
{
name: "valid config with mode disabled",
configMap: &v1.ConfigMap{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{},
Data: map[string]string{
"config.yaml": "mode: disabled",
},
},
wantConfig: &Config{
Mode: "disabled",
Endpoint: "",
},
},
{
name: "valid config with mode auto",
configMap: &v1.ConfigMap{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{},
Data: map[string]string{
"config.yaml": "mode: auto",
},
},
wantConfig: &Config{
Mode: "auto",
Endpoint: "",
},
},
{
name: "wrong key in configmap",
configMap: &v1.ConfigMap{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{},
Data: map[string]string{
"wrong-key": "",
},
},
wantError: `ConfigMap is missing expected key "config.yaml"`,
},
{
name: "illegal yaml in configmap",
configMap: &v1.ConfigMap{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{},
Data: map[string]string{
"config.yaml": "this is not yaml",
},
},
wantError: "decode yaml: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type impersonator.Config",
},
{
name: "illegal value for mode in configmap",
configMap: &v1.ConfigMap{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{},
Data: map[string]string{
"config.yaml": "mode: unexpected-value",
},
},
wantError: `illegal value for "mode": unexpected-value`,
},
}
for _, tt := range tests {
test := tt
t.Run(test.name, func(t *testing.T) {
config, err := ConfigFromConfigMap(test.configMap)
require.Equal(t, test.wantConfig, config)
if test.wantError != "" {
require.EqualError(t, err, test.wantError)
} else {
require.NoError(t, err)
}
})
}
}

View File

@ -259,55 +259,53 @@ func (c *impersonatorConfigController) doSync(syncCtx controllerlib.Context) (*v
return credentialIssuerStrategyResult, nil
}
func (c *impersonatorConfigController) loadImpersonationProxyConfiguration() (*impersonator.Config, error) {
func (c *impersonatorConfigController) loadImpersonationProxyConfiguration() (*v1alpha1.ImpersonationProxySpec, error) {
credIssuer, err := c.credIssuerInformer.Lister().Get(c.credentialIssuerResourceName)
if k8serrors.IsNotFound(err) {
plog.Info("Did not find impersonation proxy config: using default config values",
"credentialIssuer", c.credentialIssuerResourceName,
)
return impersonator.NewConfig(), nil
}
if err != nil {
return nil, fmt.Errorf("failed to get %s CredentialIssuer: %w", c.credentialIssuerResourceName, err)
}
config, err := impersonator.ConfigFromCredentialIssuer(credIssuer)
credIssuer = credIssuer.DeepCopy()
err = validateCredentialIssuerSpec(credIssuer)
if err != nil {
return nil, fmt.Errorf("invalid impersonator configuration: %v", err)
}
plog.Info("Read impersonation proxy config",
"credentialIssuer", c.credentialIssuerResourceName,
)
return config, nil
if credIssuer.Spec.ImpersonationProxy.Service.Type == "" {
credIssuer.Spec.ImpersonationProxy.Service.Type = v1alpha1.ImpersonationProxyServiceTypeLoadBalancer
}
return &credIssuer.Spec.ImpersonationProxy, nil
}
func (c *impersonatorConfigController) shouldHaveImpersonator(config *impersonator.Config) bool {
return c.enabledByAutoMode(config) || config.Mode == impersonator.ModeEnabled
func (c *impersonatorConfigController) shouldHaveImpersonator(config *v1alpha1.ImpersonationProxySpec) bool {
return c.enabledByAutoMode(config) || config.Mode == v1alpha1.ImpersonationProxyModeEnabled
}
func (c *impersonatorConfigController) enabledByAutoMode(config *impersonator.Config) bool {
return config.Mode == impersonator.ModeAuto && !*c.hasControlPlaneNodes
func (c *impersonatorConfigController) enabledByAutoMode(config *v1alpha1.ImpersonationProxySpec) bool {
return config.Mode == v1alpha1.ImpersonationProxyModeAuto && !*c.hasControlPlaneNodes
}
func (c *impersonatorConfigController) disabledByAutoMode(config *impersonator.Config) bool {
return config.Mode == impersonator.ModeAuto && *c.hasControlPlaneNodes
func (c *impersonatorConfigController) disabledByAutoMode(config *v1alpha1.ImpersonationProxySpec) bool {
return config.Mode == v1alpha1.ImpersonationProxyModeAuto && *c.hasControlPlaneNodes
}
func (c *impersonatorConfigController) disabledExplicitly(config *impersonator.Config) bool {
return config.Mode == impersonator.ModeDisabled
func (c *impersonatorConfigController) disabledExplicitly(config *v1alpha1.ImpersonationProxySpec) bool {
return config.Mode == v1alpha1.ImpersonationProxyModeDisabled
}
func (c *impersonatorConfigController) shouldHaveLoadBalancer(config *impersonator.Config) bool {
return c.shouldHaveImpersonator(config) && !config.HasEndpoint()
func (c *impersonatorConfigController) shouldHaveLoadBalancer(config *v1alpha1.ImpersonationProxySpec) bool {
return c.shouldHaveImpersonator(config) && config.Service.Type == v1alpha1.ImpersonationProxyServiceTypeLoadBalancer
}
func (c *impersonatorConfigController) shouldHaveTLSSecret(config *impersonator.Config) bool {
func (c *impersonatorConfigController) shouldHaveTLSSecret(config *v1alpha1.ImpersonationProxySpec) bool {
return c.shouldHaveImpersonator(config)
}
func (c *impersonatorConfigController) updateStrategy(ctx context.Context, strategy *v1alpha1.CredentialIssuerStrategy) error {
// TODO use informer client rather than api client for reading
return issuerconfig.UpdateStrategy(ctx, c.credentialIssuerResourceName, c.labels, c.pinnipedAPIClient, *strategy)
}
@ -652,15 +650,25 @@ func (c *impersonatorConfigController) createCASecret(ctx context.Context) (*cer
return impersonationCA, nil
}
func (c *impersonatorConfigController) findDesiredTLSCertificateName(config *impersonator.Config) (*certNameInfo, error) {
if config.HasEndpoint() {
func (c *impersonatorConfigController) findDesiredTLSCertificateName(config *v1alpha1.ImpersonationProxySpec) (*certNameInfo, error) {
// possible valid options:
// - you have a loadbalancer and are autoconfiguring the endpoint -> get cert info based on load balancer ip/hostnome
// - you have a loadbalancer AND an external endpoint -> either should work since they should be the same
// - external endpoint no loadbalancer or other service -> use the endpoint config
// - external endpoint and ClusterIP -> use external endpoint?
//
// - is it legal to have a clusterip and no external endpoint???
if config.ExternalEndpoint != "" {
return c.findTLSCertificateNameFromEndpointConfig(config), nil
} else if config.Service.Type == v1alpha1.ImpersonationProxyServiceTypeClusterIP {
// c.findTLSCertificateNameFromClusterIPService()
// TODO implement this
}
return c.findTLSCertificateNameFromLoadBalancer()
}
func (c *impersonatorConfigController) findTLSCertificateNameFromEndpointConfig(config *impersonator.Config) *certNameInfo {
endpointMaybeWithPort := config.Endpoint
func (c *impersonatorConfigController) findTLSCertificateNameFromEndpointConfig(config *v1alpha1.ImpersonationProxySpec) *certNameInfo {
endpointMaybeWithPort := config.ExternalEndpoint
endpointWithoutPort := strings.Split(endpointMaybeWithPort, ":")[0]
parsedAsIP := net.ParseIP(endpointWithoutPort)
if parsedAsIP != nil {
@ -820,7 +828,7 @@ func (c *impersonatorConfigController) clearSignerCA() {
c.impersonationSigningCertProvider.UnsetCertKeyContent()
}
func (c *impersonatorConfigController) doSyncResult(nameInfo *certNameInfo, config *impersonator.Config, ca *certauthority.CA) *v1alpha1.CredentialIssuerStrategy {
func (c *impersonatorConfigController) doSyncResult(nameInfo *certNameInfo, config *v1alpha1.ImpersonationProxySpec, ca *certauthority.CA) *v1alpha1.CredentialIssuerStrategy {
switch {
case c.disabledExplicitly(config):
return &v1alpha1.CredentialIssuerStrategy{
@ -863,3 +871,16 @@ func (c *impersonatorConfigController) doSyncResult(nameInfo *certNameInfo, conf
}
}
}
func validateCredentialIssuerSpec(credIssuer *v1alpha1.CredentialIssuer) error {
// TODO check external endpoint for valid ip or hostname
// TODO if service type is none and externalendpoint is "" return error
switch mode := credIssuer.Spec.ImpersonationProxy.Mode; mode {
case v1alpha1.ImpersonationProxyModeAuto:
case v1alpha1.ImpersonationProxyModeDisabled:
case v1alpha1.ImpersonationProxyModeEnabled:
default:
return fmt.Errorf("invalid impersonation proxy mode %q, valid values are auto, disabled, or enabled", mode)
}
return nil
}

View File

@ -980,11 +980,87 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
closeTestHTTPServer()
})
when("the CredentialIssuer does not yet exist or it was deleted (defaults to auto mode)", func() {
when("the CredentialIssuer does not yet exist or it was deleted (sync returns an error)", func() {
it.Before(func() {
addSecretToTrackers(signingCASecret, kubeInformerClient)
})
when("there are visible control plane nodes and a loadbalancer and a tls Secret", func() {
it.Before(func() {
addNodeWithRoleToTracker("control-plane", kubeAPIClient)
addLoadBalancerServiceToTracker(loadBalancerServiceName, kubeInformerClient)
addLoadBalancerServiceToTracker(loadBalancerServiceName, kubeAPIClient)
addSecretToTrackers(newEmptySecret(tlsSecretName), kubeAPIClient, kubeInformerClient)
})
it("errors and does nothing else", func() {
startInformersAndController()
r.EqualError(runControllerSync(), "failed to get some-credential-issuer-resource-name CredentialIssuer: credentialissuer.config.concierge.pinniped.dev \"some-credential-issuer-resource-name\" not found")
requireTLSServerWasNeverStarted()
r.Len(kubeAPIClient.Actions(), 0)
})
})
})
when("the configuration is auto mode with an endpoint and service type none", func() {
it.Before(func() {
addSecretToTrackers(signingCASecret, kubeInformerClient)
addCredentialIssuerToTracker(credentialIssuerResourceName, v1alpha1.CredentialIssuerSpec{
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeAuto,
ExternalEndpoint: localhostIP,
Service: v1alpha1.ImpersonationProxyServiceSpec{
Type: v1alpha1.ImpersonationProxyServiceTypeNone,
},
},
}, pinnipedInformerClient)
})
when("there are visible control plane nodes", func() {
it.Before(func() {
addNodeWithRoleToTracker("control-plane", kubeAPIClient)
})
it("does not start the impersonator", func() {
startInformersAndController()
r.NoError(runControllerSync())
requireTLSServerWasNeverStarted()
requireNodesListed(kubeAPIClient.Actions()[0])
r.Len(kubeAPIClient.Actions(), 1)
requireCredentialIssuer(newAutoDisabledStrategy())
requireSigningCertProviderIsEmpty()
})
})
when("there are not visible control plane nodes", func() {
it.Before(func() {
addNodeWithRoleToTracker("worker", kubeAPIClient)
})
it("starts the impersonator according to the settings in the CredentialIssuer", func() {
startInformersAndController()
r.NoError(runControllerSync())
r.Len(kubeAPIClient.Actions(), 3)
requireNodesListed(kubeAPIClient.Actions()[0])
ca := requireCASecretWasCreated(kubeAPIClient.Actions()[1])
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
requireTLSServerIsRunning(ca, testServerAddr(), nil)
requireCredentialIssuer(newSuccessStrategy(localhostIP, ca))
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
})
})
})
when("the configuration is auto mode", func() {
it.Before(func() {
addSecretToTrackers(signingCASecret, kubeInformerClient)
addCredentialIssuerToTracker(credentialIssuerResourceName, v1alpha1.CredentialIssuerSpec{
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeAuto,
},
}, pinnipedInformerClient)
})
when("there are visible control plane nodes", func() {
it.Before(func() {
addNodeWithRoleToTracker("control-plane", kubeAPIClient)
@ -1271,60 +1347,12 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireSigningCertProviderIsEmpty()
})
})
})
when("the CredentialIssuer is already present", func() {
it.Before(func() {
addSecretToTrackers(signingCASecret, kubeInformerClient)
})
when("the configuration is auto mode with an endpoint", func() {
it.Before(func() {
addCredentialIssuerToTracker(credentialIssuerResourceName, v1alpha1.CredentialIssuerSpec{
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeAuto,
ExternalEndpoint: localhostIP,
},
}, pinnipedInformerClient)
})
when("there are visible control plane nodes", func() {
it.Before(func() {
addNodeWithRoleToTracker("control-plane", kubeAPIClient)
})
it("does not start the impersonator", func() {
startInformersAndController()
r.NoError(runControllerSync())
requireTLSServerWasNeverStarted()
requireNodesListed(kubeAPIClient.Actions()[0])
r.Len(kubeAPIClient.Actions(), 1)
requireCredentialIssuer(newAutoDisabledStrategy())
requireSigningCertProviderIsEmpty()
})
})
when("there are not visible control plane nodes", func() {
it.Before(func() {
addNodeWithRoleToTracker("worker", kubeAPIClient)
})
it("starts the impersonator according to the settings in the CredentialIssuer", func() {
startInformersAndController()
r.NoError(runControllerSync())
r.Len(kubeAPIClient.Actions(), 3)
requireNodesListed(kubeAPIClient.Actions()[0])
ca := requireCASecretWasCreated(kubeAPIClient.Actions()[1])
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
requireTLSServerIsRunning(ca, testServerAddr(), nil)
requireCredentialIssuer(newSuccessStrategy(localhostIP, ca))
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
})
})
})
when("the configuration is disabled mode", func() {
it.Before(func() {
addSecretToTrackers(signingCASecret, kubeInformerClient)
addCredentialIssuerToTracker(credentialIssuerResourceName, v1alpha1.CredentialIssuerSpec{
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeDisabled,
@ -1345,6 +1373,9 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
})
when("the configuration is enabled mode", func() {
it.Before(func() {
addSecretToTrackers(signingCASecret, kubeInformerClient)
})
when("no load balancer", func() {
it.Before(func() {
addCredentialIssuerToTracker(credentialIssuerResourceName, v1alpha1.CredentialIssuerSpec{
@ -1438,13 +1469,16 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
})
})
when("the CredentialIssuer has a hostname specified for the endpoint", func() {
when("the CredentialIssuer has a hostname specified and service type none", func() {
const fakeHostname = "fake.example.com"
it.Before(func() {
addCredentialIssuerToTracker(credentialIssuerResourceName, v1alpha1.CredentialIssuerSpec{
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeEnabled,
ExternalEndpoint: fakeHostname,
Service: v1alpha1.ImpersonationProxyServiceSpec{
Type: v1alpha1.ImpersonationProxyServiceTypeNone,
},
},
}, pinnipedInformerClient)
addNodeWithRoleToTracker("worker", kubeAPIClient)
@ -1471,6 +1505,9 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeEnabled,
ExternalEndpoint: fakeIPWithPort,
Service: v1alpha1.ImpersonationProxyServiceSpec{
Type: v1alpha1.ImpersonationProxyServiceTypeNone,
},
},
}, pinnipedInformerClient)
addNodeWithRoleToTracker("worker", kubeAPIClient)
@ -1490,13 +1527,16 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
})
})
when("the CredentialIssuer has a endpoint which is a hostname with a port", func() {
when("the CredentialIssuer has a endpoint which is a hostname with a port, service type none", func() {
const fakeHostnameWithPort = "fake.example.com:3000"
it.Before(func() {
addCredentialIssuerToTracker(credentialIssuerResourceName, v1alpha1.CredentialIssuerSpec{
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeEnabled,
ExternalEndpoint: fakeHostnameWithPort,
Service: v1alpha1.ImpersonationProxyServiceSpec{
Type: v1alpha1.ImpersonationProxyServiceTypeNone,
},
},
}, pinnipedInformerClient)
addNodeWithRoleToTracker("worker", kubeAPIClient)
@ -1524,12 +1564,18 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeEnabled,
ExternalEndpoint: fakeHostname,
Service: v1alpha1.ImpersonationProxyServiceSpec{
Type: v1alpha1.ImpersonationProxyServiceTypeNone,
},
},
}
var ipAddressConfig = v1alpha1.CredentialIssuerSpec{
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeEnabled,
ExternalEndpoint: fakeIP,
Service: v1alpha1.ImpersonationProxyServiceSpec{
Type: v1alpha1.ImpersonationProxyServiceTypeNone,
},
},
}
@ -1592,6 +1638,9 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeEnabled,
ExternalEndpoint: fakeHostname,
Service: v1alpha1.ImpersonationProxyServiceSpec{
Type: v1alpha1.ImpersonationProxyServiceTypeNone,
},
},
}, pinnipedInformerClient)
addNodeWithRoleToTracker("worker", kubeAPIClient)
@ -1634,6 +1683,9 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeEnabled,
ExternalEndpoint: fakeHostname,
Service: v1alpha1.ImpersonationProxyServiceSpec{
Type: v1alpha1.ImpersonationProxyServiceTypeNone,
},
},
}, pinnipedInformerClient)
addNodeWithRoleToTracker("worker", kubeAPIClient)
@ -1679,6 +1731,9 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeEnabled,
ExternalEndpoint: fakeHostname,
Service: v1alpha1.ImpersonationProxyServiceSpec{
Type: v1alpha1.ImpersonationProxyServiceTypeNone,
},
},
}, pinnipedInformerClient)
addNodeWithRoleToTracker("worker", kubeAPIClient)
@ -1743,6 +1798,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
when("the configuration switches from enabled to disabled mode", func() {
it.Before(func() {
addSecretToTrackers(signingCASecret, kubeInformerClient)
addCredentialIssuerToTracker(credentialIssuerResourceName, v1alpha1.CredentialIssuerSpec{
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeEnabled,
@ -1800,12 +1856,16 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
})
})
when("the endpoint switches from specified, to not specified, to specified again", func() {
when("the endpoint and mode switch from specified with no service, to not specified, to specified again", func() {
it.Before(func() {
addSecretToTrackers(signingCASecret, kubeInformerClient)
addCredentialIssuerToTracker(credentialIssuerResourceName, v1alpha1.CredentialIssuerSpec{
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeEnabled,
ExternalEndpoint: localhostIP,
Service: v1alpha1.ImpersonationProxyServiceSpec{
Type: v1alpha1.ImpersonationProxyServiceTypeNone,
},
},
}, pinnipedInformerClient)
addNodeWithRoleToTracker("worker", kubeAPIClient)
@ -1869,11 +1929,14 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
// Simulate the informer cache's background update from its watch.
addObjectFromCreateActionToInformerAndWait(kubeAPIClient.Actions()[5], kubeInformers.Core().V1().Secrets())
// Now switch back to having the "endpoint" specified, so the load balancer is not needed anymore.
// Now switch back to having the "endpoint" specified and explicitly saying that we don't want the load balancer service.
updateCredentialIssuerInInformerAndWait(credentialIssuerResourceName, v1alpha1.CredentialIssuerSpec{
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeEnabled,
ExternalEndpoint: localhostIP,
Service: v1alpha1.ImpersonationProxyServiceSpec{
Type: v1alpha1.ImpersonationProxyServiceTypeNone,
},
},
}, pinnipedInformers.Config().V1alpha1().CredentialIssuers())
@ -1887,12 +1950,16 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
})
})
})
when("sync is called more than once", func() {
it.Before(func() {
addSecretToTrackers(signingCASecret, kubeInformerClient)
addNodeWithRoleToTracker("worker", kubeAPIClient)
addCredentialIssuerToTracker(credentialIssuerResourceName, v1alpha1.CredentialIssuerSpec{
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeAuto,
},
}, pinnipedInformerClient)
})
it("only starts the impersonator once and only lists the cluster's nodes once", func() {
@ -2028,6 +2095,11 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
when("getting the control plane nodes returns an error, e.g. when there are no nodes", func() {
it("returns an error", func() {
addCredentialIssuerToTracker(credentialIssuerResourceName, v1alpha1.CredentialIssuerSpec{
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeAuto,
},
}, pinnipedInformerClient)
startInformersAndController()
r.EqualError(runControllerSync(), "no nodes found")
requireCredentialIssuer(newErrorStrategy("no nodes found"))
@ -2040,6 +2112,11 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
it.Before(func() {
addNodeWithRoleToTracker("worker", kubeAPIClient)
impersonatorFuncReturnedFuncError = errors.New("some immediate impersonator startup error")
addCredentialIssuerToTracker(credentialIssuerResourceName, v1alpha1.CredentialIssuerSpec{
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeAuto,
},
}, pinnipedInformerClient)
})
it("causes an immediate resync, returns an error on that next sync, and then restarts the server in a following sync", func() {
@ -2095,6 +2172,11 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
when("the impersonator server dies for no apparent reason after running for a while", func() {
it.Before(func() {
addNodeWithRoleToTracker("worker", kubeAPIClient)
addCredentialIssuerToTracker(credentialIssuerResourceName, v1alpha1.CredentialIssuerSpec{
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeAuto,
},
}, pinnipedInformerClient)
})
it("causes an immediate resync, returns an error on that next sync, and then restarts the server in a following sync", func() {
@ -2169,6 +2251,11 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
kubeAPIClient.PrependReactor("create", "services", func(action coretesting.Action) (handled bool, ret runtime.Object, err error) {
return true, nil, fmt.Errorf("error on create")
})
addCredentialIssuerToTracker(credentialIssuerResourceName, v1alpha1.CredentialIssuerSpec{
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeAuto,
},
}, pinnipedInformerClient)
})
it("returns an error", func() {
@ -2186,6 +2273,9 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeEnabled,
ExternalEndpoint: "example.com",
Service: v1alpha1.ImpersonationProxyServiceSpec{
Type: v1alpha1.ImpersonationProxyServiceTypeNone,
},
},
}, pinnipedInformerClient)
addNodeWithRoleToTracker("control-plane", kubeAPIClient)
@ -2217,6 +2307,9 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeEnabled,
ExternalEndpoint: "example.com",
Service: v1alpha1.ImpersonationProxyServiceSpec{
Type: v1alpha1.ImpersonationProxyServiceTypeNone,
},
},
}, pinnipedInformerClient)
addNodeWithRoleToTracker("control-plane", kubeAPIClient)
@ -2248,6 +2341,9 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeEnabled,
ExternalEndpoint: "example.com",
Service: v1alpha1.ImpersonationProxyServiceSpec{
Type: v1alpha1.ImpersonationProxyServiceTypeNone,
},
},
}, pinnipedInformerClient)
addSecretToTrackers(newEmptySecret(caSecretName), kubeAPIClient, kubeInformerClient)
@ -2319,6 +2415,9 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeEnabled,
ExternalEndpoint: localhostIP,
Service: v1alpha1.ImpersonationProxyServiceSpec{
Type: v1alpha1.ImpersonationProxyServiceTypeNone,
},
},
}, pinnipedInformerClient)
addNodeWithRoleToTracker("worker", kubeAPIClient)
@ -2438,6 +2537,11 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
addSecretToTrackers(tlsSecret, kubeAPIClient, kubeInformerClient)
addLoadBalancerServiceWithIngressToTracker(loadBalancerServiceName, []corev1.LoadBalancerIngress{{IP: localhostIP}}, kubeInformerClient)
addLoadBalancerServiceWithIngressToTracker(loadBalancerServiceName, []corev1.LoadBalancerIngress{{IP: localhostIP}}, kubeAPIClient)
addCredentialIssuerToTracker(credentialIssuerResourceName, v1alpha1.CredentialIssuerSpec{
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeAuto,
},
}, pinnipedInformerClient)
})
it("deletes the invalid certs, creates new certs, and starts the impersonator", func() {
@ -2509,6 +2613,9 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
ImpersonationProxy: v1alpha1.ImpersonationProxySpec{
Mode: v1alpha1.ImpersonationProxyModeEnabled,
ExternalEndpoint: fakeHostname,
Service: v1alpha1.ImpersonationProxyServiceSpec{
Type: v1alpha1.ImpersonationProxyServiceTypeNone,
},
},
}, pinnipedInformerClient)
addNodeWithRoleToTracker("worker", kubeAPIClient)