internal/sourcemap/sourcemap_fetcher.go (62 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" "fmt" "net/url" "github.com/go-sourcemap/sourcemap" "github.com/elastic/apm-server/internal/logs" "github.com/elastic/elastic-agent-libs/logp" ) type SourcemapFetcher struct { metadata MetadataFetcher backend Fetcher logger *logp.Logger } func NewSourcemapFetcher(metadata MetadataFetcher, backend Fetcher, logger *logp.Logger) *SourcemapFetcher { s := &SourcemapFetcher{ metadata: metadata, backend: backend, logger: logger.Named(logs.Sourcemap), } return s } func (s *SourcemapFetcher) Fetch(ctx context.Context, name, version, path string) (*sourcemap.Consumer, error) { original := identifier{name: name, version: version, path: path} select { case <-s.metadata.ready(): if err := s.metadata.err(); err != nil { return nil, err } case <-ctx.Done(): return nil, fmt.Errorf("error waiting for metadata fetcher to be ready: %w", ctx.Err()) default: return nil, fmt.Errorf("metadata fetcher is not ready: %w", errFetcherUnvailable) } if i, ok := s.metadata.getID(original); ok { // Only fetch from ES if the sourcemap id exists return s.fetch(ctx, i) } if urlPath, err := url.Parse(path); err == nil { // The sourcemap coule be stored in ES with a relative // bundle filepath but the request came in with an // absolute path original.path = urlPath.Path if urlPath.Path != path { // The sourcemap could be stored on ES under a certain host // but a request came in from a different host. // Look for an alias to the url path to retrieve the correct // host and fetch the sourcemap if i, ok := s.metadata.getID(original); ok { return s.fetch(ctx, i) } } // Clean the url and try again if the result is different from // the original bundle filepath urlPath.RawQuery = "" urlPath.Fragment = "" urlPath = urlPath.JoinPath() cleanPath := urlPath.String() if cleanPath != path { s.logger.Debugf("original filepath %s converted to %s", path, cleanPath) return s.Fetch(ctx, name, version, cleanPath) } } return nil, fmt.Errorf("unable to find sourcemap.url for service.name=%s service.version=%s bundle.path=%s", name, version, path) } func (s *SourcemapFetcher) fetch(ctx context.Context, key *identifier) (*sourcemap.Consumer, error) { c, err := s.backend.Fetch(ctx, key.name, key.version, key.path) // log a message if the sourcemap is present in the cache but the backend fetcher did not // find it. if err == nil && c == nil { return nil, fmt.Errorf("unable to find sourcemap for service.name=%s service.version=%s bundle.path=%s", key.name, key.version, key.path) } return c, err }