Read the names of the impersonation-related resources from the config

They were previously temporarily hardcoded. Now they are set at deploy
time via the static ConfigMap in deployment.yaml
This commit is contained in:
Ryan Richard 2021-03-02 09:31:24 -08:00
parent 41140766f0
commit a75c2194bc
6 changed files with 211 additions and 61 deletions

View File

@ -42,6 +42,10 @@ data:
servingCertificateSecret: (@= defaultResourceNameWithSuffix("api-tls-serving-certificate") @) servingCertificateSecret: (@= defaultResourceNameWithSuffix("api-tls-serving-certificate") @)
credentialIssuer: (@= defaultResourceNameWithSuffix("config") @) credentialIssuer: (@= defaultResourceNameWithSuffix("config") @)
apiService: (@= defaultResourceNameWithSuffix("api") @) apiService: (@= defaultResourceNameWithSuffix("api") @)
impersonationConfigMap: (@= defaultResourceNameWithSuffix("impersonation-proxy-config") @)
impersonationLoadBalancerService: (@= defaultResourceNameWithSuffix("impersonation-proxy-load-balancer") @)
impersonationTLSCertificateSecret: (@= defaultResourceNameWithSuffix("impersonation-proxy-tls-serving-certificate") @)
impersonationCACertificateSecret: (@= defaultResourceNameWithSuffix("impersonation-proxy-ca-certificate") @)
labels: (@= json.encode(labels()).rstrip() @) labels: (@= json.encode(labels()).rstrip() @)
kubeCertAgent: kubeCertAgent:
namePrefix: (@= defaultResourceNameWithSuffix("kube-cert-agent-") @) namePrefix: (@= defaultResourceNameWithSuffix("kube-cert-agent-") @)

View File

