github.com/grafana/synthetic-monitoring-agent@v0.64.2/internal/prober/multihttp/multihttp.go (about)

     1  package multihttp
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"net/http"
     7  	"strings"
     8  
     9  	"github.com/grafana/synthetic-monitoring-agent/internal/secrets"
    10  
    11  	"github.com/grafana/synthetic-monitoring-agent/internal/k6runner"
    12  	"github.com/grafana/synthetic-monitoring-agent/internal/model"
    13  	"github.com/grafana/synthetic-monitoring-agent/internal/prober/logger"
    14  	sm "github.com/grafana/synthetic-monitoring-agent/pkg/pb/synthetic_monitoring"
    15  	"github.com/prometheus/client_golang/prometheus"
    16  	"github.com/rs/zerolog"
    17  )
    18  
    19  const proberName = "multihttp"
    20  
    21  var errUnsupportedCheck = errors.New("unsupported check")
    22  
    23  type Module struct {
    24  	Prober string
    25  	Script k6runner.Script
    26  }
    27  
    28  type Prober struct {
    29  	logger           zerolog.Logger
    30  	module           Module
    31  	processor        *k6runner.Processor
    32  	secretsRetriever func(context.Context) (k6runner.SecretStore, error)
    33  }
    34  
    35  func NewProber(ctx context.Context, check model.Check, logger zerolog.Logger, runner k6runner.Runner, reservedHeaders http.Header, store secrets.SecretProvider) (Prober, error) {
    36  	var p Prober
    37  
    38  	if check.Settings.Multihttp == nil {
    39  		return p, errUnsupportedCheck
    40  	}
    41  
    42  	if err := check.Settings.Multihttp.Validate(); err != nil {
    43  		return p, err
    44  	}
    45  
    46  	if len(reservedHeaders) > 0 {
    47  		augmentHttpHeaders(&check.Check, reservedHeaders)
    48  	}
    49  
    50  	script, err := settingsToScript(check.Settings.Multihttp)
    51  	if err != nil {
    52  		return p, err
    53  	}
    54  
    55  	// Use latest k6 version available.
    56  	// TestSettingsToScript verifies that multihttp implementation is
    57  	// compatible with each supported k6 version.
    58  	const multiHTTPManifest = "*"
    59  
    60  	p.module = Module{
    61  		Prober: sm.CheckTypeMultiHttp.String(),
    62  		Script: k6runner.Script{
    63  			Script: script,
    64  			Settings: k6runner.Settings{
    65  				Timeout: check.Timeout,
    66  			},
    67  			CheckInfo:         k6runner.CheckInfoFromSM(check),
    68  			K6ChannelManifest: multiHTTPManifest,
    69  		},
    70  	}
    71  
    72  	processor, err := k6runner.NewProcessor(p.module.Script, runner)
    73  	if err != nil {
    74  		return p, err
    75  	}
    76  
    77  	logger.Debug().
    78  		Int64("tenantId", check.TenantId).
    79  		Int64("checkId", check.Id).
    80  		Str("target", check.Target).
    81  		Str("job", check.Job).
    82  		Bytes("script", script).
    83  		Msg("created prober")
    84  
    85  	p.processor = processor
    86  	p.logger = logger
    87  	p.secretsRetriever = newCredentialsRetriever(store, check.GlobalTenantID(), p.logger)
    88  
    89  	return p, nil
    90  }
    91  
    92  func (p Prober) Name() string {
    93  	return proberName
    94  }
    95  
    96  func (p Prober) Probe(ctx context.Context, target string, registry *prometheus.Registry, logger logger.Logger, executionID string) (bool, float64) {
    97  	secretStore, err := p.secretsRetriever(ctx)
    98  	if err != nil {
    99  		p.logger.Error().Err(err).Msg("running probe")
   100  		return false, 0
   101  	}
   102  
   103  	success, duration, err := p.processor.Run(ctx, registry, logger, p.logger, secretStore, executionID)
   104  	if err != nil {
   105  		p.logger.Error().Err(err).Msg("running probe")
   106  		return false, 0
   107  	}
   108  
   109  	return success, duration.Seconds()
   110  }
   111  
   112  // Overrides any user-provided headers with our own augmented values
   113  // for 'reserved' headers.
   114  func augmentHttpHeaders(check *sm.Check, reservedHeaders http.Header) {
   115  	updatedHeaders := []*sm.HttpHeader{}
   116  	for key, values := range reservedHeaders {
   117  		updatedHeaders = append(updatedHeaders, &sm.HttpHeader{Name: key, Value: strings.Join(values, ",")})
   118  	}
   119  
   120  	for _, entry := range check.Settings.Multihttp.Entries {
   121  		heads := entry.Request.Headers
   122  		for _, headerPtr := range heads {
   123  			_, present := reservedHeaders[http.CanonicalHeaderKey(headerPtr.Name)]
   124  
   125  			if present {
   126  				continue // users can't override reserved headers with their own values
   127  			}
   128  
   129  			updatedHeaders = append(updatedHeaders, headerPtr)
   130  		}
   131  
   132  		entry.Request.Headers = updatedHeaders
   133  	}
   134  }
   135  
   136  func newCredentialsRetriever(provider secrets.SecretProvider, tenantID model.GlobalID, logger zerolog.Logger) func(context.Context) (k6runner.SecretStore, error) {
   137  	return func(ctx context.Context) (k6runner.SecretStore, error) {
   138  		var store k6runner.SecretStore
   139  
   140  		logger.Debug().
   141  			Int64("tenantId", int64(tenantID)).
   142  			Msg("credentials retriever: getting secret credentials")
   143  
   144  		credentials, err := provider.GetSecretCredentials(ctx, tenantID)
   145  		if err != nil {
   146  			logger.Error().
   147  				Err(err).
   148  				Int64("tenantId", int64(tenantID)).
   149  				Msg("credentials retriever: failed to get secret credentials")
   150  
   151  			return store, err
   152  		}
   153  
   154  		if credentials != nil {
   155  			store = k6runner.SecretStore{
   156  				Url:   credentials.Url,
   157  				Token: credentials.Token,
   158  			}
   159  			logger.Debug().
   160  				Int64("tenantId", int64(tenantID)).
   161  				Str("secretStoreUrl", credentials.Url).
   162  				Bool("hasSecretStoreToken", credentials.Token != "").
   163  				Float64("secretStoreExpiry", credentials.Expiry).
   164  				Msg("credentials retriever: secret store configured")
   165  		} else {
   166  			logger.Debug().
   167  				Int64("tenantId", int64(tenantID)).
   168  				Msg("credentials retriever: no secret store configuration")
   169  		}
   170  
   171  		return store, nil
   172  	}
   173  }