From d75e3896d0bd64050648c6357d038a63977f036f Mon Sep 17 00:00:00 2001 From: Jason Harley Date: Thu, 29 Feb 2024 15:09:03 -0500 Subject: [PATCH 1/3] feat: add Classic Ingest Key support --- otlp/common.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/otlp/common.go b/otlp/common.go index 6e052dc..5979c73 100644 --- a/otlp/common.go +++ b/otlp/common.go @@ -48,7 +48,8 @@ const ( const fieldSizeMax = math.MaxUint16 var ( - legacyApiKeyPattern = regexp.MustCompile("^[0-9a-f]{32}$") + legacyApiKeyPattern = regexp.MustCompile("^[0-9a-f]*$") + classicIngestKeyPattern = regexp.MustCompile("^hc[a-z]ic_[0-9a-z]*$") // Incoming OpenTelemetry HTTP Content-Types (e.g. "application/protobuf") we support supportedContentTypes = []string{ "application/protobuf", @@ -87,6 +88,18 @@ func IsContentTypeSupported(contentType string) bool { return false } +// IsLegacyApiKey checks if the given API key is a legacy (classic) API key. +func IsLegacyApiKey(key string) bool { + if len(key) == 0 { + return true + } else if len(key) == 32 { + return legacyApiKeyPattern.MatchString(key) + } else if len(key) == 64 { + return classicIngestKeyPattern.MatchString(key) + } + return false +} + // List of HTTP Content Encodings supported for OTLP ingest. func GetSupportedContentEncodings() []string { return supportedContentEncodings @@ -127,7 +140,7 @@ type RequestInfo struct { } func (ri RequestInfo) hasLegacyKey() bool { - return legacyApiKeyPattern.MatchString(ri.ApiKey) + return IsLegacyApiKey(ri.ApiKey) } // ValidateTracesHeaders validates required headers/metadata for a trace OTLP request From 0165313a991f3f1d96b6c9bd3bda33df79842eea Mon Sep 17 00:00:00 2001 From: Jason Harley Date: Thu, 29 Feb 2024 16:29:19 -0500 Subject: [PATCH 2/3] legacy -> classic --- otlp/common.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/otlp/common.go b/otlp/common.go index 5979c73..b7006b6 100644 --- a/otlp/common.go +++ b/otlp/common.go @@ -48,7 +48,7 @@ const ( const fieldSizeMax = math.MaxUint16 var ( - legacyApiKeyPattern = regexp.MustCompile("^[0-9a-f]*$") + classicApiKeyPattern = regexp.MustCompile("^[0-9a-f]*$") classicIngestKeyPattern = regexp.MustCompile("^hc[a-z]ic_[0-9a-z]*$") // Incoming OpenTelemetry HTTP Content-Types (e.g. "application/protobuf") we support supportedContentTypes = []string{ @@ -88,12 +88,12 @@ func IsContentTypeSupported(contentType string) bool { return false } -// IsLegacyApiKey checks if the given API key is a legacy (classic) API key. -func IsLegacyApiKey(key string) bool { +// IsClassicApiKey checks if the given API key is a Classic API key. +func IsClassicApiKey(key string) bool { if len(key) == 0 { return true } else if len(key) == 32 { - return legacyApiKeyPattern.MatchString(key) + return classicApiKeyPattern.MatchString(key) } else if len(key) == 64 { return classicIngestKeyPattern.MatchString(key) } @@ -139,8 +139,8 @@ type RequestInfo struct { GRPCAcceptEncoding string } -func (ri RequestInfo) hasLegacyKey() bool { - return IsLegacyApiKey(ri.ApiKey) +func (ri RequestInfo) hasClassicKey() bool { + return IsClassicApiKey(ri.ApiKey) } // ValidateTracesHeaders validates required headers/metadata for a trace OTLP request @@ -151,7 +151,7 @@ func (ri *RequestInfo) ValidateTracesHeaders() error { if len(ri.ApiKey) == 0 { return ErrMissingAPIKeyHeader } - if ri.hasLegacyKey() && len(ri.Dataset) == 0 { + if ri.hasClassicKey() && len(ri.Dataset) == 0 { return ErrMissingDatasetHeader } return nil // no error, headers passed all the validations @@ -165,7 +165,7 @@ func (ri *RequestInfo) ValidateMetricsHeaders() error { if len(ri.ApiKey) == 0 { return ErrMissingAPIKeyHeader } - if ri.hasLegacyKey() && len(ri.Dataset) == 0 { + if ri.hasClassicKey() && len(ri.Dataset) == 0 { return ErrMissingDatasetHeader } return nil // no error, headers passed all the validations @@ -325,7 +325,7 @@ func isInstrumentationLibrary(libraryName string) bool { func getDataset(ri RequestInfo, attrs map[string]interface{}) string { var dataset string - if ri.hasLegacyKey() { + if ri.hasClassicKey() { dataset = ri.Dataset } else { serviceName, ok := attrs["service.name"].(string) From b93a94a4ce467040a1d9f88115b25cf9b05c3add Mon Sep 17 00:00:00 2001 From: Jason Harley Date: Fri, 1 Mar 2024 10:11:41 -0500 Subject: [PATCH 3/3] flesh out tests and remove len=0 case --- otlp/common.go | 4 +--- otlp/common_test.go | 12 ++++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/otlp/common.go b/otlp/common.go index b7006b6..6c6b714 100644 --- a/otlp/common.go +++ b/otlp/common.go @@ -90,9 +90,7 @@ func IsContentTypeSupported(contentType string) bool { // IsClassicApiKey checks if the given API key is a Classic API key. func IsClassicApiKey(key string) bool { - if len(key) == 0 { - return true - } else if len(key) == 32 { + if len(key) == 32 { return classicApiKeyPattern.MatchString(key) } else if len(key) == 64 { return classicIngestKeyPattern.MatchString(key) diff --git a/otlp/common_test.go b/otlp/common_test.go index dfbef6d..d070a1a 100644 --- a/otlp/common_test.go +++ b/otlp/common_test.go @@ -123,9 +123,13 @@ func TestValidateTracesHeaders(t *testing.T) { {name: "no key, no dataset", apikey: "", dataset: "", contentType: "application/protobuf", err: ErrMissingAPIKeyHeader}, {name: "no key, dataset present", apikey: "", dataset: "dataset", contentType: "application/protobuf", err: ErrMissingAPIKeyHeader}, {name: "classic/no dataset", apikey: "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1", dataset: "", contentType: "application/protobuf", err: ErrMissingDatasetHeader}, + {name: "classic ingest key/no dataset", apikey: "hcxic_1234567890123456789012345678901234567890123456789012345678", dataset: "", contentType: "application/protobuf", err: ErrMissingDatasetHeader}, {name: "classic/dataset present", apikey: "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1", dataset: "dataset", contentType: "application/protobuf", err: nil}, + {name: "classic ingest key/dataset present", apikey: "hcxic_1234567890123456789012345678901234567890123456789012345678", dataset: "dataset", contentType: "application/protobuf", err: nil}, {name: "E&S/no dataset", apikey: "abc123DEF456ghi789jklm", dataset: "", contentType: "application/protobuf", err: nil}, + {name: "E&S ingest key/no dataset", apikey: "hcxik_1234567890123456789012345678901234567890123456789012345678", dataset: "", contentType: "application/protobuf", err: nil}, {name: "E&S/dataset present", apikey: "abc123DEF456ghi789jklm", dataset: "dataset", contentType: "application/protobuf", err: nil}, + {name: "E&S ingest key/dataset present", apikey: "hcxik_1234567890123456789012345678901234567890123456789012345678", dataset: "dataset", contentType: "application/protobuf", err: nil}, {name: "content-type/(missing)", apikey: "apikey", dataset: "dataset", contentType: "", err: ErrInvalidContentType}, {name: "content-type/javascript", apikey: "apikey", dataset: "dataset", contentType: "application/javascript", err: ErrInvalidContentType}, {name: "content-type/xml", apikey: "apikey", dataset: "dataset", contentType: "application/xml", err: ErrInvalidContentType}, @@ -161,10 +165,14 @@ func TestValidateMetricsHeaders(t *testing.T) { {name: "no key, dataset present", apikey: "", dataset: "dataset", contentType: "application/protobuf", err: ErrMissingAPIKeyHeader}, // classic environments need to tell us which dataset to put metrics in {name: "classic/no dataset", apikey: "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1", dataset: "", contentType: "application/protobuf", err: ErrMissingDatasetHeader}, + {name: "classic ingest key/no dataset", apikey: "hcxic_1234567890123456789012345678901234567890123456789012345678", dataset: "", contentType: "application/protobuf", err: ErrMissingDatasetHeader}, {name: "classic/dataset present", apikey: "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1", dataset: "dataset", contentType: "application/protobuf", err: nil}, + {name: "classic ingest key/dataset present", apikey: "hcxic_1234567890123456789012345678901234567890123456789012345678", dataset: "dataset", contentType: "application/protobuf", err: nil}, // dataset header not required for E&S, there's a fallback {name: "E&S/no dataset", apikey: "abc123DEF456ghi789jklm", dataset: "", contentType: "application/protobuf", err: nil}, + {name: "E&S ingest key/no dataset", apikey: "hcxik_1234567890123456789012345678901234567890123456789012345678", dataset: "", contentType: "application/protobuf", err: nil}, {name: "E&S/dataset present", apikey: "abc123DEF456ghi789jklm", dataset: "dataset", contentType: "application/protobuf", err: nil}, + {name: "E&S ingest key/dataset present", apikey: "hcxik_1234567890123456789012345678901234567890123456789012345678", dataset: "dataset", contentType: "application/protobuf", err: nil}, {name: "content-type/(missing)", apikey: "apikey", dataset: "dataset", contentType: "", err: ErrInvalidContentType}, {name: "content-type/javascript", apikey: "apikey", dataset: "dataset", contentType: "application/javascript", err: ErrInvalidContentType}, {name: "content-type/xml", apikey: "apikey", dataset: "dataset", contentType: "application/xml", err: ErrInvalidContentType}, @@ -201,9 +209,13 @@ func TestValidateLogsHeaders(t *testing.T) { // logs will use dataset header if present, but log ingest will also use service.name in the data // and we will have a sensible default if neither are present, so a missing dataset header is not an error here {name: "classic/no dataset", apikey: "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1", dataset: "", contentType: "application/protobuf", err: nil}, + {name: "classic ingest key/no dataset", apikey: "hcxic_1234567890123456789012345678901234567890123456789012345678", dataset: "", contentType: "application/protobuf", err: nil}, {name: "classic/dataset present", apikey: "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1", dataset: "dataset", contentType: "application/protobuf", err: nil}, + {name: "classic ingest key/dataset present", apikey: "hcxic_1234567890123456789012345678901234567890123456789012345678", dataset: "dataset", contentType: "application/protobuf", err: nil}, {name: "E&S/no dataset", apikey: "abc123DEF456ghi789jklm", dataset: "", contentType: "application/protobuf", err: nil}, + {name: "E&S ingest key/no dataset", apikey: "hcxik_1234567890123456789012345678901234567890123456789012345678", dataset: "", contentType: "application/protobuf", err: nil}, {name: "E&S/dataset present", apikey: "abc123DEF456ghi789jklm", dataset: "dataset", contentType: "application/protobuf", err: nil}, + {name: "E&S ingest key/dataset present", apikey: "hcxik_1234567890123456789012345678901234567890123456789012345678", dataset: "dataset", contentType: "application/protobuf", err: nil}, {name: "content-type/(missing)", apikey: "apikey", dataset: "dataset", contentType: "", err: ErrInvalidContentType}, {name: "content-type/javascript", apikey: "apikey", dataset: "dataset", contentType: "application/javascript", err: ErrInvalidContentType}, {name: "content-type/xml", apikey: "apikey", dataset: "dataset", contentType: "application/xml", err: ErrInvalidContentType},