feat: OpenTelemetry module integration (#9062)
* OpenTelemetry module integration * e2e test * e2e test fix * default OpentelemetryConfig * e2e values * mount otel module for otel test only * propagate IS_CHROOT * propagate IS_CHROOT e2e test * code doc * comments * golint * opentelemetry doc * zipkin * zipkin * typo * update e2e test OpenTelemetry value * use opentelemetry value * revert merge conflict * fix * format * review comments * clean
This commit is contained in:
parent
c075793ae5
commit
c8cb9167d3
23 changed files with 1131 additions and 2 deletions
|
|
@ -265,6 +265,7 @@ var (
|
|||
"buildAuthSignURL": buildAuthSignURL,
|
||||
"buildAuthSignURLLocation": buildAuthSignURLLocation,
|
||||
"buildOpentracing": buildOpentracing,
|
||||
"buildOpentelemetry": buildOpentelemetry,
|
||||
"proxySetHeader": proxySetHeader,
|
||||
"buildInfluxDB": buildInfluxDB,
|
||||
"enforceRegexModifier": enforceRegexModifier,
|
||||
|
|
@ -274,7 +275,9 @@ var (
|
|||
"buildHTTPListener": buildHTTPListener,
|
||||
"buildHTTPSListener": buildHTTPSListener,
|
||||
"buildOpentracingForLocation": buildOpentracingForLocation,
|
||||
"buildOpentelemetryForLocation": buildOpentelemetryForLocation,
|
||||
"shouldLoadOpentracingModule": shouldLoadOpentracingModule,
|
||||
"shouldLoadOpentelemetryModule": shouldLoadOpentelemetryModule,
|
||||
"buildModSecurityForLocation": buildModSecurityForLocation,
|
||||
"buildMirrorLocations": buildMirrorLocations,
|
||||
"shouldLoadAuthDigestModule": shouldLoadAuthDigestModule,
|
||||
|
|
@ -1239,6 +1242,33 @@ func buildOpentracing(c interface{}, s interface{}) string {
|
|||
return buf.String()
|
||||
}
|
||||
|
||||
func buildOpentelemetry(c interface{}, s interface{}) string {
|
||||
cfg, ok := c.(config.Configuration)
|
||||
if !ok {
|
||||
klog.Errorf("expected a 'config.Configuration' type but %T was returned", c)
|
||||
return ""
|
||||
}
|
||||
|
||||
servers, ok := s.([]*ingress.Server)
|
||||
if !ok {
|
||||
klog.Errorf("expected an '[]*ingress.Server' type but %T was returned", s)
|
||||
return ""
|
||||
}
|
||||
|
||||
if !shouldLoadOpentelemetryModule(cfg, servers) {
|
||||
return ""
|
||||
}
|
||||
|
||||
buf := bytes.NewBufferString("")
|
||||
|
||||
buf.WriteString("\r\n")
|
||||
|
||||
if cfg.OpentelemetryOperationName != "" {
|
||||
buf.WriteString(fmt.Sprintf("opentelemetry_operation_name \"%s\";\n", cfg.OpentelemetryOperationName))
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// buildInfluxDB produces the single line configuration
|
||||
// needed by the InfluxDB module to send request's metrics
|
||||
// for the current resource
|
||||
|
|
@ -1360,6 +1390,13 @@ func opentracingPropagateContext(location *ingress.Location) string {
|
|||
return "opentracing_propagate_context;"
|
||||
}
|
||||
|
||||
func opentelemetryPropagateContext(location *ingress.Location) string {
|
||||
if location == nil {
|
||||
return ""
|
||||
}
|
||||
return "opentelemetry_propagate;"
|
||||
}
|
||||
|
||||
// shouldLoadModSecurityModule determines whether or not the ModSecurity module needs to be loaded.
|
||||
// First, it checks if `enable-modsecurity` is set in the ConfigMap. If it is not, it iterates over all locations to
|
||||
// check if ModSecurity is enabled by the annotation `nginx.ingress.kubernetes.io/enable-modsecurity`.
|
||||
|
|
@ -1575,6 +1612,36 @@ func buildOpentracingForLocation(isOTEnabled bool, isOTTrustSet bool, location *
|
|||
return opc
|
||||
}
|
||||
|
||||
func buildOpentelemetryForLocation(isOTEnabled bool, isOTTrustSet bool, location *ingress.Location) string {
|
||||
isOTEnabledInLoc := location.Opentelemetry.Enabled
|
||||
isOTSetInLoc := location.Opentelemetry.Set
|
||||
|
||||
if isOTEnabled {
|
||||
if isOTSetInLoc && !isOTEnabledInLoc {
|
||||
return "opentelemetry off;"
|
||||
}
|
||||
} else if !isOTSetInLoc || !isOTEnabledInLoc {
|
||||
return ""
|
||||
}
|
||||
|
||||
opc := opentelemetryPropagateContext(location)
|
||||
if opc != "" {
|
||||
opc = fmt.Sprintf("opentelemetry on;\n%v", opc)
|
||||
}
|
||||
|
||||
if location.Opentelemetry.OperationName != "" {
|
||||
opc = opc + "\nopentelemetry_operation_name " + location.Opentelemetry.OperationName + ";"
|
||||
}
|
||||
|
||||
if (!isOTTrustSet && !location.Opentelemetry.TrustSet) ||
|
||||
(location.Opentelemetry.TrustSet && !location.Opentelemetry.TrustEnabled) {
|
||||
opc = opc + "\nopentelemetry_trust_incoming_spans off;"
|
||||
} else {
|
||||
opc = opc + "\nopentelemetry_trust_incoming_spans on;"
|
||||
}
|
||||
return opc
|
||||
}
|
||||
|
||||
// shouldLoadOpentracingModule determines whether or not the Opentracing module needs to be loaded.
|
||||
// First, it checks if `enable-opentracing` is set in the ConfigMap. If it is not, it iterates over all locations to
|
||||
// check if Opentracing is enabled by the annotation `nginx.ingress.kubernetes.io/enable-opentracing`.
|
||||
|
|
@ -1606,6 +1673,35 @@ func shouldLoadOpentracingModule(c interface{}, s interface{}) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// shouldLoadOpentelemetryModule determines whether or not the Opentelemetry module needs to be loaded.
|
||||
// It checks if `enable-opentelemetry` is set in the ConfigMap.
|
||||
func shouldLoadOpentelemetryModule(c interface{}, s interface{}) bool {
|
||||
cfg, ok := c.(config.Configuration)
|
||||
if !ok {
|
||||
klog.Errorf("expected a 'config.Configuration' type but %T was returned", c)
|
||||
return false
|
||||
}
|
||||
|
||||
servers, ok := s.([]*ingress.Server)
|
||||
if !ok {
|
||||
klog.Errorf("expected an '[]*ingress.Server' type but %T was returned", s)
|
||||
return false
|
||||
}
|
||||
|
||||
if cfg.EnableOpentelemetry {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, server := range servers {
|
||||
for _, location := range server.Locations {
|
||||
if location.Opentelemetry.Enabled {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func buildModSecurityForLocation(cfg config.Configuration, location *ingress.Location) string {
|
||||
isMSEnabledInLoc := location.ModSecurity.Enable
|
||||
isMSEnableSetInLoc := location.ModSecurity.EnableSet
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import (
|
|||
"k8s.io/ingress-nginx/internal/ingress/annotations/authreq"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/influxdb"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/modsecurity"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/opentelemetry"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/opentracing"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/ratelimit"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/rewrite"
|
||||
|
|
@ -1150,6 +1151,26 @@ func TestOpentracingPropagateContext(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestOpentelemetryPropagateContext(t *testing.T) {
|
||||
tests := map[*ingress.Location]string{
|
||||
{BackendProtocol: "HTTP"}: "opentelemetry_propagate;",
|
||||
{BackendProtocol: "HTTPS"}: "opentelemetry_propagate;",
|
||||
{BackendProtocol: "AUTO_HTTP"}: "opentelemetry_propagate;",
|
||||
{BackendProtocol: "GRPC"}: "opentelemetry_propagate;",
|
||||
{BackendProtocol: "GRPCS"}: "opentelemetry_propagate;",
|
||||
{BackendProtocol: "AJP"}: "opentelemetry_propagate;",
|
||||
{BackendProtocol: "FCGI"}: "opentelemetry_propagate;",
|
||||
nil: "",
|
||||
}
|
||||
|
||||
for loc, expectedDirective := range tests {
|
||||
actualDirective := opentelemetryPropagateContext(loc)
|
||||
if actualDirective != expectedDirective {
|
||||
t.Errorf("Expected %v but returned %v", expectedDirective, actualDirective)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetIngressInformation(t *testing.T) {
|
||||
|
||||
testcases := map[string]struct {
|
||||
|
|
@ -1723,6 +1744,37 @@ func TestBuildOpenTracing(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
func TestBuildOpenTelemetry(t *testing.T) {
|
||||
invalidType := &ingress.Ingress{}
|
||||
expected := ""
|
||||
actual := buildOpentelemetry(invalidType, []*ingress.Server{})
|
||||
|
||||
if expected != actual {
|
||||
t.Errorf("Expected '%v' but returned '%v'", expected, actual)
|
||||
}
|
||||
|
||||
cfgNoHost := config.Configuration{
|
||||
EnableOpentelemetry: true,
|
||||
}
|
||||
expected = "\r\n"
|
||||
actual = buildOpentelemetry(cfgNoHost, []*ingress.Server{})
|
||||
|
||||
if expected != actual {
|
||||
t.Errorf("Expected '%v' but returned '%v'", expected, actual)
|
||||
}
|
||||
|
||||
cfgOpenTelemetry := config.Configuration{
|
||||
EnableOpentelemetry: true,
|
||||
OpentelemetryOperationName: "my-operation-name",
|
||||
}
|
||||
expected = "\r\n"
|
||||
expected += "opentelemetry_operation_name \"my-operation-name\";\n"
|
||||
actual = buildOpentelemetry(cfgOpenTelemetry, []*ingress.Server{})
|
||||
if expected != actual {
|
||||
t.Errorf("Expected '%v' but returned '%v'", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnforceRegexModifier(t *testing.T) {
|
||||
invalidType := &ingress.Ingress{}
|
||||
expected := false
|
||||
|
|
@ -1903,6 +1955,107 @@ func TestShouldLoadOpentracingModule(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestOpentelemetryForLocation(t *testing.T) {
|
||||
trueVal := true
|
||||
falseVal := false
|
||||
|
||||
loadOT := `opentelemetry on;
|
||||
opentelemetry_propagate;
|
||||
opentelemetry_trust_incoming_spans on;`
|
||||
loadOTUntrustedSpan := `opentelemetry on;
|
||||
opentelemetry_propagate;
|
||||
opentelemetry_trust_incoming_spans off;`
|
||||
testCases := []struct {
|
||||
description string
|
||||
globalOT bool
|
||||
isSetInLoc bool
|
||||
isOTInLoc *bool
|
||||
globalTrust bool
|
||||
isTrustSetInLoc bool
|
||||
isTrustInLoc *bool
|
||||
expected string
|
||||
}{
|
||||
{"globally enabled, without annotation", true, false, nil, true, false, nil, loadOT},
|
||||
{"globally enabled and enabled in location", true, true, &trueVal, true, false, nil, loadOT},
|
||||
{"globally disabled and not enabled in location", false, false, nil, true, false, nil, ""},
|
||||
{"globally disabled but enabled in location", false, true, &trueVal, true, false, nil, loadOT},
|
||||
{"globally trusted, not trusted in location", true, false, nil, true, true, &falseVal, loadOTUntrustedSpan},
|
||||
{"not globally trusted, trust set in location", true, false, nil, false, true, &trueVal, loadOT},
|
||||
{"not globally trusted, trust not set in location", true, false, nil, false, false, nil, loadOTUntrustedSpan},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
il := &ingress.Location{
|
||||
Opentelemetry: opentelemetry.Config{Set: testCase.isSetInLoc, TrustSet: testCase.isTrustSetInLoc},
|
||||
}
|
||||
if il.Opentelemetry.Set {
|
||||
il.Opentelemetry.Enabled = *testCase.isOTInLoc
|
||||
}
|
||||
if il.Opentelemetry.TrustSet {
|
||||
il.Opentelemetry.TrustEnabled = *testCase.isTrustInLoc
|
||||
}
|
||||
|
||||
actual := buildOpentelemetryForLocation(testCase.globalOT, testCase.globalTrust, il)
|
||||
|
||||
if testCase.expected != actual {
|
||||
t.Errorf("%v: expected '%v' but returned '%v'", testCase.description, testCase.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestShouldLoadOpentelemetryModule(t *testing.T) {
|
||||
// ### Invalid argument type tests ###
|
||||
// The first tests should return false.
|
||||
expected := false
|
||||
|
||||
invalidType := &ingress.Ingress{}
|
||||
actual := shouldLoadOpentelemetryModule(config.Configuration{}, invalidType)
|
||||
if expected != actual {
|
||||
t.Errorf("Expected '%v' but returned '%v'", expected, actual)
|
||||
}
|
||||
|
||||
actual = shouldLoadOpentelemetryModule(invalidType, []*ingress.Server{})
|
||||
if expected != actual {
|
||||
t.Errorf("Expected '%v' but returned '%v'", expected, actual)
|
||||
}
|
||||
|
||||
// ### Functional tests ###
|
||||
actual = shouldLoadOpentelemetryModule(config.Configuration{}, []*ingress.Server{})
|
||||
if expected != actual {
|
||||
t.Errorf("Expected '%v' but returned '%v'", expected, actual)
|
||||
}
|
||||
|
||||
// All further tests should return true.
|
||||
expected = true
|
||||
|
||||
configuration := config.Configuration{EnableOpentelemetry: true}
|
||||
actual = shouldLoadOpentelemetryModule(configuration, []*ingress.Server{})
|
||||
if expected != actual {
|
||||
t.Errorf("Expected '%v' but returned '%v'", expected, actual)
|
||||
}
|
||||
|
||||
servers := []*ingress.Server{
|
||||
{
|
||||
Locations: []*ingress.Location{
|
||||
{
|
||||
Opentelemetry: opentelemetry.Config{
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
actual = shouldLoadOpentelemetryModule(config.Configuration{}, servers)
|
||||
if expected != actual {
|
||||
t.Errorf("Expected '%v' but returned '%v'", expected, actual)
|
||||
}
|
||||
|
||||
actual = shouldLoadOpentelemetryModule(configuration, servers)
|
||||
if expected != actual {
|
||||
t.Errorf("Expected '%v' but returned '%v'", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestModSecurityForLocation(t *testing.T) {
|
||||
loadModule := `modsecurity on;
|
||||
`
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue