diff --git a/cmd/entrypoint/consul.go b/cmd/entrypoint/consul.go index ec03d66b97..11315596ad 100644 --- a/cmd/entrypoint/consul.go +++ b/cmd/entrypoint/consul.go @@ -20,16 +20,18 @@ type consulMapping struct { } func ReconcileConsul(ctx context.Context, consulWatcher *consulWatcher, s *snapshotTypes.KubernetesSnapshot) error { + envAmbID := GetAmbassadorID() + var mappings []consulMapping for _, list := range s.Annotations { for _, a := range list { switch m := a.(type) { case *amb.Mapping: - if include(m.Spec.AmbassadorID) { + if m.Spec.AmbassadorID.Matches(envAmbID) { mappings = append(mappings, consulMapping{Service: m.Spec.Service, Resolver: m.Spec.Resolver}) } case *amb.TCPMapping: - if include(m.Spec.AmbassadorID) { + if m.Spec.AmbassadorID.Matches(envAmbID) { mappings = append(mappings, consulMapping{Service: m.Spec.Service, Resolver: m.Spec.Resolver}) } } @@ -38,19 +40,19 @@ func ReconcileConsul(ctx context.Context, consulWatcher *consulWatcher, s *snaps var resolvers []*amb.ConsulResolver for _, cr := range s.ConsulResolvers { - if include(cr.Spec.AmbassadorID) { + if cr.Spec.AmbassadorID.Matches(envAmbID) { resolvers = append(resolvers, cr) } } for _, m := range s.Mappings { - if include(m.Spec.AmbassadorID) { + if m.Spec.AmbassadorID.Matches(envAmbID) { mappings = append(mappings, consulMapping{Service: m.Spec.Service, Resolver: m.Spec.Resolver}) } } for _, tm := range s.TCPMappings { - if include(tm.Spec.AmbassadorID) { + if tm.Spec.AmbassadorID.Matches(envAmbID) { mappings = append(mappings, consulMapping{Service: tm.Spec.Service, Resolver: tm.Spec.Resolver}) } } @@ -325,7 +327,7 @@ func watchConsul( w.Watch(func(endpoints consulwatch.Endpoints, e error) { if endpoints.Id == "" { - // For Ambassador, overwrite the Id with the resolver's datacenter -- the + // For Ambassador, overwrite the ID with the resolver's datacenter -- the // Consul watcher doesn't actually hand back the DC, and we need it. endpoints.Id = resolver.Spec.Datacenter } diff --git a/cmd/entrypoint/endpoints.go b/cmd/entrypoint/endpoints.go index acefde1682..b20cf58b55 100644 --- a/cmd/entrypoint/endpoints.go +++ b/cmd/entrypoint/endpoints.go @@ -63,6 +63,8 @@ func newEndpointRoutingInfo() endpointRoutingInfo { } func (eri *endpointRoutingInfo) reconcileEndpointWatches(ctx context.Context, s *snapshotTypes.KubernetesSnapshot) { + envAmbID := GetAmbassadorID() + // Reset our state except for the previous endpoint watches. We keep them so we can detect if // the set of things we are interested in has changed. eri.resolverTypes = map[string]ResolverType{} @@ -78,7 +80,7 @@ func (eri *endpointRoutingInfo) reconcileEndpointWatches(ctx context.Context, s if _, isInvalid := a.(*kates.Unstructured); isInvalid { continue } - if include(GetAmbId(ctx, a)) { + if GetAmbID(ctx, a).Matches(envAmbID) { eri.checkResourcePhase1(ctx, a, "annotation") } } @@ -89,25 +91,25 @@ func (eri *endpointRoutingInfo) reconcileEndpointWatches(ctx context.Context, s // need to test every resource, and no need to walk over things we're not // interested in. for _, m := range s.Modules { - if include(m.Spec.AmbassadorID) { + if m.Spec.AmbassadorID.Matches(envAmbID) { eri.checkModule(ctx, m, "CRD") } } for _, r := range s.KubernetesServiceResolvers { - if include(r.Spec.AmbassadorID) { + if r.Spec.AmbassadorID.Matches(envAmbID) { eri.saveResolver(ctx, r.GetName(), KubernetesServiceResolver, "CRD") } } for _, r := range s.KubernetesEndpointResolvers { - if include(r.Spec.AmbassadorID) { + if r.Spec.AmbassadorID.Matches(envAmbID) { eri.saveResolver(ctx, r.GetName(), KubernetesEndpointResolver, "CRD") } } for _, r := range s.ConsulResolvers { - if include(r.Spec.AmbassadorID) { + if r.Spec.AmbassadorID.Matches(envAmbID) { eri.saveResolver(ctx, r.GetName(), ConsulResolver, "CRD") } } @@ -128,20 +130,20 @@ func (eri *endpointRoutingInfo) reconcileEndpointWatches(ctx context.Context, s if _, isInvalid := a.(*kates.Unstructured); isInvalid { continue } - if include(GetAmbId(ctx, a)) { + if GetAmbID(ctx, a).Matches(envAmbID) { eri.checkResourcePhase2(ctx, a, "annotation") } } } for _, m := range s.Mappings { - if include(m.Spec.AmbassadorID) { + if m.Spec.AmbassadorID.Matches(envAmbID) { eri.checkMapping(ctx, m, "CRD") } } for _, t := range s.TCPMappings { - if include(t.Spec.AmbassadorID) { + if t.Spec.AmbassadorID.Matches(envAmbID) { eri.checkTCPMapping(ctx, t, "CRD") } } diff --git a/cmd/entrypoint/entrypoint.go b/cmd/entrypoint/entrypoint.go index c303e8d69b..ee58604a5f 100644 --- a/cmd/entrypoint/entrypoint.go +++ b/cmd/entrypoint/entrypoint.go @@ -77,8 +77,6 @@ import ( // manager (e.g. kubernetes) is expected to take note and restart if // appropriate. -const envAmbassadorDemoMode string = "AMBASSADOR_DEMO_MODE" - func Main(ctx context.Context, Version string, args ...string) error { // Setup logging according to AES_LOG_LEVEL busy.SetLogLevel(logutil.DefaultLogLevel) @@ -105,8 +103,6 @@ func Main(ctx context.Context, Version string, args ...string) error { // Demo mode! dlog.Infof(ctx, "DEMO MODE") demoMode = true - // Set an environment variable so that other parts of the code can check if demo mode is active (mainly used for disabling synthetic authservice injection) - os.Setenv(envAmbassadorDemoMode, "true") } clusterID := GetClusterID(ctx) @@ -221,7 +217,7 @@ func Main(ctx context.Context, Version string, args ...string) error { } func clusterIDFromRootID(rootID string) string { - clusterUrl := fmt.Sprintf("d6e_id://%s/%s", rootID, GetAmbassadorId()) + clusterUrl := fmt.Sprintf("d6e_id://%s/%s", rootID, GetAmbassadorID()) uid := uuid.NewSHA1(uuid.NameSpaceURL, []byte(clusterUrl)) return strings.ToLower(uid.String()) diff --git a/cmd/entrypoint/env.go b/cmd/entrypoint/env.go index a62017ce2c..07f205fa21 100644 --- a/cmd/entrypoint/env.go +++ b/cmd/entrypoint/env.go @@ -19,7 +19,7 @@ func GetAgentService() string { return "" } -func GetAmbassadorId() string { +func GetAmbassadorID() string { id := os.Getenv("AMBASSADOR_ID") if id != "" { return id @@ -67,7 +67,7 @@ func GetEnvoyBootstrapFile() string { return env("ENVOY_BOOTSTRAP_FILE", path.Join(GetAmbassadorConfigBaseDir(), "bootstrap-ads.json")) } -func GetEnvoyBaseId() string { +func GetEnvoyBaseID() string { return env("AMBASSADOR_ENVOY_BASE_ID", "0") } @@ -149,7 +149,7 @@ func isDebug(name string) bool { } func GetEnvoyFlags() []string { - result := []string{"-c", GetEnvoyBootstrapFile(), "--base-id", GetEnvoyBaseId()} + result := []string{"-c", GetEnvoyBootstrapFile(), "--base-id", GetEnvoyBaseID()} svc := GetAgentService() if svc != "" { result = append(result, "--drain-time-s", "1") diff --git a/cmd/entrypoint/helpers.go b/cmd/entrypoint/helpers.go index 12de3c4679..f9944996b0 100644 --- a/cmd/entrypoint/helpers.go +++ b/cmd/entrypoint/helpers.go @@ -7,7 +7,6 @@ import ( "strings" "github.com/datawire/dlib/dexec" - amb "github.com/emissary-ingress/emissary/v3/pkg/api/getambassador.io/v3alpha1" ) func envbool(name string) bool { @@ -64,33 +63,3 @@ func convert(in interface{}, out interface{}) error { return nil } - -// Should we pay attention to a given AmbassadorID set? -// -// XXX Yes, amb.AmbassadorID is a singular name for a plural type. Sigh. -func include(id amb.AmbassadorID) bool { - // We always pay attention to the "_automatic_" ID -- it gives us a - // to easily always include certain configuration resources for Edge - // Stack. - if len(id) == 1 && id[0] == "_automatic_" { - return true - } - - // It's not "_automatic_", so we have to actually do the work. Grab - // our AmbassadorID... - me := GetAmbassadorId() - - // ...force an empty AmbassadorID to "default", per the documentation... - if len(id) == 0 { - id = amb.AmbassadorID{"default"} - } - - // ...and then see if our AmbassadorID is in the list. - for _, name := range id { - if me == name { - return true - } - } - - return false -} diff --git a/cmd/entrypoint/secrets.go b/cmd/entrypoint/secrets.go index ce8ad8bc4f..0e835974c4 100644 --- a/cmd/entrypoint/secrets.go +++ b/cmd/entrypoint/secrets.go @@ -175,6 +175,8 @@ func checkSecret( // since we don't want to send secrets to Ambassador unless we're // using them, since any secret we send will be saved to disk. func ReconcileSecrets(ctx context.Context, sh *SnapshotHolder) error { + envAmbID := GetAmbassadorID() + // Start by building up a list of all the K8s objects that are // allowed to mention secrets. Note that we vet the ambassador_id // for all of these before putting them on the list. @@ -190,7 +192,7 @@ func ReconcileSecrets(ctx context.Context, sh *SnapshotHolder) error { if _, isInvalid := a.(*kates.Unstructured); isInvalid { continue } - if include(GetAmbId(ctx, a)) { + if GetAmbID(ctx, a).Matches(envAmbID) { resources = append(resources, a) } } @@ -203,19 +205,19 @@ func ReconcileSecrets(ctx context.Context, sh *SnapshotHolder) error { if len(h.Spec.AmbassadorID) > 0 { id = h.Spec.AmbassadorID } - if include(id) { + if id.Matches(envAmbID) { resources = append(resources, h) } } // TLSContexts, Modules, and Ingresses are all straightforward. for _, t := range sh.k8sSnapshot.TLSContexts { - if include(t.Spec.AmbassadorID) { + if t.Spec.AmbassadorID.Matches(envAmbID) { resources = append(resources, t) } } for _, m := range sh.k8sSnapshot.Modules { - if include(m.Spec.AmbassadorID) { + if m.Spec.AmbassadorID.Matches(envAmbID) { resources = append(resources, m) } } diff --git a/cmd/entrypoint/snapshot.go b/cmd/entrypoint/snapshot.go index 7d172492fc..46791b9dae 100644 --- a/cmd/entrypoint/snapshot.go +++ b/cmd/entrypoint/snapshot.go @@ -19,8 +19,8 @@ func NewKubernetesSnapshot() *snapshotTypes.KubernetesSnapshot { return a } -// GetAmbId extracts the AmbassadorId from the kubernetes resource. -func GetAmbId(ctx context.Context, resource kates.Object) amb.AmbassadorID { +// GetAmbID extracts the AmbassadorID from the kubernetes resource. +func GetAmbID(ctx context.Context, resource kates.Object) amb.AmbassadorID { switch r := resource.(type) { case *amb.Host: var id amb.AmbassadorID diff --git a/cmd/entrypoint/syntheticauth.go b/cmd/entrypoint/syntheticauth.go index 35b6b71db5..925c037932 100644 --- a/cmd/entrypoint/syntheticauth.go +++ b/cmd/entrypoint/syntheticauth.go @@ -2,188 +2,117 @@ package entrypoint import ( "context" + "fmt" "github.com/datawire/dlib/dlog" "github.com/emissary-ingress/emissary/v3/pkg/api/getambassador.io/v3alpha1" "github.com/emissary-ingress/emissary/v3/pkg/emissaryutil" "github.com/emissary-ingress/emissary/v3/pkg/kates" - "github.com/emissary-ingress/emissary/v3/pkg/snapshot/v1" ) -// Iterates over the annotations in a snapshot to check if any AuthServices are present. -func annotationsContainAuthService(annotations map[string]snapshot.AnnotationList) bool { - for _, list := range annotations { - for _, obj := range list { - switch obj.(type) { - case *v3alpha1.AuthService: - return true - default: - continue - } - } - } - return false -} - // Checks if the provided string is a loopback IP address with port 8500 func IsLocalhost8500(svcStr string) bool { _, hostname, port, err := emissaryutil.ParseServiceName(svcStr) return err == nil && port == 8500 && emissaryutil.IsLocalhost(hostname) } +func iterateOverAuthServices(sh *SnapshotHolder, cb func( + authService *v3alpha1.AuthService, // duh + name string, // name to unambiguously refer to the authService by; might be more complex than "name.namespace" if it's an annotation + parentName string, // name of the thing that the annotation is on (or empty if not an annotation) + idx int, // index of the authService; either in sh.k8sSnapshot.AuthServices or in sh.k8sSnapshot.Annotations[parentName] +)) { + envAmbID := GetAmbassadorID() + + for i, authService := range sh.k8sSnapshot.AuthServices { + if authService.Spec.AmbassadorID.Matches(envAmbID) { + name := authService.TypeMeta.Kind + "/" + authService.ObjectMeta.Name + "." + authService.ObjectMeta.Namespace + cb(authService, name, "", i) + } + } + for parentName, list := range sh.k8sSnapshot.Annotations { + for i, obj := range list { + if authService, ok := obj.(*v3alpha1.AuthService); ok && authService.Spec.AmbassadorID.Matches(envAmbID) { + name := fmt.Sprintf("%s#%d", parentName, i) + cb(authService, name, parentName, i) + } + } + } +} + // This is a gross hack to remove all AuthServices using protocol_version: v2 only when running Edge-Stack and then inject an // AuthService with protocol_version: v3 if needed. The purpose of this hack is to prevent Edge-Stack 2.3 from // using any other AuthService than the default one running as part of amb-sidecar and force the protocol version to v3. func ReconcileAuthServices(ctx context.Context, sh *SnapshotHolder, deltas *[]*kates.Delta) error { // We only want to remove AuthServices if this is an instance of Edge-Stack - isEdgeStack, err := IsEdgeStack() - if err != nil { - return err + if isEdgeStack, err := IsEdgeStack(); err != nil { + return fmt.Errorf("ReconcileAuthServices: %w", err) } else if !isEdgeStack { return nil } - // We also dont want to do anything with AuthServices if the Docker demo mode is running - if envbool("AMBASSADOR_DEMO_MODE") { - return nil - } - // Construct a synthetic AuthService to be injected if we dont find any valid AuthServices - injectSyntheticAuth := true - syntheticAuth := &v3alpha1.AuthService{ - TypeMeta: kates.TypeMeta{ - Kind: "AuthService", - APIVersion: "getambassador.io/v3alpha1", - }, - ObjectMeta: kates.ObjectMeta{ - Name: "synthetic-edge-stack-auth", - Namespace: GetAmbassadorNamespace(), - }, - Spec: v3alpha1.AuthServiceSpec{ - AuthService: "127.0.0.1:8500", - Proto: "grpc", - ProtocolVersion: "v3", - AmbassadorID: []string{"_automatic_"}, - }, - } - - var authServices []*v3alpha1.AuthService - syntheticAuthExists := false - for _, authService := range sh.k8sSnapshot.AuthServices { - // check if the AuthService points at 127.0.0.1:8500 (edge-stack) + // using a name with underscores prevents it from colliding with anything real in the + // cluster--Kubernetes resources can't have underscores in their name. + const syntheticAuthServiceName = "synthetic_edge_stack_auth" + + var ( + numAuthServices uint64 + syntheticAuth *v3alpha1.AuthService + syntheticAuthIdx int + ) + iterateOverAuthServices(sh, func(authService *v3alpha1.AuthService, name, parentName string, i int) { + numAuthServices++ if IsLocalhost8500(authService.Spec.AuthService) { - // If it does point at localhost, make sure it is v3, otherwise we need to inject the synthetic AuthService - if authService.Spec.ProtocolVersion == "v3" { - injectSyntheticAuth = false - if authService.ObjectMeta.Name == "synthetic-edge-stack-auth" { - syntheticAuthExists = true - } else { - authServices = append(authServices, authService) - } - } else { - // In the event that there is an AuthService that does not have protocol_version: v3 - // Then we use the spec of that AuthService as the Synthetic v3 AuthService we will inject later - syntheticAuth.Spec = authService.Spec - syntheticAuth.Spec.ProtocolVersion = "v3" + if parentName == "" && authService.ObjectMeta.Name == syntheticAuthServiceName { + syntheticAuth = authService + syntheticAuthIdx = i } - } else { - // By default we keep any custom AuthServices that do not point at localhost - authServices = append(authServices, authService) - injectSyntheticAuth = false - } - } - - // TODO if there are v3 authServices, still remove any that are not `v3` - - // Also loop over the annotations and remove authservices that are not v3. We do - // this by looping over each entry in the annotations map, removing all the non-v3 - // AuthService entries, and then removing any keys that end up with empty lists. - - // OK. Loop over all the keys and their corrauthServicesesponding lists of annotations... - if annotationsContainAuthService(sh.k8sSnapshot.Annotations) { - for key, list := range sh.k8sSnapshot.Annotations { - // ...and build up our edited list of things. - editedList := snapshot.AnnotationList{} - - for _, obj := range list { - switch annotationObj := obj.(type) { - case *v3alpha1.AuthService: - if IsLocalhost8500(annotationObj.Spec.AuthService) { - // If it does point at localhost, make sure it is v3, otherwise we need to inject the synthetic AuthService - if annotationObj.Spec.ProtocolVersion == "v3" { - injectSyntheticAuth = false - if annotationObj.ObjectMeta.Name == "synthetic-edge-stack-auth" { - syntheticAuthExists = true - } else { - authServices = append(authServices, annotationObj) - editedList = append(editedList, annotationObj) - } - } else { - // In the event that there is an AuthService that does not have protocol_version: v3 - // Then we use the spec of that AuthService as the Synthetic v3 AuthService we will inject later - syntheticAuth.Spec = annotationObj.Spec - syntheticAuth.Spec.ProtocolVersion = "v3" - } - } else { - // By default we keep any custom AuthServices that do not point at localhost - authServices = append(authServices, annotationObj) - editedList = append(editedList, annotationObj) - injectSyntheticAuth = false - } - default: - // This isn't an AuthService at all, so we'll keep it. - editedList = append(editedList, annotationObj) - } - } - - // Once here, is our editedList is empty? - if len(editedList) == 0 { - // Yes. Delete the whole key for this list. - delete(sh.k8sSnapshot.Annotations, key) - } else { - // Nope, not empty. Save the edited list. - sh.k8sSnapshot.Annotations[key] = editedList + if authService.Spec.ProtocolVersion != "v3" { + // Force the Edge Stack AuthService to be protocol_version=v3. This + // is important so that <2.3 and >=2.3 installations can coexist. + // This is important, because for zero-downtime upgrades, they must + // coexist briefly while the new Deployment is getting rolled out. + dlog.Debugf(ctx, "ReconcileAuthServices: Forcing protocol_version=v3 on %s", name) + authService.Spec.ProtocolVersion = "v3" } } - } - - if injectSyntheticAuth { - dlog.Debugf(ctx, "[WATCHER]: No valid AuthServices with protocol_version: v3 detected, injecting Synthetic AuthService") - // There are no valid AuthServices with protocol_version: v3. A synthetic one needs to be injected. - authServices = append(authServices, syntheticAuth) - - // loop through the deltas and remove any AuthService deltas adding other AuthServices before the Synthetic delta is inserted - var newDeltas []*kates.Delta - for _, delta := range *deltas { - // Keep all the deltas that are not for AuthServices. The AuthService deltas can be kept as long as they are not an add delta. - if (delta.Kind != "AuthService") || (delta.Kind == "AuthService" && delta.DeltaType != kates.ObjectAdd) { - newDeltas = append(newDeltas, delta) - } + }) + + switch { + case numAuthServices == 0: // add the synthetic auth service + dlog.Debug(ctx, "ReconcileAuthServices: No user-provided AuthServices detected; injecting synthetic AuthService") + syntheticAuth = &v3alpha1.AuthService{ + TypeMeta: kates.TypeMeta{ + Kind: "AuthService", + APIVersion: "getambassador.io/v3alpha1", + }, + ObjectMeta: kates.ObjectMeta{ + Name: syntheticAuthServiceName, + Namespace: GetAmbassadorNamespace(), + }, + Spec: v3alpha1.AuthServiceSpec{ + AmbassadorID: []string{GetAmbassadorID()}, + AuthService: "127.0.0.1:8500", + Proto: "grpc", + ProtocolVersion: "v3", + }, } - newDeltas = append(newDeltas, &kates.Delta{ + sh.k8sSnapshot.AuthServices = append(sh.k8sSnapshot.AuthServices, syntheticAuth) + *deltas = append(*deltas, &kates.Delta{ TypeMeta: syntheticAuth.TypeMeta, ObjectMeta: syntheticAuth.ObjectMeta, DeltaType: kates.ObjectAdd, }) - - *deltas = newDeltas - sh.k8sSnapshot.AuthServices = authServices - } else if len(authServices) >= 1 { - // Write back the list of valid AuthServices. - sh.k8sSnapshot.AuthServices = authServices - - // The synthetic AuthService needs to be removed since one or more valid AuthServices are present. - if syntheticAuthExists { - dlog.Debugf(ctx, "[WATCHER]: Valid AuthServices using protocol_version: v3 detected alongside the Synthetic AuthService, removing Synthetic...") - // One or more Valid AuthServices are present. The synthetic AuthService exists and needs to be removed now. - var newDeltas []*kates.Delta - *deltas = append(*deltas, &kates.Delta{ - TypeMeta: syntheticAuth.TypeMeta, - ObjectMeta: syntheticAuth.ObjectMeta, - DeltaType: kates.ObjectDelete, - }) - - *deltas = newDeltas - } + case numAuthServices > 1 && syntheticAuth != nil: // remove the synthetic auth service + dlog.Debugf(ctx, "ReconcileAuthServices: %d user-provided AuthServices detected; removing synthetic AuthService", numAuthServices-1) + sh.k8sSnapshot.AuthServices = append( + sh.k8sSnapshot.AuthServices[:syntheticAuthIdx], + sh.k8sSnapshot.AuthServices[syntheticAuthIdx+1:]...) + *deltas = append(*deltas, &kates.Delta{ + TypeMeta: syntheticAuth.TypeMeta, + ObjectMeta: syntheticAuth.ObjectMeta, + DeltaType: kates.ObjectDelete, + }) } return nil diff --git a/cmd/entrypoint/testutil_fake_filtersecrets_test.go b/cmd/entrypoint/testutil_fake_filtersecrets_test.go index ffe85de16e..8a78d27117 100644 --- a/cmd/entrypoint/testutil_fake_filtersecrets_test.go +++ b/cmd/entrypoint/testutil_fake_filtersecrets_test.go @@ -67,7 +67,6 @@ type: Opaque snap, err := f.GetSnapshot(hasSecret("namespaced-secret", "bar")) require.NoError(t, err) assert.NotNil(t, snap) - t.Setenv("EDGE_STACK", "") }) } } @@ -112,7 +111,6 @@ type: Opaque snap, err := f.GetSnapshot(hasSecret("namespaced-secret", "foo")) require.NoError(t, err) assert.NotNil(t, snap) - t.Setenv("EDGE_STACK", "") }) } } diff --git a/cmd/entrypoint/testutil_fake_syntheticauth_test.go b/cmd/entrypoint/testutil_fake_syntheticauth_test.go index b2353f21bb..9404cf1a6b 100644 --- a/cmd/entrypoint/testutil_fake_syntheticauth_test.go +++ b/cmd/entrypoint/testutil_fake_syntheticauth_test.go @@ -13,7 +13,8 @@ import ( "github.com/emissary-ingress/emissary/v3/pkg/snapshot/v1" ) -// This predicate is used to check k8s snapshots for an AuthService matching the provided name and namespace +// This predicate is used to check k8s snapshots for an AuthService matching the provided name and +// namespace. func HasAuthService(namespace, name string) func(snapshot *snapshot.Snapshot) bool { return func(snapshot *snapshot.Snapshot) bool { for _, m := range snapshot.Kubernetes.AuthServices { @@ -25,67 +26,19 @@ func HasAuthService(namespace, name string) func(snapshot *snapshot.Snapshot) bo } } -// Tests the synthetic auth generation when a valid AuthService is created -// This authservice has protocol_Version: v3 and should not be replaced by the synthetic AuthService +// Tests the synthetic auth generation when a valid AuthService is created. This AuthService has +// `protocol_version: v3` and should not be replaced by the synthetic AuthService. func TestSyntheticAuthValid(t *testing.T) { - t.Setenv("EDGE_STACK", "true") - - f := entrypoint.RunFake(t, entrypoint.FakeConfig{EnvoyConfig: true}, nil) - - err := f.UpsertYAML(` ---- -apiVersion: getambassador.io/v3alpha1 -kind: AuthService -metadata: - name: edge-stack-auth-test - namespace: foo -spec: - auth_service: 127.0.0.1:8500 - protocol_version: "v3" - proto: "grpc" -`) - assert.NoError(t, err) - f.Flush() - - // Use the predicate above to check that the snapshot contains the AuthService defined above - // The AuthService has protocol_Version: v3 so it should not be removed/replaced by the synthetic AuthService - // injected by syntheticauth.go - snap, err := f.GetSnapshot(HasAuthService("foo", "edge-stack-auth-test")) - assert.NoError(t, err) - assert.NotNil(t, snap) - - assert.Equal(t, "edge-stack-auth-test", snap.Kubernetes.AuthServices[0].Name) - // In edge-stack we should only ever have 1 AuthService. - assert.Equal(t, 1, len(snap.Kubernetes.AuthServices)) - - // Check for an ext_authz cluster name matching the provided AuthService (Http_Filters are harder to check since they always have the same name) - // the namespace for this extauthz cluster should be foo (since that is the namespace of the valid AuthService above) - isAuthCluster := func(c *v3cluster.Cluster) bool { - return strings.Contains(c.Name, "cluster_extauth_127_0_0_1_8500_foo") - } - - // Grab the next Envoy config that has an Edge Stack auth cluster on 127.0.0.1:8500 - envoyConfig, err := f.GetEnvoyConfig(func(envoy *v3bootstrap.Bootstrap) bool { - return FindCluster(envoy, isAuthCluster) != nil - }) - require.NoError(t, err) + for _, apiVersion := range []string{"v2", "v3alpha1"} { + apiVersion := apiVersion // capture loop variable + t.Run(apiVersion, func(t *testing.T) { + t.Setenv("EDGE_STACK", "true") - // Make sure an Envoy Config containing a extauth cluster for the AuthService that was defined - assert.NotNil(t, envoyConfig) - - t.Setenv("EDGE_STACK", "") -} - -// Tests the synthetic auth generation when a valid AuthService is created as a getambassador.io/v2 resource -// This authservice has protocol_Version: v3 and should not be replaced by the synthetic AuthService -func TestSyntheticAuthValidV2(t *testing.T) { - t.Setenv("EDGE_STACK", "true") - - f := entrypoint.RunFake(t, entrypoint.FakeConfig{EnvoyConfig: true}, nil) + f := entrypoint.RunFake(t, entrypoint.FakeConfig{EnvoyConfig: true}, nil) - err := f.UpsertYAML(` + err := f.UpsertYAML(` --- -apiVersion: getambassador.io/v2 +apiVersion: getambassador.io/` + apiVersion + ` kind: AuthService metadata: name: edge-stack-auth-test @@ -95,48 +48,56 @@ spec: protocol_version: "v3" proto: "grpc" `) - assert.NoError(t, err) - f.Flush() - - // Use the predicate above to check that the snapshot contains the AuthService defined above - // The AuthService has protocol_Version: v3 so it should not be removed/replaced by the synthetic AuthService - // injected by syntheticauth.go - snap, err := f.GetSnapshot(HasAuthService("foo", "edge-stack-auth-test")) - assert.NoError(t, err) - assert.NotNil(t, snap) + assert.NoError(t, err) + f.Flush() + + // Use the predicate above to check that the snapshot contains the + // AuthService defined above. The AuthService has `protocol_version: v3` so + // it should not be removed/replaced by the synthetic AuthService injected + // by syntheticauth.go + snap, err := f.GetSnapshot(HasAuthService("foo", "edge-stack-auth-test")) + assert.NoError(t, err) + assert.NotNil(t, snap) + + // In edge-stack we should only ever have 1 AuthService. + assert.Equal(t, 1, len(snap.Kubernetes.AuthServices)) + assert.Equal(t, "edge-stack-auth-test", snap.Kubernetes.AuthServices[0].Name) + + // Check for an ext_authz cluster name matching the provided AuthService + // (Http_Filters are harder to check since they always have the same name). + // The namespace for this extauthz cluster should be foo (since that is the + // namespace of the valid AuthService above). + isAuthCluster := func(c *v3cluster.Cluster) bool { + return strings.Contains(c.Name, "cluster_extauth_127_0_0_1_8500_foo") + } - assert.Equal(t, "edge-stack-auth-test", snap.Kubernetes.AuthServices[0].Name) - // In edge-stack we should only ever have 1 AuthService. - assert.Equal(t, 1, len(snap.Kubernetes.AuthServices)) + // Grab the next Envoy config that has an Edge Stack auth cluster on + // 127.0.0.1:8500 + envoyConfig, err := f.GetEnvoyConfig(func(envoy *v3bootstrap.Bootstrap) bool { + return FindCluster(envoy, isAuthCluster) != nil + }) + require.NoError(t, err) - // Check for an ext_authz cluster name matching the provided AuthService (Http_Filters are harder to check since they always have the same name) - // the namespace for this extauthz cluster should be foo (since that is the namespace of the valid AuthService above) - isAuthCluster := func(c *v3cluster.Cluster) bool { - return strings.Contains(c.Name, "cluster_extauth_127_0_0_1_8500_foo") + // Make sure an Envoy Config containing a extauth cluster for the + // AuthService that was defined. + assert.NotNil(t, envoyConfig) + }) } - - // Grab the next Envoy config that has an Edge Stack auth cluster on 127.0.0.1:8500 - envoyConfig, err := f.GetEnvoyConfig(func(envoy *v3bootstrap.Bootstrap) bool { - return FindCluster(envoy, isAuthCluster) != nil - }) - require.NoError(t, err) - - // Make sure an Envoy Config containing a extauth cluster for the AuthService that was defined - assert.NotNil(t, envoyConfig) - - t.Setenv("EDGE_STACK", "") } -// This tests with a provided AuthService that has no protocol_version (which defaults to v2) -// The synthetic AuthService should be created instead +// This tests with a provided AuthService that has no protocol_version (which defaults to v2). It +// should get forcibly overridden to be v3. func TestSyntheticAuthReplace(t *testing.T) { - t.Setenv("EDGE_STACK", "true") + for _, apiVersion := range []string{"v2", "v3alpha1"} { + apiVersion := apiVersion // capture loop variable + t.Run(apiVersion, func(t *testing.T) { + t.Setenv("EDGE_STACK", "true") - f := entrypoint.RunFake(t, entrypoint.FakeConfig{EnvoyConfig: true}, nil) + f := entrypoint.RunFake(t, entrypoint.FakeConfig{EnvoyConfig: true}, nil) - err := f.UpsertYAML(` + err := f.UpsertYAML(` --- -apiVersion: getambassador.io/v3alpha1 +apiVersion: getambassador.io/` + apiVersion + ` kind: AuthService metadata: name: edge-stack-auth-test @@ -145,101 +106,59 @@ spec: auth_service: 127.0.0.1:8500 proto: "grpc" `) - assert.NoError(t, err) - f.Flush() - - // Use the predicate above to check that the snapshot contains the AuthService defined above - // The AuthService does not have protocol_Version: v3 so it should be removed and replaced by the synthetic AuthService - // injected by syntheticauth.go - snap, err := f.GetSnapshot(HasAuthService("default", "synthetic-edge-stack-auth")) - assert.NoError(t, err) - assert.NotNil(t, snap) - - // The snapshot should only have the synthetic AuthService and not the one defined above - assert.Equal(t, "synthetic-edge-stack-auth", snap.Kubernetes.AuthServices[0].Name) - // In edge-stack we should only ever have 1 AuthService. - assert.Equal(t, 1, len(snap.Kubernetes.AuthServices)) - - // Check for an ext_authz cluster name matching the provided AuthService (Http_Filters are harder to check since they always have the same name) - // the namespace for this extauthz cluster should be default (since that is the namespace of the synthetic AuthService) - isAuthCluster := func(c *v3cluster.Cluster) bool { - return strings.Contains(c.Name, "cluster_extauth_127_0_0_1_8500_default") - } - - // Grab the next Envoy config that has an Edge Stack auth cluster on 127.0.0.1:8500 - envoyConfig, err := f.GetEnvoyConfig(func(envoy *v3bootstrap.Bootstrap) bool { - return FindCluster(envoy, isAuthCluster) != nil - }) - require.NoError(t, err) - - // Make sure an Envoy Config containing a extauth cluster for the AuthService that was defined - assert.NotNil(t, envoyConfig) - - t.Setenv("EDGE_STACK", "") -} - -// This tests with a provided AuthService that has no protocol_version (which defaults to v2) -// The synthetic AuthService should be created instead -func TestSyntheticAuthReplaceV2(t *testing.T) { - t.Setenv("EDGE_STACK", "true") - - f := entrypoint.RunFake(t, entrypoint.FakeConfig{EnvoyConfig: true}, nil) - - err := f.UpsertYAML(` ---- -apiVersion: getambassador.io/v2 -kind: AuthService -metadata: - name: edge-stack-auth-test - namespace: foo -spec: - auth_service: 127.0.0.1:8500 - proto: "grpc" -`) - assert.NoError(t, err) - f.Flush() - - // Use the predicate above to check that the snapshot contains the AuthService defined above - // The AuthService does not have protocol_Version: v3 so it should be removed and replaced by the synthetic AuthService - // injected by syntheticauth.go - snap, err := f.GetSnapshot(HasAuthService("default", "synthetic-edge-stack-auth")) - assert.NoError(t, err) - assert.NotNil(t, snap) + assert.NoError(t, err) + f.Flush() + + // The AuthService does not have `protocol_version: v3` so it should be + // forcibly edited to say `protocol_version: v3` by syntheticauth.go + snap, err := f.GetSnapshot(HasAuthService("foo", "edge-stack-auth-test")) + assert.NoError(t, err) + assert.NotNil(t, snap) + + // In edge-stack we should only ever have 1 AuthService. + assert.Equal(t, 1, len(snap.Kubernetes.AuthServices)) + // The snapshot should only have the one defined above. + assert.Equal(t, "edge-stack-auth-test", snap.Kubernetes.AuthServices[0].Name) + // The protocol version should be forcibly set to v3. + assert.Equal(t, "v3", snap.Kubernetes.AuthServices[0].Spec.ProtocolVersion) + + // Check for an ext_authz cluster name matching the provided AuthService + // (Http_Filters are harder to check since they always have the same name). + // The namespace for this extauthz cluster should be "foo" (since that is + // the namespace of the AuthService). + isAuthCluster := func(c *v3cluster.Cluster) bool { + return strings.Contains(c.Name, "cluster_extauth_127_0_0_1_8500_foo") + } - // The snapshot should only have the synthetic AuthService and not the one defined above - assert.Equal(t, "synthetic-edge-stack-auth", snap.Kubernetes.AuthServices[0].Name) - // In edge-stack we should only ever have 1 AuthService. - assert.Equal(t, 1, len(snap.Kubernetes.AuthServices)) + // Grab the next Envoy config that has an Edge Stack auth cluster on + // 127.0.0.1:8500 + envoyConfig, err := f.GetEnvoyConfig(func(envoy *v3bootstrap.Bootstrap) bool { + return FindCluster(envoy, isAuthCluster) != nil + }) + require.NoError(t, err) - // Check for an ext_authz cluster name matching the provided AuthService (Http_Filters are harder to check since they always have the same name) - // the namespace for this extauthz cluster should be default (since that is the namespace of the synthetic AuthService) - isAuthCluster := func(c *v3cluster.Cluster) bool { - return strings.Contains(c.Name, "cluster_extauth_127_0_0_1_8500_default") + // Make sure an Envoy Config containing a extauth cluster for the + // AuthService that was defined. + assert.NotNil(t, envoyConfig) + }) } - - // Grab the next Envoy config that has an Edge Stack auth cluster on 127.0.0.1:8500 - envoyConfig, err := f.GetEnvoyConfig(func(envoy *v3bootstrap.Bootstrap) bool { - return FindCluster(envoy, isAuthCluster) != nil - }) - require.NoError(t, err) - - // Make sure an Envoy Config containing a extauth cluster for the AuthService that was defined - assert.NotNil(t, envoyConfig) - - t.Setenv("EDGE_STACK", "") } -// Tests the synthetic auth generation when an invalid AuthService is created as a getambassador.io/v2 resource -// This authservice has protocol_Version: v3 and should not be replaced by the synthetic AuthService even though it has a bogus value -// because the bogus field will be dropped when it is loaded and we will be left with a Valid AuthService +// Tests the synthetic auth generation when an invalid AuthService is created. This AuthService has +// `protocol_version: v3` and should not be replaced by the synthetic AuthService even though it has +// a bogus value because the bogus field will be dropped when it is loaded and we will be left with +// a valid AuthService. func TestSyntheticAuthBogusField(t *testing.T) { - t.Setenv("EDGE_STACK", "true") + for _, apiVersion := range []string{"v2", "v3alpha1"} { + apiVersion := apiVersion // capture loop variable + t.Run(apiVersion, func(t *testing.T) { + t.Setenv("EDGE_STACK", "true") - f := entrypoint.RunFake(t, entrypoint.FakeConfig{EnvoyConfig: true}, nil) + f := entrypoint.RunFake(t, entrypoint.FakeConfig{EnvoyConfig: true}, nil) - err := f.UpsertYAML(` + err := f.UpsertYAML(` --- -apiVersion: getambassador.io/v3alpha1 +apiVersion: getambassador.io/` + apiVersion + ` kind: AuthService metadata: name: edge-stack-auth-test @@ -250,93 +169,46 @@ spec: proto: "grpc" bogus_field: "foo" `) - assert.NoError(t, err) - f.Flush() - - // Use the predicate above to check that the snapshot contains the AuthService defined above - // The AuthService has protocol_Version: v3 so it should not be removed/replaced by the synthetic AuthService - // injected by syntheticauth.go - snap, err := f.GetSnapshot(HasAuthService("foo", "edge-stack-auth-test")) - assert.NoError(t, err) - assert.NotNil(t, snap) - - assert.Equal(t, "edge-stack-auth-test", snap.Kubernetes.AuthServices[0].Name) - // In edge-stack we should only ever have 1 AuthService. - assert.Equal(t, 1, len(snap.Kubernetes.AuthServices)) - - // Check for an ext_authz cluster name matching the provided AuthService (Http_Filters are harder to check since they always have the same name) - // the namespace for this extauthz cluster should be foo (since that is the namespace of the valid AuthService above) - isAuthCluster := func(c *v3cluster.Cluster) bool { - return strings.Contains(c.Name, "cluster_extauth_127_0_0_1_8500_foo") - } - - // Grab the next Envoy config that has an Edge Stack auth cluster on 127.0.0.1:8500 - envoyConfig, err := f.GetEnvoyConfig(func(envoy *v3bootstrap.Bootstrap) bool { - return FindCluster(envoy, isAuthCluster) != nil - }) - require.NoError(t, err) - - // Make sure an Envoy Config containing a extauth cluster for the AuthService that was defined - assert.NotNil(t, envoyConfig) - - t.Setenv("EDGE_STACK", "") -} - -// Tests the synthetic auth generation when an invalid AuthService is created as a getambassador.io/v2 resource -// This authservice has protocol_Version: v3 and should be replaced by the synthetic AuthService because it contains a bogus field and is not valid. -func TestSyntheticAuthBogusFieldV2(t *testing.T) { - t.Setenv("EDGE_STACK", "true") - - f := entrypoint.RunFake(t, entrypoint.FakeConfig{EnvoyConfig: true}, nil) - - err := f.UpsertYAML(` ---- -apiVersion: getambassador.io/v2 -kind: AuthService -metadata: - name: edge-stack-auth-test - namespace: foo -spec: - auth_service: 127.0.0.1:8500 - protocol_version: "v3" - proto: "grpc" - bogus_field: "foo" -`) - assert.NoError(t, err) - f.Flush() + assert.NoError(t, err) + f.Flush() + + // Use the predicate above to check that the snapshot contains the + // AuthService defined above. The AuthService has `protocol_version: v3` so + // it should not be removed/replaced by the synthetic AuthService injected + // by syntheticauth.go + snap, err := f.GetSnapshot(HasAuthService("foo", "edge-stack-auth-test")) + assert.NoError(t, err) + assert.NotNil(t, snap) + + // In edge-stack we should only ever have 1 AuthService. + assert.Equal(t, 1, len(snap.Kubernetes.AuthServices)) + assert.Equal(t, "edge-stack-auth-test", snap.Kubernetes.AuthServices[0].Name) + + // Check for an ext_authz cluster name matching the provided AuthService + // (Http_Filters are harder to check since they always have the same name). + // The namespace for this extauthz cluster should be "foo" (since that is + // the namespace of the valid AuthService above). + isAuthCluster := func(c *v3cluster.Cluster) bool { + return strings.Contains(c.Name, "cluster_extauth_127_0_0_1_8500_foo") + } - // Use the predicate above to check that the snapshot contains the AuthService defined above - // The AuthService has protocol_Version: v3 so it should not be removed/replaced by the synthetic AuthService - // injected by syntheticauth.go - snap, err := f.GetSnapshot(HasAuthService("foo", "edge-stack-auth-test")) - assert.NoError(t, err) - assert.NotNil(t, snap) + // Grab the next Envoy config that has an Edge Stack auth cluster on + // 127.0.0.1:8500 + envoyConfig, err := f.GetEnvoyConfig(func(envoy *v3bootstrap.Bootstrap) bool { + return FindCluster(envoy, isAuthCluster) != nil + }) + require.NoError(t, err) - assert.Equal(t, "edge-stack-auth-test", snap.Kubernetes.AuthServices[0].Name) - // In edge-stack we should only ever have 1 AuthService. - assert.Equal(t, 1, len(snap.Kubernetes.AuthServices)) - - // Check for an ext_authz cluster name matching the provided AuthService (Http_Filters are harder to check since they always have the same name) - // the namespace for this extauthz cluster should be foo (since that is the namespace of the valid AuthService above) - isAuthCluster := func(c *v3cluster.Cluster) bool { - return strings.Contains(c.Name, "cluster_extauth_127_0_0_1_8500_foo") + // Make sure an Envoy Config containing a extauth cluster for the + // AuthService that was defined. + assert.NotNil(t, envoyConfig) + }) } - - // Grab the next Envoy config that has an Edge Stack auth cluster on 127.0.0.1:8500 - envoyConfig, err := f.GetEnvoyConfig(func(envoy *v3bootstrap.Bootstrap) bool { - return FindCluster(envoy, isAuthCluster) != nil - }) - require.NoError(t, err) - - // Make sure an Envoy Config containing a extauth cluster for the AuthService that was defined - assert.NotNil(t, envoyConfig) - - t.Setenv("EDGE_STACK", "") - } -// Tests the synthetic auth generation when an invalid AuthService (because the protocol_version is invalid for the supported enums) -// This AuthService should be tossed out an the synthetic AuthService should be injected +// Tests the synthetic auth generation when an invalid AuthService (because the protocol_version is +// invalid for the supported enums). This AuthService should be tossed out an the synthetic +// AuthService should be injected. func TestSyntheticAuthInvalidProtocolVer(t *testing.T) { t.Setenv("EDGE_STACK", "true") @@ -351,26 +223,28 @@ metadata: namespace: foo spec: auth_service: 127.0.0.1:8500 - protocol_version: "v4" + protocol_version: "vBogus" proto: "grpc" bogus_field: "foo" `) assert.NoError(t, err) f.Flush() - // Use the predicate above to check that the snapshot contains the Synthetic AuthService - // The AuthService has protocol_Version: v3, but it has a bogus field so it should not be validated and instead we inject the synthetic authservice - snap, err := f.GetSnapshot(HasAuthService("default", "synthetic-edge-stack-auth")) + // Use the predicate above to check that the snapshot contains the synthetic AuthService. + // The AuthService has `protocol_version: v3`, but it has a bogus field so it should not be + // validated and instead we inject the synthetic AuthService. + snap, err := f.GetSnapshot(HasAuthService("default", "synthetic_edge_stack_auth")) assert.NoError(t, err) assert.NotNil(t, snap) - // The snapshot should only have the synthetic AuthService and not the one defined above - assert.Equal(t, "synthetic-edge-stack-auth", snap.Kubernetes.AuthServices[0].Name) // In edge-stack we should only ever have 1 AuthService. assert.Equal(t, 1, len(snap.Kubernetes.AuthServices)) + // The snapshot should only have the synthetic AuthService and not the one defined above. + assert.Equal(t, "synthetic_edge_stack_auth", snap.Kubernetes.AuthServices[0].Name) - // Check for an ext_authz cluster name matching the synthetic AuthService. - // the namespace for this extauthz cluster should be default (since that is the namespace of the synthetic AuthService) + // Check for an ext_authz cluster name matching the synthetic AuthService. The namespace + // for this extauthz cluster should be default (since that is the namespace of the synthetic + // AuthService). isAuthCluster := func(c *v3cluster.Cluster) bool { return strings.Contains(c.Name, "cluster_extauth_127_0_0_1_8500_default") } @@ -381,16 +255,16 @@ spec: }) require.NoError(t, err) - // Make sure an Envoy Config containing a extauth cluster for the AuthService that was defined + // Make sure an Envoy Config containing a extauth cluster for the AuthService that was + // defined. assert.NotNil(t, envoyConfig) - - t.Setenv("EDGE_STACK", "") } -// Tests the synthetic auth generation when an invalid AuthService is created and edited several times in succession. -// After the config is edited several times, we should see that the final result is our provided valid AuthService. -// There should not be any duplicate AuthService resources, and the synthetic AuthService that gets created when the first -// Invalid AuthService is applied should be removed when the final edit makes it a valid AuthService. +// Tests the synthetic auth generation when an invalid AuthService is created and edited several +// times in succession. After the config is edited several times, we should see that the final +// result is our provided valid AuthService. There should not be any duplicate AuthService +// resources, and the synthetic AuthService that gets created when the first invalid AuthService is +// applied should be removed when the final edit makes it a valid AuthService. func TestSyntheticAuthChurn(t *testing.T) { t.Setenv("EDGE_STACK", "true") @@ -448,20 +322,21 @@ spec: `) assert.NoError(t, err) - // Use the predicate above to check that the snapshot contains the AuthService defined above - // The AuthService has protocol_Version: v3 so it should not be removed/replaced by the synthetic AuthService - // injected by syntheticauth.go + // Use the predicate above to check that the snapshot contains the AuthService defined + // above. The AuthService has `protocol_version: v3` so it should not be removed/replaced + // by the synthetic AuthService injected by syntheticauth.go snap, err := f.GetSnapshot(HasAuthService("foo", "edge-stack-auth-test")) assert.NoError(t, err) assert.NotNil(t, snap) - // The snapshot should only have the synthetic AuthService and not the one defined above - assert.Equal(t, "edge-stack-auth-test", snap.Kubernetes.AuthServices[0].Name) // In edge-stack we should only ever have 1 AuthService. assert.Equal(t, 1, len(snap.Kubernetes.AuthServices)) + // The snapshot should only have the synthetic AuthService and not the one defined above. + assert.Equal(t, "edge-stack-auth-test", snap.Kubernetes.AuthServices[0].Name) - // Check for an ext_authz cluster name matching the provided AuthService (Http_Filters are harder to check since they always have the same name) - // the namespace for this extauthz cluster should be foo (since that is the namespace of the valid AuthService above) + // Check for an ext_authz cluster name matching the provided AuthService (Http_Filters are + // harder to check since they always have the same name). The namespace for this extauthz + // cluster should be foo (since that is the namespace of the valid AuthService above) isAuthCluster := func(c *v3cluster.Cluster) bool { return strings.Contains(c.Name, "cluster_extauth_127_0_0_1_8500_foo") } @@ -474,19 +349,18 @@ spec: // Make sure an Envoy Config containing a extauth cluster for the AuthService that was defined assert.NotNil(t, envoyConfig) - - t.Setenv("EDGE_STACK", "") } -// Tests the synthetic auth generation by first creating an invalid AuthService and confirming that the synthetic AuthService gets injected. -// Afterwards, a valid AuthService is applied and we expect the synthetic AuthService to be removed in favor of the new valid AuthService. +// Tests the synthetic auth generation by first creating an invalid AuthService and confirming that +// the synthetic AuthService gets injected. Afterwards, a valid AuthService is applied and we +// expect the synthetic AuthService to be removed in favor of the new valid AuthService. func TestSyntheticAuthInjectAndRemove(t *testing.T) { t.Setenv("EDGE_STACK", "true") f := entrypoint.RunFake(t, entrypoint.FakeConfig{EnvoyConfig: true}, nil) f.AutoFlush(true) - // This will cause a synthethic authservice to be injected + // This will cause a synthethic AuthService to be injected. err := f.UpsertYAML(` --- apiVersion: getambassador.io/v3alpha1 @@ -497,23 +371,25 @@ metadata: spec: auth_service: 127.0.0.1:8500 proto: "grpc" - bogus_field: "foo" + protocol_version: "vBogus" `) assert.NoError(t, err) - // Use the predicate above to check that the snapshot contains the Synthetic AuthService - // The AuthService has protocol_Version: v3, but it has a bogus field so it should not be validated and instead we inject the synthetic authservice - snap, err := f.GetSnapshot(HasAuthService("default", "synthetic-edge-stack-auth")) + // Use the predicate above to check that the snapshot contains the synthetic AuthService. + // The user-provided AuthService is invalid and so it should be ignored and instead we + // inject the synthetic AuthService. + snap, err := f.GetSnapshot(HasAuthService("default", "synthetic_edge_stack_auth")) assert.NoError(t, err) assert.NotNil(t, snap) - // The snapshot should only have the synthetic AuthService and not the one defined above - assert.Equal(t, "synthetic-edge-stack-auth", snap.Kubernetes.AuthServices[0].Name) // We should only have 1 AuthService. assert.Equal(t, 1, len(snap.Kubernetes.AuthServices)) + // The snapshot should only have the synthetic AuthService and not the one defined above. + assert.Equal(t, "synthetic_edge_stack_auth", snap.Kubernetes.AuthServices[0].Name) - // Check for an ext_authz cluster name matching the synthetic AuthService. - // the namespace for this extauthz cluster should be default (since that is the namespace of the synthetic AuthService) + // Check for an ext_authz cluster name matching the synthetic AuthService. The namespace + // for this extauthz cluster should be default (since that is the namespace of the synthetic + // AuthService). isAuthCluster := func(c *v3cluster.Cluster) bool { return strings.Contains(c.Name, "cluster_extauth_127_0_0_1_8500_default") } @@ -524,13 +400,13 @@ spec: }) require.NoError(t, err) - // Make sure an Envoy Config containing a extauth cluster for the AuthService that was defined + // Make sure an Envoy Config containing a extauth cluster for the AuthService that was + // defined. assert.NotNil(t, envoyConfig) - t.Setenv("EDGE_STACK", "") - - // Updating the yaml for that AuthService to include protocol_version: v3 should make it valid and then - // Remove our synthetic AuthService and allow the now valid AuthService to be used. + // Updating the yaml for that AuthService to include `protocol_version: v3` should make it + // valid and then remove our synthetic AuthService and allow the now valid AuthService to be + // used. err = f.UpsertYAML(` --- apiVersion: getambassador.io/v3alpha1 @@ -545,19 +421,20 @@ spec: `) assert.NoError(t, err) - // Use the predicate above to check that the snapshot contains the AuthService defined above - // The AuthService has protocol_Version: v3 so it should not be removed/replaced by the synthetic AuthService - // injected by syntheticauth.go + // Use the predicate above to check that the snapshot contains the AuthService defined + // above. The AuthService has `protocol_version: v3` so it should not be removed/replaced + // by the synthetic AuthService injected by syntheticauth.go snap, err = f.GetSnapshot(HasAuthService("foo", "edge-stack-auth-test")) assert.NoError(t, err) assert.NotNil(t, snap) - assert.Equal(t, "edge-stack-auth-test", snap.Kubernetes.AuthServices[0].Name) // In edge-stack we should only ever have 1 AuthService. assert.Equal(t, 1, len(snap.Kubernetes.AuthServices)) + assert.Equal(t, "edge-stack-auth-test", snap.Kubernetes.AuthServices[0].Name) - // Check for an ext_authz cluster name matching the provided AuthService (Http_Filters are harder to check since they always have the same name) - // the namespace for this extauthz cluster should be foo (since that is the namespace of the valid AuthService above) + // Check for an ext_authz cluster name matching the provided AuthService (Http_Filters are + // harder to check since they always have the same name). The namespace for this extauthz + // cluster should be foo (since that is the namespace of the valid AuthService above). isAuthCluster = func(c *v3cluster.Cluster) bool { return strings.Contains(c.Name, "cluster_extauth_127_0_0_1_8500_foo") } @@ -568,15 +445,14 @@ spec: }) require.NoError(t, err) - // Make sure an Envoy Config containing a extauth cluster for the AuthService that was defined + // Make sure an Envoy Config containing a extauth cluster for the AuthService that was + // defined. assert.NotNil(t, envoyConfig) - - t.Setenv("EDGE_STACK", "") - } -// This AuthService points at 127.0.0.1:8500, but it does not have protocol_version: v3. It also has additional fields set. -// The correct action is to create a SyntheticAuth copy of this AuthService with the same fields but with protocol_version: v3 +// This AuthService points at 127.0.0.1:8500, but it does not have `protocol_version: v3`. It also +// has additional fields set. The correct action is to edit the AuthService to say +// `protocol_version: v3`. func TestSyntheticAuthCopyFields(t *testing.T) { t.Setenv("EDGE_STACK", "true") @@ -598,29 +474,24 @@ spec: assert.NoError(t, err) f.Flush() - // Use the predicate above to check that the snapshot contains the Synthetic AuthService - // The AuthService has protocol_Version: v3, but it is missing the protocol_version: v3 field. - // We expect the synthetic AuthService to be injected, but later we will check that the synthetic AuthService has - // Our custom timeout_ms field - snap, err := f.GetSnapshot(HasAuthService("default", "synthetic-edge-stack-auth")) + // Use the predicate above to check that the snapshot contains the AuthService. + snap, err := f.GetSnapshot(HasAuthService("foo", "edge-stack-auth-test")) assert.NoError(t, err) assert.NotNil(t, snap) - // The snapshot should only have the synthetic AuthService - assert.Equal(t, "synthetic-edge-stack-auth", snap.Kubernetes.AuthServices[0].Name) // In edge-stack we should only ever have 1 AuthService. assert.Equal(t, 1, len(snap.Kubernetes.AuthServices)) + // It should be that user-provided AuthService... + assert.Equal(t, "edge-stack-auth-test", snap.Kubernetes.AuthServices[0].Name) + assert.Equal(t, int64(12345), snap.Kubernetes.AuthServices[0].Spec.Timeout.Duration.Milliseconds()) + // ... but with `protocol_version: v3` set. + assert.Equal(t, "v3", snap.Kubernetes.AuthServices[0].Spec.ProtocolVersion) - // Even though it is the synthetic AuthService, we should have the custom timeout_ms and v3 protocol version - for _, authService := range snap.Kubernetes.AuthServices { - assert.Equal(t, int64(12345), authService.Spec.Timeout.Duration.Milliseconds()) - assert.Equal(t, "v3", authService.Spec.ProtocolVersion) - } - - // Check for an ext_authz cluster name matching the synthetic AuthService. - // the namespace for this extauthz cluster should be default (since that is the namespace of the synthetic AuthService) + // Check for an ext_authz cluster name matching the synthetic AuthService. The namespace + // for this extauthz cluster should be default (since that is the namespace of the synthetic + // AuthService). isAuthCluster := func(c *v3cluster.Cluster) bool { - return strings.Contains(c.Name, "cluster_extauth_127_0_0_1_8500_default") + return strings.Contains(c.Name, "cluster_extauth_127_0_0_1_8500_foo") } // Grab the next Envoy config that has an Edge Stack auth cluster on 127.0.0.1:8500 @@ -629,13 +500,13 @@ spec: }) require.NoError(t, err) - // Make sure an Envoy Config containing a extauth cluster for the AuthService that was defined + // Make sure an Envoy Config containing a extauth cluster for the AuthService that was + // defined. assert.NotNil(t, envoyConfig) - - t.Setenv("EDGE_STACK", "") } -// This AuthService does not point at 127.0.0.1:8500, we leave it alone rather than adding a synthetic one +// This AuthService does not point at 127.0.0.1:8500, we leave it alone rather than adding a +// synthetic one. func TestSyntheticAuthCustomAuthService(t *testing.T) { t.Setenv("EDGE_STACK", "true") @@ -657,23 +528,24 @@ spec: assert.NoError(t, err) f.Flush() - // Use the predicate above to check that the snapshot contains the AuthService defined above - // The AuthService has protocol_Version: v3 so it should not be removed/replaced by the synthetic AuthService - // injected by syntheticauth.go + // Use the predicate above to check that the snapshot contains the AuthService defined + // above. The AuthService has `protocol_version: v3` so it should not be removed/replaced + // by the synthetic AuthService injected by syntheticauth.go snap, err := f.GetSnapshot(HasAuthService("foo", "edge-stack-auth-test")) assert.NoError(t, err) assert.NotNil(t, snap) - assert.Equal(t, "edge-stack-auth-test", snap.Kubernetes.AuthServices[0].Name) // In edge-stack we should only ever have 1 AuthService. assert.Equal(t, 1, len(snap.Kubernetes.AuthServices)) + assert.Equal(t, "edge-stack-auth-test", snap.Kubernetes.AuthServices[0].Name) for _, authService := range snap.Kubernetes.AuthServices { assert.Equal(t, "dummy-service", authService.Spec.AuthService) } - // Check for an ext_authz cluster name matching the provided AuthService (Http_Filters are harder to check since they always have the same name) - // the namespace for this extauthz cluster should be foo (since that is the namespace of the valid AuthService above) + // Check for an ext_authz cluster name matching the provided AuthService (Http_Filters are + // harder to check since they always have the same name). the namespace for this extauthz + // cluster should be foo (since that is the namespace of the valid AuthService above). isAuthCluster := func(c *v3cluster.Cluster) bool { return strings.Contains(c.Name, "cluster_extauth_dummy_service_foo") } @@ -684,14 +556,13 @@ spec: }) require.NoError(t, err) - // Make sure an Envoy Config containing a extauth cluster for the AuthService that was defined + // Make sure an Envoy Config containing a extauth cluster for the AuthService that was + // defined. assert.NotNil(t, envoyConfig) - - t.Setenv("EDGE_STACK", "") } -// When deciding if we need to inject a synthetic AuthService or not, we need to be able to reliably determine if that -// AuthService points at a localhost:8500 or not +// When deciding if we need to inject a synthetic AuthService or not, we need to be able to reliably +// determine if that AuthService points at a localhost:8500 or not. func TestIsLocalhost8500(t *testing.T) { t.Parallel() diff --git a/cmd/entrypoint/watcher.go b/cmd/entrypoint/watcher.go index 9d0eefc654..aea4fb6b7e 100644 --- a/cmd/entrypoint/watcher.go +++ b/cmd/entrypoint/watcher.go @@ -44,7 +44,7 @@ func WatchAllTheThings( interestingTypes := GetInterestingTypes(ctx, serverTypeList) queries := GetQueries(ctx, interestingTypes) - ambassadorMeta := getAmbassadorMeta(GetAmbassadorId(), clusterID, version, client) + ambassadorMeta := getAmbassadorMeta(GetAmbassadorID(), clusterID, version, client) // **** SETUP DONE for the Kubernetes Watcher diff --git a/pkg/api/getambassador.io/v3alpha1/common.go b/pkg/api/getambassador.io/v3alpha1/common.go index fef03eac2f..ef40afd824 100644 --- a/pkg/api/getambassador.io/v3alpha1/common.go +++ b/pkg/api/getambassador.io/v3alpha1/common.go @@ -130,6 +130,11 @@ func (aid AmbassadorID) Matches(envVar string) bool { if item == envVar { return true } + if item == "_automatic_" { + // We always pay attention to the "_automatic_" ID -- it gives us a to + // easily always include certain configuration resources for Edge Stack. + return true + } } return false } diff --git a/python/ambassador/envoy/v3/v3httpfilter.py b/python/ambassador/envoy/v3/v3httpfilter.py index 08a537ab04..63acff77ac 100644 --- a/python/ambassador/envoy/v3/v3httpfilter.py +++ b/python/ambassador/envoy/v3/v3httpfilter.py @@ -237,11 +237,11 @@ def V3HTTPFilter_authv1(auth: IRAuth, v3config: 'V3Config'): request_handle:respond( {[":status"] = "500", ["content-type"] = "application/json"}, - '{'.. - ' "message": "the """+auth.rkey+""" AuthService is misconfigured; see the logs for more information",'.. - ' "request_id": "'..request_handle:headers():get('x-request-id')..'",'.. - ' "status_code": 500,'.. - '}') + '{\\n'.. + ' "message": "the """+auth.rkey+""" AuthService is misconfigured; see the logs for more information",\\n'.. + ' "request_id": "'..request_handle:headers():get('x-request-id')..'",\\n'.. + ' "status_code": 500\\n'.. + '}\\n') end """, }, diff --git a/python/tests/kat/t_extauth.py b/python/tests/kat/t_extauth.py index 3cfba27ab8..0e82e6bcdd 100644 --- a/python/tests/kat/t_extauth.py +++ b/python/tests/kat/t_extauth.py @@ -1004,6 +1004,13 @@ def queries(self): def check(self): if self.expected_protocol_version == 'invalid': + for i, result in enumerate(self.results): + # Verify the basic structure of the HTTP 500's JSON body. + assert result.json, f"self.results[{i}] does not have a JSON body" + assert result.json['status_code'] == 500, f"self.results[{i}] JSON body={repr(result.json)} does not have status_code=500" + assert result.json['request_id'], f"self.results[{i}] JSON body={repr(result.json)} does not have request_id" + assert self.path.k8s in result.json['message'], f"self.results[{i}] JSON body={repr(result.json)} does not have thing-containing-the-annotation-containing-the-AuthService name {repr(self.path.k8s)} in message" + assert 'AuthService' in result.json['message'], f"self.results[{i}] JSON body={repr(result.json)} does not have type 'AuthService' in message" return # [0] Verifies all request headers sent to the authorization server.