internal/sourcemap/kibana.go (64 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you 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 sourcemap import ( "context" "encoding/json" "fmt" "io" "net/http" "net/url" "github.com/go-sourcemap/sourcemap" "github.com/elastic/elastic-agent-libs/logp" "github.com/elastic/apm-server/internal/kibana" "github.com/elastic/apm-server/internal/logs" ) const sourcemapArtifactType = "sourcemap" type kibanaFetcher struct { client *kibana.Client logger *logp.Logger } type kibanaSourceMapArtifact struct { Type string `json:"type"` Body struct { ServiceName string `json:"serviceName"` ServiceVersion string `json:"serviceVersion"` BundleFilepath string `json:"bundleFilepath"` SourceMap json.RawMessage `json:"sourceMap"` } `json:"body"` } // NewKibanaFetcher returns a Fetcher that fetches source maps stored by Kibana. func NewKibanaFetcher(c *kibana.Client, logger *logp.Logger) Fetcher { return &kibanaFetcher{c, logger.Named(logs.Sourcemap)} } // Fetch fetches a source map from Kibana. func (s *kibanaFetcher) Fetch(ctx context.Context, name, version, path string) (*sourcemap.Consumer, error) { resp, err := s.client.Send(ctx, "GET", "/api/apm/sourcemaps", nil, nil, nil) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) return nil, fmt.Errorf("failed to query source maps (%s): %s", resp.Status, body) } var result struct { Artifacts []kibanaSourceMapArtifact `json:"artifacts"` } if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return nil, err } path = maybeParseURLPath(path) for _, a := range result.Artifacts { if a.Type != sourcemapArtifactType { continue } if a.Body.ServiceName == name && a.Body.ServiceVersion == version && maybeParseURLPath(a.Body.BundleFilepath) == path { return parseSourceMap(a.Body.SourceMap) } } return nil, nil } // maybeParseURLPath attempts to parse s as a URL, returning its path component // if successful. If s cannot be parsed as a URL, s is returned. func maybeParseURLPath(s string) string { url, err := url.Parse(s) if err != nil { return s } return url.Path }