From bc97340d5ddee5ea122203f7048b0b12b602e024 Mon Sep 17 00:00:00 2001
From: yy
Date: Wed, 28 Dec 2022 10:18:51 +0800
Subject: [PATCH] add tracing support
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: yy
add some unit test
Signed-off-by: yy
git rebase
Signed-off-by: yy
expose configuration for envoy's RateLimitedAsResourceExhausted (#4971)
The Rate Limit filter in Envoy translates a 429 HTTP response code
to UNAVAILABLE as specified in the gRPC mapping document, but Google recommends
translating it to RESOURCE_EXHAUSTED
(see https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md)
This commit introduces a new setting to allow contour to forward the same parameter
introduced in https://github.com/envoyproxy/envoy/pull/4879
The default value is disabled to retain the original behaviour of returning UNAVAILABLE,
as changing it would be a breaking change.
Closes #4901.
Signed-off-by: Víctor Roldán Betancort
Signed-off-by: yy
rebase
Signed-off-by: yy
update tracing config validate
Signed-off-by: yy
make generate
Signed-off-by: yy
add chengelog
Signed-off-by: yy
update make general
Signed-off-by: yy
goimport
Signed-off-by: yy
update tracing
Signed-off-by: yy
fix golint
Signed-off-by: yy
update test
Signed-off-by: yy
delete unused code
Signed-off-by: yy
delete error file
Signed-off-by: yy
update changelog
Signed-off-by: yy
fix some mistake
Signed-off-by: yy
feat: Add HTTP support for External Auth (#4994)
Support globally configuring an external auth
server which is enabled by default for all vhosts,
both HTTP and HTTPS.
Closes #4954.
Signed-off-by: claytonig
Signed-off-by: yy
refactor DAG and DAG consumers to support >2 Listeners (#5128)
Updates #4960.
Signed-off-by: Steve Kriss
Signed-off-by: yy
resolve conflict
Signed-off-by: yy
fix
Signed-off-by: yy
---
apis/projectcontour/v1alpha1/contourconfig.go | 50 +++++
.../v1alpha1/zz_generated.deepcopy.go | 67 ++++++
changelogs/unreleased/5043-yangyy93-minor.md | 14 ++
cmd/contour/serve.go | 75 +++++++
cmd/contour/servecontext.go | 27 +++
cmd/contour/servecontext_test.go | 50 ++++-
examples/contour/01-crds.yaml | 111 ++++++++++
examples/render/contour-deployment.yaml | 111 ++++++++++
.../render/contour-gateway-provisioner.yaml | 111 ++++++++++
examples/render/contour-gateway.yaml | 111 ++++++++++
examples/render/contour.yaml | 111 ++++++++++
internal/dag/dag.go | 38 ++++
internal/envoy/v3/listener.go | 10 +
internal/envoy/v3/tracing.go | 93 +++++++++
internal/envoy/v3/tracing_test.go | 193 ++++++++++++++++++
internal/featuretests/v3/tracing_test.go | 126 ++++++++++++
internal/xdscache/v3/listener.go | 8 +
pkg/config/parameters.go | 91 +++++++++
pkg/config/parameters_test.go | 78 +++++++
.../docs/main/config/api-reference.html | 191 ++++++++++++++++-
20 files changed, 1662 insertions(+), 4 deletions(-)
create mode 100644 changelogs/unreleased/5043-yangyy93-minor.md
create mode 100644 internal/envoy/v3/tracing.go
create mode 100644 internal/envoy/v3/tracing_test.go
create mode 100644 internal/featuretests/v3/tracing_test.go
diff --git a/apis/projectcontour/v1alpha1/contourconfig.go b/apis/projectcontour/v1alpha1/contourconfig.go
index f810a0cfbd0..2f2d2866d9f 100644
--- a/apis/projectcontour/v1alpha1/contourconfig.go
+++ b/apis/projectcontour/v1alpha1/contourconfig.go
@@ -81,6 +81,9 @@ type ContourConfigurationSpec struct {
// Contour's default is { address: "0.0.0.0", port: 8000 }.
// +optional
Metrics *MetricsConfig `json:"metrics,omitempty"`
+
+ // Tracing defines properties for exporting trace data to OpenTelemetry.
+ Tracing *TracingConfig `json:"tracing,omitempty"`
}
// XDSServerType is the type of xDS server implementation.
@@ -659,6 +662,53 @@ type RateLimitServiceConfig struct {
EnableResourceExhaustedCode *bool `json:"enableResourceExhaustedCode,omitempty"`
}
+// TracingConfig defines properties for exporting trace data to OpenTelemetry.
+type TracingConfig struct {
+ // IncludePodDetail defines a flag.
+ // If it is true, contour will add the pod name and namespace to the span of the trace.
+ // the default is true.
+ // +optional
+ IncludePodDetail *bool `json:"includePodDetail,omitempty"`
+
+ // ServiceName defines the name for the service.
+ // contour's default is contour
+ ServiceName *string `json:"serviceName,omitempty"`
+
+ // OverallSampling defines the sampling rate of trace data.
+ // contour's default is 100
+ // +optional
+ OverallSampling *string `json:"overallSampling,omitempty"`
+
+ // OverallSampling defines maximum length of the request path
+ // to extract and include in the HttpUrl tag.
+ // contour's default is 256.
+ // +optional
+ MaxPathTagLength *uint32 `json:"maxPathTagLength,omitempty"`
+
+ // CustomTags defines a list of custom tags with unique tag name.
+ // +optional
+ CustomTags []*CustomTag `json:"customTags,omitempty"`
+
+ // ExtensionService identifies the extension service defining the otle-collector.
+ ExtensionService NamespacedName `json:"extensionService,omitempty"`
+}
+
+// CustomTag defines custom tags with unique tag name
+// to create tags for the active span.
+type CustomTag struct {
+ // TagName is the unique name of the custom tag.
+ TagName string `json:"tagName,omitempty"`
+
+ // Literal is a static custom tag value.
+ // +optional
+ Literal string `json:"literal,omitempty"`
+
+ // RequestHeaderName indicates which request header
+ // the label value is obtained from.
+ // +optional
+ RequestHeaderName string `json:"requestHeaderName,omitempty"`
+}
+
// PolicyConfig holds default policy used if not explicitly set by the user
type PolicyConfig struct {
// RequestHeadersPolicy defines the request headers set/removed on all routes
diff --git a/apis/projectcontour/v1alpha1/zz_generated.deepcopy.go b/apis/projectcontour/v1alpha1/zz_generated.deepcopy.go
index 5cc64d74f5f..566a13744ef 100644
--- a/apis/projectcontour/v1alpha1/zz_generated.deepcopy.go
+++ b/apis/projectcontour/v1alpha1/zz_generated.deepcopy.go
@@ -185,6 +185,11 @@ func (in *ContourConfigurationSpec) DeepCopyInto(out *ContourConfigurationSpec)
*out = new(MetricsConfig)
(*in).DeepCopyInto(*out)
}
+ if in.Tracing != nil {
+ in, out := &in.Tracing, &out.Tracing
+ *out = new(TracingConfig)
+ (*in).DeepCopyInto(*out)
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContourConfigurationSpec.
@@ -363,6 +368,21 @@ func (in *ContourSettings) DeepCopy() *ContourSettings {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *CustomTag) DeepCopyInto(out *CustomTag) {
+ *out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomTag.
+func (in *CustomTag) DeepCopy() *CustomTag {
+ if in == nil {
+ return nil
+ }
+ out := new(CustomTag)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DaemonSetSettings) DeepCopyInto(out *DaemonSetSettings) {
*out = *in
@@ -1145,6 +1165,53 @@ func (in *TimeoutParameters) DeepCopy() *TimeoutParameters {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *TracingConfig) DeepCopyInto(out *TracingConfig) {
+ *out = *in
+ if in.IncludePodDetail != nil {
+ in, out := &in.IncludePodDetail, &out.IncludePodDetail
+ *out = new(bool)
+ **out = **in
+ }
+ if in.ServiceName != nil {
+ in, out := &in.ServiceName, &out.ServiceName
+ *out = new(string)
+ **out = **in
+ }
+ if in.OverallSampling != nil {
+ in, out := &in.OverallSampling, &out.OverallSampling
+ *out = new(string)
+ **out = **in
+ }
+ if in.MaxPathTagLength != nil {
+ in, out := &in.MaxPathTagLength, &out.MaxPathTagLength
+ *out = new(uint32)
+ **out = **in
+ }
+ if in.CustomTags != nil {
+ in, out := &in.CustomTags, &out.CustomTags
+ *out = make([]*CustomTag, len(*in))
+ for i := range *in {
+ if (*in)[i] != nil {
+ in, out := &(*in)[i], &(*out)[i]
+ *out = new(CustomTag)
+ **out = **in
+ }
+ }
+ }
+ out.ExtensionService = in.ExtensionService
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TracingConfig.
+func (in *TracingConfig) DeepCopy() *TracingConfig {
+ if in == nil {
+ return nil
+ }
+ out := new(TracingConfig)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *XDSServerConfig) DeepCopyInto(out *XDSServerConfig) {
*out = *in
diff --git a/changelogs/unreleased/5043-yangyy93-minor.md b/changelogs/unreleased/5043-yangyy93-minor.md
new file mode 100644
index 00000000000..96ea4258de6
--- /dev/null
+++ b/changelogs/unreleased/5043-yangyy93-minor.md
@@ -0,0 +1,14 @@
+## Add Tracing Support
+
+Contour now supports exporting tracing data to [OpenTelemetry][1]
+
+The Contour configuration file and ContourConfiguration CRD will be extended with a new optional `tracing` section. This configuration block, if present, will enable tracing and will define the trace properties needed to generate and export trace data.
+
+### Contour supports the following configurations
+- Custom service name, the default is `contour`.
+- Custom sampling rate, the default is `100`.
+- Custom the maximum length of the request path, the default is `256`.
+- Customize span tags from literal and request headers.
+- Customize whether to include the pod's hostname and namespace.
+
+[1]: https://opentelemetry.io/
diff --git a/cmd/contour/serve.go b/cmd/contour/serve.go
index e2aaaeef8ee..fc2b769dc7c 100644
--- a/cmd/contour/serve.go
+++ b/cmd/contour/serve.go
@@ -24,6 +24,8 @@ import (
"time"
"github.com/alecthomas/kingpin/v2"
+ "k8s.io/utils/pointer"
+
envoy_server_v3 "github.com/envoyproxy/go-control-plane/pkg/server/v3"
contour_api_v1 "github.com/projectcontour/contour/apis/projectcontour/v1"
contour_api_v1alpha1 "github.com/projectcontour/contour/apis/projectcontour/v1alpha1"
@@ -354,6 +356,10 @@ func (s *Server) doServe() error {
ConnectionBalancer: contourConfiguration.Envoy.Listener.ConnectionBalancer,
}
+ if listenerConfig.TracingConfig, err = s.setupTracingService(contourConfiguration.Tracing); err != nil {
+ return err
+ }
+
if listenerConfig.RateLimitConfig, err = s.setupRateLimitService(contourConfiguration); err != nil {
return err
}
@@ -594,6 +600,75 @@ func (s *Server) doServe() error {
return s.mgr.Start(signals.SetupSignalHandler())
}
+func (s *Server) setupTracingService(tracingConfig *contour_api_v1alpha1.TracingConfig) (*dag.TracingConfig, error) {
+ if tracingConfig == nil {
+ return nil, nil
+ }
+
+ // ensure the specified ExtensionService exists
+ extensionSvc := &contour_api_v1alpha1.ExtensionService{}
+ key := client.ObjectKey{
+ Namespace: tracingConfig.ExtensionService.Namespace,
+ Name: tracingConfig.ExtensionService.Name,
+ }
+ // Using GetAPIReader() here because the manager's caches won't be started yet,
+ // so reads from the manager's client (which uses the caches for reads) will fail.
+ if err := s.mgr.GetAPIReader().Get(context.Background(), key, extensionSvc); err != nil {
+ return nil, fmt.Errorf("error getting tracing extension service %s: %v", key, err)
+ }
+ // get the response timeout from the ExtensionService
+ var responseTimeout timeout.Setting
+ var err error
+
+ if tp := extensionSvc.Spec.TimeoutPolicy; tp != nil {
+ responseTimeout, err = timeout.Parse(tp.Response)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing tracing extension service %s response timeout: %v", key, err)
+ }
+ }
+
+ var sni string
+ if extensionSvc.Spec.UpstreamValidation != nil {
+ sni = extensionSvc.Spec.UpstreamValidation.SubjectName
+ }
+
+ var customTags []*dag.CustomTag
+
+ if pointer.BoolDeref(tracingConfig.IncludePodDetail, true) {
+ customTags = append(customTags, &dag.CustomTag{
+ TagName: "podName",
+ EnvironmentName: "HOSTNAME",
+ }, &dag.CustomTag{
+ TagName: "podNamespaceName",
+ EnvironmentName: "CONTOUR_NAMESPACE",
+ })
+ }
+
+ for _, customTag := range tracingConfig.CustomTags {
+ customTags = append(customTags, &dag.CustomTag{
+ TagName: customTag.TagName,
+ Literal: customTag.Literal,
+ RequestHeaderName: customTag.RequestHeaderName,
+ })
+ }
+
+ overallSampling, err := strconv.ParseFloat(*tracingConfig.OverallSampling, 64)
+ if err != nil || overallSampling == 0 {
+ overallSampling = 100.0
+ }
+
+ return &dag.TracingConfig{
+ ServiceName: pointer.StringDeref(tracingConfig.ServiceName, "contour"),
+ ExtensionService: key,
+ SNI: sni,
+ Timeout: responseTimeout,
+ OverallSampling: overallSampling,
+ MaxPathTagLength: pointer.Uint32Deref(tracingConfig.MaxPathTagLength, 256),
+ CustomTags: customTags,
+ }, nil
+
+}
+
func (s *Server) setupRateLimitService(contourConfiguration contour_api_v1alpha1.ContourConfigurationSpec) (*xdscache_v3.RateLimitConfig, error) {
if contourConfiguration.RateLimitService == nil {
return nil, nil
diff --git a/cmd/contour/servecontext.go b/cmd/contour/servecontext.go
index 00b07e7900c..ff725351c83 100644
--- a/cmd/contour/servecontext.go
+++ b/cmd/contour/servecontext.go
@@ -20,6 +20,7 @@ import (
"errors"
"fmt"
"os"
+ "strconv"
"strings"
"time"
@@ -30,6 +31,7 @@ import (
"github.com/projectcontour/contour/internal/ref"
xdscache_v3 "github.com/projectcontour/contour/internal/xdscache/v3"
"github.com/projectcontour/contour/pkg/config"
+ "k8s.io/utils/pointer"
"github.com/sirupsen/logrus"
"google.golang.org/grpc"
@@ -366,6 +368,30 @@ func (ctx *serveContext) convertToContourConfigurationSpec() contour_api_v1alpha
dnsLookupFamily = contour_api_v1alpha1.AllClusterDNSFamily
}
+ var tracingConfig *contour_api_v1alpha1.TracingConfig
+ if ctx.Config.Tracing.ExtensionService != "" {
+ namespacedName := k8s.NamespacedNameFrom(ctx.Config.Tracing.ExtensionService)
+ var customTags []*contour_api_v1alpha1.CustomTag
+ for _, customTag := range ctx.Config.Tracing.CustomTags {
+ customTags = append(customTags, &contour_api_v1alpha1.CustomTag{
+ TagName: customTag.TagName,
+ Literal: customTag.Literal,
+ RequestHeaderName: customTag.RequestHeaderName,
+ })
+ }
+ tracingConfig = &contour_api_v1alpha1.TracingConfig{
+ IncludePodDetail: ctx.Config.Tracing.IncludePodDetail,
+ ServiceName: pointer.String(ctx.Config.Tracing.ServiceName),
+ OverallSampling: pointer.String(strconv.FormatFloat(ctx.Config.Tracing.OverallSampling, 'f', 1, 64)),
+ MaxPathTagLength: pointer.Uint32(ctx.Config.Tracing.MaxPathTagLength),
+ CustomTags: customTags,
+ ExtensionService: contour_api_v1alpha1.NamespacedName{
+ Name: namespacedName.Name,
+ Namespace: namespacedName.Namespace,
+ },
+ }
+ }
+
var rateLimitService *contour_api_v1alpha1.RateLimitServiceConfig
if ctx.Config.RateLimitService.ExtensionService != "" {
@@ -538,6 +564,7 @@ func (ctx *serveContext) convertToContourConfigurationSpec() contour_api_v1alpha
RateLimitService: rateLimitService,
Policy: policy,
Metrics: &contourMetrics,
+ Tracing: tracingConfig,
}
xdsServerType := contour_api_v1alpha1.ContourServerType
diff --git a/cmd/contour/servecontext_test.go b/cmd/contour/servecontext_test.go
index cc537a78715..efcc553ed03 100644
--- a/cmd/contour/servecontext_test.go
+++ b/cmd/contour/servecontext_test.go
@@ -24,17 +24,17 @@ import (
"testing"
"time"
- "github.com/projectcontour/contour/pkg/config"
- "github.com/tsaarni/certyaml"
-
contour_api_v1 "github.com/projectcontour/contour/apis/projectcontour/v1"
contour_api_v1alpha1 "github.com/projectcontour/contour/apis/projectcontour/v1alpha1"
"github.com/projectcontour/contour/internal/contourconfig"
envoy_v3 "github.com/projectcontour/contour/internal/envoy/v3"
"github.com/projectcontour/contour/internal/fixture"
"github.com/projectcontour/contour/internal/ref"
+ "github.com/projectcontour/contour/pkg/config"
"github.com/stretchr/testify/assert"
+ "github.com/tsaarni/certyaml"
"google.golang.org/grpc"
+ "k8s.io/utils/pointer"
)
func TestServeContextProxyRootNamespaces(t *testing.T) {
@@ -741,6 +741,50 @@ func TestConvertServeContext(t *testing.T) {
return cfg
},
},
+ "tracing config": {
+ getServeContext: func(ctx *serveContext) *serveContext {
+ ctx.Config.Tracing = config.Tracing{
+ IncludePodDetail: nil,
+ ServiceName: "contour",
+ OverallSampling: 100,
+ MaxPathTagLength: 256,
+ CustomTags: []config.CustomTag{
+ {
+ TagName: "literal",
+ Literal: "this is literal",
+ },
+ {
+ TagName: "header",
+ RequestHeaderName: ":method",
+ },
+ },
+ ExtensionService: "otel/otel-collector",
+ }
+ return ctx
+ },
+ getContourConfiguration: func(cfg contour_api_v1alpha1.ContourConfigurationSpec) contour_api_v1alpha1.ContourConfigurationSpec {
+ cfg.Tracing = &contour_api_v1alpha1.TracingConfig{
+ ServiceName: pointer.String("contour"),
+ OverallSampling: pointer.String("100.0"),
+ MaxPathTagLength: pointer.Uint32(256),
+ CustomTags: []*contour_api_v1alpha1.CustomTag{
+ {
+ TagName: "literal",
+ Literal: "this is literal",
+ },
+ {
+ TagName: "header",
+ RequestHeaderName: ":method",
+ },
+ },
+ ExtensionService: contour_api_v1alpha1.NamespacedName{
+ Name: "otel-collector",
+ Namespace: "otel",
+ },
+ }
+ return cfg
+ },
+ },
}
for name, tc := range cases {
diff --git a/examples/contour/01-crds.yaml b/examples/contour/01-crds.yaml
index 6395cd2d569..4087ef8ef83 100644
--- a/examples/contour/01-crds.yaml
+++ b/examples/contour/01-crds.yaml
@@ -646,6 +646,61 @@ spec:
required:
- extensionService
type: object
+ tracing:
+ description: Tracing defines properties for exporting trace data to
+ OpenTelemetry.
+ properties:
+ customTags:
+ description: CustomTags defines a list of custom tags with unique
+ tag name.
+ items:
+ description: CustomTag defines custom tags with unique tag name
+ to create tags for the active span.
+ properties:
+ literal:
+ description: Literal is a static custom tag value.
+ type: string
+ requestHeaderName:
+ description: RequestHeaderName indicates which request header
+ the label value is obtained from.
+ type: string
+ tagName:
+ description: TagName is the unique name of the custom tag.
+ type: string
+ type: object
+ type: array
+ extensionService:
+ description: ExtensionService identifies the extension service
+ defining the otle-collector.
+ properties:
+ name:
+ type: string
+ namespace:
+ type: string
+ required:
+ - name
+ - namespace
+ type: object
+ includePodDetail:
+ description: IncludePodDetail defines a flag. If it is true, contour
+ will add the pod name and namespace to the span of the trace.
+ the default is true.
+ type: boolean
+ maxPathTagLength:
+ description: OverallSampling defines maximum length of the request
+ path to extract and include in the HttpUrl tag. contour's default
+ is 256.
+ format: int32
+ type: integer
+ overallSampling:
+ description: OverallSampling defines the sampling rate of trace
+ data. contour's default is 100
+ type: string
+ serviceName:
+ description: ServiceName defines the name for the service. contour's
+ default is contour
+ type: string
+ type: object
xdsServer:
description: XDSServer contains parameters for the xDS server.
properties:
@@ -3755,6 +3810,62 @@ spec:
required:
- extensionService
type: object
+ tracing:
+ description: Tracing defines properties for exporting trace data
+ to OpenTelemetry.
+ properties:
+ customTags:
+ description: CustomTags defines a list of custom tags with
+ unique tag name.
+ items:
+ description: CustomTag defines custom tags with unique tag
+ name to create tags for the active span.
+ properties:
+ literal:
+ description: Literal is a static custom tag value.
+ type: string
+ requestHeaderName:
+ description: RequestHeaderName indicates which request
+ header the label value is obtained from.
+ type: string
+ tagName:
+ description: TagName is the unique name of the custom
+ tag.
+ type: string
+ type: object
+ type: array
+ extensionService:
+ description: ExtensionService identifies the extension service
+ defining the otle-collector.
+ properties:
+ name:
+ type: string
+ namespace:
+ type: string
+ required:
+ - name
+ - namespace
+ type: object
+ includePodDetail:
+ description: IncludePodDetail defines a flag. If it is true,
+ contour will add the pod name and namespace to the span
+ of the trace. the default is true.
+ type: boolean
+ maxPathTagLength:
+ description: OverallSampling defines maximum length of the
+ request path to extract and include in the HttpUrl tag.
+ contour's default is 256.
+ format: int32
+ type: integer
+ overallSampling:
+ description: OverallSampling defines the sampling rate of
+ trace data. contour's default is 100
+ type: string
+ serviceName:
+ description: ServiceName defines the name for the service.
+ contour's default is contour
+ type: string
+ type: object
xdsServer:
description: XDSServer contains parameters for the xDS server.
properties:
diff --git a/examples/render/contour-deployment.yaml b/examples/render/contour-deployment.yaml
index 063e42afef1..b771b18b65f 100644
--- a/examples/render/contour-deployment.yaml
+++ b/examples/render/contour-deployment.yaml
@@ -859,6 +859,61 @@ spec:
required:
- extensionService
type: object
+ tracing:
+ description: Tracing defines properties for exporting trace data to
+ OpenTelemetry.
+ properties:
+ customTags:
+ description: CustomTags defines a list of custom tags with unique
+ tag name.
+ items:
+ description: CustomTag defines custom tags with unique tag name
+ to create tags for the active span.
+ properties:
+ literal:
+ description: Literal is a static custom tag value.
+ type: string
+ requestHeaderName:
+ description: RequestHeaderName indicates which request header
+ the label value is obtained from.
+ type: string
+ tagName:
+ description: TagName is the unique name of the custom tag.
+ type: string
+ type: object
+ type: array
+ extensionService:
+ description: ExtensionService identifies the extension service
+ defining the otle-collector.
+ properties:
+ name:
+ type: string
+ namespace:
+ type: string
+ required:
+ - name
+ - namespace
+ type: object
+ includePodDetail:
+ description: IncludePodDetail defines a flag. If it is true, contour
+ will add the pod name and namespace to the span of the trace.
+ the default is true.
+ type: boolean
+ maxPathTagLength:
+ description: OverallSampling defines maximum length of the request
+ path to extract and include in the HttpUrl tag. contour's default
+ is 256.
+ format: int32
+ type: integer
+ overallSampling:
+ description: OverallSampling defines the sampling rate of trace
+ data. contour's default is 100
+ type: string
+ serviceName:
+ description: ServiceName defines the name for the service. contour's
+ default is contour
+ type: string
+ type: object
xdsServer:
description: XDSServer contains parameters for the xDS server.
properties:
@@ -3968,6 +4023,62 @@ spec:
required:
- extensionService
type: object
+ tracing:
+ description: Tracing defines properties for exporting trace data
+ to OpenTelemetry.
+ properties:
+ customTags:
+ description: CustomTags defines a list of custom tags with
+ unique tag name.
+ items:
+ description: CustomTag defines custom tags with unique tag
+ name to create tags for the active span.
+ properties:
+ literal:
+ description: Literal is a static custom tag value.
+ type: string
+ requestHeaderName:
+ description: RequestHeaderName indicates which request
+ header the label value is obtained from.
+ type: string
+ tagName:
+ description: TagName is the unique name of the custom
+ tag.
+ type: string
+ type: object
+ type: array
+ extensionService:
+ description: ExtensionService identifies the extension service
+ defining the otle-collector.
+ properties:
+ name:
+ type: string
+ namespace:
+ type: string
+ required:
+ - name
+ - namespace
+ type: object
+ includePodDetail:
+ description: IncludePodDetail defines a flag. If it is true,
+ contour will add the pod name and namespace to the span
+ of the trace. the default is true.
+ type: boolean
+ maxPathTagLength:
+ description: OverallSampling defines maximum length of the
+ request path to extract and include in the HttpUrl tag.
+ contour's default is 256.
+ format: int32
+ type: integer
+ overallSampling:
+ description: OverallSampling defines the sampling rate of
+ trace data. contour's default is 100
+ type: string
+ serviceName:
+ description: ServiceName defines the name for the service.
+ contour's default is contour
+ type: string
+ type: object
xdsServer:
description: XDSServer contains parameters for the xDS server.
properties:
diff --git a/examples/render/contour-gateway-provisioner.yaml b/examples/render/contour-gateway-provisioner.yaml
index 6307c404a5e..2d978267712 100644
--- a/examples/render/contour-gateway-provisioner.yaml
+++ b/examples/render/contour-gateway-provisioner.yaml
@@ -660,6 +660,61 @@ spec:
required:
- extensionService
type: object
+ tracing:
+ description: Tracing defines properties for exporting trace data to
+ OpenTelemetry.
+ properties:
+ customTags:
+ description: CustomTags defines a list of custom tags with unique
+ tag name.
+ items:
+ description: CustomTag defines custom tags with unique tag name
+ to create tags for the active span.
+ properties:
+ literal:
+ description: Literal is a static custom tag value.
+ type: string
+ requestHeaderName:
+ description: RequestHeaderName indicates which request header
+ the label value is obtained from.
+ type: string
+ tagName:
+ description: TagName is the unique name of the custom tag.
+ type: string
+ type: object
+ type: array
+ extensionService:
+ description: ExtensionService identifies the extension service
+ defining the otle-collector.
+ properties:
+ name:
+ type: string
+ namespace:
+ type: string
+ required:
+ - name
+ - namespace
+ type: object
+ includePodDetail:
+ description: IncludePodDetail defines a flag. If it is true, contour
+ will add the pod name and namespace to the span of the trace.
+ the default is true.
+ type: boolean
+ maxPathTagLength:
+ description: OverallSampling defines maximum length of the request
+ path to extract and include in the HttpUrl tag. contour's default
+ is 256.
+ format: int32
+ type: integer
+ overallSampling:
+ description: OverallSampling defines the sampling rate of trace
+ data. contour's default is 100
+ type: string
+ serviceName:
+ description: ServiceName defines the name for the service. contour's
+ default is contour
+ type: string
+ type: object
xdsServer:
description: XDSServer contains parameters for the xDS server.
properties:
@@ -3769,6 +3824,62 @@ spec:
required:
- extensionService
type: object
+ tracing:
+ description: Tracing defines properties for exporting trace data
+ to OpenTelemetry.
+ properties:
+ customTags:
+ description: CustomTags defines a list of custom tags with
+ unique tag name.
+ items:
+ description: CustomTag defines custom tags with unique tag
+ name to create tags for the active span.
+ properties:
+ literal:
+ description: Literal is a static custom tag value.
+ type: string
+ requestHeaderName:
+ description: RequestHeaderName indicates which request
+ header the label value is obtained from.
+ type: string
+ tagName:
+ description: TagName is the unique name of the custom
+ tag.
+ type: string
+ type: object
+ type: array
+ extensionService:
+ description: ExtensionService identifies the extension service
+ defining the otle-collector.
+ properties:
+ name:
+ type: string
+ namespace:
+ type: string
+ required:
+ - name
+ - namespace
+ type: object
+ includePodDetail:
+ description: IncludePodDetail defines a flag. If it is true,
+ contour will add the pod name and namespace to the span
+ of the trace. the default is true.
+ type: boolean
+ maxPathTagLength:
+ description: OverallSampling defines maximum length of the
+ request path to extract and include in the HttpUrl tag.
+ contour's default is 256.
+ format: int32
+ type: integer
+ overallSampling:
+ description: OverallSampling defines the sampling rate of
+ trace data. contour's default is 100
+ type: string
+ serviceName:
+ description: ServiceName defines the name for the service.
+ contour's default is contour
+ type: string
+ type: object
xdsServer:
description: XDSServer contains parameters for the xDS server.
properties:
diff --git a/examples/render/contour-gateway.yaml b/examples/render/contour-gateway.yaml
index 37211a40e61..c14f3af6d58 100644
--- a/examples/render/contour-gateway.yaml
+++ b/examples/render/contour-gateway.yaml
@@ -865,6 +865,61 @@ spec:
required:
- extensionService
type: object
+ tracing:
+ description: Tracing defines properties for exporting trace data to
+ OpenTelemetry.
+ properties:
+ customTags:
+ description: CustomTags defines a list of custom tags with unique
+ tag name.
+ items:
+ description: CustomTag defines custom tags with unique tag name
+ to create tags for the active span.
+ properties:
+ literal:
+ description: Literal is a static custom tag value.
+ type: string
+ requestHeaderName:
+ description: RequestHeaderName indicates which request header
+ the label value is obtained from.
+ type: string
+ tagName:
+ description: TagName is the unique name of the custom tag.
+ type: string
+ type: object
+ type: array
+ extensionService:
+ description: ExtensionService identifies the extension service
+ defining the otle-collector.
+ properties:
+ name:
+ type: string
+ namespace:
+ type: string
+ required:
+ - name
+ - namespace
+ type: object
+ includePodDetail:
+ description: IncludePodDetail defines a flag. If it is true, contour
+ will add the pod name and namespace to the span of the trace.
+ the default is true.
+ type: boolean
+ maxPathTagLength:
+ description: OverallSampling defines maximum length of the request
+ path to extract and include in the HttpUrl tag. contour's default
+ is 256.
+ format: int32
+ type: integer
+ overallSampling:
+ description: OverallSampling defines the sampling rate of trace
+ data. contour's default is 100
+ type: string
+ serviceName:
+ description: ServiceName defines the name for the service. contour's
+ default is contour
+ type: string
+ type: object
xdsServer:
description: XDSServer contains parameters for the xDS server.
properties:
@@ -3974,6 +4029,62 @@ spec:
required:
- extensionService
type: object
+ tracing:
+ description: Tracing defines properties for exporting trace data
+ to OpenTelemetry.
+ properties:
+ customTags:
+ description: CustomTags defines a list of custom tags with
+ unique tag name.
+ items:
+ description: CustomTag defines custom tags with unique tag
+ name to create tags for the active span.
+ properties:
+ literal:
+ description: Literal is a static custom tag value.
+ type: string
+ requestHeaderName:
+ description: RequestHeaderName indicates which request
+ header the label value is obtained from.
+ type: string
+ tagName:
+ description: TagName is the unique name of the custom
+ tag.
+ type: string
+ type: object
+ type: array
+ extensionService:
+ description: ExtensionService identifies the extension service
+ defining the otle-collector.
+ properties:
+ name:
+ type: string
+ namespace:
+ type: string
+ required:
+ - name
+ - namespace
+ type: object
+ includePodDetail:
+ description: IncludePodDetail defines a flag. If it is true,
+ contour will add the pod name and namespace to the span
+ of the trace. the default is true.
+ type: boolean
+ maxPathTagLength:
+ description: OverallSampling defines maximum length of the
+ request path to extract and include in the HttpUrl tag.
+ contour's default is 256.
+ format: int32
+ type: integer
+ overallSampling:
+ description: OverallSampling defines the sampling rate of
+ trace data. contour's default is 100
+ type: string
+ serviceName:
+ description: ServiceName defines the name for the service.
+ contour's default is contour
+ type: string
+ type: object
xdsServer:
description: XDSServer contains parameters for the xDS server.
properties:
diff --git a/examples/render/contour.yaml b/examples/render/contour.yaml
index 3089ef047a7..5ed5a810edd 100644
--- a/examples/render/contour.yaml
+++ b/examples/render/contour.yaml
@@ -859,6 +859,61 @@ spec:
required:
- extensionService
type: object
+ tracing:
+ description: Tracing defines properties for exporting trace data to
+ OpenTelemetry.
+ properties:
+ customTags:
+ description: CustomTags defines a list of custom tags with unique
+ tag name.
+ items:
+ description: CustomTag defines custom tags with unique tag name
+ to create tags for the active span.
+ properties:
+ literal:
+ description: Literal is a static custom tag value.
+ type: string
+ requestHeaderName:
+ description: RequestHeaderName indicates which request header
+ the label value is obtained from.
+ type: string
+ tagName:
+ description: TagName is the unique name of the custom tag.
+ type: string
+ type: object
+ type: array
+ extensionService:
+ description: ExtensionService identifies the extension service
+ defining the otle-collector.
+ properties:
+ name:
+ type: string
+ namespace:
+ type: string
+ required:
+ - name
+ - namespace
+ type: object
+ includePodDetail:
+ description: IncludePodDetail defines a flag. If it is true, contour
+ will add the pod name and namespace to the span of the trace.
+ the default is true.
+ type: boolean
+ maxPathTagLength:
+ description: OverallSampling defines maximum length of the request
+ path to extract and include in the HttpUrl tag. contour's default
+ is 256.
+ format: int32
+ type: integer
+ overallSampling:
+ description: OverallSampling defines the sampling rate of trace
+ data. contour's default is 100
+ type: string
+ serviceName:
+ description: ServiceName defines the name for the service. contour's
+ default is contour
+ type: string
+ type: object
xdsServer:
description: XDSServer contains parameters for the xDS server.
properties:
@@ -3968,6 +4023,62 @@ spec:
required:
- extensionService
type: object
+ tracing:
+ description: Tracing defines properties for exporting trace data
+ to OpenTelemetry.
+ properties:
+ customTags:
+ description: CustomTags defines a list of custom tags with
+ unique tag name.
+ items:
+ description: CustomTag defines custom tags with unique tag
+ name to create tags for the active span.
+ properties:
+ literal:
+ description: Literal is a static custom tag value.
+ type: string
+ requestHeaderName:
+ description: RequestHeaderName indicates which request
+ header the label value is obtained from.
+ type: string
+ tagName:
+ description: TagName is the unique name of the custom
+ tag.
+ type: string
+ type: object
+ type: array
+ extensionService:
+ description: ExtensionService identifies the extension service
+ defining the otle-collector.
+ properties:
+ name:
+ type: string
+ namespace:
+ type: string
+ required:
+ - name
+ - namespace
+ type: object
+ includePodDetail:
+ description: IncludePodDetail defines a flag. If it is true,
+ contour will add the pod name and namespace to the span
+ of the trace. the default is true.
+ type: boolean
+ maxPathTagLength:
+ description: OverallSampling defines maximum length of the
+ request path to extract and include in the HttpUrl tag.
+ contour's default is 256.
+ format: int32
+ type: integer
+ overallSampling:
+ description: OverallSampling defines the sampling rate of
+ trace data. contour's default is 100
+ type: string
+ serviceName:
+ description: ServiceName defines the name for the service.
+ contour's default is contour
+ type: string
+ type: object
xdsServer:
description: XDSServer contains parameters for the xDS server.
properties:
diff --git a/internal/dag/dag.go b/internal/dag/dag.go
index e06d3018988..cbe641fb6eb 100644
--- a/internal/dag/dag.go
+++ b/internal/dag/dag.go
@@ -729,6 +729,44 @@ type SecureVirtualHost struct {
// JWTProviders specify how to verify JWTs.
JWTProviders []JWTProvider
+
+ // Tracing defines custom tags and the service to send trace data
+ Tracing TracingConfig
+}
+
+type TracingConfig struct {
+ ExtensionService types.NamespacedName
+
+ ServiceName string
+
+ SNI string
+
+ Timeout timeout.Setting
+
+ OverallSampling float64
+
+ MaxPathTagLength uint32
+
+ CustomTags []*CustomTag
+}
+
+type CustomTag struct {
+ // TagName is the unique name of the custom tag.
+ TagName string `json:"tagName"`
+
+ // Literal is a static custom tag value.
+ // +optional
+ Literal string `json:"literal"`
+
+ // EnvironmentName indicates that the label value is obtained
+ // from the environment variable.
+ // +optional
+ EnvironmentName string `json:"environment"`
+
+ // RequestHeaderName indicates which request header
+ // the label value is obtained from.
+ // +optional
+ RequestHeaderName string `json:"requestHeaderName"`
}
type JWTProvider struct {
diff --git a/internal/envoy/v3/listener.go b/internal/envoy/v3/listener.go
index f4a2caef4ae..5629f17ec86 100644
--- a/internal/envoy/v3/listener.go
+++ b/internal/envoy/v3/listener.go
@@ -165,6 +165,7 @@ type httpConnectionManagerBuilder struct {
serverHeaderTransformation http.HttpConnectionManager_ServerHeaderTransformation
forwardClientCertificate *dag.ClientCertificateDetails
numTrustedHops uint32
+ tracingConfig *http.HttpConnectionManager_Tracing
}
// RouteConfigName sets the name of the RDS element that contains
@@ -401,6 +402,14 @@ func (b *httpConnectionManagerBuilder) AddFilter(f *http.HttpFilter) *httpConnec
return b
}
+func (b *httpConnectionManagerBuilder) Tracing(tracing *http.HttpConnectionManager_Tracing) *httpConnectionManagerBuilder {
+ if tracing == nil {
+ return b
+ }
+ b.tracingConfig = tracing
+ return b
+}
+
// Validate runs builtin validation rules against the current builder state.
func (b *httpConnectionManagerBuilder) Validate() error {
@@ -442,6 +451,7 @@ func (b *httpConnectionManagerBuilder) Get() *envoy_listener_v3.Filter {
ConfigSource: ConfigSource("contour"),
},
},
+ Tracing: b.tracingConfig,
HttpFilters: b.filters,
CommonHttpProtocolOptions: &envoy_core_v3.HttpProtocolOptions{
IdleTimeout: envoy.Timeout(b.connectionIdleTimeout),
diff --git a/internal/envoy/v3/tracing.go b/internal/envoy/v3/tracing.go
new file mode 100644
index 00000000000..382590f2f2b
--- /dev/null
+++ b/internal/envoy/v3/tracing.go
@@ -0,0 +1,93 @@
+// Copyright Project Contour Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package v3
+
+import (
+ envoy_config_trace_v3 "github.com/envoyproxy/go-control-plane/envoy/config/trace/v3"
+ http "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
+ envoy_trace_v3 "github.com/envoyproxy/go-control-plane/envoy/type/tracing/v3"
+ envoy_type "github.com/envoyproxy/go-control-plane/envoy/type/v3"
+ "github.com/projectcontour/contour/internal/dag"
+ "github.com/projectcontour/contour/internal/protobuf"
+ "google.golang.org/protobuf/types/known/wrapperspb"
+)
+
+// TracingConfig returns a tracing config,
+// or nil if config is nil.
+func TracingConfig(tracing *dag.TracingConfig) *http.HttpConnectionManager_Tracing {
+ if tracing == nil {
+ return nil
+ }
+
+ var customTags []*envoy_trace_v3.CustomTag
+ for _, tag := range tracing.CustomTags {
+ if traceCustomTag := customTag(tag); traceCustomTag != nil {
+ customTags = append(customTags, traceCustomTag)
+ }
+ }
+
+ return &http.HttpConnectionManager_Tracing{
+ OverallSampling: &envoy_type.Percent{
+ Value: tracing.OverallSampling,
+ },
+ MaxPathTagLength: wrapperspb.UInt32(tracing.MaxPathTagLength),
+ CustomTags: customTags,
+ Provider: &envoy_config_trace_v3.Tracing_Http{
+ Name: "envoy.tracers.opentelemetry",
+ ConfigType: &envoy_config_trace_v3.Tracing_Http_TypedConfig{
+ TypedConfig: protobuf.MustMarshalAny(&envoy_config_trace_v3.OpenTelemetryConfig{
+ GrpcService: GrpcService(dag.ExtensionClusterName(tracing.ExtensionService), tracing.SNI, tracing.Timeout),
+ ServiceName: tracing.ServiceName,
+ }),
+ },
+ },
+ }
+}
+
+func customTag(tag *dag.CustomTag) *envoy_trace_v3.CustomTag {
+ if tag == nil {
+ return nil
+ }
+ if tag.Literal != "" {
+ return &envoy_trace_v3.CustomTag{
+ Tag: tag.TagName,
+ Type: &envoy_trace_v3.CustomTag_Literal_{
+ Literal: &envoy_trace_v3.CustomTag_Literal{
+ Value: tag.Literal,
+ },
+ },
+ }
+ }
+ if tag.EnvironmentName != "" {
+ return &envoy_trace_v3.CustomTag{
+ Tag: tag.TagName,
+ Type: &envoy_trace_v3.CustomTag_Environment_{
+ Environment: &envoy_trace_v3.CustomTag_Environment{
+ Name: tag.EnvironmentName,
+ },
+ },
+ }
+ }
+ if tag.RequestHeaderName != "" {
+ return &envoy_trace_v3.CustomTag{
+ Tag: tag.TagName,
+ Type: &envoy_trace_v3.CustomTag_RequestHeader{
+ RequestHeader: &envoy_trace_v3.CustomTag_Header{
+ Name: tag.RequestHeaderName,
+ },
+ },
+ }
+ }
+ return nil
+}
diff --git a/internal/envoy/v3/tracing_test.go b/internal/envoy/v3/tracing_test.go
new file mode 100644
index 00000000000..8bd09c58521
--- /dev/null
+++ b/internal/envoy/v3/tracing_test.go
@@ -0,0 +1,193 @@
+// Copyright Project Contour Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package v3
+
+import (
+ "testing"
+ "time"
+
+ envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+ envoy_config_trace_v3 "github.com/envoyproxy/go-control-plane/envoy/config/trace/v3"
+ http "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
+ envoy_trace_v3 "github.com/envoyproxy/go-control-plane/envoy/type/tracing/v3"
+ envoy_type_v3 "github.com/envoyproxy/go-control-plane/envoy/type/v3"
+ "github.com/projectcontour/contour/internal/dag"
+ "github.com/projectcontour/contour/internal/k8s"
+ "github.com/projectcontour/contour/internal/protobuf"
+ "github.com/projectcontour/contour/internal/timeout"
+ "github.com/stretchr/testify/assert"
+ "google.golang.org/protobuf/types/known/durationpb"
+ "google.golang.org/protobuf/types/known/wrapperspb"
+)
+
+func TestTracingConfig(t *testing.T) {
+ tests := map[string]struct {
+ tracing *dag.TracingConfig
+ want *http.HttpConnectionManager_Tracing
+ }{
+ "nil config": {
+ tracing: nil,
+ want: nil,
+ },
+ "normal config": {
+ tracing: &dag.TracingConfig{
+ ExtensionService: k8s.NamespacedNameFrom("projectcontour/otel-collector"),
+ ServiceName: "contour",
+ SNI: "some-server.com",
+ Timeout: timeout.DurationSetting(5 * time.Second),
+ OverallSampling: 100,
+ MaxPathTagLength: 256,
+ CustomTags: []*dag.CustomTag{
+ {
+ TagName: "literal",
+ Literal: "this is literal",
+ },
+ {
+ TagName: "podName",
+ EnvironmentName: "HOSTNAME",
+ },
+ {
+ TagName: "requestHeaderName",
+ RequestHeaderName: ":path",
+ },
+ },
+ },
+ want: &http.HttpConnectionManager_Tracing{
+ OverallSampling: &envoy_type_v3.Percent{
+ Value: 100.0,
+ },
+ MaxPathTagLength: wrapperspb.UInt32(256),
+ CustomTags: []*envoy_trace_v3.CustomTag{
+ {
+ Tag: "literal",
+ Type: &envoy_trace_v3.CustomTag_Literal_{
+ Literal: &envoy_trace_v3.CustomTag_Literal{
+ Value: "this is literal",
+ },
+ },
+ },
+ {
+ Tag: "podName",
+ Type: &envoy_trace_v3.CustomTag_Environment_{
+ Environment: &envoy_trace_v3.CustomTag_Environment{
+ Name: "HOSTNAME",
+ },
+ },
+ },
+ {
+ Tag: "requestHeaderName",
+ Type: &envoy_trace_v3.CustomTag_RequestHeader{
+ RequestHeader: &envoy_trace_v3.CustomTag_Header{
+ Name: ":path",
+ },
+ },
+ },
+ },
+ Provider: &envoy_config_trace_v3.Tracing_Http{
+ Name: "envoy.tracers.opentelemetry",
+ ConfigType: &envoy_config_trace_v3.Tracing_Http_TypedConfig{
+ TypedConfig: protobuf.MustMarshalAny(&envoy_config_trace_v3.OpenTelemetryConfig{
+ GrpcService: &envoy_core_v3.GrpcService{
+ TargetSpecifier: &envoy_core_v3.GrpcService_EnvoyGrpc_{
+ EnvoyGrpc: &envoy_core_v3.GrpcService_EnvoyGrpc{
+ ClusterName: "extension/projectcontour/otel-collector",
+ Authority: "some-server.com",
+ },
+ },
+ Timeout: durationpb.New(5 * time.Second),
+ },
+ ServiceName: "contour",
+ }),
+ },
+ },
+ },
+ },
+ "no custom tag": {
+ tracing: &dag.TracingConfig{
+ ExtensionService: k8s.NamespacedNameFrom("projectcontour/otel-collector"),
+ ServiceName: "contour",
+ SNI: "some-server.com",
+ Timeout: timeout.DurationSetting(5 * time.Second),
+ OverallSampling: 100,
+ MaxPathTagLength: 256,
+ CustomTags: nil,
+ },
+ want: &http.HttpConnectionManager_Tracing{
+ OverallSampling: &envoy_type_v3.Percent{
+ Value: 100.0,
+ },
+ MaxPathTagLength: wrapperspb.UInt32(256),
+ CustomTags: nil,
+ Provider: &envoy_config_trace_v3.Tracing_Http{
+ Name: "envoy.tracers.opentelemetry",
+ ConfigType: &envoy_config_trace_v3.Tracing_Http_TypedConfig{
+ TypedConfig: protobuf.MustMarshalAny(&envoy_config_trace_v3.OpenTelemetryConfig{
+ GrpcService: &envoy_core_v3.GrpcService{
+ TargetSpecifier: &envoy_core_v3.GrpcService_EnvoyGrpc_{
+ EnvoyGrpc: &envoy_core_v3.GrpcService_EnvoyGrpc{
+ ClusterName: "extension/projectcontour/otel-collector",
+ Authority: "some-server.com",
+ },
+ },
+ Timeout: durationpb.New(5 * time.Second),
+ },
+ ServiceName: "contour",
+ }),
+ },
+ },
+ },
+ },
+ "no SNI set": {
+ tracing: &dag.TracingConfig{
+ ExtensionService: k8s.NamespacedNameFrom("projectcontour/otel-collector"),
+ ServiceName: "contour",
+ SNI: "",
+ Timeout: timeout.DurationSetting(5 * time.Second),
+ OverallSampling: 100,
+ MaxPathTagLength: 256,
+ CustomTags: nil,
+ },
+ want: &http.HttpConnectionManager_Tracing{
+ OverallSampling: &envoy_type_v3.Percent{
+ Value: 100.0,
+ },
+ MaxPathTagLength: wrapperspb.UInt32(256),
+ CustomTags: nil,
+ Provider: &envoy_config_trace_v3.Tracing_Http{
+ Name: "envoy.tracers.opentelemetry",
+ ConfigType: &envoy_config_trace_v3.Tracing_Http_TypedConfig{
+ TypedConfig: protobuf.MustMarshalAny(&envoy_config_trace_v3.OpenTelemetryConfig{
+ GrpcService: &envoy_core_v3.GrpcService{
+ TargetSpecifier: &envoy_core_v3.GrpcService_EnvoyGrpc_{
+ EnvoyGrpc: &envoy_core_v3.GrpcService_EnvoyGrpc{
+ ClusterName: "extension/projectcontour/otel-collector",
+ Authority: "extension.projectcontour.otel-collector",
+ },
+ },
+ Timeout: durationpb.New(5 * time.Second),
+ },
+ ServiceName: "contour",
+ }),
+ },
+ },
+ },
+ },
+ }
+ for name, tc := range tests {
+ t.Run(name, func(t *testing.T) {
+ got := TracingConfig(tc.tracing)
+ assert.Equal(t, tc.want, got)
+ })
+ }
+}
diff --git a/internal/featuretests/v3/tracing_test.go b/internal/featuretests/v3/tracing_test.go
new file mode 100644
index 00000000000..f18c7a75baf
--- /dev/null
+++ b/internal/featuretests/v3/tracing_test.go
@@ -0,0 +1,126 @@
+// Copyright Project Contour Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package v3
+
+import (
+ "testing"
+
+ envoy_discovery_v3 "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
+ contour_api_v1 "github.com/projectcontour/contour/apis/projectcontour/v1"
+ "github.com/projectcontour/contour/apis/projectcontour/v1alpha1"
+ "github.com/projectcontour/contour/internal/dag"
+ envoy_v3 "github.com/projectcontour/contour/internal/envoy/v3"
+ "github.com/projectcontour/contour/internal/featuretests"
+ "github.com/projectcontour/contour/internal/fixture"
+ "github.com/projectcontour/contour/internal/k8s"
+ "github.com/projectcontour/contour/internal/timeout"
+ xdscache_v3 "github.com/projectcontour/contour/internal/xdscache/v3"
+ corev1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/utils/pointer"
+)
+
+func TestTracing(t *testing.T) {
+ tracingConfig := &dag.TracingConfig{
+ ExtensionService: k8s.NamespacedNameFrom("projectcontour/otel-collector"),
+ ServiceName: "contour",
+ Timeout: timeout.DefaultSetting(),
+ OverallSampling: 100,
+ MaxPathTagLength: 256,
+ }
+ withTrace := func(conf *xdscache_v3.ListenerConfig) {
+ conf.TracingConfig = tracingConfig
+ }
+ rh, c, done := setup(t, withTrace)
+ defer done()
+
+ rh.OnAdd(fixture.NewService("projectcontour/otel-collector").
+ WithPorts(corev1.ServicePort{Port: 4317}))
+
+ rh.OnAdd(featuretests.Endpoints("projectcontour", "otel-collector", corev1.EndpointSubset{
+ Addresses: featuretests.Addresses("10.244.41.241"),
+ Ports: featuretests.Ports(featuretests.Port("", 4317)),
+ }))
+
+ rh.OnAdd(&v1alpha1.ExtensionService{
+ ObjectMeta: fixture.ObjectMeta("projectcontour/otel-collector"),
+ Spec: v1alpha1.ExtensionServiceSpec{
+ Services: []v1alpha1.ExtensionServiceTarget{
+ {Name: "otel-collector", Port: 4317},
+ },
+ Protocol: pointer.String("h2c"),
+ TimeoutPolicy: &contour_api_v1.TimeoutPolicy{
+ Response: defaultResponseTimeout.String(),
+ },
+ },
+ })
+
+ rh.OnAdd(fixture.NewService("projectcontour/app-server").
+ WithPorts(corev1.ServicePort{Port: 80}))
+
+ rh.OnAdd(featuretests.Endpoints("projectcontour", "app-server", corev1.EndpointSubset{
+ Addresses: featuretests.Addresses("10.244.184.102"),
+ Ports: featuretests.Ports(featuretests.Port("", 80)),
+ }))
+
+ p := &contour_api_v1.HTTPProxy{
+ ObjectMeta: metav1.ObjectMeta{
+ Namespace: "projectcontour",
+ Name: "app-server",
+ },
+ Spec: contour_api_v1.HTTPProxySpec{
+ VirtualHost: &contour_api_v1.VirtualHost{
+ Fqdn: "foo.com",
+ },
+ Routes: []contour_api_v1.Route{
+ {
+ Services: []contour_api_v1.Service{
+ {
+ Name: "app-server",
+ Port: 80,
+ },
+ },
+ },
+ },
+ },
+ }
+ rh.OnAdd(p)
+
+ httpListener := defaultHTTPListener()
+ httpListener.FilterChains = envoy_v3.FilterChains(envoy_v3.HTTPConnectionManagerBuilder().
+ RouteConfigName(xdscache_v3.ENVOY_HTTP_LISTENER).
+ MetricsPrefix(xdscache_v3.ENVOY_HTTP_LISTENER).
+ AccessLoggers(envoy_v3.FileAccessLogEnvoy(xdscache_v3.DEFAULT_HTTP_ACCESS_LOG, "", nil, v1alpha1.LogLevelInfo)).
+ DefaultFilters().
+ Tracing(envoy_v3.TracingConfig(tracingConfig)).
+ Get(),
+ )
+
+ c.Request(listenerType, xdscache_v3.ENVOY_HTTP_LISTENER).Equals(&envoy_discovery_v3.DiscoveryResponse{
+ TypeUrl: listenerType,
+ Resources: resources(t, httpListener),
+ })
+
+ c.Request(clusterType).Equals(&envoy_discovery_v3.DiscoveryResponse{
+ TypeUrl: clusterType,
+ Resources: resources(t,
+ DefaultCluster(
+ h2cCluster(cluster("extension/projectcontour/otel-collector", "extension/projectcontour/otel-collector", "extension_projectcontour_otel-collector")),
+ ),
+ DefaultCluster(
+ cluster("projectcontour/app-server/80/da39a3ee5e", "projectcontour/app-server", "projectcontour_app-server_80"),
+ ),
+ ),
+ })
+}
diff --git a/internal/xdscache/v3/listener.go b/internal/xdscache/v3/listener.go
index 6a24fcddc45..a942977925e 100644
--- a/internal/xdscache/v3/listener.go
+++ b/internal/xdscache/v3/listener.go
@@ -128,6 +128,10 @@ type ListenerConfig struct {
// GlobalExternalAuthConfig optionally configures the global external authorization Service to be
// used.
GlobalExternalAuthConfig *GlobalExternalAuthConfig
+
+ // TracingConfig optionally configures the tracing collector Service to be
+ // used.
+ TracingConfig *dag.TracingConfig
}
type RateLimitConfig struct {
@@ -331,8 +335,10 @@ func (c *ListenerCache) OnChange(root *dag.DAG) {
MergeSlashes(cfg.MergeSlashes).
ServerHeaderTransformation(cfg.ServerHeaderTransformation).
NumTrustedHops(cfg.XffNumTrustedHops).
+ Tracing(envoy_v3.TracingConfig(cfg.TracingConfig)).
AddFilter(envoy_v3.GlobalRateLimitFilter(envoyGlobalRateLimitConfig(cfg.RateLimitConfig))).
AddFilter(httpGlobalExternalAuthConfig(cfg.GlobalExternalAuthConfig)).
+ Tracing(envoy_v3.TracingConfig(cfg.TracingConfig)).
Get()
listeners[listener.Name] = envoy_v3.Listener(
@@ -398,6 +404,7 @@ func (c *ListenerCache) OnChange(root *dag.DAG) {
MergeSlashes(cfg.MergeSlashes).
ServerHeaderTransformation(cfg.ServerHeaderTransformation).
NumTrustedHops(cfg.XffNumTrustedHops).
+ Tracing(envoy_v3.TracingConfig(cfg.TracingConfig)).
AddFilter(envoy_v3.GlobalRateLimitFilter(envoyGlobalRateLimitConfig(cfg.RateLimitConfig))).
ForwardClientCertificate(forwardClientCertificate).
Get()
@@ -461,6 +468,7 @@ func (c *ListenerCache) OnChange(root *dag.DAG) {
MergeSlashes(cfg.MergeSlashes).
ServerHeaderTransformation(cfg.ServerHeaderTransformation).
NumTrustedHops(cfg.XffNumTrustedHops).
+ Tracing(envoy_v3.TracingConfig(cfg.TracingConfig)).
AddFilter(envoy_v3.GlobalRateLimitFilter(envoyGlobalRateLimitConfig(cfg.RateLimitConfig))).
ForwardClientCertificate(forwardClientCertificate).
Get()
diff --git a/pkg/config/parameters.go b/pkg/config/parameters.go
index d3235b002e9..1d856693b6e 100644
--- a/pkg/config/parameters.go
+++ b/pkg/config/parameters.go
@@ -560,6 +560,51 @@ type Parameters struct {
// MetricsParameters holds configurable parameters for Contour and Envoy metrics.
Metrics MetricsParameters `yaml:"metrics,omitempty"`
+
+ // Tracing holds the relevant configuration for exporting trace data to OpenTelemetry.
+ Tracing Tracing `yaml:"tracing,omitempty"`
+}
+
+// Tracing defines properties for exporting trace data to OpenTelemetry.
+type Tracing struct {
+ // IncludePodDetail defines a flag.
+ // If it is true, contour will add the pod name and namespace to the span of the trace.
+ // the default is true.
+ IncludePodDetail *bool `yaml:"includePodDetail,omitempty"`
+
+ // ServiceName defines the name for the service
+ // contour's default is contour
+ ServiceName string `yaml:"serviceName,omitempty"`
+
+ // OverallSampling defines the sampling rate of trace data.
+ // the default value is 100
+ OverallSampling float64 `yaml:"overallSampling,omitempty"`
+
+ // OverallSampling defines maximum length of the request path
+ // to extract and include in the HttpUrl tag.
+ // the default value is 256
+ MaxPathTagLength uint32 `yaml:"maxPathTagLength,omitempty"`
+
+ // CustomTags defines a list of custom tags with unique tag name.
+ CustomTags []CustomTag `yaml:"customTags,omitempty"`
+
+ // ExtensionService identifies the extension service defining the otle-collector,
+ // formatted as /.
+ ExtensionService string `yaml:"extensionService,omitempty"`
+}
+
+// CustomTag defines custom tags with unique tag name
+// to create tags for the active span.
+type CustomTag struct {
+ // TagName is the unique name of the custom tag.
+ TagName string `yaml:"tagName,omitempty"`
+
+ // Literal is a static custom tag value.
+ Literal string `yaml:"literal,omitempty"`
+
+ // RequestHeaderName indicates which request header
+ // the label value is obtained from.
+ RequestHeaderName string `yaml:"requestHeaderName,omitempty"`
}
// GlobalExternalAuthorizationConfig defines properties of global external authorization.
@@ -689,6 +734,47 @@ func (p *MetricsParameters) Validate() error {
return nil
}
+func (t *Tracing) Validate() error {
+ if t == nil {
+ return nil
+ }
+ if t.OverallSampling == 0 && t.MaxPathTagLength == 0 && t.ExtensionService == "" && t.CustomTags == nil {
+ return nil
+ }
+
+ if t.ExtensionService == "" {
+ return errors.New("tracing.extensionService must be defined")
+ }
+
+ var customTagNames []string
+
+ for _, customTag := range t.CustomTags {
+ var fieldCount int
+ if customTag.TagName == "" {
+ return errors.New("tracing.customTag.tagName must be defined")
+ }
+
+ for _, customTagName := range customTagNames {
+ if customTagName == customTag.TagName {
+ return fmt.Errorf("tagName %s is duplicate", customTagName)
+ }
+ }
+
+ if customTag.Literal != "" {
+ fieldCount++
+ }
+
+ if customTag.RequestHeaderName != "" {
+ fieldCount++
+ }
+ if fieldCount != 1 {
+ return errors.New("must set exactly one of Literal or RequestHeaderName")
+ }
+ customTagNames = append(customTagNames, customTag.TagName)
+ }
+ return nil
+}
+
func (p *MetricsServerParameters) Validate() error {
// Check that both certificate and key are provided if either one is provided.
if (p.ServerCert != "") != (p.ServerKey != "") {
@@ -770,6 +856,10 @@ func (p *Parameters) Validate() error {
return err
}
+ if err := p.Tracing.Validate(); err != nil {
+ return err
+ }
+
return p.Listener.Validate()
}
@@ -817,6 +907,7 @@ func Defaults() Parameters {
Listener: ListenerParameters{
ConnectionBalancer: "",
},
+ Tracing: Tracing{},
}
}
diff --git a/pkg/config/parameters_test.go b/pkg/config/parameters_test.go
index d136eea3467..3ad68b7f3c0 100644
--- a/pkg/config/parameters_test.go
+++ b/pkg/config/parameters_test.go
@@ -21,6 +21,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
+ "k8s.io/utils/pointer"
)
func TestGetenvOr(t *testing.T) {
@@ -492,3 +493,80 @@ func TestListenerValidation(t *testing.T) {
}
require.Error(t, l.Validate())
}
+
+func TestTracingConfigValidation(t *testing.T) {
+ var trace *Tracing
+ require.NoError(t, trace.Validate())
+
+ trace = &Tracing{
+ IncludePodDetail: pointer.Bool(false),
+ ServiceName: "contour",
+ OverallSampling: 100,
+ MaxPathTagLength: 256,
+ CustomTags: nil,
+ ExtensionService: "projectcontour/otel-collector",
+ }
+ require.NoError(t, trace.Validate())
+
+ trace = &Tracing{
+ IncludePodDetail: pointer.Bool(false),
+ ServiceName: "contour",
+ OverallSampling: 100,
+ MaxPathTagLength: 256,
+ CustomTags: nil,
+ }
+ require.Error(t, trace.Validate())
+
+ trace = &Tracing{
+ IncludePodDetail: pointer.Bool(false),
+ OverallSampling: 100,
+ MaxPathTagLength: 256,
+ CustomTags: nil,
+ ExtensionService: "projectcontour/otel-collector",
+ }
+ require.NoError(t, trace.Validate())
+
+ trace = &Tracing{
+ OverallSampling: 100,
+ MaxPathTagLength: 256,
+ CustomTags: []CustomTag{
+ {
+ TagName: "first",
+ Literal: "literal",
+ RequestHeaderName: ":path",
+ },
+ },
+ ExtensionService: "projectcontour/otel-collector",
+ }
+ require.Error(t, trace.Validate())
+
+ trace = &Tracing{
+ OverallSampling: 100,
+ MaxPathTagLength: 256,
+ CustomTags: []CustomTag{
+ {
+ Literal: "literal",
+ },
+ },
+ ExtensionService: "projectcontour/otel-collector",
+ }
+ require.Error(t, trace.Validate())
+
+ trace = &Tracing{
+ IncludePodDetail: pointer.Bool(true),
+ OverallSampling: 100,
+ MaxPathTagLength: 256,
+ CustomTags: []CustomTag{
+ {
+ TagName: "first",
+ Literal: "literal",
+ },
+ {
+ TagName: "first",
+ RequestHeaderName: ":path",
+ },
+ },
+ ExtensionService: "projectcontour/otel-collector",
+ }
+ require.Error(t, trace.Validate())
+}
diff --git a/site/content/docs/main/config/api-reference.html b/site/content/docs/main/config/api-reference.html
index 9905210fff6..0b17d3871b2 100644
--- a/site/content/docs/main/config/api-reference.html
+++ b/site/content/docs/main/config/api-reference.html
@@ -4790,6 +4790,20 @@ ContourConfiguration
Contour’s default is { address: “0.0.0.0”, port: 8000 }.
+
+
+tracing
+
+
+
+TracingConfig
+
+
+ |
+
+ Tracing defines properties for exporting trace data to OpenTelemetry.
+ |
+
@@ -5484,6 +5498,20 @@ ContourConfiguratio
Contour’s default is { address: “0.0.0.0”, port: 8000 }.
+
+
+tracing
+
+
+
+TracingConfig
+
+
+ |
+
+ Tracing defines properties for exporting trace data to OpenTelemetry.
+ |
+
ContourConfigurationStatus
@@ -5757,6 +5785,61 @@ ContourSettings
+CustomTag
+
+
+
CustomTag defines custom tags with unique tag name
+to create tags for the active span.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+tagName
+
+
+string
+
+ |
+
+ TagName is the unique name of the custom tag.
+ |
+
+
+
+literal
+
+
+string
+
+ |
+
+(Optional)
+ Literal is a static custom tag value.
+ |
+
+
+
+requestHeaderName
+
+
+string
+
+ |
+
+(Optional)
+ RequestHeaderName indicates which request header
+the label value is obtained from.
+ |
+
+
+
DaemonSetSettings
@@ -7311,7 +7394,8 @@
NamespacedName defines the namespace/name of the Kubernetes resource referred from the config file.
@@ -7981,6 +8065,111 @@
TimeoutParameters
+TracingConfig
+
+
+(Appears on:
+ContourConfigurationSpec)
+
+
+
TracingConfig defines properties for exporting trace data to OpenTelemetry.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+includePodDetail
+
+
+bool
+
+ |
+
+(Optional)
+ IncludePodDetail defines a flag.
+If it is true, contour will add the pod name and namespace to the span of the trace.
+the default is true.
+ |
+
+
+
+serviceName
+
+
+string
+
+ |
+
+ ServiceName defines the name for the service.
+contour’s default is contour
+ |
+
+
+
+overallSampling
+
+
+string
+
+ |
+
+(Optional)
+ OverallSampling defines the sampling rate of trace data.
+contour’s default is 100
+ |
+
+
+
+maxPathTagLength
+
+
+uint32
+
+ |
+
+(Optional)
+ OverallSampling defines maximum length of the request path
+to extract and include in the HttpUrl tag.
+contour’s default is 256.
+ |
+
+
+
+customTags
+
+
+
+[]*github.com/projectcontour/contour/apis/projectcontour/v1alpha1.CustomTag
+
+
+ |
+
+(Optional)
+ CustomTags defines a list of custom tags with unique tag name.
+ |
+
+
+
+extensionService
+
+
+
+NamespacedName
+
+
+ |
+
+ ExtensionService identifies the extension service defining the otle-collector.
+ |
+
+
+
WorkloadType
(string
alias)