Skip to content

Commit 9854fa5

Browse files
authored
Import config v2 HTTP settings (#2467)
* Import config v2 HTTP settings Map the exported v2 HTTP route, payload extraction, enrichment, and application filter shapes back into the runtime config import path. This keeps stacked import parity moving without wiring the v2 shape into runtime loading or expanding unsupported schema behavior. * Validate v2 HTTP import inputs Reject unknown HTTP payload extractor names during v2 runtime import so misspelled enabled entries fail with a precise config path instead of being ignored. Import one-sided HTTP application filters and reject conflicting trace and metric filter maps, avoiding silent no-ops when a partial config supplies only one signal. * Use errors.New for static import error Replace a static fmt.Errorf call in the v2 HTTP import validation path with errors.New so golangci-lint accepts the new validation code.
1 parent 878ce46 commit 9854fa5

2 files changed

Lines changed: 503 additions & 12 deletions

File tree

internal/config/convert/import.go

Lines changed: 277 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package convert // import "go.opentelemetry.io/obi/internal/config/convert"
55

66
import (
7+
"errors"
78
"fmt"
89
"reflect"
910
"slices"
@@ -39,6 +40,12 @@ func V2ToRuntime(src *schema.Extension) (*obi.Config, error) {
3940
if err := validateV2RulePatterns(src.Capture.Rules); err != nil {
4041
return nil, err
4142
}
43+
if err := validateV2HTTPFilters(src.Capture.Instrumentation.HTTP.Filters); err != nil {
44+
return nil, err
45+
}
46+
if err := validateV2HTTPPayloadExtraction(src.Capture.Instrumentation.HTTP.PayloadExtraction); err != nil {
47+
return nil, err
48+
}
4249

4350
cfg := runtimeConfigDefaults()
4451
applyV2Capture(&cfg, src)
@@ -187,6 +194,52 @@ func validateV2RulePatterns(rules []schema.Rule) error {
187194
return nil
188195
}
189196

197+
func validateV2HTTPFilters(filters schema.SignalFilters) error {
198+
if len(filters.Traces) == 0 || len(filters.Metrics) == 0 {
199+
return nil
200+
}
201+
if reflect.DeepEqual(filters.Traces, filters.Metrics) {
202+
return nil
203+
}
204+
return errors.New("capture.instrumentation.http.filters: trace and metric filters cannot differ")
205+
}
206+
207+
func validateV2HTTPPayloadExtraction(payload schema.PayloadExtraction) error {
208+
for i, extractor := range payload.Enabled {
209+
if !validV2HTTPPayloadExtractor(extractor) {
210+
return fmt.Errorf(
211+
"capture.instrumentation.http.payload_extraction.enabled[%d]: unknown payload extractor %q",
212+
i,
213+
extractor,
214+
)
215+
}
216+
}
217+
return nil
218+
}
219+
220+
func validV2HTTPPayloadExtractor(extractor string) bool {
221+
switch extractor {
222+
case payloadExtractorGraphQL,
223+
payloadExtractorElasticsearch,
224+
payloadExtractorAWS,
225+
payloadExtractorSQLPP,
226+
payloadExtractorOpenAI,
227+
payloadExtractorAnthropic,
228+
payloadExtractorGemini,
229+
payloadExtractorQwen,
230+
payloadExtractorBedrock,
231+
payloadExtractorMCP,
232+
payloadExtractorEmbedding,
233+
payloadExtractorRerank,
234+
payloadExtractorRetrieval,
235+
payloadExtractorJSONRPC,
236+
payloadExtractorEnrichment:
237+
return true
238+
default:
239+
return false
240+
}
241+
}
242+
190243
func validateV2RuleProcessGlobPatterns(path string, match schema.RuleProcessMatch) error {
191244
if err := validateGlobAttr(path+".language_glob", match.LanguageGlob); err != nil {
192245
return err
@@ -623,9 +676,7 @@ func applyV2Instrumentation(cfg *obi.Config, instrumentation schema.Instrumentat
623676
}
624677

625678
func applyFullV2Instrumentation(cfg *obi.Config, instrumentation schema.Instrumentation) {
626-
cfg.EBPF.TrackRequestHeaders = instrumentation.HTTP.TrackRequestHeaders
627-
cfg.EBPF.HTTPRequestTimeout = instrumentation.HTTP.RequestTimeout.TimeDuration()
628-
cfg.EBPF.BufferSizes.HTTP = instrumentation.HTTP.BufferSize
679+
applyFullV2HTTPInstrumentation(cfg, instrumentation.HTTP)
629680

630681
cfg.EBPF.HeuristicSQLDetect = instrumentation.SQL.HeuristicDetect
631682
cfg.EBPF.BufferSizes.MySQL = instrumentation.SQL.MySQL.BufferSize
@@ -649,15 +700,7 @@ func applyFullV2Instrumentation(cfg *obi.Config, instrumentation schema.Instrume
649700

650701
func applyPartialV2Instrumentation(cfg *obi.Config, instrumentation schema.Instrumentation) {
651702
if !zeroValue(instrumentation.HTTP) {
652-
if instrumentation.HTTP.TrackRequestHeaders {
653-
cfg.EBPF.TrackRequestHeaders = true
654-
}
655-
if !zeroValue(instrumentation.HTTP.RequestTimeout) {
656-
cfg.EBPF.HTTPRequestTimeout = instrumentation.HTTP.RequestTimeout.TimeDuration()
657-
}
658-
if instrumentation.HTTP.BufferSize != 0 {
659-
cfg.EBPF.BufferSizes.HTTP = instrumentation.HTTP.BufferSize
660-
}
703+
applyPartialV2HTTPInstrumentation(cfg, instrumentation.HTTP)
661704
}
662705

663706
if !zeroValue(instrumentation.SQL) {
@@ -713,6 +756,214 @@ func applyPartialV2Instrumentation(cfg *obi.Config, instrumentation schema.Instr
713756
}
714757
}
715758

759+
func applyFullV2HTTPInstrumentation(cfg *obi.Config, http schema.HTTPInstrumentation) {
760+
cfg.EBPF.TrackRequestHeaders = http.TrackRequestHeaders
761+
cfg.EBPF.HTTPRequestTimeout = http.RequestTimeout.TimeDuration()
762+
cfg.EBPF.BufferSizes.HTTP = http.BufferSize
763+
764+
applyV2HTTPFilters(cfg, http.Filters, true)
765+
applyFullV2HTTPRoutes(cfg, http.Routes)
766+
applyFullV2HTTPPayloadExtraction(cfg, http.PayloadExtraction)
767+
}
768+
769+
func applyPartialV2HTTPInstrumentation(cfg *obi.Config, http schema.HTTPInstrumentation) {
770+
if http.TrackRequestHeaders {
771+
cfg.EBPF.TrackRequestHeaders = true
772+
}
773+
if !zeroValue(http.RequestTimeout) {
774+
cfg.EBPF.HTTPRequestTimeout = http.RequestTimeout.TimeDuration()
775+
}
776+
if http.BufferSize != 0 {
777+
cfg.EBPF.BufferSizes.HTTP = http.BufferSize
778+
}
779+
applyV2HTTPFilters(cfg, http.Filters, false)
780+
applyPartialV2HTTPRoutes(cfg, http.Routes)
781+
applyPartialV2HTTPPayloadExtraction(cfg, http.PayloadExtraction)
782+
}
783+
784+
func applyV2HTTPFilters(cfg *obi.Config, filters schema.SignalFilters, complete bool) {
785+
if zeroValue(filters) && !complete {
786+
return
787+
}
788+
cfg.Filters.Application = attributeFilterMap(v2HTTPFilterMap(filters))
789+
}
790+
791+
func v2HTTPFilterMap(filters schema.SignalFilters) schema.AttributeFilters {
792+
if len(filters.Traces) != 0 {
793+
return filters.Traces
794+
}
795+
return filters.Metrics
796+
}
797+
798+
func applyFullV2HTTPRoutes(cfg *obi.Config, routes schema.HTTPRoutes) {
799+
applyV2HTTPRouteDiscovery(cfg, routes.Discovery, true)
800+
if !hasV2HTTPRouteConfig(routes) {
801+
cfg.Routes = nil
802+
return
803+
}
804+
805+
cfg.Routes = &transform.RoutesConfig{}
806+
applyV2HTTPRouteConfig(cfg.Routes, routes)
807+
}
808+
809+
func applyPartialV2HTTPRoutes(cfg *obi.Config, routes schema.HTTPRoutes) {
810+
if zeroValue(routes) {
811+
return
812+
}
813+
applyV2HTTPRouteDiscovery(cfg, routes.Discovery, false)
814+
if !hasV2HTTPRouteConfig(routes) {
815+
return
816+
}
817+
if cfg.Routes == nil {
818+
cfg.Routes = &transform.RoutesConfig{}
819+
}
820+
applyV2HTTPRouteConfig(cfg.Routes, routes)
821+
}
822+
823+
func applyV2HTTPRouteDiscovery(cfg *obi.Config, discovery schema.HTTPRouteDiscovery, complete bool) {
824+
if zeroValue(discovery) && !complete {
825+
return
826+
}
827+
if complete || !zeroValue(discovery.Timeout) {
828+
cfg.Discovery.RouteHarvesterTimeout = discovery.Timeout.TimeDuration()
829+
}
830+
if complete || discovery.DisabledLanguages != nil {
831+
cfg.Discovery.DisabledRouteHarvesters = cloneRouteHarvesterLanguages(discovery.DisabledLanguages)
832+
}
833+
if complete || !zeroValue(discovery.Java.Delay) {
834+
cfg.Discovery.RouteHarvestConfig.JavaHarvestDelay = discovery.Java.Delay.TimeDuration()
835+
}
836+
}
837+
838+
func hasV2HTTPRouteConfig(routes schema.HTTPRoutes) bool {
839+
return routes.Unmatched != nil ||
840+
routes.Patterns != nil ||
841+
routes.IgnoredPatterns != nil ||
842+
routes.IgnoreMode != nil ||
843+
routes.WildcardChar != nil ||
844+
routes.MaxPathSegmentCardinality != nil
845+
}
846+
847+
func applyV2HTTPRouteConfig(dst *transform.RoutesConfig, routes schema.HTTPRoutes) {
848+
if routes.Unmatched != nil {
849+
dst.Unmatch = *routes.Unmatched
850+
}
851+
if routes.Patterns != nil {
852+
dst.Patterns = cloneStrings(*routes.Patterns)
853+
}
854+
if routes.IgnoredPatterns != nil {
855+
dst.IgnorePatterns = cloneStrings(*routes.IgnoredPatterns)
856+
}
857+
if routes.IgnoreMode != nil {
858+
dst.IgnoredEvents = *routes.IgnoreMode
859+
}
860+
if routes.WildcardChar != nil {
861+
dst.WildcardChar = *routes.WildcardChar
862+
}
863+
if routes.MaxPathSegmentCardinality != nil {
864+
dst.MaxPathSegmentCardinality = *routes.MaxPathSegmentCardinality
865+
}
866+
}
867+
868+
func applyFullV2HTTPPayloadExtraction(cfg *obi.Config, payload schema.PayloadExtraction) {
869+
http := &cfg.EBPF.PayloadExtraction.HTTP
870+
applyV2HTTPPayloadExtractorMembership(http, payload.Enabled)
871+
http.SQLPP.EndpointPatterns = cloneStrings(payload.SQLPP.EndpointPatterns)
872+
applyFullV2HTTPEnrichment(http, payload.Enrichment)
873+
}
874+
875+
func applyPartialV2HTTPPayloadExtraction(cfg *obi.Config, payload schema.PayloadExtraction) {
876+
if zeroValue(payload) {
877+
return
878+
}
879+
880+
http := &cfg.EBPF.PayloadExtraction.HTTP
881+
if payload.Enabled != nil {
882+
applyV2HTTPPayloadExtractorMembership(http, payload.Enabled)
883+
}
884+
if payload.SQLPP.EndpointPatterns != nil {
885+
http.SQLPP.EndpointPatterns = cloneStrings(payload.SQLPP.EndpointPatterns)
886+
}
887+
if !zeroValue(payload.Enrichment) {
888+
applyPartialV2HTTPEnrichment(http, payload.Enrichment)
889+
}
890+
}
891+
892+
func applyV2HTTPPayloadExtractorMembership(http *obiconfig.HTTPConfig, enabled []string) {
893+
http.GraphQL.Enabled = false
894+
http.Elasticsearch.Enabled = false
895+
http.AWS.Enabled = false
896+
http.SQLPP.Enabled = false
897+
http.GenAI.OpenAI.Enabled = false
898+
http.GenAI.Anthropic.Enabled = false
899+
http.GenAI.Gemini.Enabled = false
900+
http.GenAI.Qwen.Enabled = false
901+
http.GenAI.Bedrock.Enabled = false
902+
http.GenAI.MCP.Enabled = false
903+
http.GenAI.Embedding.Enabled = false
904+
http.GenAI.Rerank.Enabled = false
905+
http.GenAI.Retrieval.Enabled = false
906+
http.JSONRPC.Enabled = false
907+
http.Enrichment.Enabled = false
908+
909+
for _, extractor := range enabled {
910+
switch extractor {
911+
case payloadExtractorGraphQL:
912+
http.GraphQL.Enabled = true
913+
case payloadExtractorElasticsearch:
914+
http.Elasticsearch.Enabled = true
915+
case payloadExtractorAWS:
916+
http.AWS.Enabled = true
917+
case payloadExtractorSQLPP:
918+
http.SQLPP.Enabled = true
919+
case payloadExtractorOpenAI:
920+
http.GenAI.OpenAI.Enabled = true
921+
case payloadExtractorAnthropic:
922+
http.GenAI.Anthropic.Enabled = true
923+
case payloadExtractorGemini:
924+
http.GenAI.Gemini.Enabled = true
925+
case payloadExtractorQwen:
926+
http.GenAI.Qwen.Enabled = true
927+
case payloadExtractorBedrock:
928+
http.GenAI.Bedrock.Enabled = true
929+
case payloadExtractorMCP:
930+
http.GenAI.MCP.Enabled = true
931+
case payloadExtractorEmbedding:
932+
http.GenAI.Embedding.Enabled = true
933+
case payloadExtractorRerank:
934+
http.GenAI.Rerank.Enabled = true
935+
case payloadExtractorRetrieval:
936+
http.GenAI.Retrieval.Enabled = true
937+
case payloadExtractorJSONRPC:
938+
http.JSONRPC.Enabled = true
939+
case payloadExtractorEnrichment:
940+
http.Enrichment.Enabled = true
941+
}
942+
}
943+
}
944+
945+
func applyFullV2HTTPEnrichment(http *obiconfig.HTTPConfig, enrichment schema.HTTPEnrichment) {
946+
http.Enrichment.Policy.DefaultAction.Headers = enrichment.Policy.DefaultAction.Headers
947+
http.Enrichment.Policy.DefaultAction.Body = enrichment.Policy.DefaultAction.Body
948+
http.Enrichment.Policy.ObfuscationString = enrichment.Policy.ObfuscationString
949+
http.Enrichment.Rules = cloneHTTPParsingRules(enrichment.Rules)
950+
}
951+
952+
func applyPartialV2HTTPEnrichment(http *obiconfig.HTTPConfig, enrichment schema.HTTPEnrichment) {
953+
if !zeroValue(enrichment.Policy.DefaultAction.Headers) {
954+
http.Enrichment.Policy.DefaultAction.Headers = enrichment.Policy.DefaultAction.Headers
955+
}
956+
if !zeroValue(enrichment.Policy.DefaultAction.Body) {
957+
http.Enrichment.Policy.DefaultAction.Body = enrichment.Policy.DefaultAction.Body
958+
}
959+
if enrichment.Policy.ObfuscationString != "" {
960+
http.Enrichment.Policy.ObfuscationString = enrichment.Policy.ObfuscationString
961+
}
962+
if enrichment.Rules != nil {
963+
http.Enrichment.Rules = cloneHTTPParsingRules(enrichment.Rules)
964+
}
965+
}
966+
716967
func applyProtocolEnablement(cfg *obi.Config, instrumentation schema.Instrumentation, complete bool) {
717968
cfg.Traces.Instrumentations = applySignalEnablement(cfg.Traces.Instrumentations, instrumentation, "traces", complete)
718969
cfg.OTELMetrics.Instrumentations = applySignalEnablement(cfg.OTELMetrics.Instrumentations, instrumentation, "metrics", complete)
@@ -1437,6 +1688,20 @@ func cloneStrings(values []string) []string {
14371688
return append([]string(nil), values...)
14381689
}
14391690

1691+
func cloneRouteHarvesterLanguages(values []services.RouteHarvesterLanguage) []services.RouteHarvesterLanguage {
1692+
if values == nil {
1693+
return nil
1694+
}
1695+
return append([]services.RouteHarvesterLanguage(nil), values...)
1696+
}
1697+
1698+
func cloneHTTPParsingRules(values []obiconfig.HTTPParsingRule) []obiconfig.HTTPParsingRule {
1699+
if values == nil {
1700+
return nil
1701+
}
1702+
return append([]obiconfig.HTTPParsingRule(nil), values...)
1703+
}
1704+
14401705
type runtimeCIDRDefinition interface {
14411706
~struct {
14421707
CIDR string `yaml:"cidr" json:"cidr"`

0 commit comments

Comments
 (0)