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 }