@ -96,17 +96,28 @@ func maybeSetKubeCertAgentDefaults(cfg *KubeCertAgentSpec) {
func validateNames(names *NamesConfigSpec) error { func validateNames(names *NamesConfigSpec) error {
missingNames := []string{} missingNames := []string{}
if names == nil { if names == nil {
missingNames = append(missingNames, "servingCertificateSecret", "credentialIssuer", "apiService") names = &NamesConfigSpec{}
} else { }
if names.ServingCertificateSecret == "" { if names.ServingCertificateSecret == "" {
missingNames = append(missingNames, "servingCertificateSecret") missingNames = append(missingNames, "servingCertificateSecret")
} }
if names.CredentialIssuer == "" { if names.CredentialIssuer == "" {
missingNames = append(missingNames, "credentialIssuer") missingNames = append(missingNames, "credentialIssuer")
} }
if names.APIService == "" { if names.APIService == "" {
missingNames = append(missingNames, "apiService") missingNames = append(missingNames, "apiService")
} }
if names.ImpersonationConfigMap == "" {
missingNames = append(missingNames, "impersonationConfigMap")
}
if names.ImpersonationLoadBalancerService == "" {
missingNames = append(missingNames, "impersonationLoadBalancerService")
}
if names.ImpersonationTLSCertificateSecret == "" {
missingNames = append(missingNames, "impersonationTLSCertificateSecret")
}
if names.ImpersonationCACertificateSecret == "" {
missingNames = append(missingNames, "impersonationCACertificateSecret")
} }
if len(missingNames) > 0 { if len(missingNames) > 0 {
return constable.Error("missing required names: " + strings.Join(missingNames, ", ")) return constable.Error("missing required names: " + strings.Join(missingNames, ", "))

View File

@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.pinniped.dev/internal/here" "go.pinniped.dev/internal/here"
"go.pinniped.dev/internal/plog"
) )
func TestFromPath(t *testing.T) { func TestFromPath(t *testing.T) {
@ -21,7 +22,7 @@ func TestFromPath(t *testing.T) {
wantError string wantError string
}{ }{
{ {
name: "Happy", name: "Fully filled out",
yaml: here.Doc(` yaml: here.Doc(`
--- ---
discovery: discovery:
@ -36,13 +37,18 @@ func TestFromPath(t *testing.T) {
credentialIssuer: pinniped-config credentialIssuer: pinniped-config
apiService: pinniped-api apiService: pinniped-api
kubeCertAgentPrefix: kube-cert-agent-prefix kubeCertAgentPrefix: kube-cert-agent-prefix
impersonationConfigMap: impersonationConfigMap-value
impersonationLoadBalancerService: impersonationLoadBalancerService-value
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
impersonationCACertificateSecret: impersonationCACertificateSecret-value
labels: labels:
myLabelKey1: myLabelValue1 myLabelKey1: myLabelValue1
myLabelKey2: myLabelValue2 myLabelKey2: myLabelValue2
KubeCertAgent: kubeCertAgent:
namePrefix: kube-cert-agent-name-prefix- namePrefix: kube-cert-agent-name-prefix-
image: kube-cert-agent-image image: kube-cert-agent-image
imagePullSecrets: [kube-cert-agent-image-pull-secret] imagePullSecrets: [kube-cert-agent-image-pull-secret]
logLevel: debug
`), `),
wantConfig: &Config{ wantConfig: &Config{
DiscoveryInfo: DiscoveryInfoSpec{ DiscoveryInfo: DiscoveryInfoSpec{
@ -56,9 +62,13 @@ func TestFromPath(t *testing.T) {
}, },
APIGroupSuffix: stringPtr("some.suffix.com"), APIGroupSuffix: stringPtr("some.suffix.com"),
NamesConfig: NamesConfigSpec{ NamesConfig: NamesConfigSpec{
ServingCertificateSecret: "pinniped-concierge-api-tls-serving-certificate", ServingCertificateSecret: "pinniped-concierge-api-tls-serving-certificate",
CredentialIssuer: "pinniped-config", CredentialIssuer: "pinniped-config",
APIService: "pinniped-api", APIService: "pinniped-api",
ImpersonationConfigMap: "impersonationConfigMap-value",
ImpersonationLoadBalancerService: "impersonationLoadBalancerService-value",
ImpersonationTLSCertificateSecret: "impersonationTLSCertificateSecret-value",
ImpersonationCACertificateSecret: "impersonationCACertificateSecret-value",
}, },
Labels: map[string]string{ Labels: map[string]string{
"myLabelKey1": "myLabelValue1", "myLabelKey1": "myLabelValue1",
@ -69,6 +79,7 @@ func TestFromPath(t *testing.T) {
Image: stringPtr("kube-cert-agent-image"), Image: stringPtr("kube-cert-agent-image"),
ImagePullSecrets: []string{"kube-cert-agent-image-pull-secret"}, ImagePullSecrets: []string{"kube-cert-agent-image-pull-secret"},
}, },
LogLevel: plog.LevelDebug,
}, },
}, },
{ {
@ -79,6 +90,10 @@ func TestFromPath(t *testing.T) {
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
credentialIssuer: pinniped-config credentialIssuer: pinniped-config
apiService: pinniped-api apiService: pinniped-api
impersonationConfigMap: impersonationConfigMap-value
impersonationLoadBalancerService: impersonationLoadBalancerService-value
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
impersonationCACertificateSecret: impersonationCACertificateSecret-value
`), `),
wantConfig: &Config{ wantConfig: &Config{
DiscoveryInfo: DiscoveryInfoSpec{ DiscoveryInfo: DiscoveryInfoSpec{
@ -92,9 +107,13 @@ func TestFromPath(t *testing.T) {
}, },
}, },
NamesConfig: NamesConfigSpec{ NamesConfig: NamesConfigSpec{
ServingCertificateSecret: "pinniped-concierge-api-tls-serving-certificate", ServingCertificateSecret: "pinniped-concierge-api-tls-serving-certificate",
CredentialIssuer: "pinniped-config", CredentialIssuer: "pinniped-config",
APIService: "pinniped-api", APIService: "pinniped-api",
ImpersonationConfigMap: "impersonationConfigMap-value",
ImpersonationLoadBalancerService: "impersonationLoadBalancerService-value",
ImpersonationTLSCertificateSecret: "impersonationTLSCertificateSecret-value",
ImpersonationCACertificateSecret: "impersonationCACertificateSecret-value",
}, },
Labels: map[string]string{}, Labels: map[string]string{},
KubeCertAgentConfig: KubeCertAgentSpec{ KubeCertAgentConfig: KubeCertAgentSpec{
@ -104,9 +123,11 @@ func TestFromPath(t *testing.T) {
}, },
}, },
{ {
name: "Empty", name: "Empty",
yaml: here.Doc(``), yaml: here.Doc(``),
wantError: "validate names: missing required names: servingCertificateSecret, credentialIssuer, apiService", wantError: "validate names: missing required names: servingCertificateSecret, credentialIssuer, " +
"apiService, impersonationConfigMap, impersonationLoadBalancerService, " +
"impersonationTLSCertificateSecret, impersonationCACertificateSecret",
}, },
{ {
name: "Missing apiService name", name: "Missing apiService name",
@ -115,6 +136,10 @@ func TestFromPath(t *testing.T) {
names: names:
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
credentialIssuer: pinniped-config credentialIssuer: pinniped-config
impersonationConfigMap: impersonationConfigMap-value
impersonationLoadBalancerService: impersonationLoadBalancerService-value
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
impersonationCACertificateSecret: impersonationCACertificateSecret-value
`), `),
wantError: "validate names: missing required names: apiService", wantError: "validate names: missing required names: apiService",
}, },
@ -125,6 +150,10 @@ func TestFromPath(t *testing.T) {
names: names:
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
apiService: pinniped-api apiService: pinniped-api
impersonationConfigMap: impersonationConfigMap-value
impersonationLoadBalancerService: impersonationLoadBalancerService-value
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
impersonationCACertificateSecret: impersonationCACertificateSecret-value
`), `),
wantError: "validate names: missing required names: credentialIssuer", wantError: "validate names: missing required names: credentialIssuer",
}, },
@ -135,9 +164,82 @@ func TestFromPath(t *testing.T) {
names: names:
credentialIssuer: pinniped-config credentialIssuer: pinniped-config
apiService: pinniped-api apiService: pinniped-api
impersonationConfigMap: impersonationConfigMap-value
impersonationLoadBalancerService: impersonationLoadBalancerService-value
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
impersonationCACertificateSecret: impersonationCACertificateSecret-value
`), `),
wantError: "validate names: missing required names: servingCertificateSecret", wantError: "validate names: missing required names: servingCertificateSecret",
}, },
{
name: "Missing impersonationConfigMap name",
yaml: here.Doc(`
---
names:
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
credentialIssuer: pinniped-config
apiService: pinniped-api
impersonationLoadBalancerService: impersonationLoadBalancerService-value
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
impersonationCACertificateSecret: impersonationCACertificateSecret-value
`),
wantError: "validate names: missing required names: impersonationConfigMap",
},
{
name: "Missing impersonationLoadBalancerService name",
yaml: here.Doc(`
---
names:
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
credentialIssuer: pinniped-config
apiService: pinniped-api
impersonationConfigMap: impersonationConfigMap-value
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
impersonationCACertificateSecret: impersonationCACertificateSecret-value
`),
wantError: "validate names: missing required names: impersonationLoadBalancerService",
},
{
name: "Missing impersonationTLSCertificateSecret name",
yaml: here.Doc(`
---
names:
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
credentialIssuer: pinniped-config
apiService: pinniped-api
impersonationConfigMap: impersonationConfigMap-value
impersonationLoadBalancerService: impersonationLoadBalancerService-value
impersonationCACertificateSecret: impersonationCACertificateSecret-value
`),
wantError: "validate names: missing required names: impersonationTLSCertificateSecret",
},
{
name: "Missing impersonationCACertificateSecret name",
yaml: here.Doc(`
---
names:
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
credentialIssuer: pinniped-config
apiService: pinniped-api
impersonationConfigMap: impersonationConfigMap-value
impersonationLoadBalancerService: impersonationLoadBalancerService-value
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
`),
wantError: "validate names: missing required names: impersonationCACertificateSecret",
},
{
name: "Missing several required names",
yaml: here.Doc(`
---
names:
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
credentialIssuer: pinniped-config
apiService: pinniped-api
impersonationLoadBalancerService: impersonationLoadBalancerService-value
`),
wantError: "validate names: missing required names: impersonationConfigMap, " +
"impersonationTLSCertificateSecret, impersonationCACertificateSecret",
},
{ {
name: "InvalidDurationRenewBefore", name: "InvalidDurationRenewBefore",
yaml: here.Doc(` yaml: here.Doc(`
@ -150,6 +252,10 @@ func TestFromPath(t *testing.T) {
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
credentialIssuer: pinniped-config credentialIssuer: pinniped-config
apiService: pinniped-api apiService: pinniped-api
impersonationConfigMap: impersonationConfigMap-value
impersonationLoadBalancerService: impersonationLoadBalancerService-value
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
impersonationCACertificateSecret: impersonationCACertificateSecret-value
`), `),
wantError: "validate api: durationSeconds cannot be smaller than renewBeforeSeconds", wantError: "validate api: durationSeconds cannot be smaller than renewBeforeSeconds",
}, },
@ -165,6 +271,10 @@ func TestFromPath(t *testing.T) {
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
credentialIssuer: pinniped-config credentialIssuer: pinniped-config
apiService: pinniped-api apiService: pinniped-api
impersonationConfigMap: impersonationConfigMap-value
impersonationLoadBalancerService: impersonationLoadBalancerService-value
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
impersonationCACertificateSecret: impersonationCACertificateSecret-value
`), `),
wantError: "validate api: renewBefore must be positive", wantError: "validate api: renewBefore must be positive",
}, },
@ -180,6 +290,10 @@ func TestFromPath(t *testing.T) {
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
credentialIssuer: pinniped-config credentialIssuer: pinniped-config
apiService: pinniped-api apiService: pinniped-api
impersonationConfigMap: impersonationConfigMap-value
impersonationLoadBalancerService: impersonationLoadBalancerService-value
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
impersonationCACertificateSecret: impersonationCACertificateSecret-value
`), `),
wantError: "validate api: renewBefore must be positive", wantError: "validate api: renewBefore must be positive",
}, },
@ -196,6 +310,10 @@ func TestFromPath(t *testing.T) {
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
credentialIssuer: pinniped-config credentialIssuer: pinniped-config
apiService: pinniped-api apiService: pinniped-api
impersonationConfigMap: impersonationConfigMap-value
impersonationLoadBalancerService: impersonationLoadBalancerService-value
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
impersonationCACertificateSecret: impersonationCACertificateSecret-value
`), `),
wantError: "validate apiGroupSuffix: a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')", wantError: "validate apiGroupSuffix: a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')",
}, },

View File

@ -33,9 +33,13 @@ type APIConfigSpec struct {
// NamesConfigSpec configures the names of some Kubernetes resources for the Concierge. // NamesConfigSpec configures the names of some Kubernetes resources for the Concierge.
type NamesConfigSpec struct { type NamesConfigSpec struct {
ServingCertificateSecret string `json:"servingCertificateSecret"` ServingCertificateSecret string `json:"servingCertificateSecret"`
CredentialIssuer string `json:"credentialIssuer"` CredentialIssuer string `json:"credentialIssuer"`
APIService string `json:"apiService"` APIService string `json:"apiService"`
ImpersonationConfigMap string `json:"impersonationConfigMap"`
ImpersonationLoadBalancerService string `json:"impersonationLoadBalancerService"`
ImpersonationTLSCertificateSecret string `json:"impersonationTLSCertificateSecret"`
ImpersonationCACertificateSecret string `json:"impersonationCACertificateSecret"`
} }
// ServingCertificateConfigSpec contains the configuration knobs for the API's // ServingCertificateConfigSpec contains the configuration knobs for the API's

View File

@ -292,20 +292,24 @@ func PrepareControllers(c *Config) (func(ctx context.Context), error) {
WithController( WithController(
impersonatorconfig.NewImpersonatorConfigController( impersonatorconfig.NewImpersonatorConfigController(
c.ServerInstallationInfo.Namespace, c.ServerInstallationInfo.Namespace,
"pinniped-concierge-impersonation-proxy-config", // TODO this string should come from `c.NamesConfig` c.NamesConfig.ImpersonationConfigMap,
client.Kubernetes, client.Kubernetes,
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(),
controllerlib.WithInformer, controllerlib.WithInformer,
controllerlib.WithInitialEvent, controllerlib.WithInitialEvent,
"pinniped-concierge-impersonation-proxy-load-balancer", // TODO this string should come from `c.NamesConfig` c.NamesConfig.ImpersonationLoadBalancerService,
"pinniped-concierge-impersonation-proxy-tls-serving-certificate", // TODO this string should come from `c.NamesConfig` c.NamesConfig.ImpersonationTLSCertificateSecret,
"pinniped-concierge-impersonation-proxy-ca-certificate", // TODO this string should come from `c.NamesConfig` c.NamesConfig.ImpersonationCACertificateSecret,
c.Labels, c.Labels,
tls.Listen, tls.Listen,
func() (http.Handler, error) { func() (http.Handler, error) {
impersonationProxyHandler, err := impersonator.New(c.AuthenticatorCache, c.LoginJSONDecoder, klogr.New().WithName("impersonation-proxy")) impersonationProxyHandler, err := impersonator.New(
c.AuthenticatorCache,
c.LoginJSONDecoder,
klogr.New().WithName("impersonation-proxy"),
)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not create impersonation proxy: %w", err) return nil, fmt.Errorf("could not create impersonation proxy: %w", err)
} }

View File

@ -29,14 +29,6 @@ import (
"go.pinniped.dev/test/library" "go.pinniped.dev/test/library"
) )
const (
// TODO don't hard code "pinniped-concierge-" in these strings. It should be constructed from the env app name.
impersonationProxyConfigMapName = "pinniped-concierge-impersonation-proxy-config"
impersonationProxyTLSSecretName = "pinniped-concierge-impersonation-proxy-tls-serving-certificate" //nolint:gosec // this is not a credential
impersonationProxyCASecretName = "pinniped-concierge-impersonation-proxy-ca-certificate" //nolint:gosec // this is not a credential
impersonationProxyLoadBalancerName = "pinniped-concierge-impersonation-proxy-load-balancer"
)
// Note that this test supports being run on all of our integration test cluster types: // Note that this test supports being run on all of our integration test cluster types:
// - load balancers not supported, has squid proxy (e.g. kind) // - load balancers not supported, has squid proxy (e.g. kind)
// - load balancers supported, has squid proxy (e.g. EKS) // - load balancers supported, has squid proxy (e.g. EKS)
@ -94,11 +86,11 @@ func TestImpersonationProxy(t *testing.T) {
return impersonationProxyClient return impersonationProxyClient
} }
oldConfigMap, err := adminClient.CoreV1().ConfigMaps(env.ConciergeNamespace).Get(ctx, impersonationProxyConfigMapName, metav1.GetOptions{}) oldConfigMap, err := adminClient.CoreV1().ConfigMaps(env.ConciergeNamespace).Get(ctx, impersonationProxyConfigMapName(env), metav1.GetOptions{})
if !k8serrors.IsNotFound(err) { if !k8serrors.IsNotFound(err) {
require.NoError(t, err) // other errors aside from NotFound are unexpected require.NoError(t, err) // other errors aside from NotFound are unexpected
t.Logf("stashing a pre-existing configmap %s", oldConfigMap.Name) t.Logf("stashing a pre-existing configmap %s", oldConfigMap.Name)
require.NoError(t, adminClient.CoreV1().ConfigMaps(env.ConciergeNamespace).Delete(ctx, impersonationProxyConfigMapName, metav1.DeleteOptions{})) require.NoError(t, adminClient.CoreV1().ConfigMaps(env.ConciergeNamespace).Delete(ctx, impersonationProxyConfigMapName(env), metav1.DeleteOptions{}))
} }
impersonationProxyLoadBalancerIngress := "" impersonationProxyLoadBalancerIngress := ""
@ -106,13 +98,14 @@ func TestImpersonationProxy(t *testing.T) {
if env.HasCapability(library.HasExternalLoadBalancerProvider) { //nolint:nestif // come on... it's just a test if env.HasCapability(library.HasExternalLoadBalancerProvider) { //nolint:nestif // come on... it's just a test
// Check that load balancer has been created. // Check that load balancer has been created.
library.RequireEventuallyWithoutError(t, func() (bool, error) { library.RequireEventuallyWithoutError(t, func() (bool, error) {
return hasImpersonationProxyLoadBalancerService(ctx, adminClient, env.ConciergeNamespace) return hasImpersonationProxyLoadBalancerService(ctx, env, adminClient)
}, 10*time.Second, 500*time.Millisecond) }, 10*time.Second, 500*time.Millisecond)
// TODO this information should come from the CredentialIssuer status once that is implemented
// Wait for the load balancer to get an ingress and make a note of its address. // Wait for the load balancer to get an ingress and make a note of its address.
var ingress *corev1.LoadBalancerIngress var ingress *corev1.LoadBalancerIngress
library.RequireEventuallyWithoutError(t, func() (bool, error) { library.RequireEventuallyWithoutError(t, func() (bool, error) {
ingress, err = getImpersonationProxyLoadBalancerIngress(ctx, adminClient, env.ConciergeNamespace) ingress, err = getImpersonationProxyLoadBalancerIngress(ctx, env, adminClient)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -131,7 +124,7 @@ func TestImpersonationProxy(t *testing.T) {
// Check that no load balancer has been created. // Check that no load balancer has been created.
library.RequireNeverWithoutError(t, func() (bool, error) { library.RequireNeverWithoutError(t, func() (bool, error) {
return hasImpersonationProxyLoadBalancerService(ctx, adminClient, env.ConciergeNamespace) return hasImpersonationProxyLoadBalancerService(ctx, env, adminClient)
}, 10*time.Second, 500*time.Millisecond) }, 10*time.Second, 500*time.Millisecond)
// Check that we can't use the impersonation proxy to execute kubectl commands yet. // Check that we can't use the impersonation proxy to execute kubectl commands yet.
@ -139,7 +132,7 @@ func TestImpersonationProxy(t *testing.T) {
require.EqualError(t, err, serviceUnavailableViaSquidError) require.EqualError(t, err, serviceUnavailableViaSquidError)
// Create configuration to make the impersonation proxy turn on with a hard coded endpoint (without a LoadBalancer). // Create configuration to make the impersonation proxy turn on with a hard coded endpoint (without a LoadBalancer).
configMap := configMapForConfig(t, impersonator.Config{ configMap := configMapForConfig(t, env, impersonator.Config{
Mode: impersonator.ModeEnabled, Mode: impersonator.ModeEnabled,
Endpoint: proxyServiceEndpoint, Endpoint: proxyServiceEndpoint,
TLS: nil, TLS: nil,
@ -152,8 +145,8 @@ func TestImpersonationProxy(t *testing.T) {
t.Cleanup(func() { t.Cleanup(func() {
ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
t.Logf("cleaning up configmap at end of test %s", impersonationProxyConfigMapName) t.Logf("cleaning up configmap at end of test %s", impersonationProxyConfigMapName(env))
err = adminClient.CoreV1().ConfigMaps(env.ConciergeNamespace).Delete(ctx, impersonationProxyConfigMapName, metav1.DeleteOptions{}) err = adminClient.CoreV1().ConfigMaps(env.ConciergeNamespace).Delete(ctx, impersonationProxyConfigMapName(env), metav1.DeleteOptions{})
require.NoError(t, err) require.NoError(t, err)
if len(oldConfigMap.Data) != 0 { if len(oldConfigMap.Data) != 0 {
@ -171,7 +164,7 @@ func TestImpersonationProxy(t *testing.T) {
// TODO We should be getting the CA data from the CredentialIssuer's status instead, once that is implemented. // TODO We should be getting the CA data from the CredentialIssuer's status instead, once that is implemented.
var caSecret *corev1.Secret var caSecret *corev1.Secret
require.Eventually(t, func() bool { require.Eventually(t, func() bool {
caSecret, err = adminClient.CoreV1().Secrets(env.ConciergeNamespace).Get(ctx, impersonationProxyCASecretName, metav1.GetOptions{}) caSecret, err = adminClient.CoreV1().Secrets(env.ConciergeNamespace).Get(ctx, impersonationProxyCASecretName(env), metav1.GetOptions{})
return err == nil && caSecret != nil && caSecret.Data["ca.crt"] != nil return err == nil && caSecret != nil && caSecret.Data["ca.crt"] != nil
}, 10*time.Second, 250*time.Millisecond) }, 10*time.Second, 250*time.Millisecond)
impersonationProxyCACertPEM := caSecret.Data["ca.crt"] impersonationProxyCACertPEM := caSecret.Data["ca.crt"]
@ -180,7 +173,7 @@ func TestImpersonationProxy(t *testing.T) {
// This could take a while if we are waiting for the load balancer to get an IP or hostname assigned to it, and it // This could take a while if we are waiting for the load balancer to get an IP or hostname assigned to it, and it
// should be fast when we are not waiting for a load balancer (e.g. on kind). // should be fast when we are not waiting for a load balancer (e.g. on kind).
require.Eventually(t, func() bool { require.Eventually(t, func() bool {
_, err = adminClient.CoreV1().Secrets(env.ConciergeNamespace).Get(ctx, impersonationProxyTLSSecretName, metav1.GetOptions{}) _, err = adminClient.CoreV1().Secrets(env.ConciergeNamespace).Get(ctx, impersonationProxyTLSSecretName(env), metav1.GetOptions{})
return err == nil return err == nil
}, 5*time.Minute, 250*time.Millisecond) }, 5*time.Minute, 250*time.Millisecond)
@ -209,7 +202,7 @@ func TestImpersonationProxy(t *testing.T) {
// Try more Kube API verbs through the impersonation proxy. // Try more Kube API verbs through the impersonation proxy.
t.Run("watching all the basic verbs", func(t *testing.T) { t.Run("watching all the basic verbs", func(t *testing.T) {
// Create a namespace, because it will be easier to exercise deletecollection if we have a namespace. // Create a namespace, because it will be easier to exercise "deletecollection" if we have a namespace.
namespace, err := adminClient.CoreV1().Namespaces().Create(ctx, &corev1.Namespace{ namespace, err := adminClient.CoreV1().Namespaces().Create(ctx, &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{GenerateName: "impersonation-integration-test-"}, ObjectMeta: metav1.ObjectMeta{GenerateName: "impersonation-integration-test-"},
}, metav1.CreateOptions{}) }, metav1.CreateOptions{})
@ -369,19 +362,19 @@ func TestImpersonationProxy(t *testing.T) {
// We already know that this Secret exists because we checked above. Now see that we can get it through // We already know that this Secret exists because we checked above. Now see that we can get it through
// the impersonation proxy without any impersonation headers on the request. // the impersonation proxy without any impersonation headers on the request.
_, err = impersonationProxyClient.CoreV1().Secrets(env.ConciergeNamespace).Get(ctx, impersonationProxyTLSSecretName, metav1.GetOptions{}) _, err = impersonationProxyClient.CoreV1().Secrets(env.ConciergeNamespace).Get(ctx, impersonationProxyTLSSecretName(env), metav1.GetOptions{})
require.NoError(t, err) require.NoError(t, err)
// Now we'll see what happens when we add an impersonation header to the request. This should generate a // Now we'll see what happens when we add an impersonation header to the request. This should generate a
// request similar to the one above, except that it will have an impersonation header. // request similar to the one above, except that it will have an impersonation header.
_, err = doubleImpersonationClient.CoreV1().Secrets(env.ConciergeNamespace).Get(ctx, impersonationProxyTLSSecretName, metav1.GetOptions{}) _, err = doubleImpersonationClient.CoreV1().Secrets(env.ConciergeNamespace).Get(ctx, impersonationProxyTLSSecretName(env), metav1.GetOptions{})
// Double impersonation is not supported yet, so we should get an error. // Double impersonation is not supported yet, so we should get an error.
expectedErr := fmt.Sprintf("the server rejected our request for an unknown reason (get secrets %s)", impersonationProxyTLSSecretName) expectedErr := fmt.Sprintf("the server rejected our request for an unknown reason (get secrets %s)", impersonationProxyTLSSecretName(env))
require.EqualError(t, err, expectedErr) require.EqualError(t, err, expectedErr)
}) })
// Update configuration to force the proxy to disabled mode // Update configuration to force the proxy to disabled mode
configMap := configMapForConfig(t, impersonator.Config{Mode: impersonator.ModeDisabled}) configMap := configMapForConfig(t, env, impersonator.Config{Mode: impersonator.ModeDisabled})
if env.HasCapability(library.HasExternalLoadBalancerProvider) { if env.HasCapability(library.HasExternalLoadBalancerProvider) {
t.Logf("creating configmap %s", configMap.Name) t.Logf("creating configmap %s", configMap.Name)
_, err = adminClient.CoreV1().ConfigMaps(env.ConciergeNamespace).Create(ctx, &configMap, metav1.CreateOptions{}) _, err = adminClient.CoreV1().ConfigMaps(env.ConciergeNamespace).Create(ctx, &configMap, metav1.CreateOptions{})
@ -396,7 +389,7 @@ func TestImpersonationProxy(t *testing.T) {
// The load balancer should not exist after we disable the impersonation proxy. // The load balancer should not exist after we disable the impersonation proxy.
// Note that this can take kind of a long time on real cloud providers (e.g. ~22 seconds on EKS). // Note that this can take kind of a long time on real cloud providers (e.g. ~22 seconds on EKS).
library.RequireEventuallyWithoutError(t, func() (bool, error) { library.RequireEventuallyWithoutError(t, func() (bool, error) {
hasService, err := hasImpersonationProxyLoadBalancerService(ctx, adminClient, env.ConciergeNamespace) hasService, err := hasImpersonationProxyLoadBalancerService(ctx, env, adminClient)
return !hasService, err return !hasService, err
}, time.Minute, 500*time.Millisecond) }, time.Minute, 500*time.Millisecond)
} }
@ -417,17 +410,17 @@ func TestImpersonationProxy(t *testing.T) {
// Check that the generated TLS cert Secret was deleted by the controller. // Check that the generated TLS cert Secret was deleted by the controller.
require.Eventually(t, func() bool { require.Eventually(t, func() bool {
_, err = adminClient.CoreV1().Secrets(env.ConciergeNamespace).Get(ctx, impersonationProxyTLSSecretName, metav1.GetOptions{}) _, err = adminClient.CoreV1().Secrets(env.ConciergeNamespace).Get(ctx, impersonationProxyTLSSecretName(env), metav1.GetOptions{})
return k8serrors.IsNotFound(err) return k8serrors.IsNotFound(err)
}, 10*time.Second, 250*time.Millisecond) }, 10*time.Second, 250*time.Millisecond)
} }
func configMapForConfig(t *testing.T, config impersonator.Config) corev1.ConfigMap { func configMapForConfig(t *testing.T, env *library.TestEnv, config impersonator.Config) corev1.ConfigMap {
configString, err := yaml.Marshal(config) configString, err := yaml.Marshal(config)
require.NoError(t, err) require.NoError(t, err)
configMap := corev1.ConfigMap{ configMap := corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: impersonationProxyConfigMapName, Name: impersonationProxyConfigMapName(env),
}, },
Data: map[string]string{ Data: map[string]string{
"config.yaml": string(configString), "config.yaml": string(configString),
@ -435,8 +428,8 @@ func configMapForConfig(t *testing.T, config impersonator.Config) corev1.ConfigM
return configMap return configMap
} }
func hasImpersonationProxyLoadBalancerService(ctx context.Context, client kubernetes.Interface, namespace string) (bool, error) { func hasImpersonationProxyLoadBalancerService(ctx context.Context, env *library.TestEnv, client kubernetes.Interface) (bool, error) {
service, err := client.CoreV1().Services(namespace).Get(ctx, impersonationProxyLoadBalancerName, metav1.GetOptions{}) service, err := client.CoreV1().Services(env.ConciergeNamespace).Get(ctx, impersonationProxyLoadBalancerName(env), metav1.GetOptions{})
if k8serrors.IsNotFound(err) { if k8serrors.IsNotFound(err) {
return false, nil return false, nil
} }
@ -446,8 +439,8 @@ func hasImpersonationProxyLoadBalancerService(ctx context.Context, client kubern
return service.Spec.Type == corev1.ServiceTypeLoadBalancer, nil return service.Spec.Type == corev1.ServiceTypeLoadBalancer, nil
} }
func getImpersonationProxyLoadBalancerIngress(ctx context.Context, client kubernetes.Interface, namespace string) (*corev1.LoadBalancerIngress, error) { func getImpersonationProxyLoadBalancerIngress(ctx context.Context, env *library.TestEnv, client kubernetes.Interface) (*corev1.LoadBalancerIngress, error) {
service, err := client.CoreV1().Services(namespace).Get(ctx, impersonationProxyLoadBalancerName, metav1.GetOptions{}) service, err := client.CoreV1().Services(env.ConciergeNamespace).Get(ctx, impersonationProxyLoadBalancerName(env), metav1.GetOptions{})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -460,3 +453,19 @@ func getImpersonationProxyLoadBalancerIngress(ctx context.Context, client kubern
} }
return &ingresses[0], nil return &ingresses[0], nil
} }
func impersonationProxyConfigMapName(env *library.TestEnv) string {
return env.ConciergeAppName + "-impersonation-proxy-config"
}
func impersonationProxyTLSSecretName(env *library.TestEnv) string {
return env.ConciergeAppName + "-impersonation-proxy-tls-serving-certificate"
}
func impersonationProxyCASecretName(env *library.TestEnv) string {
return env.ConciergeAppName + "-impersonation-proxy-ca-certificate"
}
func impersonationProxyLoadBalancerName(env *library.TestEnv) string {
return env.ConciergeAppName + "-impersonation-proxy-load-balancer"
}