From 831c618c0145c59ef7b45b4587f1cdf6b737bdfb Mon Sep 17 00:00:00 2001 From: Nick Young Date: Mon, 16 Sep 2019 15:14:53 +1000 Subject: [PATCH] Add support for JSON logging. Updates #624 Signed-off-by: Nick Young --- cmd/contour/serve.go | 3 ++ cmd/contour/servecontext.go | 34 ++++++++++++ internal/contour/listener.go | 83 +++++++++++++++++++++++++++-- internal/contour/listener_test.go | 83 +++++++++++++++++------------ internal/contour/visitor_test.go | 2 +- internal/e2e/lds_test.go | 60 ++++++++++----------- internal/envoy/accesslog.go | 67 +++++++++++++++++++++++ internal/envoy/accesslog_test.go | 88 ++++++++++++++++++++++++++++++- internal/envoy/listener.go | 12 +++-- internal/envoy/listener_test.go | 34 ++++++------ 10 files changed, 376 insertions(+), 90 deletions(-) diff --git a/cmd/contour/serve.go b/cmd/contour/serve.go index a306a72cfa4..8567895f677 100644 --- a/cmd/contour/serve.go +++ b/cmd/contour/serve.go @@ -108,6 +108,7 @@ func registerServe(app *kingpin.Application) (*kingpin.CmdClause, *serveContext) serve.Flag("envoy-service-https-port", "Kubernetes Service port for HTTPS requests").IntVar(&ctx.httpsPort) serve.Flag("use-proxy-protocol", "Use PROXY protocol for all listeners").BoolVar(&ctx.useProxyProto) + serve.Flag("accesslog-format", "Format for Envoy access logs").StringVar(&ctx.AccessLogFormat) serve.Flag("disable-leader-election", "Disable leader election mechanism").BoolVar(&ctx.DisableLeaderElection) return serve, ctx } @@ -141,6 +142,8 @@ func doServe(log logrus.FieldLogger, ctx *serveContext) error { HTTPSAddress: ctx.httpsAddr, HTTPSPort: ctx.httpsPort, HTTPSAccessLog: ctx.httpsAccessLog, + AccessLogType: ctx.AccessLogFormat, + AccessLogFields: ctx.AccessLogFields, MinimumProtocolVersion: dag.MinProtoVersion(ctx.TLSConfig.MinimumProtocolVersion), }, ListenerCache: contour.NewListenerCache(ctx.statsAddr, ctx.statsPort), diff --git a/cmd/contour/servecontext.go b/cmd/contour/servecontext.go index fcde8dd9054..c00cf77752c 100644 --- a/cmd/contour/servecontext.go +++ b/cmd/contour/servecontext.go @@ -71,6 +71,16 @@ type serveContext struct { httpsPort int httpsAccessLog string + // Envoy's access logging format options + + // AccessLogFormat sets the global access log format. + // Valid options are 'clf' or 'json' + AccessLogFormat string `yaml:"accesslog-format"` + + // AccessLogFields sets the fields that JSON logging will + // output when AccessLogFormat is json. + AccessLogFields []string `yaml:"json-fields"` + // PermitInsecureGRPC disables TLS on Contour's gRPC listener. PermitInsecureGRPC bool `yaml:"-"` @@ -109,6 +119,30 @@ func newServeContext() *serveContext { PermitInsecureGRPC: false, DisablePermitInsecure: false, DisableLeaderElection: false, + AccessLogFormat: "clf", + AccessLogFields: []string{ + "@timestamp", + "authority", + "bytes_received", + "bytes_sent", + "downstream_local_address", + "downstream_remote_address", + "duration", + "method", + "path", + "protocol", + "request_id", + "requested_server_name", + "response_code", + "response_flags", + "uber_trace_id", + "upstream_cluster", + "upstream_host", + "upstream_local_address", + "upstream_service_time", + "user_agent", + "x_forwarded_for", + }, LeaderElectionConfig: LeaderElectionConfig{ LeaseDuration: time.Second * 15, RenewDeadline: time.Second * 10, diff --git a/internal/contour/listener.go b/internal/contour/listener.go index da1e302099c..48d9b0928c6 100644 --- a/internal/contour/listener.go +++ b/internal/contour/listener.go @@ -19,6 +19,7 @@ import ( envoy_api_v2_auth "github.com/envoyproxy/go-control-plane/envoy/api/v2/auth" envoy_api_v2_listener "github.com/envoyproxy/go-control-plane/envoy/api/v2/listener" + envoy_api_v2_accesslog "github.com/envoyproxy/go-control-plane/envoy/config/filter/accesslog/v2" v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2" "github.com/envoyproxy/go-control-plane/pkg/cache" @@ -36,6 +37,7 @@ const ( DEFAULT_HTTPS_ACCESS_LOG = "/dev/stdout" DEFAULT_HTTPS_LISTENER_ADDRESS = DEFAULT_HTTP_LISTENER_ADDRESS DEFAULT_HTTPS_LISTENER_PORT = 8443 + DEFAULT_ACCESS_LOG_TYPE = "clf" ) // ListenerVisitorConfig holds configuration parameters for visitListeners. @@ -71,6 +73,16 @@ type ListenerVisitorConfig struct { // MinimumProtocolVersion defines the min tls protocol version to be used MinimumProtocolVersion envoy_api_v2_auth.TlsParameters_TlsProtocol + + // AccessLogType defines if Envoy logs should be output as CLF or JSON. + // Valid values: 'clf', 'json' + // If not set, defaults to 'clf' + AccessLogType string + + // AccessLogFields sets the fields that should be shown in JSON logs. + // Valid entries are the keys from internal/envoy/accesslog.go:jsonheaders + // Defaults to a particular set of fields. + AccessLogFields []string } // httpAddress returns the port for the HTTP (non TLS) @@ -127,6 +139,68 @@ func (lvc *ListenerVisitorConfig) httpsAccessLog() string { return DEFAULT_HTTPS_ACCESS_LOG } +// accesslogType returns the access log type that should be configured +// across all listener types or DEFAULT_ACCESS_LOG_TYPE if not configured. +func (lvc *ListenerVisitorConfig) accesslogType() string { + if lvc.AccessLogType != "" { + return lvc.AccessLogType + } + return DEFAULT_ACCESS_LOG_TYPE +} + +// accesslogFields returns the access log fields that should be configured +// for Envoy, or a default set if not configured. +func (lvc *ListenerVisitorConfig) accesslogFields() []string { + if lvc.AccessLogFields != nil { + return lvc.AccessLogFields + } + return []string{ + "@timestamp", + "authority", + "bytes_received", + "bytes_sent", + "downstream_local_address", + "downstream_remote_address", + "duration", + "method", + "path", + "protocol", + "request_id", + "requested_server_name", + "response_code", + "response_flags", + "uber_trace_id", + "upstream_cluster", + "upstream_host", + "upstream_local_address", + "upstream_service_time", + "user_agent", + "x_forwarded_for", + } +} + +func (lvc *ListenerVisitorConfig) newInsecureAccessLog() []*envoy_api_v2_accesslog.AccessLog { + var accesslogger []*envoy_api_v2_accesslog.AccessLog + switch lvc.accesslogType() { + case "json": + accesslogger = envoy.FileAccessLogJSON(lvc.httpAccessLog(), lvc.accesslogFields()) + default: + accesslogger = envoy.FileAccessLog(lvc.httpAccessLog()) + } + return accesslogger +} + +func (lvc *ListenerVisitorConfig) newSecureAccessLog() []*envoy_api_v2_accesslog.AccessLog { + var accesslogger []*envoy_api_v2_accesslog.AccessLog + switch lvc.accesslogType() { + case "json": + accesslogger = envoy.FileAccessLogJSON(lvc.httpsAccessLog(), lvc.accesslogFields()) + default: + accesslogger = envoy.FileAccessLog(lvc.httpsAccessLog()) + } + return accesslogger +} + // minProtocolVersion returns the requested minimum TLS protocol // version or envoy_api_v2_auth.TlsParameters_TLSv1_1 if not configured { func (lvc *ListenerVisitorConfig) minProtoVersion() envoy_api_v2_auth.TlsParameters_TlsProtocol { @@ -178,6 +252,8 @@ func (c *ListenerCache) Contents() []proto.Message { return values } +// Query returns the proto.Messages in the ListenerCache that match +// a slice of strings func (c *ListenerCache) Query(names []string) []proto.Message { c.mu.Lock() defer c.mu.Unlock() @@ -209,6 +285,7 @@ func (l listenersByName) Less(i, j int) bool { return l[i].(*v2.Listener).Name < l[j].(*v2.Listener).Name } +// TypeURL returns the cache's Listener Type. func (*ListenerCache) TypeURL() string { return cache.ListenerType } type listenerVisitor struct { @@ -237,7 +314,7 @@ func visitListeners(root dag.Vertex, lvc *ListenerVisitorConfig) map[string]*v2. ENVOY_HTTP_LISTENER, lvc.httpAddress(), lvc.httpPort(), proxyProtocol(lvc.UseProxyProto), - envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, lvc.httpAccessLog()), + envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, lvc.newInsecureAccessLog()), ) } @@ -289,12 +366,12 @@ func (v *listenerVisitor) visit(vertex dag.Vertex) { v.http = true case *dag.SecureVirtualHost: filters := envoy.Filters( - envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, v.httpsAccessLog()), + envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, v.ListenerVisitorConfig.newSecureAccessLog()), ) alpnProtos := []string{"h2", "http/1.1"} if vh.TCPProxy != nil { filters = envoy.Filters( - envoy.TCPProxy(ENVOY_HTTPS_LISTENER, vh.TCPProxy, v.httpsAccessLog()), + envoy.TCPProxy(ENVOY_HTTPS_LISTENER, vh.TCPProxy, v.ListenerVisitorConfig.newSecureAccessLog()), ) alpnProtos = nil // do not offer ALPN } diff --git a/internal/contour/listener_test.go b/internal/contour/listener_test.go index 42c6a3a2c00..f4e54651d39 100644 --- a/internal/contour/listener_test.go +++ b/internal/contour/listener_test.go @@ -20,7 +20,10 @@ import ( envoy_api_v2_auth "github.com/envoyproxy/go-control-plane/envoy/api/v2/auth" envoy_api_v2_listener "github.com/envoyproxy/go-control-plane/envoy/api/v2/listener" "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + any "github.com/golang/protobuf/ptypes/any" "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" ingressroutev1 "github.com/projectcontour/contour/apis/contour/v1beta1" projcontour "github.com/projectcontour/contour/apis/projectcontour/v1alpha1" "github.com/projectcontour/contour/internal/envoy" @@ -42,13 +45,13 @@ func TestListenerCacheContents(t *testing.T) { contents: listenermap(&v2.Listener{ Name: ENVOY_HTTP_LISTENER, Address: envoy.SocketAddress("0.0.0.0", 8080), - FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, DEFAULT_HTTP_ACCESS_LOG)), + FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }), want: []proto.Message{ &v2.Listener{ Name: ENVOY_HTTP_LISTENER, Address: envoy.SocketAddress("0.0.0.0", 8080), - FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, DEFAULT_HTTP_ACCESS_LOG)), + FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }, }, }, @@ -59,7 +62,7 @@ func TestListenerCacheContents(t *testing.T) { var lc ListenerCache lc.Update(tc.contents) got := lc.Contents() - if diff := cmp.Diff(tc.want, got); diff != "" { + if diff := cmp.Diff(tc.want, got, cmpopts.AcyclicTransformer("unmarshalAny", unmarshalAny)); diff != "" { t.Fatal(diff) } }) @@ -76,14 +79,14 @@ func TestListenerCacheQuery(t *testing.T) { contents: listenermap(&v2.Listener{ Name: ENVOY_HTTP_LISTENER, Address: envoy.SocketAddress("0.0.0.0", 8080), - FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, DEFAULT_HTTP_ACCESS_LOG)), + FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }), query: []string{ENVOY_HTTP_LISTENER}, want: []proto.Message{ &v2.Listener{ Name: ENVOY_HTTP_LISTENER, Address: envoy.SocketAddress("0.0.0.0", 8080), - FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, DEFAULT_HTTP_ACCESS_LOG)), + FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }, }, }, @@ -91,14 +94,14 @@ func TestListenerCacheQuery(t *testing.T) { contents: listenermap(&v2.Listener{ Name: ENVOY_HTTP_LISTENER, Address: envoy.SocketAddress("0.0.0.0", 8080), - FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, DEFAULT_HTTP_ACCESS_LOG)), + FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }), query: []string{ENVOY_HTTP_LISTENER, "stats-listener"}, want: []proto.Message{ &v2.Listener{ Name: ENVOY_HTTP_LISTENER, Address: envoy.SocketAddress("0.0.0.0", 8080), - FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, DEFAULT_HTTP_ACCESS_LOG)), + FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }, }, }, @@ -106,7 +109,7 @@ func TestListenerCacheQuery(t *testing.T) { contents: listenermap(&v2.Listener{ Name: ENVOY_HTTP_LISTENER, Address: envoy.SocketAddress("0.0.0.0", 8080), - FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, DEFAULT_HTTP_ACCESS_LOG)), + FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }), query: []string{"stats-listener"}, want: nil, @@ -118,7 +121,7 @@ func TestListenerCacheQuery(t *testing.T) { var lc ListenerCache lc.Update(tc.contents) got := lc.Query(tc.query) - if diff := cmp.Diff(tc.want, got); diff != "" { + if diff := cmp.Diff(tc.want, got, cmpopts.AcyclicTransformer("unmarshalAny", unmarshalAny)); diff != "" { t.Fatal(diff) } }) @@ -163,7 +166,7 @@ func TestListenerVisit(t *testing.T) { want: listenermap(&v2.Listener{ Name: ENVOY_HTTP_LISTENER, Address: envoy.SocketAddress("0.0.0.0", 8080), - FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, DEFAULT_HTTP_ACCESS_LOG)), + FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }), }, "one http only ingressroute": { @@ -204,7 +207,7 @@ func TestListenerVisit(t *testing.T) { want: listenermap(&v2.Listener{ Name: ENVOY_HTTP_LISTENER, Address: envoy.SocketAddress("0.0.0.0", 8080), - FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, DEFAULT_HTTP_ACCESS_LOG)), + FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }), }, "simple ingress with secret": { @@ -256,7 +259,7 @@ func TestListenerVisit(t *testing.T) { want: listenermap(&v2.Listener{ Name: ENVOY_HTTP_LISTENER, Address: envoy.SocketAddress("0.0.0.0", 8080), - FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, DEFAULT_HTTP_ACCESS_LOG)), + FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }, &v2.Listener{ Name: ENVOY_HTTPS_LISTENER, Address: envoy.SocketAddress("0.0.0.0", 8443), @@ -268,7 +271,7 @@ func TestListenerVisit(t *testing.T) { ServerNames: []string{"whatever.example.com"}, }, TlsContext: tlscontext(envoy_api_v2_auth.TlsParameters_TLSv1_1, "h2", "http/1.1"), - Filters: envoy.Filters(envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, DEFAULT_HTTPS_ACCESS_LOG)), + Filters: envoy.Filters(envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }}, }), }, @@ -343,7 +346,7 @@ func TestListenerVisit(t *testing.T) { want: listenermap(&v2.Listener{ Name: ENVOY_HTTP_LISTENER, Address: envoy.SocketAddress("0.0.0.0", 8080), - FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, DEFAULT_HTTP_ACCESS_LOG)), + FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }, &v2.Listener{ Name: ENVOY_HTTPS_LISTENER, Address: envoy.SocketAddress("0.0.0.0", 8443), @@ -355,13 +358,13 @@ func TestListenerVisit(t *testing.T) { ServerNames: []string{"sortedfirst.example.com"}, }, TlsContext: tlscontext(envoy_api_v2_auth.TlsParameters_TLSv1_1, "h2", "http/1.1"), - Filters: envoy.Filters(envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, DEFAULT_HTTPS_ACCESS_LOG)), + Filters: envoy.Filters(envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }, { FilterChainMatch: &envoy_api_v2_listener.FilterChainMatch{ ServerNames: []string{"sortedsecond.example.com"}, }, TlsContext: tlscontext(envoy_api_v2_auth.TlsParameters_TLSv1_1, "h2", "http/1.1"), - Filters: envoy.Filters(envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, DEFAULT_HTTPS_ACCESS_LOG)), + Filters: envoy.Filters(envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }}, }), }, @@ -414,7 +417,7 @@ func TestListenerVisit(t *testing.T) { want: listenermap(&v2.Listener{ Name: ENVOY_HTTP_LISTENER, Address: envoy.SocketAddress("0.0.0.0", 8080), - FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, DEFAULT_HTTP_ACCESS_LOG)), + FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }), }, "simple ingressroute with secret": { @@ -468,7 +471,7 @@ func TestListenerVisit(t *testing.T) { want: listenermap(&v2.Listener{ Name: ENVOY_HTTP_LISTENER, Address: envoy.SocketAddress("0.0.0.0", 8080), - FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, DEFAULT_HTTP_ACCESS_LOG)), + FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }, &v2.Listener{ Name: ENVOY_HTTPS_LISTENER, Address: envoy.SocketAddress("0.0.0.0", 8443), @@ -477,7 +480,7 @@ func TestListenerVisit(t *testing.T) { ServerNames: []string{"www.example.com"}, }, TlsContext: tlscontext(envoy_api_v2_auth.TlsParameters_TLSv1_1, "h2", "http/1.1"), - Filters: envoy.Filters(envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, DEFAULT_HTTPS_ACCESS_LOG)), + Filters: envoy.Filters(envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }}, ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), @@ -558,7 +561,7 @@ func TestListenerVisit(t *testing.T) { ServerNames: []string{"www.example.com"}, }, TlsContext: tlscontext(envoy_api_v2_auth.TlsParameters_TLSv1_1, "h2", "http/1.1"), - Filters: envoy.Filters(envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, DEFAULT_HTTPS_ACCESS_LOG)), + Filters: envoy.Filters(envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }}, ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), @@ -620,7 +623,7 @@ func TestListenerVisit(t *testing.T) { want: listenermap(&v2.Listener{ Name: ENVOY_HTTP_LISTENER, Address: envoy.SocketAddress("127.0.0.100", 9100), - FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, DEFAULT_HTTP_ACCESS_LOG)), + FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }, &v2.Listener{ Name: ENVOY_HTTPS_LISTENER, Address: envoy.SocketAddress("127.0.0.200", 9200), @@ -632,7 +635,7 @@ func TestListenerVisit(t *testing.T) { ServerNames: []string{"whatever.example.com"}, }, TlsContext: tlscontext(envoy_api_v2_auth.TlsParameters_TLSv1_1, "h2", "http/1.1"), - Filters: envoy.Filters(envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, DEFAULT_HTTPS_ACCESS_LOG)), + Filters: envoy.Filters(envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }}, }), }, @@ -691,7 +694,7 @@ func TestListenerVisit(t *testing.T) { ListenerFilters: envoy.ListenerFilters( envoy.ProxyProtocol(), ), - FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, DEFAULT_HTTP_ACCESS_LOG)), + FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }, &v2.Listener{ Name: ENVOY_HTTPS_LISTENER, Address: envoy.SocketAddress("0.0.0.0", 8443), @@ -704,7 +707,7 @@ func TestListenerVisit(t *testing.T) { ServerNames: []string{"whatever.example.com"}, }, TlsContext: tlscontext(envoy_api_v2_auth.TlsParameters_TLSv1_1, "h2", "http/1.1"), - Filters: envoy.Filters(envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, DEFAULT_HTTPS_ACCESS_LOG)), + Filters: envoy.Filters(envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }}, }), }, @@ -761,7 +764,7 @@ func TestListenerVisit(t *testing.T) { want: listenermap(&v2.Listener{ Name: ENVOY_HTTP_LISTENER, Address: envoy.SocketAddress(DEFAULT_HTTP_LISTENER_ADDRESS, DEFAULT_HTTP_LISTENER_PORT), - FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, "/tmp/http_access.log")), + FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, envoy.FileAccessLog("/tmp/http_access.log"))), }, &v2.Listener{ Name: ENVOY_HTTPS_LISTENER, Address: envoy.SocketAddress(DEFAULT_HTTPS_LISTENER_ADDRESS, DEFAULT_HTTPS_LISTENER_PORT), @@ -773,7 +776,7 @@ func TestListenerVisit(t *testing.T) { ServerNames: []string{"whatever.example.com"}, }, TlsContext: tlscontext(envoy_api_v2_auth.TlsParameters_TLSv1_1, "h2", "http/1.1"), - Filters: envoy.Filters(envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, "/tmp/https_access.log")), + Filters: envoy.Filters(envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, envoy.FileAccessLog("/tmp/https_access.log"))), }}, }), }, @@ -829,7 +832,7 @@ func TestListenerVisit(t *testing.T) { want: listenermap(&v2.Listener{ Name: ENVOY_HTTP_LISTENER, Address: envoy.SocketAddress("0.0.0.0", 8080), - FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, DEFAULT_HTTP_ACCESS_LOG)), + FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }, &v2.Listener{ Name: ENVOY_HTTPS_LISTENER, Address: envoy.SocketAddress("0.0.0.0", 8443), @@ -838,7 +841,7 @@ func TestListenerVisit(t *testing.T) { ServerNames: []string{"whatever.example.com"}, }, TlsContext: tlscontext(envoy_api_v2_auth.TlsParameters_TLSv1_3, "h2", "http/1.1"), - Filters: envoy.Filters(envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, DEFAULT_HTTPS_ACCESS_LOG)), + Filters: envoy.Filters(envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }}, ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), @@ -900,7 +903,7 @@ func TestListenerVisit(t *testing.T) { want: listenermap(&v2.Listener{ Name: ENVOY_HTTP_LISTENER, Address: envoy.SocketAddress("0.0.0.0", 8080), - FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, DEFAULT_HTTP_ACCESS_LOG)), + FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }, &v2.Listener{ Name: ENVOY_HTTPS_LISTENER, Address: envoy.SocketAddress("0.0.0.0", 8443), @@ -909,7 +912,7 @@ func TestListenerVisit(t *testing.T) { ServerNames: []string{"whatever.example.com"}, }, TlsContext: tlscontext(envoy_api_v2_auth.TlsParameters_TLSv1_3, "h2", "http/1.1"), // note, cannot downgrade from the configured version - Filters: envoy.Filters(envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, DEFAULT_HTTPS_ACCESS_LOG)), + Filters: envoy.Filters(envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }}, ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), @@ -971,7 +974,7 @@ func TestListenerVisit(t *testing.T) { want: listenermap(&v2.Listener{ Name: ENVOY_HTTP_LISTENER, Address: envoy.SocketAddress("0.0.0.0", 8080), - FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, DEFAULT_HTTP_ACCESS_LOG)), + FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }, &v2.Listener{ Name: ENVOY_HTTPS_LISTENER, Address: envoy.SocketAddress("0.0.0.0", 8443), @@ -980,7 +983,7 @@ func TestListenerVisit(t *testing.T) { ServerNames: []string{"www.example.com"}, }, TlsContext: tlscontext(envoy_api_v2_auth.TlsParameters_TLSv1_3, "h2", "http/1.1"), // note, cannot downgrade from the configured version - Filters: envoy.Filters(envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, DEFAULT_HTTPS_ACCESS_LOG)), + Filters: envoy.Filters(envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, envoy.FileAccessLog(DEFAULT_HTTP_ACCESS_LOG))), }}, ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), @@ -993,8 +996,8 @@ func TestListenerVisit(t *testing.T) { t.Run(name, func(t *testing.T) { root := buildDAG(tc.objs...) got := visitListeners(root, &tc.ListenerVisitorConfig) - if !cmp.Equal(tc.want, got) { - t.Fatalf("expected:\n%+v\ngot:\n%+v", tc.want, got) + if diff := cmp.Diff(tc.want, got, cmpopts.AcyclicTransformer("unmarshalAny", unmarshalAny)); diff != "" { + t.Fatal(diff) } }) } @@ -1018,3 +1021,15 @@ func listenermap(listeners ...*v2.Listener) map[string]*v2.Listener { } return m } + +func unmarshalAny(a *any.Any) proto.Message { + pb, err := ptypes.Empty(a) + if err != nil { + panic(err.Error()) + } + err = ptypes.UnmarshalAny(a, pb) + if err != nil { + panic(err.Error()) + } + return pb +} diff --git a/internal/contour/visitor_test.go b/internal/contour/visitor_test.go index bd3bbbbcc97..b0b833d04a8 100644 --- a/internal/contour/visitor_test.go +++ b/internal/contour/visitor_test.go @@ -137,7 +137,7 @@ func TestVisitListeners(t *testing.T) { ServerNames: []string{"tcpproxy.example.com"}, }, TlsContext: tlscontext(envoy_api_v2_auth.TlsParameters_TLSv1_1), - Filters: envoy.Filters(envoy.TCPProxy(ENVOY_HTTPS_LISTENER, p1, DEFAULT_HTTPS_ACCESS_LOG)), + Filters: envoy.Filters(envoy.TCPProxy(ENVOY_HTTPS_LISTENER, p1, envoy.FileAccessLog(DEFAULT_HTTPS_ACCESS_LOG))), }}, ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), diff --git a/internal/e2e/lds_test.go b/internal/e2e/lds_test.go index a4b56d27177..ddb2bf459a9 100644 --- a/internal/e2e/lds_test.go +++ b/internal/e2e/lds_test.go @@ -85,7 +85,7 @@ func TestNonTLSListener(t *testing.T) { &v2.Listener{ Name: "ingress_http", Address: envoy.SocketAddress("0.0.0.0", 8080), - FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager("ingress_http", "/dev/stdout")), + FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager("ingress_http", envoy.FileAccessLog("/dev/stdout"))), }, staticListener(), ), @@ -141,7 +141,7 @@ func TestNonTLSListener(t *testing.T) { &v2.Listener{ Name: "ingress_http", Address: envoy.SocketAddress("0.0.0.0", 8080), - FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager("ingress_http", "/dev/stdout")), + FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager("ingress_http", envoy.FileAccessLog("/dev/stdout"))), }, staticListener(), ), @@ -226,7 +226,7 @@ func TestTLSListener(t *testing.T) { &v2.Listener{ Name: "ingress_http", Address: envoy.SocketAddress("0.0.0.0", 8080), - FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager("ingress_http", "/dev/stdout")), + FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager("ingress_http", envoy.FileAccessLog("/dev/stdout"))), }, &v2.Listener{ Name: "ingress_https", @@ -234,7 +234,7 @@ func TestTLSListener(t *testing.T) { ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), - FilterChains: filterchaintls("kuard.example.com", s1, envoy.HTTPConnectionManager("ingress_https", "/dev/stdout"), "h2", "http/1.1"), + FilterChains: filterchaintls("kuard.example.com", s1, envoy.HTTPConnectionManager("ingress_https", envoy.FileAccessLog("/dev/stdout")), "h2", "http/1.1"), }, staticListener(), ), @@ -280,7 +280,7 @@ func TestTLSListener(t *testing.T) { ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), - FilterChains: filterchaintls("kuard.example.com", s1, envoy.HTTPConnectionManager("ingress_https", "/dev/stdout"), "h2", "http/1.1"), + FilterChains: filterchaintls("kuard.example.com", s1, envoy.HTTPConnectionManager("ingress_https", envoy.FileAccessLog("/dev/stdout")), "h2", "http/1.1"), }, staticListener(), ), @@ -398,7 +398,7 @@ func TestIngressRouteTLSListener(t *testing.T) { ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), - FilterChains: filterchaintls("kuard.example.com", secret1, envoy.HTTPConnectionManager("ingress_https", "/dev/stdout"), "h2", "http/1.1"), + FilterChains: filterchaintls("kuard.example.com", secret1, envoy.HTTPConnectionManager("ingress_https", envoy.FileAccessLog("/dev/stdout")), "h2", "http/1.1"), } l1.FilterChains[0].TlsContext.CommonTlsContext.TlsParams.TlsMinimumProtocolVersion = envoy_api_v2_auth.TlsParameters_TLSv1_1 @@ -416,7 +416,7 @@ func TestIngressRouteTLSListener(t *testing.T) { Name: "ingress_http", Address: envoy.SocketAddress("0.0.0.0", 8080), FilterChains: envoy.FilterChains( - envoy.HTTPConnectionManager("ingress_http", "/dev/stdout"), + envoy.HTTPConnectionManager("ingress_http", envoy.FileAccessLog("/dev/stdout")), ), }, l1, @@ -447,7 +447,7 @@ func TestIngressRouteTLSListener(t *testing.T) { ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), - FilterChains: filterchaintls("kuard.example.com", secret1, envoy.HTTPConnectionManager("ingress_https", "/dev/stdout"), "h2", "http/1.1"), + FilterChains: filterchaintls("kuard.example.com", secret1, envoy.HTTPConnectionManager("ingress_https", envoy.FileAccessLog("/dev/stdout")), "h2", "http/1.1"), } l2.FilterChains[0].TlsContext.CommonTlsContext.TlsParams.TlsMinimumProtocolVersion = envoy_api_v2_auth.TlsParameters_TLSv1_3 @@ -461,7 +461,7 @@ func TestIngressRouteTLSListener(t *testing.T) { Name: "ingress_http", Address: envoy.SocketAddress("0.0.0.0", 8080), FilterChains: envoy.FilterChains( - envoy.HTTPConnectionManager("ingress_http", "/dev/stdout"), + envoy.HTTPConnectionManager("ingress_http", envoy.FileAccessLog("/dev/stdout")), ), }, l2, @@ -541,7 +541,7 @@ func TestLDSFilter(t *testing.T) { ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), - FilterChains: filterchaintls("kuard.example.com", s1, envoy.HTTPConnectionManager("ingress_https", "/dev/stdout"), "h2", "http/1.1"), + FilterChains: filterchaintls("kuard.example.com", s1, envoy.HTTPConnectionManager("ingress_https", envoy.FileAccessLog("/dev/stdout")), "h2", "http/1.1"), }, ), TypeUrl: listenerType, @@ -556,7 +556,7 @@ func TestLDSFilter(t *testing.T) { Name: "ingress_http", Address: envoy.SocketAddress("0.0.0.0", 8080), FilterChains: envoy.FilterChains( - envoy.HTTPConnectionManager("ingress_http", "/dev/stdout"), + envoy.HTTPConnectionManager("ingress_http", envoy.FileAccessLog("/dev/stdout")), ), }, ), @@ -653,7 +653,7 @@ func TestLDSTLSMinimumProtocolVersion(t *testing.T) { ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), - FilterChains: filterchaintls("kuard.example.com", s1, envoy.HTTPConnectionManager("ingress_https", "/dev/stdout"), "h2", "http/1.1"), + FilterChains: filterchaintls("kuard.example.com", s1, envoy.HTTPConnectionManager("ingress_https", envoy.FileAccessLog("/dev/stdout")), "h2", "http/1.1"), }, ), TypeUrl: listenerType, @@ -695,7 +695,7 @@ func TestLDSTLSMinimumProtocolVersion(t *testing.T) { ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), - FilterChains: filterchaintls("kuard.example.com", s1, envoy.HTTPConnectionManager("ingress_https", "/dev/stdout"), "h2", "http/1.1"), + FilterChains: filterchaintls("kuard.example.com", s1, envoy.HTTPConnectionManager("ingress_https", envoy.FileAccessLog("/dev/stdout")), "h2", "http/1.1"), } // easier to patch this up than add more params to filterchaintls l1.FilterChains[0].TlsContext.CommonTlsContext.TlsParams.TlsMinimumProtocolVersion = envoy_api_v2_auth.TlsParameters_TLSv1_3 @@ -763,7 +763,7 @@ func TestLDSIngressHTTPUseProxyProtocol(t *testing.T) { ListenerFilters: envoy.ListenerFilters( envoy.ProxyProtocol(), ), - FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager("ingress_http", "/dev/stdout")), + FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager("ingress_http", envoy.FileAccessLog("/dev/stdout"))), }, staticListener(), ), @@ -853,7 +853,7 @@ func TestLDSIngressHTTPSUseProxyProtocol(t *testing.T) { envoy.ProxyProtocol(), envoy.TLSInspector(), ), - FilterChains: filterchaintls("kuard.example.com", s1, envoy.HTTPConnectionManager("ingress_https", "/dev/stdout"), "h2", "http/1.1"), + FilterChains: filterchaintls("kuard.example.com", s1, envoy.HTTPConnectionManager("ingress_https", envoy.FileAccessLog("/dev/stdout")), "h2", "http/1.1"), } assertEqual(t, &v2.DiscoveryResponse{ VersionInfo: "1", @@ -864,7 +864,7 @@ func TestLDSIngressHTTPSUseProxyProtocol(t *testing.T) { ListenerFilters: envoy.ListenerFilters( envoy.ProxyProtocol(), ), - FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager("ingress_http", "/dev/stdout")), + FilterChains: envoy.FilterChains(envoy.HTTPConnectionManager("ingress_http", envoy.FileAccessLog("/dev/stdout"))), }, ingress_https, staticListener(), @@ -955,7 +955,7 @@ func TestLDSCustomAddressAndPort(t *testing.T) { Name: "ingress_http", Address: envoy.SocketAddress("127.0.0.100", 9100), FilterChains: envoy.FilterChains( - envoy.HTTPConnectionManager("ingress_http", "/dev/stdout"), + envoy.HTTPConnectionManager("ingress_http", envoy.FileAccessLog("/dev/stdout")), ), } ingress_https := &v2.Listener{ @@ -964,7 +964,7 @@ func TestLDSCustomAddressAndPort(t *testing.T) { ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), - FilterChains: filterchaintls("kuard.example.com", s1, envoy.HTTPConnectionManager("ingress_https", "/dev/stdout"), "h2", "http/1.1"), + FilterChains: filterchaintls("kuard.example.com", s1, envoy.HTTPConnectionManager("ingress_https", envoy.FileAccessLog("/dev/stdout")), "h2", "http/1.1"), } assertEqual(t, &v2.DiscoveryResponse{ VersionInfo: "1", @@ -1055,7 +1055,7 @@ func TestLDSCustomAccessLogPaths(t *testing.T) { Name: "ingress_http", Address: envoy.SocketAddress("0.0.0.0", 8080), FilterChains: envoy.FilterChains( - envoy.HTTPConnectionManager("ingress_http", "/tmp/http_access.log"), + envoy.HTTPConnectionManager("ingress_http", envoy.FileAccessLog("/tmp/http_access.log")), ), } ingress_https := &v2.Listener{ @@ -1064,7 +1064,7 @@ func TestLDSCustomAccessLogPaths(t *testing.T) { ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), - FilterChains: filterchaintls("kuard.example.com", s1, envoy.HTTPConnectionManager("ingress_https", "/tmp/https_access.log"), "h2", "http/1.1"), + FilterChains: filterchaintls("kuard.example.com", s1, envoy.HTTPConnectionManager("ingress_https", envoy.FileAccessLog("/tmp/https_access.log")), "h2", "http/1.1"), } assertEqual(t, &v2.DiscoveryResponse{ VersionInfo: "1", @@ -1138,7 +1138,7 @@ func TestLDSIngressRouteInsideRootNamespaces(t *testing.T) { Name: "ingress_http", Address: envoy.SocketAddress("0.0.0.0", 8080), FilterChains: envoy.FilterChains( - envoy.HTTPConnectionManager("ingress_http", "/dev/stdout"), + envoy.HTTPConnectionManager("ingress_http", envoy.FileAccessLog("/dev/stdout")), ), }, staticListener(), @@ -1273,7 +1273,7 @@ func TestIngressRouteHTTPS(t *testing.T) { Name: "ingress_http", Address: envoy.SocketAddress("0.0.0.0", 8080), FilterChains: envoy.FilterChains( - envoy.HTTPConnectionManager("ingress_http", "/dev/stdout"), + envoy.HTTPConnectionManager("ingress_http", envoy.FileAccessLog("/dev/stdout")), ), } @@ -1283,7 +1283,7 @@ func TestIngressRouteHTTPS(t *testing.T) { ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), - FilterChains: filterchaintls("example.com", s1, envoy.HTTPConnectionManager("ingress_https", "/dev/stdout"), "h2", "http/1.1"), + FilterChains: filterchaintls("example.com", s1, envoy.HTTPConnectionManager("ingress_https", envoy.FileAccessLog("/dev/stdout")), "h2", "http/1.1"), } assertEqual(t, &v2.DiscoveryResponse{ VersionInfo: "1", @@ -1537,7 +1537,7 @@ func TestIngressRouteTLSCertificateDelegation(t *testing.T) { Name: "ingress_http", Address: envoy.SocketAddress("0.0.0.0", 8080), FilterChains: envoy.FilterChains( - envoy.HTTPConnectionManager("ingress_http", "/dev/stdout"), + envoy.HTTPConnectionManager("ingress_http", envoy.FileAccessLog("/dev/stdout")), ), } @@ -1547,7 +1547,7 @@ func TestIngressRouteTLSCertificateDelegation(t *testing.T) { ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), - FilterChains: filterchaintls("example.com", s1, envoy.HTTPConnectionManager("ingress_https", "/dev/stdout"), "h2", "http/1.1"), + FilterChains: filterchaintls("example.com", s1, envoy.HTTPConnectionManager("ingress_https", envoy.FileAccessLog("/dev/stdout")), "h2", "http/1.1"), } assertEqual(t, &v2.DiscoveryResponse{ @@ -1710,7 +1710,7 @@ func TestIngressRouteMinimumTLSVersion(t *testing.T) { ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), - FilterChains: filterchaintls("kuard.example.com", secret1, envoy.HTTPConnectionManager("ingress_https", "/dev/stdout"), "h2", "http/1.1"), + FilterChains: filterchaintls("kuard.example.com", secret1, envoy.HTTPConnectionManager("ingress_https", envoy.FileAccessLog("/dev/stdout")), "h2", "http/1.1"), } l1.FilterChains[0].TlsContext.CommonTlsContext.TlsParams.TlsMinimumProtocolVersion = envoy_api_v2_auth.TlsParameters_TLSv1_2 @@ -1722,7 +1722,7 @@ func TestIngressRouteMinimumTLSVersion(t *testing.T) { Name: "ingress_http", Address: envoy.SocketAddress("0.0.0.0", 8080), FilterChains: envoy.FilterChains( - envoy.HTTPConnectionManager("ingress_http", "/dev/stdout"), + envoy.HTTPConnectionManager("ingress_http", envoy.FileAccessLog("/dev/stdout")), ), }, l1, @@ -1763,7 +1763,7 @@ func TestIngressRouteMinimumTLSVersion(t *testing.T) { ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), - FilterChains: filterchaintls("kuard.example.com", secret1, envoy.HTTPConnectionManager("ingress_https", "/dev/stdout"), "h2", "http/1.1"), + FilterChains: filterchaintls("kuard.example.com", secret1, envoy.HTTPConnectionManager("ingress_https", envoy.FileAccessLog("/dev/stdout")), "h2", "http/1.1"), } l2.FilterChains[0].TlsContext.CommonTlsContext.TlsParams.TlsMinimumProtocolVersion = envoy_api_v2_auth.TlsParameters_TLSv1_3 @@ -1775,7 +1775,7 @@ func TestIngressRouteMinimumTLSVersion(t *testing.T) { Name: "ingress_http", Address: envoy.SocketAddress("0.0.0.0", 8080), FilterChains: envoy.FilterChains( - envoy.HTTPConnectionManager("ingress_http", "/dev/stdout"), + envoy.HTTPConnectionManager("ingress_http", envoy.FileAccessLog("/dev/stdout")), ), }, l2, @@ -1854,7 +1854,7 @@ func TestLDSIngressRouteRootCannotDelegateToAnotherRoot(t *testing.T) { Name: "ingress_http", Address: envoy.SocketAddress("0.0.0.0", 8080), FilterChains: envoy.FilterChains( - envoy.HTTPConnectionManager("ingress_http", "/dev/stdout"), + envoy.HTTPConnectionManager("ingress_http", envoy.FileAccessLog("/dev/stdout")), ), }, staticListener(), diff --git a/internal/envoy/accesslog.go b/internal/envoy/accesslog.go index 4f6339333fb..505126b4577 100644 --- a/internal/envoy/accesslog.go +++ b/internal/envoy/accesslog.go @@ -17,8 +17,37 @@ import ( accesslogv2 "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v2" accesslog "github.com/envoyproxy/go-control-plane/envoy/config/filter/accesslog/v2" "github.com/envoyproxy/go-control-plane/pkg/wellknown" + _struct "github.com/golang/protobuf/ptypes/struct" ) +//JSONFields is the canonical translation table for JSON fields to Envoy log template formats, +//used for specifying fields for Envoy to log when JSON logging is enabled. +//Only fields specified in this map may be used for JSON logging. +var JSONFields = map[string]string{ + "@timestamp": "%START_TIME%", + "ts": "%START_TIME%", + "authority": "%REQ(:AUTHORITY)%", + "bytes_received": "%BYTES_RECEIVED%", + "bytes_sent": "%BYTES_SENT%", + "downstream_local_address": "%DOWNSTREAM_LOCAL_ADDRESS%", + "downstream_remote_address": "%DOWNSTREAM_REMOTE_ADDRESS%", + "duration": "%DURATION%", + "method": "%REQ(:METHOD)%", + "path": "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%", + "protocol": "%PROTOCOL%", + "request_id": "%REQ(X-REQUEST-ID)%", + "requested_server_name": "%REQUESTED_SERVER_NAME%", + "response_code": "%RESPONSE_CODE%", + "response_flags": "%RESPONSE_FLAGS%", + "uber_trace_id": "%REQ(UBER-TRACE-ID)%", + "upstream_cluster": "%UPSTREAM_CLUSTER%", + "upstream_host": "%UPSTREAM_HOST%", + "upstream_local_address": "%UPSTREAM_LOCAL_ADDRESS%", + "upstream_service_time": "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%", + "user_agent": "%REQ(USER-AGENT)%", + "x_forwarded_for": "%REQ(X-FORWARDED-FOR)%", +} + // FileAccessLog returns a new file based access log filter. func FileAccessLog(path string) []*accesslog.AccessLog { return []*accesslog.AccessLog{{ @@ -31,3 +60,41 @@ func FileAccessLog(path string) []*accesslog.AccessLog { }, }} } + +// FileAccessLogJSON returns a new file based access log filter +// that will log in JSON format +func FileAccessLogJSON(path string, keys []string) []*accesslog.AccessLog { + + jsonformat := &_struct.Struct{ + Fields: make(map[string]*_struct.Value), + } + + for _, k := range keys { + // This will silently ignore invalid headers. + // TODO(youngnick): this should tell users if a header is not valid + // https://github.com/projectcontour/contour/issues/1507 + if template, ok := JSONFields[k]; ok { + jsonformat.Fields[k] = sv(template) + } + } + + return []*accesslog.AccessLog{{ + Name: wellknown.FileAccessLog, + ConfigType: &accesslog.AccessLog_TypedConfig{ + TypedConfig: toAny(&accesslogv2.FileAccessLog{ + Path: path, + AccessLogFormat: &accesslogv2.FileAccessLog_JsonFormat{ + JsonFormat: jsonformat, + }, + }), + }, + }} +} + +func sv(s string) *_struct.Value { + return &_struct.Value{ + Kind: &_struct.Value_StringValue{ + StringValue: s, + }, + } +} diff --git a/internal/envoy/accesslog_test.go b/internal/envoy/accesslog_test.go index d14a2229dfe..fcea66f4607 100644 --- a/internal/envoy/accesslog_test.go +++ b/internal/envoy/accesslog_test.go @@ -14,12 +14,19 @@ package envoy import ( + "encoding/json" + "fmt" "testing" accesslog_v2 "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v2" envoy_accesslog "github.com/envoyproxy/go-control-plane/envoy/config/filter/accesslog/v2" "github.com/envoyproxy/go-control-plane/pkg/wellknown" + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + any "github.com/golang/protobuf/ptypes/any" + _struct "github.com/golang/protobuf/ptypes/struct" "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" ) func TestFileAccessLog(t *testing.T) { @@ -42,9 +49,88 @@ func TestFileAccessLog(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { got := FileAccessLog(tc.path) - if diff := cmp.Diff(tc.want, got); diff != "" { + if diff := cmp.Diff(tc.want, got, cmpopts.AcyclicTransformer("unmarshalAny", unmarshalAny)); diff != "" { t.Fatal(diff) } }) } } + +func TestJSONFileAccessLog(t *testing.T) { + tests := map[string]struct { + path string + headers []string + want []*envoy_accesslog.AccessLog + }{ + "only timestamp": { + path: "/dev/stdout", + headers: []string{"@timestamp"}, + want: []*envoy_accesslog.AccessLog{{ + Name: wellknown.FileAccessLog, + ConfigType: &envoy_accesslog.AccessLog_TypedConfig{ + TypedConfig: toAny(&accesslog_v2.FileAccessLog{ + Path: "/dev/stdout", + AccessLogFormat: &accesslog_v2.FileAccessLog_JsonFormat{ + JsonFormat: &_struct.Struct{ + Fields: map[string]*_struct.Value{ + "@timestamp": sv("%START_TIME%"), + }, + }, + }, + }), + }, + }, + }, + }, + "invalid header should disappear": { + path: "/dev/stdout", + headers: []string{ + "@timestamp", + "invalid", + "method", + }, + want: []*envoy_accesslog.AccessLog{{ + Name: wellknown.FileAccessLog, + ConfigType: &envoy_accesslog.AccessLog_TypedConfig{ + TypedConfig: toAny(&accesslog_v2.FileAccessLog{ + Path: "/dev/stdout", + AccessLogFormat: &accesslog_v2.FileAccessLog_JsonFormat{ + JsonFormat: &_struct.Struct{ + Fields: map[string]*_struct.Value{ + "@timestamp": sv(JSONFields["@timestamp"]), + "method": sv(JSONFields["method"]), + }, + }, + }, + }), + }, + }, + }, + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + got := FileAccessLogJSON(tc.path, tc.headers) + output, err := json.Marshal(got) + if err != nil { + t.Fatal(err) + } + fmt.Printf("%s\n", output) + if diff := cmp.Diff(tc.want, got, cmpopts.AcyclicTransformer("unmarshalAny", unmarshalAny)); diff != "" { + t.Fatal(diff) + } + }) + } +} + +func unmarshalAny(a *any.Any) proto.Message { + pb, err := ptypes.Empty(a) + if err != nil { + panic(err.Error()) + } + err = ptypes.UnmarshalAny(a, pb) + if err != nil { + panic(err.Error()) + } + return pb +} diff --git a/internal/envoy/listener.go b/internal/envoy/listener.go index e0095086689..44291bacd19 100644 --- a/internal/envoy/listener.go +++ b/internal/envoy/listener.go @@ -21,6 +21,7 @@ import ( envoy_api_v2_auth "github.com/envoyproxy/go-control-plane/envoy/api/v2/auth" envoy_api_v2_core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" envoy_api_v2_listener "github.com/envoyproxy/go-control-plane/envoy/api/v2/listener" + accesslog "github.com/envoyproxy/go-control-plane/envoy/config/filter/accesslog/v2" http "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/http_connection_manager/v2" tcp "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/tcp_proxy/v2" "github.com/envoyproxy/go-control-plane/pkg/wellknown" @@ -65,7 +66,8 @@ func Listener(name, address string, port int, lf []*envoy_api_v2_listener.Listen // HTTPConnectionManager creates a new HTTP Connection Manager filter // for the supplied route and access log. -func HTTPConnectionManager(routename, accessLogPath string) *envoy_api_v2_listener.Filter { +func HTTPConnectionManager(routename string, accesslogger []*accesslog.AccessLog) *envoy_api_v2_listener.Filter { + return &envoy_api_v2_listener.Filter{ Name: wellknown.HTTPConnectionManager, ConfigType: &envoy_api_v2_listener.Filter_TypedConfig{ @@ -102,7 +104,7 @@ func HTTPConnectionManager(routename, accessLogPath string) *envoy_api_v2_listen // a Host: header. See #537. AcceptHttp_10: true, }, - AccessLog: FileAccessLog(accessLogPath), + AccessLog: accesslogger, UseRemoteAddress: protobuf.Bool(true), NormalizePath: protobuf.Bool(true), // Sets the idle timeout for HTTP connections to 60 seconds. @@ -115,7 +117,7 @@ func HTTPConnectionManager(routename, accessLogPath string) *envoy_api_v2_listen } // TCPProxy creates a new TCPProxy filter. -func TCPProxy(statPrefix string, proxy *dag.TCPProxy, accessLogPath string) *envoy_api_v2_listener.Filter { +func TCPProxy(statPrefix string, proxy *dag.TCPProxy, accesslogger []*accesslog.AccessLog) *envoy_api_v2_listener.Filter { // Set the idle timeout in seconds for connections through a TCP Proxy type filter. // The value of two and a half hours for reasons documented at // https://github.com/projectcontour/contour/issues/1074 @@ -132,7 +134,7 @@ func TCPProxy(statPrefix string, proxy *dag.TCPProxy, accessLogPath string) *env ClusterSpecifier: &tcp.TcpProxy_Cluster{ Cluster: Clustername(proxy.Clusters[0]), }, - AccessLog: FileAccessLog(accessLogPath), + AccessLog: accesslogger, IdleTimeout: idleTimeout, }), }, @@ -160,7 +162,7 @@ func TCPProxy(statPrefix string, proxy *dag.TCPProxy, accessLogPath string) *env Clusters: clusters, }, }, - AccessLog: FileAccessLog(accessLogPath), + AccessLog: accesslogger, IdleTimeout: idleTimeout, }), }, diff --git a/internal/envoy/listener_test.go b/internal/envoy/listener_test.go index 762ab72ad4e..00057db76ac 100644 --- a/internal/envoy/listener_test.go +++ b/internal/envoy/listener_test.go @@ -21,10 +21,12 @@ import ( envoy_api_v2_auth "github.com/envoyproxy/go-control-plane/envoy/api/v2/auth" envoy_api_v2_core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" envoy_api_v2_listener "github.com/envoyproxy/go-control-plane/envoy/api/v2/listener" + envoy_api_v2_accesslog "github.com/envoyproxy/go-control-plane/envoy/config/filter/accesslog/v2" http "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/http_connection_manager/v2" envoy_config_v2_tcpproxy "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/tcp_proxy/v2" "github.com/envoyproxy/go-control-plane/pkg/wellknown" "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/projectcontour/contour/internal/dag" "github.com/projectcontour/contour/internal/protobuf" v1 "k8s.io/api/core/v1" @@ -44,13 +46,13 @@ func TestListener(t *testing.T) { address: "0.0.0.0", port: 9000, f: []*envoy_api_v2_listener.Filter{ - HTTPConnectionManager("http", "/dev/null"), + HTTPConnectionManager("http", FileAccessLog("/dev/null")), }, want: &v2.Listener{ Name: "http", Address: SocketAddress("0.0.0.0", 9000), FilterChains: FilterChains( - HTTPConnectionManager("http", "/dev/null"), + HTTPConnectionManager("http", FileAccessLog("/dev/null")), ), }, }, @@ -62,7 +64,7 @@ func TestListener(t *testing.T) { ProxyProtocol(), }, f: []*envoy_api_v2_listener.Filter{ - HTTPConnectionManager("http-proxy", "/dev/null"), + HTTPConnectionManager("http-proxy", FileAccessLog("/dev/null")), }, want: &v2.Listener{ Name: "http-proxy", @@ -71,7 +73,7 @@ func TestListener(t *testing.T) { ProxyProtocol(), ), FilterChains: FilterChains( - HTTPConnectionManager("http-proxy", "/dev/null"), + HTTPConnectionManager("http-proxy", FileAccessLog("/dev/null")), ), }, }, @@ -112,7 +114,7 @@ func TestListener(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { got := Listener(tc.name, tc.address, tc.port, tc.lf, tc.f...) - if diff := cmp.Diff(tc.want, got); diff != "" { + if diff := cmp.Diff(tc.want, got, cmpopts.AcyclicTransformer("unmarshalAny", unmarshalAny)); diff != "" { t.Fatal(diff) } }) @@ -154,7 +156,7 @@ func TestSocketAddress(t *testing.T) { }, }, } - if diff := cmp.Diff(want, got); diff != "" { + if diff := cmp.Diff(want, got, cmpopts.AcyclicTransformer("unmarshalAny", unmarshalAny)); diff != "" { t.Fatal(diff) } } @@ -199,20 +201,20 @@ func TestDownstreamTLSContext(t *testing.T) { AlpnProtocols: []string{"h2", "http/1.1"}, }, } - if diff := cmp.Diff(want, got); diff != "" { + if diff := cmp.Diff(want, got, cmpopts.AcyclicTransformer("unmarshalAny", unmarshalAny)); diff != "" { t.Fatal(diff) } } func TestHTTPConnectionManager(t *testing.T) { tests := map[string]struct { - routename string - accesslog string - want *envoy_api_v2_listener.Filter + routename string + accesslogger []*envoy_api_v2_accesslog.AccessLog + want *envoy_api_v2_listener.Filter }{ "default": { - routename: "default/kuard", - accesslog: "/dev/stdout", + routename: "default/kuard", + accesslogger: FileAccessLog("/dev/stdout"), want: &envoy_api_v2_listener.Filter{ Name: wellknown.HTTPConnectionManager, ConfigType: &envoy_api_v2_listener.Filter_TypedConfig{ @@ -260,8 +262,8 @@ func TestHTTPConnectionManager(t *testing.T) { } for name, tc := range tests { t.Run(name, func(t *testing.T) { - got := HTTPConnectionManager(tc.routename, tc.accesslog) - if diff := cmp.Diff(tc.want, got); diff != "" { + got := HTTPConnectionManager(tc.routename, tc.accesslogger) + if diff := cmp.Diff(tc.want, got, cmpopts.AcyclicTransformer("unmarshalAny", unmarshalAny)); diff != "" { t.Fatal(diff) } }) @@ -350,8 +352,8 @@ func TestTCPProxy(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { - got := TCPProxy(statPrefix, tc.proxy, accessLogPath) - if diff := cmp.Diff(tc.want, got); diff != "" { + got := TCPProxy(statPrefix, tc.proxy, FileAccessLog(accessLogPath)) + if diff := cmp.Diff(tc.want, got, cmpopts.AcyclicTransformer("unmarshalAny", unmarshalAny)); diff != "" { t.Fatal(diff) } })