diff --git a/internal/cmd/egctl/translate_test.go b/internal/cmd/egctl/translate_test.go index 9207c8bb75b5..c413f8f2b564 100644 --- a/internal/cmd/egctl/translate_test.go +++ b/internal/cmd/egctl/translate_test.go @@ -22,6 +22,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/yaml" + "github.com/envoyproxy/gateway/internal/gatewayapi/resource" "github.com/envoyproxy/gateway/internal/utils/field" "github.com/envoyproxy/gateway/internal/utils/file" ) @@ -362,8 +363,12 @@ func TestTranslate(t *testing.T) { // want.GatewayClass.Status.SupportedFeatures = status.GatewaySupportedFeatures // } - opts := cmpopts.IgnoreFields(metav1.Condition{}, "LastTransitionTime") - require.Empty(t, cmp.Diff(want, got, opts)) + opts := []cmp.Option{ + cmpopts.IgnoreFields(metav1.Condition{}, "LastTransitionTime"), + cmpopts.IgnoreFields(resource.Resources{}, "Cache"), + cmpopts.EquateEmpty(), + } + require.Empty(t, cmp.Diff(want, got, opts...)) }) } } diff --git a/internal/gatewayapi/resource/load.go b/internal/gatewayapi/resource/load.go index 2445a459c742..a85b2c8a698e 100644 --- a/internal/gatewayapi/resource/load.go +++ b/internal/gatewayapi/resource/load.go @@ -141,7 +141,7 @@ func loadKubernetesYAMLToResources(input []byte, addMissingResources bool) (*Res }, Spec: typedSpec.(gwapiv1.GatewaySpec), } - resources.Gateways = append(resources.Gateways, gateway) + resources.AppendResource(gateway) case KindTCPRoute: typedSpec := spec.Interface() tcpRoute := &gwapiv1a2.TCPRoute{ @@ -213,7 +213,7 @@ func loadKubernetesYAMLToResources(input []byte, addMissingResources bool) (*Res Name: name, }, } - resources.Namespaces = append(resources.Namespaces, namespace) + resources.AppendResource(namespace) providedNamespaceMap.Insert(name) case KindService: typedSpec := spec.Interface() @@ -241,7 +241,7 @@ func loadKubernetesYAMLToResources(input []byte, addMissingResources bool) (*Res }, Spec: typedSpec.(egv1a1.EnvoyPatchPolicySpec), } - resources.EnvoyPatchPolicies = append(resources.EnvoyPatchPolicies, envoyPatchPolicy) + resources.AppendResource(envoyPatchPolicy) case KindClientTrafficPolicy: typedSpec := spec.Interface() clientTrafficPolicy := &egv1a1.ClientTrafficPolicy{ @@ -254,7 +254,7 @@ func loadKubernetesYAMLToResources(input []byte, addMissingResources bool) (*Res }, Spec: typedSpec.(egv1a1.ClientTrafficPolicySpec), } - resources.ClientTrafficPolicies = append(resources.ClientTrafficPolicies, clientTrafficPolicy) + resources.AppendResource(clientTrafficPolicy) case KindBackendTrafficPolicy: typedSpec := spec.Interface() backendTrafficPolicy := &egv1a1.BackendTrafficPolicy{ @@ -267,7 +267,7 @@ func loadKubernetesYAMLToResources(input []byte, addMissingResources bool) (*Res }, Spec: typedSpec.(egv1a1.BackendTrafficPolicySpec), } - resources.BackendTrafficPolicies = append(resources.BackendTrafficPolicies, backendTrafficPolicy) + resources.AppendResource(backendTrafficPolicy) case KindSecurityPolicy: typedSpec := spec.Interface() securityPolicy := &egv1a1.SecurityPolicy{ @@ -294,7 +294,7 @@ func loadKubernetesYAMLToResources(input []byte, addMissingResources bool) (*Res }, Spec: typedSpec.(egv1a1.HTTPRouteFilterSpec), } - resources.HTTPRouteFilters = append(resources.HTTPRouteFilters, httpRouteFilter) + resources.AppendResource(httpRouteFilter) } return nil @@ -309,7 +309,7 @@ func loadKubernetesYAMLToResources(input []byte, addMissingResources bool) (*Res Name: config.DefaultNamespace, }, } - resources.Namespaces = append(resources.Namespaces, namespace) + resources.AppendResource(namespace) providedNamespaceMap.Insert(config.DefaultNamespace) } } @@ -322,7 +322,7 @@ func loadKubernetesYAMLToResources(input []byte, addMissingResources bool) (*Res Name: ns, }, } - resources.Namespaces = append(resources.Namespaces, namespace) + resources.AppendResource(namespace) } } diff --git a/internal/gatewayapi/resource/resource.go b/internal/gatewayapi/resource/resource.go index 97468511fa83..e25f7d82211d 100644 --- a/internal/gatewayapi/resource/resource.go +++ b/internal/gatewayapi/resource/resource.go @@ -7,8 +7,9 @@ package resource import ( "cmp" - "reflect" + gcmp "github.com/google/go-cmp/cmp" + gcmpopt "github.com/google/go-cmp/cmp/cmpopts" "golang.org/x/exp/slices" corev1 "k8s.io/api/core/v1" discoveryv1 "k8s.io/api/discovery/v1" @@ -64,6 +65,7 @@ type Resources struct { ExtensionServerPolicies []unstructured.Unstructured `json:"extensionServerPolicies,omitempty" yaml:"extensionServerPolicies,omitempty"` Backends []*egv1a1.Backend `json:"backends,omitempty" yaml:"backends,omitempty"` HTTPRouteFilters []*egv1a1.HTTPRouteFilter `json:"httpFilters,omitempty" yaml:"httpFilters,omitempty"` + Cache *Cache `json:"-" yaml:"-"` } func NewResources() *Resources { @@ -88,97 +90,10 @@ func NewResources() *Resources { ExtensionServerPolicies: []unstructured.Unstructured{}, Backends: []*egv1a1.Backend{}, HTTPRouteFilters: []*egv1a1.HTTPRouteFilter{}, + Cache: newCache(), } } -func (r *Resources) GetNamespace(name string) *corev1.Namespace { - for _, ns := range r.Namespaces { - if ns.Name == name { - return ns - } - } - - return nil -} - -func (r *Resources) GetEnvoyProxy(namespace, name string) *egv1a1.EnvoyProxy { - for _, ep := range r.EnvoyProxiesForGateways { - if ep.Namespace == namespace && ep.Name == name { - return ep - } - } - - return nil -} - -func (r *Resources) GetService(namespace, name string) *corev1.Service { - for _, svc := range r.Services { - if svc.Namespace == namespace && svc.Name == name { - return svc - } - } - - return nil -} - -func (r *Resources) GetServiceImport(namespace, name string) *mcsapiv1a1.ServiceImport { - for _, svcImp := range r.ServiceImports { - if svcImp.Namespace == namespace && svcImp.Name == name { - return svcImp - } - } - - return nil -} - -func (r *Resources) GetBackend(namespace, name string) *egv1a1.Backend { - for _, be := range r.Backends { - if be.Namespace == namespace && be.Name == name { - return be - } - } - - return nil -} - -func (r *Resources) GetSecret(namespace, name string) *corev1.Secret { - for _, secret := range r.Secrets { - if secret.Namespace == namespace && secret.Name == name { - return secret - } - } - - return nil -} - -func (r *Resources) GetConfigMap(namespace, name string) *corev1.ConfigMap { - for _, configMap := range r.ConfigMaps { - if configMap.Namespace == namespace && configMap.Name == name { - return configMap - } - } - - return nil -} - -func (r *Resources) GetEndpointSlicesForBackend(svcNamespace, svcName string, backendKind string) []*discoveryv1.EndpointSlice { - var endpointSlices []*discoveryv1.EndpointSlice - for _, endpointSlice := range r.EndpointSlices { - var backendSelectorLabel string - switch backendKind { - case KindService: - backendSelectorLabel = discoveryv1.LabelServiceName - case KindServiceImport: - backendSelectorLabel = mcsapiv1a1.LabelServiceName - } - if svcNamespace == endpointSlice.Namespace && - endpointSlice.GetLabels()[backendSelectorLabel] == svcName { - endpointSlices = append(endpointSlices, endpointSlice) - } - } - return endpointSlices -} - // ControllerResources holds all the GatewayAPI resources per GatewayClass type ControllerResources []*Resources @@ -200,7 +115,7 @@ func (c *ControllerResources) Equal(y *ControllerResources) bool { c.sort() y = y.DeepCopy() y.sort() - return reflect.DeepEqual(c, y) + return gcmp.Equal(*c, *y, gcmpopt.IgnoreFields(Resources{}, "Cache")) } func (c *ControllerResources) sort() { diff --git a/internal/gatewayapi/resource/resource_cache.go b/internal/gatewayapi/resource/resource_cache.go new file mode 100644 index 000000000000..cb062bc104b3 --- /dev/null +++ b/internal/gatewayapi/resource/resource_cache.go @@ -0,0 +1,66 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package resource + +var cacheKinds = []string{ + "Gateway", "ReferenceGrant", "Namespace", + "ServiceImport", "EndpointSlice", + "Secret", "ConfigMap", "EnvoyPatchPolicy", + "ClientTrafficPolicy", "BackendTrafficPolicy", "SecurityPolicy", + "BackendTLSPolicy", "EnvoyExtensionPolicy", "HTTPRouteFilter", +} + +// Set holds the resources with the kind. +// +k8s:deepcopy-gen=true +type Set struct { + Values map[string]string `json:"-" yaml:"-"` +} + +func newSet() *Set { + return &Set{ + Values: map[string]string{}, + } +} + +func (s *Set) Has(item string) bool { + _, contained := s.Values[item] + return contained +} + +func (s *Set) Insert(items ...string) { + for _, item := range items { + s.Values[item] = "" + } +} + +// Cache holds some duplicate resources in memory. +// +k8s:deepcopy-gen=true +type Cache struct { + ResourceSet map[string]*Set `json:"-" yaml:"-"` +} + +func (r *Resources) InitCache() { + r.Cache = newCache() +} + +func newCache() *Cache { + resourceSets := make(map[string]*Set, len(cacheKinds)) + for _, kind := range cacheKinds { + resourceSets[kind] = newSet() + } + + return &Cache{ + ResourceSet: resourceSets, + } +} + +func (r *Resources) resourceCache(kind string) *Set { + if rs, ok := r.Cache.ResourceSet[kind]; ok { + return rs + } + + return newSet() +} diff --git a/internal/gatewayapi/resource/resource_cache_test.go b/internal/gatewayapi/resource/resource_cache_test.go new file mode 100644 index 000000000000..701da4978342 --- /dev/null +++ b/internal/gatewayapi/resource/resource_cache_test.go @@ -0,0 +1,126 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package resource + +import ( + "fmt" + "testing" + + discoveryv1 "k8s.io/api/discovery/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" +) + +func TestResourcesCache(t *testing.T) { + type fields struct { + gw map[string]*gwapiv1.Gateway + endpointSlice map[string]*discoveryv1.EndpointSlice + } + tests := []struct { + name string + fields fields + }{ + { + name: "kind exist", + fields: fields{ + gw: map[string]*gwapiv1.Gateway{ + "Gateway": { + TypeMeta: metav1.TypeMeta{ + Kind: "Gateway", + APIVersion: "gateway.networking.k8s.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "gw", + }, + }, + }, + endpointSlice: map[string]*discoveryv1.EndpointSlice{ + "EndpointSlice": { + TypeMeta: metav1.TypeMeta{ + Kind: "EndpointSlice", + APIVersion: "discovery.k8s.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "endpointSlice", + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := NewResources() + for kind, gateway := range tt.fields.gw { + t.Logf("Append with kind %v resource %v", kind, gateway) + r.AppendResource(gateway) + resource := fmt.Sprintf("%s/%s", gateway.Namespace, gateway.Name) + if got := r.Cache.ResourceSet[kind].Has(resource); !got { + t.Errorf("Append with kind %v Error got %v", kind, got) + } + } + for kind, slice := range tt.fields.endpointSlice { + t.Logf("Append with kind %v resource %v", kind, slice) + r.AppendResource(slice) + resource := fmt.Sprintf("%s/%s", slice.Namespace, slice.Name) + if got := r.Cache.ResourceSet[kind].Has(resource); !got { + t.Errorf("Append with kind %v Error got %v", kind, got) + } + } + }) + } +} + +func TestSet(t *testing.T) { + type fields struct { + Values map[string]string + } + type args struct { + item string + } + tests := []struct { + name string + fields fields + args args + want bool + insertWant bool + }{ + { + name: "item exist", + fields: fields{Values: map[string]string{ + "item": "", + }}, + args: args{item: "item"}, + want: true, + insertWant: true, + }, + { + name: "item not exist", + fields: fields{Values: map[string]string{ + "item0": "", + }}, + args: args{item: "item"}, + want: false, + insertWant: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := &Set{Values: tt.fields.Values} + if got := s.Has(tt.args.item); got != tt.want { + t.Errorf("Has() = %v, want %v", got, tt.want) + } + }) + + t.Run(fmt.Sprintf("%s-insert", tt.name), func(t *testing.T) { + s := newSet() + s.Insert(tt.args.item) + if got := s.Has(tt.args.item); got != tt.insertWant { + t.Errorf("Insert Has() = %v, insert want %v", got, tt.insertWant) + } + }) + } +} diff --git a/internal/gatewayapi/resource/resource_mgr.go b/internal/gatewayapi/resource/resource_mgr.go new file mode 100644 index 000000000000..32170efd062e --- /dev/null +++ b/internal/gatewayapi/resource/resource_mgr.go @@ -0,0 +1,190 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package resource + +import ( + "fmt" + "sync" + + corev1 "k8s.io/api/core/v1" + discoveryv1 "k8s.io/api/discovery/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + gwapiv1a3 "sigs.k8s.io/gateway-api/apis/v1alpha3" + gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" + mcsapiv1a1 "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" +) + +var lock sync.RWMutex + +func (r *Resources) AppendResource(obj client.Object) bool { + lock.Lock() + defer lock.Unlock() + + objUID := string(obj.GetUID()) + if objUID == "" { + objUID = fmt.Sprintf("%s/%s", obj.GetNamespace(), obj.GetName()) + } + + resourceCache := r.resourceCache(obj.GetObjectKind().GroupVersionKind().Kind) + if resourceCache.Has(objUID) { + return false + } + + switch res := obj.(type) { + case *gwapiv1.Gateway: + r.Gateways = append(r.Gateways, res) + case *gwapiv1b1.ReferenceGrant: + r.ReferenceGrants = append(r.ReferenceGrants, res) + case *corev1.Namespace: + r.Namespaces = append(r.Namespaces, res) + case *mcsapiv1a1.ServiceImport: + r.ServiceImports = append(r.ServiceImports, res) + case *discoveryv1.EndpointSlice: + r.EndpointSlices = append(r.EndpointSlices, res) + case *corev1.Secret: + r.Secrets = append(r.Secrets, res) + case *corev1.ConfigMap: + r.ConfigMaps = append(r.ConfigMaps, res) + case *egv1a1.EnvoyPatchPolicy: + r.EnvoyPatchPolicies = append(r.EnvoyPatchPolicies, res) + case *egv1a1.ClientTrafficPolicy: + r.ClientTrafficPolicies = append(r.ClientTrafficPolicies, res) + case *egv1a1.BackendTrafficPolicy: + r.BackendTrafficPolicies = append(r.BackendTrafficPolicies, res) + case *egv1a1.SecurityPolicy: + r.SecurityPolicies = append(r.SecurityPolicies, res) + case *gwapiv1a3.BackendTLSPolicy: + r.BackendTLSPolicies = append(r.BackendTLSPolicies, res) + case *egv1a1.EnvoyExtensionPolicy: + r.EnvoyExtensionPolicies = append(r.EnvoyExtensionPolicies, res) + case *egv1a1.HTTPRouteFilter: + r.HTTPRouteFilters = append(r.HTTPRouteFilters, res) + } + + resourceCache.Insert(objUID) + return true +} + +func (r *Resources) AppendClientTrafficPolicies(clientTrafficPolicies ...*egv1a1.ClientTrafficPolicy) { + for _, policy := range clientTrafficPolicies { + r.AppendResource(policy) + } +} + +func (r *Resources) AppendBackendTrafficPolicies(backendTrafficPolicies ...*egv1a1.BackendTrafficPolicy) { + for _, policy := range backendTrafficPolicies { + r.AppendResource(policy) + } +} + +func (r *Resources) AppendSecurityPolicies(securityPolicies ...*egv1a1.SecurityPolicy) { + for _, policy := range securityPolicies { + r.AppendResource(policy) + } +} + +func (r *Resources) AppendBackendTLSPolicies(backendTLSPolicies ...*gwapiv1a3.BackendTLSPolicy) { + for _, policy := range backendTLSPolicies { + r.AppendResource(policy) + } +} + +func (r *Resources) AppendEnvoyExtensionPolicies(envoyExtensionPolicies ...*egv1a1.EnvoyExtensionPolicy) { + for _, policy := range envoyExtensionPolicies { + r.AppendResource(policy) + } +} + +func (r *Resources) GetNamespace(name string) *corev1.Namespace { + for _, ns := range r.Namespaces { + if ns.Name == name { + return ns + } + } + + return nil +} + +func (r *Resources) GetEnvoyProxy(namespace, name string) *egv1a1.EnvoyProxy { + for _, ep := range r.EnvoyProxiesForGateways { + if ep.Namespace == namespace && ep.Name == name { + return ep + } + } + + return nil +} + +func (r *Resources) GetService(namespace, name string) *corev1.Service { + for _, svc := range r.Services { + if svc.Namespace == namespace && svc.Name == name { + return svc + } + } + + return nil +} + +func (r *Resources) GetServiceImport(namespace, name string) *mcsapiv1a1.ServiceImport { + for _, svcImp := range r.ServiceImports { + if svcImp.Namespace == namespace && svcImp.Name == name { + return svcImp + } + } + + return nil +} + +func (r *Resources) GetBackend(namespace, name string) *egv1a1.Backend { + for _, be := range r.Backends { + if be.Namespace == namespace && be.Name == name { + return be + } + } + + return nil +} + +func (r *Resources) GetSecret(namespace, name string) *corev1.Secret { + for _, secret := range r.Secrets { + if secret.Namespace == namespace && secret.Name == name { + return secret + } + } + + return nil +} + +func (r *Resources) GetConfigMap(namespace, name string) *corev1.ConfigMap { + for _, configMap := range r.ConfigMaps { + if configMap.Namespace == namespace && configMap.Name == name { + return configMap + } + } + + return nil +} + +func (r *Resources) GetEndpointSlicesForBackend(svcNamespace, svcName string, backendKind string) []*discoveryv1.EndpointSlice { + var endpointSlices []*discoveryv1.EndpointSlice + for _, endpointSlice := range r.EndpointSlices { + var backendSelectorLabel string + switch backendKind { + case KindService: + backendSelectorLabel = discoveryv1.LabelServiceName + case KindServiceImport: + backendSelectorLabel = mcsapiv1a1.LabelServiceName + } + if svcNamespace == endpointSlice.Namespace && + endpointSlice.GetLabels()[backendSelectorLabel] == svcName { + endpointSlices = append(endpointSlices, endpointSlice) + } + } + return endpointSlices +} diff --git a/internal/gatewayapi/resource/zz_generated.deepcopy.go b/internal/gatewayapi/resource/zz_generated.deepcopy.go index 06925b1467d6..98bd2b866303 100644 --- a/internal/gatewayapi/resource/zz_generated.deepcopy.go +++ b/internal/gatewayapi/resource/zz_generated.deepcopy.go @@ -21,6 +21,37 @@ import ( apisv1alpha1 "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Cache) DeepCopyInto(out *Cache) { + *out = *in + if in.ResourceSet != nil { + in, out := &in.ResourceSet, &out.ResourceSet + *out = make(map[string]*Set, len(*in)) + for key, val := range *in { + var outVal *Set + if val == nil { + (*out)[key] = nil + } else { + inVal := (*in)[key] + in, out := &inVal, &outVal + *out = new(Set) + (*in).DeepCopyInto(*out) + } + (*out)[key] = outVal + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Cache. +func (in *Cache) DeepCopy() *Cache { + if in == nil { + return nil + } + out := new(Cache) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Resources) DeepCopyInto(out *Resources) { *out = *in @@ -290,6 +321,11 @@ func (in *Resources) DeepCopyInto(out *Resources) { } } } + if in.Cache != nil { + in, out := &in.Cache, &out.Cache + *out = new(Cache) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. @@ -301,3 +337,25 @@ func (in *Resources) DeepCopy() *Resources { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Set) DeepCopyInto(out *Set) { + *out = *in + if in.Values != nil { + in, out := &in.Values, &out.Values + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Set. +func (in *Set) DeepCopy() *Set { + if in == nil { + return nil + } + out := new(Set) + in.DeepCopyInto(out) + return out +} diff --git a/internal/gatewayapi/translator.go b/internal/gatewayapi/translator.go index 0e6d683d855c..16dcc3c12982 100644 --- a/internal/gatewayapi/translator.go +++ b/internal/gatewayapi/translator.go @@ -119,8 +119,10 @@ func newTranslateResult(gateways []*GatewayContext, InfraIR: infraIR, } + translateResult.InitCache() + for _, gateway := range gateways { - translateResult.Gateways = append(translateResult.Gateways, gateway.Gateway) + translateResult.AppendResource(gateway.Gateway) } for _, httpRoute := range httpRoutes { translateResult.HTTPRoutes = append(translateResult.HTTPRoutes, httpRoute.HTTPRoute) @@ -138,14 +140,14 @@ func newTranslateResult(gateways []*GatewayContext, translateResult.UDPRoutes = append(translateResult.UDPRoutes, udpRoute.UDPRoute) } - translateResult.ClientTrafficPolicies = append(translateResult.ClientTrafficPolicies, clientTrafficPolicies...) - translateResult.BackendTrafficPolicies = append(translateResult.BackendTrafficPolicies, backendTrafficPolicies...) - translateResult.SecurityPolicies = append(translateResult.SecurityPolicies, securityPolicies...) - translateResult.BackendTLSPolicies = append(translateResult.BackendTLSPolicies, backendTLSPolicies...) - translateResult.EnvoyExtensionPolicies = append(translateResult.EnvoyExtensionPolicies, envoyExtensionPolicies...) + translateResult.AppendClientTrafficPolicies(clientTrafficPolicies...) + translateResult.AppendBackendTrafficPolicies(backendTrafficPolicies...) + translateResult.AppendSecurityPolicies(securityPolicies...) + translateResult.AppendBackendTLSPolicies(backendTLSPolicies...) + translateResult.AppendEnvoyExtensionPolicies(envoyExtensionPolicies...) translateResult.ExtensionServerPolicies = append(translateResult.ExtensionServerPolicies, extPolicies...) - translateResult.Backends = append(translateResult.Backends, backends...) + return translateResult } diff --git a/internal/gatewayapi/translator_test.go b/internal/gatewayapi/translator_test.go index 7184326fd62a..cd996d716d3a 100644 --- a/internal/gatewayapi/translator_test.go +++ b/internal/gatewayapi/translator_test.go @@ -318,9 +318,9 @@ func TestTranslate(t *testing.T) { opts := []cmp.Option{ cmpopts.IgnoreFields(metav1.Condition{}, "LastTransitionTime"), + cmpopts.IgnoreFields(resource.Resources{}, "Cache"), cmpopts.EquateEmpty(), } - require.Empty(t, cmp.Diff(want, got, opts...)) }) } @@ -515,8 +515,12 @@ func TestTranslateWithExtensionKinds(t *testing.T) { want := &TranslateResult{} mustUnmarshal(t, output, want) - opts := cmpopts.IgnoreFields(metav1.Condition{}, "LastTransitionTime") - require.Empty(t, cmp.Diff(want, got, opts)) + opts := []cmp.Option{ + cmpopts.IgnoreFields(metav1.Condition{}, "LastTransitionTime"), + cmpopts.IgnoreFields(resource.Resources{}, "Cache"), + cmpopts.EquateEmpty(), + } + require.Empty(t, cmp.Diff(want, got, opts...)) }) } } diff --git a/internal/provider/kubernetes/controller.go b/internal/provider/kubernetes/controller.go index 7fe3c3d32ffe..9f0c94d3fb01 100644 --- a/internal/provider/kubernetes/controller.go +++ b/internal/provider/kubernetes/controller.go @@ -283,7 +283,7 @@ func (r *gatewayAPIReconciler) Reconcile(ctx context.Context, _ reconcile.Reques return reconcile.Result{}, err } - gwcResource.Namespaces = append(gwcResource.Namespaces, namespace) + gwcResource.AppendResource(namespace) } if gwcResource.EnvoyProxyForGatewayClass != nil && gwcResource.EnvoyProxyForGatewayClass.Spec.MergeGateways != nil { @@ -414,9 +414,10 @@ func (r *gatewayAPIReconciler) processBackendRefs(ctx context.Context, gwcResour "name", string(backendRef.Name)) } else { resourceMappings.allAssociatedNamespaces.Insert(serviceImport.Namespace) - gwcResource.ServiceImports = append(gwcResource.ServiceImports, serviceImport) - r.log.Info("added ServiceImport to resource tree", "namespace", string(*backendRef.Namespace), - "name", string(backendRef.Name)) + if gwcResource.AppendResource(serviceImport) { + r.log.Info("added ServiceImport to resource tree", "namespace", string(*backendRef.Namespace), + "name", string(backendRef.Name)) + } } endpointSliceLabelKey = mcsapiv1a1.LabelServiceName @@ -428,9 +429,10 @@ func (r *gatewayAPIReconciler) processBackendRefs(ctx context.Context, gwcResour "name", string(backendRef.Name)) } else { resourceMappings.allAssociatedNamespaces[backend.Namespace] = struct{}{} - gwcResource.Backends = append(gwcResource.Backends, backend) - r.log.Info("added Backend to resource tree", "namespace", string(*backendRef.Namespace), - "name", string(backendRef.Name)) + if gwcResource.AppendResource(backend) { + r.log.Info("added Backend to resource tree", "namespace", string(*backendRef.Namespace), + "name", string(backendRef.Name)) + } } } @@ -449,9 +451,10 @@ func (r *gatewayAPIReconciler) processBackendRefs(ctx context.Context, gwcResour } else { for _, endpointSlice := range endpointSliceList.Items { endpointSlice := endpointSlice //nolint:copyloopvar - r.log.Info("added EndpointSlice to resource tree", "namespace", endpointSlice.Namespace, - "name", endpointSlice.Name) - gwcResource.EndpointSlices = append(gwcResource.EndpointSlices, &endpointSlice) + if gwcResource.AppendResource(&endpointSlice) { + r.log.Info("added EndpointSlice to resource tree", "namespace", endpointSlice.Namespace, + "name", endpointSlice.Name) + } } } } @@ -555,9 +558,10 @@ func (r *gatewayAPIReconciler) processSecurityPolicyObjectRefs( r.log.Info("no matching ReferenceGrants found", "from", from.kind, "from namespace", from.namespace, "target", to.kind, "target namespace", to.namespace) default: - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) + if resourceTree.AppendResource(refGrant) { + r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, + "name", refGrant.Name) + } } } } @@ -588,7 +592,7 @@ func (r *gatewayAPIReconciler) processOIDCHMACSecret(ctx context.Context, resour return } - resourceTree.Secrets = append(resourceTree.Secrets, &secret) + resourceTree.AppendResource(&secret) r.log.Info("processing OIDC HMAC Secret", "namespace", r.namespace, "name", oidcHMACSecretName) } @@ -635,13 +639,14 @@ func (r *gatewayAPIReconciler) processSecretRef( from.kind, from.namespace, to.kind, to.namespace) default: // RefGrant found - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) + if resourceTree.AppendResource(refGrant) { + r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, + "name", refGrant.Name) + } } } resourceMap.allAssociatedNamespaces.Insert(secretNS) // TODO Zhaohuabing do we need this line? - resourceTree.Secrets = append(resourceTree.Secrets, secret) + resourceTree.AppendResource(secret) r.log.Info("processing Secret", "namespace", secretNS, "name", string(secretRef.Name)) return nil } @@ -737,13 +742,14 @@ func (r *gatewayAPIReconciler) processConfigMapRef( from.kind, from.namespace, to.kind, to.namespace) default: // RefGrant found - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) + if resourceTree.AppendResource(refGrant) { + r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, + "name", refGrant.Name) + } } } resourceMap.allAssociatedNamespaces.Insert(configMapNS) // TODO Zhaohuabing do we need this line? - resourceTree.ConfigMaps = append(resourceTree.ConfigMaps, configMap) + resourceTree.AppendResource(configMap) r.log.Info("processing ConfigMap", "namespace", configMapNS, "name", string(configMapRef.Name)) return nil } @@ -895,7 +901,7 @@ func (r *gatewayAPIReconciler) processGateways(ctx context.Context, managedGC *g // Discard Status to reduce memory consumption in watchable // It will be recomputed by the gateway-api layer gtw.Status = gwapiv1.GatewayStatus{} - resourceTree.Gateways = append(resourceTree.Gateways, >w) + resourceTree.AppendResource(>w) } return nil @@ -914,7 +920,7 @@ func (r *gatewayAPIReconciler) processEnvoyPatchPolicies(ctx context.Context, re // It will be recomputed by the gateway-api layer policy.Status = gwapiv1a2.PolicyStatus{} - resourceTree.EnvoyPatchPolicies = append(resourceTree.EnvoyPatchPolicies, &policy) + resourceTree.AppendResource(&policy) } return nil } @@ -933,7 +939,7 @@ func (r *gatewayAPIReconciler) processClientTrafficPolicies( // Discard Status to reduce memory consumption in watchable // It will be recomputed by the gateway-api layer policy.Status = gwapiv1a2.PolicyStatus{} - resourceTree.ClientTrafficPolicies = append(resourceTree.ClientTrafficPolicies, &policy) + resourceTree.AppendResource(&policy) } r.processCtpConfigMapRefs(ctx, resourceTree, resourceMap) @@ -953,7 +959,7 @@ func (r *gatewayAPIReconciler) processBackendTrafficPolicies(ctx context.Context // Discard Status to reduce memory consumption in watchable // It will be recomputed by the gateway-api layer policy.Status = gwapiv1a2.PolicyStatus{} - resourceTree.BackendTrafficPolicies = append(resourceTree.BackendTrafficPolicies, &policy) + resourceTree.AppendResource(&policy) } return nil } @@ -972,7 +978,7 @@ func (r *gatewayAPIReconciler) processSecurityPolicies( // Discard Status to reduce memory consumption in watchable // It will be recomputed by the gateway-api layer policy.Status = gwapiv1a2.PolicyStatus{} - resourceTree.SecurityPolicies = append(resourceTree.SecurityPolicies, &policy) + resourceTree.AppendResource(&policy) } // Add the referenced Resources in SecurityPolicies to the resourceTree @@ -997,7 +1003,7 @@ func (r *gatewayAPIReconciler) processBackendTLSPolicies( // Discard Status to reduce memory consumption in watchable // It will be recomputed by the gateway-api layer policy.Status = gwapiv1a2.PolicyStatus{} - resourceTree.BackendTLSPolicies = append(resourceTree.BackendTLSPolicies, &policy) + resourceTree.AppendResource(&policy) } // Add the referenced Secrets and ConfigMaps in BackendTLSPolicies to the resourceTree. @@ -1852,7 +1858,7 @@ func (r *gatewayAPIReconciler) processEnvoyExtensionPolicies( // Discard Status to reduce memory consumption in watchable // It will be recomputed by the gateway-api layer policy.Status = gwapiv1a2.PolicyStatus{} - resourceTree.EnvoyExtensionPolicies = append(resourceTree.EnvoyExtensionPolicies, &policy) + resourceTree.AppendResource(&policy) } // Add the referenced Resources in EnvoyExtensionPolicies to the resourceTree @@ -1941,9 +1947,10 @@ func (r *gatewayAPIReconciler) processEnvoyExtensionPolicyObjectRefs( r.log.Info("no matching ReferenceGrants found", "from", from.kind, "from namespace", from.namespace, "target", to.kind, "target namespace", to.namespace) default: - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) + if resourceTree.AppendResource(refGrant) { + r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, + "name", refGrant.Name) + } } } } diff --git a/internal/provider/kubernetes/routes.go b/internal/provider/kubernetes/routes.go index 3a0a9f8131e6..28d12142d836 100644 --- a/internal/provider/kubernetes/routes.go +++ b/internal/provider/kubernetes/routes.go @@ -81,9 +81,10 @@ func (r *gatewayAPIReconciler) processTLSRoutes(ctx context.Context, gatewayName r.log.Info("no matching ReferenceGrants found", "from", from.kind, "from namespace", from.namespace, "target", to.kind, "target namespace", to.namespace) default: - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) + if resourceTree.AppendResource(refGrant) { + r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, + "name", refGrant.Name) + } } } } @@ -167,9 +168,10 @@ func (r *gatewayAPIReconciler) processGRPCRoutes(ctx context.Context, gatewayNam r.log.Info("no matching ReferenceGrants found", "from", from.kind, "from namespace", from.namespace, "target", to.kind, "target namespace", to.namespace) default: - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) + if resourceTree.AppendResource(refGrant) { + r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, + "name", refGrant.Name) + } } } } @@ -310,9 +312,10 @@ func (r *gatewayAPIReconciler) processHTTPRoutes(ctx context.Context, gatewayNam r.log.Info("no matching ReferenceGrants found", "from", from.kind, "from namespace", from.namespace, "target", to.kind, "target namespace", to.namespace) default: - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) + if resourceTree.AppendResource(refGrant) { + r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, + "name", refGrant.Name) + } } } } @@ -377,9 +380,10 @@ func (r *gatewayAPIReconciler) processHTTPRoutes(ctx context.Context, gatewayNam r.log.Info("no matching ReferenceGrants found", "from", from.kind, "from namespace", from.namespace, "target", to.kind, "target namespace", to.namespace) default: - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) + if resourceTree.AppendResource(refGrant) { + r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, + "name", refGrant.Name) + } } } } else if filter.Type == gwapiv1.HTTPRouteFilterExtensionRef { @@ -404,7 +408,7 @@ func (r *gatewayAPIReconciler) processHTTPRoutes(ctx context.Context, gatewayNam continue } - resourceTree.HTTPRouteFilters = append(resourceTree.HTTPRouteFilters, httpFilter) + resourceTree.AppendResource(httpFilter) default: extRefFilter, ok := resourceMap.extensionRefFilters[key] if !ok { @@ -492,9 +496,10 @@ func (r *gatewayAPIReconciler) processTCPRoutes(ctx context.Context, gatewayName r.log.Info("no matching ReferenceGrants found", "from", from.kind, "from namespace", from.namespace, "target", to.kind, "target namespace", to.namespace) default: - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) + if resourceTree.AppendResource(refGrant) { + r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, + "name", refGrant.Name) + } } } } @@ -570,9 +575,10 @@ func (r *gatewayAPIReconciler) processUDPRoutes(ctx context.Context, gatewayName r.log.Info("no matching ReferenceGrants found", "from", from.kind, "from namespace", from.namespace, "target", to.kind, "target namespace", to.namespace) default: - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) + if resourceTree.AppendResource(refGrant) { + r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, + "name", refGrant.Name) + } } } }