Skip to content

Commit

Permalink
ingress/gateway-api: ordered envoy filterchain
Browse files Browse the repository at this point in the history
Currently, while translating K8s Ingress or Gateway API resources into Envoy resources,
the filterchain is in random order. This leads to situations (especially in combination
with Shared Ingress) where the order of the filterchains isn't guaranteed -
resulting in unnecessary reconciliations.

Therefore, this commit orders the filterchains within a Envoy Listener by the namespace
and name of the TLS secret. This makes the translation deterministic.

Signed-off-by: Marco Hofstetter <marco.hofstetter@isovalent.com>
  • Loading branch information
mhofstetter authored and sayboras committed Mar 25, 2024
1 parent a5f2a72 commit d8082f9
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 6 deletions.
9 changes: 8 additions & 1 deletion operator/pkg/model/translation/envoy_listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
package translation

import (
"cmp"
"fmt"
goslices "slices"
"syscall"

envoy_config_core_v3 "github.com/cilium/proxy/go/envoy/config/core/v3"
Expand All @@ -14,6 +16,7 @@ import (
httpConnectionManagerv3 "github.com/cilium/proxy/go/envoy/extensions/filters/network/http_connection_manager/v3"
envoy_extensions_filters_network_tcp_v3 "github.com/cilium/proxy/go/envoy/extensions/filters/network/tcp_proxy/v3"
envoy_extensions_transport_sockets_tls_v3 "github.com/cilium/proxy/go/envoy/extensions/transport_sockets/tls/v3"
"golang.org/x/exp/maps"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"

Expand Down Expand Up @@ -184,7 +187,11 @@ func NewHTTPListener(name string, ciliumSecretNamespace string, tls map[model.TL
},
})

for secret, hostNames := range tls {
orderedSecrets := maps.Keys(tls)
goslices.SortStableFunc(orderedSecrets, func(a, b model.TLSSecret) int { return cmp.Compare(a.Namespace+"/"+a.Name, b.Namespace+"/"+b.Name) })

for _, secret := range orderedSecrets {
hostNames := tls[secret]
secureHttpConnectionManagerName := fmt.Sprintf("%s-secure", name)
secureHttpConnectionManager, err := NewHTTPConnectionManager(
secureHttpConnectionManagerName,
Expand Down
26 changes: 21 additions & 5 deletions operator/pkg/model/translation/envoy_listener_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,25 @@ func TestNewHTTPListener(t *testing.T) {
require.Len(t, listener.GetFilterChains(), 1)
})

t.Run("TLS filterchain order", func(t *testing.T) {
res1, err1 := NewHTTPListener("dummy-name", "dummy-secret-namespace", map[model.TLSSecret][]string{
{Name: "dummy-secret-1", Namespace: "dummy-namespace"}: {"dummy.server.com"},
{Name: "dummy-secret-2", Namespace: "dummy-namespace"}: {"dummy.anotherserver.com"},
})
res2, err2 := NewHTTPListener("dummy-name", "dummy-secret-namespace", map[model.TLSSecret][]string{
{Name: "dummy-secret-2", Namespace: "dummy-namespace"}: {"dummy.anotherserver.com"},
{Name: "dummy-secret-1", Namespace: "dummy-namespace"}: {"dummy.server.com"},
})

require.NoError(t, err1)
require.NoError(t, err2)

diffOutput := cmp.Diff(res1, res2, protocmp.Transform())
if len(diffOutput) != 0 {
t.Errorf("Listeners filterchain order did not match:\n%s\n", diffOutput)
}
})

t.Run("TLS", func(t *testing.T) {
res, err := NewHTTPListener("dummy-name", "dummy-secret-namespace", map[model.TLSSecret][]string{
{Name: "dummy-secret-1", Namespace: "dummy-namespace"}: {"dummy.server.com"},
Expand All @@ -88,11 +107,8 @@ func TestNewHTTPListener(t *testing.T) {
require.Equal(t, "tls", listener.GetFilterChains()[1].GetFilterChainMatch().TransportProtocol)
require.Equal(t, "tls", listener.GetFilterChains()[2].GetFilterChainMatch().TransportProtocol)
require.Len(t, listener.GetFilterChains()[1].GetFilters(), 1)
var serverNames []string
serverNames = append(serverNames, listener.GetFilterChains()[1].GetFilterChainMatch().ServerNames...)
serverNames = append(serverNames, listener.GetFilterChains()[2].GetFilterChainMatch().ServerNames...)
sort.Strings(serverNames)
require.Equal(t, []string{"dummy.anotherserver.com", "dummy.server.com"}, serverNames)
require.Equal(t, []string{"dummy.server.com"}, listener.GetFilterChains()[1].GetFilterChainMatch().ServerNames)
require.Equal(t, []string{"dummy.anotherserver.com"}, listener.GetFilterChains()[2].GetFilterChainMatch().ServerNames)

downStreamTLS := &envoy_extensions_transport_sockets_tls_v3.DownstreamTlsContext{}
err = proto.Unmarshal(listener.FilterChains[1].TransportSocket.ConfigType.(*envoy_config_core_v3.TransportSocket_TypedConfig).TypedConfig.Value, downStreamTLS)
Expand Down

0 comments on commit d8082f9

Please sign in to comment.