diff --git a/prober/dns.go b/prober/dns.go index 6bf2e4650..951694f00 100644 --- a/prober/dns.go +++ b/prober/dns.go @@ -124,7 +124,7 @@ func validRcode(rcode int, valid []string, logger log.Logger) bool { return false } -func ProbeDNS(ctx context.Context, target string, module config.Module, registry *prometheus.Registry, logger log.Logger) bool { +func ProbeDNS(ctx context.Context, opts probeOpts, module config.Module, registry *prometheus.Registry, logger log.Logger) bool { var dialProtocol string probeDNSDurationGaugeVec := prometheus.NewGaugeVec(prometheus.GaugeOpts{ Name: "probe_dns_duration_seconds", @@ -187,7 +187,7 @@ func ProbeDNS(ctx context.Context, target string, module config.Module, registry return false } - targetAddr, port, err := net.SplitHostPort(target) + targetAddr, port, err := net.SplitHostPort(opts.target) if err != nil { // Target only contains host so fallback to default port and set targetAddr as target. if module.DNS.DNSOverTLS { @@ -195,7 +195,7 @@ func ProbeDNS(ctx context.Context, target string, module config.Module, registry } else { port = "53" } - targetAddr = target + targetAddr = opts.target } ip, lookupTime, err := chooseProtocol(ctx, module.DNS.IPProtocol, module.DNS.IPProtocolFallback, targetAddr, registry, logger) if err != nil { diff --git a/prober/dns_test.go b/prober/dns_test.go index 22f702cf2..6ed92a52d 100644 --- a/prober/dns_test.go +++ b/prober/dns_test.go @@ -170,7 +170,7 @@ func TestRecursiveDNSResponse(t *testing.T) { testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result := ProbeDNS(testCTX, addr.String(), config.Module{Timeout: time.Second, DNS: test.Probe}, registry, log.NewNopLogger()) + result := ProbeDNS(testCTX, probeOpts{target: addr.String()}, config.Module{Timeout: time.Second, DNS: test.Probe}, registry, log.NewNopLogger()) if result != test.ShouldSucceed { t.Fatalf("Test %d had unexpected result: %v", i, result) } @@ -374,7 +374,7 @@ func TestAuthoritativeDNSResponse(t *testing.T) { registry := prometheus.NewRegistry() testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result := ProbeDNS(testCTX, addr.String(), config.Module{Timeout: time.Second, DNS: test.Probe}, registry, log.NewNopLogger()) + result := ProbeDNS(testCTX, probeOpts{target: addr.String()}, config.Module{Timeout: time.Second, DNS: test.Probe}, registry, log.NewNopLogger()) if result != test.ShouldSucceed { t.Fatalf("Test %d had unexpected result: %v", i, result) } @@ -449,7 +449,7 @@ func TestServfailDNSResponse(t *testing.T) { registry := prometheus.NewRegistry() testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result := ProbeDNS(testCTX, addr.String(), config.Module{Timeout: time.Second, DNS: test.Probe}, registry, log.NewNopLogger()) + result := ProbeDNS(testCTX, probeOpts{target: addr.String()}, config.Module{Timeout: time.Second, DNS: test.Probe}, registry, log.NewNopLogger()) if result != test.ShouldSucceed { t.Fatalf("Test %d had unexpected result: %v", i, result) } @@ -509,7 +509,7 @@ func TestDNSProtocol(t *testing.T) { registry := prometheus.NewRegistry() testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result := ProbeDNS(testCTX, net.JoinHostPort("localhost", port), module, registry, log.NewNopLogger()) + result := ProbeDNS(testCTX, probeOpts{target: net.JoinHostPort("localhost", port)}, module, registry, log.NewNopLogger()) if !result { t.Fatalf("DNS protocol: \"%v\", preferred \"ip6\" connection test failed, expected success.", protocol) } @@ -535,7 +535,7 @@ func TestDNSProtocol(t *testing.T) { registry = prometheus.NewRegistry() testCTX, cancel = context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result = ProbeDNS(testCTX, net.JoinHostPort("localhost", port), module, registry, log.NewNopLogger()) + result = ProbeDNS(testCTX, probeOpts{target: net.JoinHostPort("localhost", port)}, module, registry, log.NewNopLogger()) if !result { t.Fatalf("DNS protocol: \"%v\", preferred \"ip4\" connection test failed, expected success.", protocol) } @@ -561,7 +561,7 @@ func TestDNSProtocol(t *testing.T) { registry = prometheus.NewRegistry() testCTX, cancel = context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result = ProbeDNS(testCTX, net.JoinHostPort("localhost", port), module, registry, log.NewNopLogger()) + result = ProbeDNS(testCTX, probeOpts{target: net.JoinHostPort("localhost", port)}, module, registry, log.NewNopLogger()) if !result { t.Fatalf("DNS protocol: \"%v\" connection test failed, expected success.", protocol) } @@ -586,7 +586,7 @@ func TestDNSProtocol(t *testing.T) { registry = prometheus.NewRegistry() testCTX, cancel = context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result = ProbeDNS(testCTX, net.JoinHostPort("localhost", port), module, registry, log.NewNopLogger()) + result = ProbeDNS(testCTX, probeOpts{target: net.JoinHostPort("localhost", port)}, module, registry, log.NewNopLogger()) if protocol == "udp" { if !result { t.Fatalf("DNS test connection with protocol %s failed, expected success.", protocol) @@ -629,7 +629,7 @@ func TestDNSMetrics(t *testing.T) { registry := prometheus.NewRegistry() testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result := ProbeDNS(testCTX, net.JoinHostPort("localhost", port), module, registry, log.NewNopLogger()) + result := ProbeDNS(testCTX, probeOpts{target: net.JoinHostPort("localhost", port)}, module, registry, log.NewNopLogger()) if !result { t.Fatalf("DNS test connection failed, expected success.") } diff --git a/prober/grpc.go b/prober/grpc.go index 9f1493edb..d8b5951c7 100644 --- a/prober/grpc.go +++ b/prober/grpc.go @@ -15,6 +15,11 @@ package prober import ( "context" + "net" + "net/url" + "strings" + "time" + "github.com/go-kit/log" "github.com/go-kit/log/level" "github.com/prometheus/blackbox_exporter/config" @@ -27,10 +32,6 @@ import ( "google.golang.org/grpc/health/grpc_health_v1" "google.golang.org/grpc/peer" "google.golang.org/grpc/status" - "net" - "net/url" - "strings" - "time" ) type GRPCHealthCheck interface { @@ -74,7 +75,7 @@ func (c *gRPCHealthCheckClient) Check(ctx context.Context, service string) (bool return false, returnStatus.Code(), nil, "", err } -func ProbeGRPC(ctx context.Context, target string, module config.Module, registry *prometheus.Registry, logger log.Logger) (success bool) { +func ProbeGRPC(ctx context.Context, opts probeOpts, module config.Module, registry *prometheus.Registry, logger log.Logger) (success bool) { var ( durationGaugeVec = prometheus.NewGaugeVec(prometheus.GaugeOpts{ @@ -125,11 +126,11 @@ func ProbeGRPC(ctx context.Context, target string, module config.Module, registr registry.MustRegister(probeTLSVersion) registry.MustRegister(probeSSLLastInformation) - if !strings.HasPrefix(target, "http://") && !strings.HasPrefix(target, "https://") { - target = "http://" + target + if !strings.HasPrefix(opts.target, "http://") && !strings.HasPrefix(opts.target, "https://") { + opts.target = "http://" + opts.target } - targetURL, err := url.Parse(target) + targetURL, err := url.Parse(opts.target) if err != nil { level.Error(logger).Log("msg", "Could not parse target URL", "err", err) return false @@ -166,23 +167,23 @@ func ProbeGRPC(ctx context.Context, target string, module config.Module, registr targetURL.Host = net.JoinHostPort(ip.String(), targetPort) } - var opts []grpc.DialOption - target = targetHost + ":" + targetPort + var grpcOpts []grpc.DialOption + opts.target = targetHost + ":" + targetPort if !module.GRPC.TLS { level.Debug(logger).Log("msg", "Dialing GRPC without TLS") - opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) + grpcOpts = append(grpcOpts, grpc.WithTransportCredentials(insecure.NewCredentials())) if len(targetPort) == 0 { - target = targetHost + ":80" + opts.target = targetHost + ":80" } } else { creds := credentials.NewTLS(tlsConfig) - opts = append(opts, grpc.WithTransportCredentials(creds)) + grpcOpts = append(grpcOpts, grpc.WithTransportCredentials(creds)) if len(targetPort) == 0 { - target = targetHost + ":443" + opts.target = targetHost + ":443" } } - conn, err := grpc.NewClient(target, opts...) + conn, err := grpc.NewClient(opts.target, grpcOpts...) if err != nil { level.Error(logger).Log("did not connect: %v", err) diff --git a/prober/grpc_test.go b/prober/grpc_test.go index 1fd140201..b27b40996 100644 --- a/prober/grpc_test.go +++ b/prober/grpc_test.go @@ -63,7 +63,7 @@ func TestGRPCConnection(t *testing.T) { defer cancel() registry := prometheus.NewRegistry() - result := ProbeGRPC(testCTX, "localhost:"+port, + result := ProbeGRPC(testCTX, probeOpts{target: "localhost:" + port}, config.Module{Timeout: time.Second, GRPC: config.GRPCProbe{ IPProtocolFallback: false, }, @@ -129,7 +129,7 @@ func TestMultipleGRPCservices(t *testing.T) { defer cancel() registryService1 := prometheus.NewRegistry() - resultService1 := ProbeGRPC(testCTX, "localhost:"+port, + resultService1 := ProbeGRPC(testCTX, probeOpts{target: "localhost:" + port}, config.Module{Timeout: time.Second, GRPC: config.GRPCProbe{ IPProtocolFallback: false, Service: "service1", @@ -141,7 +141,7 @@ func TestMultipleGRPCservices(t *testing.T) { } registryService2 := prometheus.NewRegistry() - resultService2 := ProbeGRPC(testCTX, "localhost:"+port, + resultService2 := ProbeGRPC(testCTX, probeOpts{target: "localhost:" + port}, config.Module{Timeout: time.Second, GRPC: config.GRPCProbe{ IPProtocolFallback: false, Service: "service2", @@ -153,7 +153,7 @@ func TestMultipleGRPCservices(t *testing.T) { } registryService3 := prometheus.NewRegistry() - resultService3 := ProbeGRPC(testCTX, "localhost:"+port, + resultService3 := ProbeGRPC(testCTX, probeOpts{target: "localhost:" + port}, config.Module{Timeout: time.Second, GRPC: config.GRPCProbe{ IPProtocolFallback: false, Service: "service3", @@ -225,7 +225,7 @@ func TestGRPCTLSConnection(t *testing.T) { defer cancel() registry := prometheus.NewRegistry() - result := ProbeGRPC(testCTX, "localhost:"+port, + result := ProbeGRPC(testCTX, probeOpts{target: "localhost:" + port}, config.Module{Timeout: time.Second, GRPC: config.GRPCProbe{ TLS: true, TLSConfig: pconfig.TLSConfig{InsecureSkipVerify: true}, @@ -286,7 +286,7 @@ func TestNoTLSConnection(t *testing.T) { defer cancel() registry := prometheus.NewRegistry() - result := ProbeGRPC(testCTX, "localhost:"+port, + result := ProbeGRPC(testCTX, probeOpts{target: "localhost:" + port}, config.Module{Timeout: time.Second, GRPC: config.GRPCProbe{ TLS: true, TLSConfig: pconfig.TLSConfig{InsecureSkipVerify: true}, @@ -341,7 +341,7 @@ func TestGRPCServiceNotFound(t *testing.T) { defer cancel() registry := prometheus.NewRegistry() - result := ProbeGRPC(testCTX, "localhost:"+port, + result := ProbeGRPC(testCTX, probeOpts{target: "localhost:" + port}, config.Module{Timeout: time.Second, GRPC: config.GRPCProbe{ IPProtocolFallback: false, Service: "NonExistingService", @@ -391,7 +391,7 @@ func TestGRPCHealthCheckUnimplemented(t *testing.T) { defer cancel() registry := prometheus.NewRegistry() - result := ProbeGRPC(testCTX, "localhost:"+port, + result := ProbeGRPC(testCTX, probeOpts{target: "localhost:" + port}, config.Module{Timeout: time.Second, GRPC: config.GRPCProbe{ IPProtocolFallback: false, Service: "NonExistingService", diff --git a/prober/handler.go b/prober/handler.go index 180a076b0..fab66f78d 100644 --- a/prober/handler.go +++ b/prober/handler.go @@ -116,7 +116,9 @@ func Handler(w http.ResponseWriter, r *http.Request, c *config.Config, logger lo registry := prometheus.NewRegistry() registry.MustRegister(probeSuccessGauge) registry.MustRegister(probeDurationGauge) - success := prober(ctx, target, module, registry, sl) + success := prober(ctx, + probeOpts{target: target, sourceIPAddress: params.Get("sourceIPAddress")}, + module, registry, sl) duration := time.Since(start).Seconds() probeDurationGauge.Set(duration) if success { diff --git a/prober/http.go b/prober/http.go index d79e8e1c1..e99300ab2 100644 --- a/prober/http.go +++ b/prober/http.go @@ -235,7 +235,7 @@ func (bc *byteCounter) Read(p []byte) (int, error) { var userAgentDefaultHeader = fmt.Sprintf("Blackbox Exporter/%s", version.Version) -func ProbeHTTP(ctx context.Context, target string, module config.Module, registry *prometheus.Registry, logger log.Logger) (success bool) { +func ProbeHTTP(ctx context.Context, opts probeOpts, module config.Module, registry *prometheus.Registry, logger log.Logger) (success bool) { var redirects int var ( durationGaugeVec = prometheus.NewGaugeVec(prometheus.GaugeOpts{ @@ -314,11 +314,11 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr httpConfig := module.HTTP - if !strings.HasPrefix(target, "http://") && !strings.HasPrefix(target, "https://") { - target = "http://" + target + if !strings.HasPrefix(opts.target, "http://") && !strings.HasPrefix(opts.target, "https://") { + opts.target = "http://" + opts.target } - targetURL, err := url.Parse(target) + targetURL, err := url.Parse(opts.target) if err != nil { level.Error(logger).Log("msg", "Could not parse target URL", "err", err) return false diff --git a/prober/http_test.go b/prober/http_test.go index 7427458ac..a5f12842c 100644 --- a/prober/http_test.go +++ b/prober/http_test.go @@ -68,7 +68,7 @@ func TestHTTPStatusCodes(t *testing.T) { recorder := httptest.NewRecorder() testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result := ProbeHTTP(testCTX, ts.URL, + result := ProbeHTTP(testCTX, probeOpts{target: ts.URL}, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, ValidStatusCodes: test.ValidStatusCodes}}, registry, log.NewNopLogger()) body := recorder.Body.String() if result != test.ShouldSucceed { @@ -93,7 +93,7 @@ func TestValidHTTPVersion(t *testing.T) { defer ts.Close() recorder := httptest.NewRecorder() registry := prometheus.NewRegistry() - result := ProbeHTTP(context.Background(), ts.URL, + result := ProbeHTTP(context.Background(), probeOpts{target: ts.URL}, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{ IPProtocolFallback: true, ValidHTTPVersions: test.ValidHTTPVersions, @@ -235,7 +235,7 @@ func TestContentLength(t *testing.T) { registry := prometheus.NewRegistry() var logbuf bytes.Buffer result := ProbeHTTP(testCTX, - ts.URL, + probeOpts{target: ts.URL}, config.Module{ Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true}, @@ -489,7 +489,7 @@ func TestHandlingOfCompressionSetting(t *testing.T) { registry := prometheus.NewRegistry() var logbuf bytes.Buffer result := ProbeHTTP(testCTX, - ts.URL, + probeOpts{target: ts.URL}, config.Module{ Timeout: time.Second, HTTP: tc.httpConfig, @@ -605,7 +605,7 @@ func TestMaxResponseLength(t *testing.T) { result := ProbeHTTP( testCTX, - ts.URL+tc.target, + probeOpts{target: ts.URL + tc.target}, config.Module{ Timeout: time.Second, HTTP: config.HTTPProbe{ @@ -649,7 +649,8 @@ func TestRedirectFollowed(t *testing.T) { registry := prometheus.NewRegistry() testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result := ProbeHTTP(testCTX, ts.URL, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, HTTPClientConfig: pconfig.DefaultHTTPClientConfig}}, registry, log.NewNopLogger()) + + result := ProbeHTTP(testCTX, probeOpts{target: ts.URL}, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, HTTPClientConfig: pconfig.DefaultHTTPClientConfig}}, registry, log.NewNopLogger()) body := recorder.Body.String() if !result { t.Fatalf("Redirect test failed unexpectedly, got %s", body) @@ -676,7 +677,7 @@ func TestRedirectNotFollowed(t *testing.T) { registry := prometheus.NewRegistry() testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result := ProbeHTTP(testCTX, ts.URL, + result := ProbeHTTP(testCTX, probeOpts{target: ts.URL}, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, HTTPClientConfig: pconfig.HTTPClientConfig{FollowRedirects: false}, ValidStatusCodes: []int{302}}}, registry, log.NewNopLogger()) body := recorder.Body.String() if !result { @@ -723,7 +724,7 @@ func TestRedirectionLimit(t *testing.T) { result := ProbeHTTP( testCTX, - ts.URL, + probeOpts{target: ts.URL}, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, HTTPClientConfig: pconfig.DefaultHTTPClientConfig}}, registry, log.NewNopLogger()) @@ -759,7 +760,7 @@ func TestPost(t *testing.T) { registry := prometheus.NewRegistry() testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result := ProbeHTTP(testCTX, ts.URL, + result := ProbeHTTP(testCTX, probeOpts{target: ts.URL}, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, Method: "POST"}}, registry, log.NewNopLogger()) body := recorder.Body.String() if !result { @@ -776,7 +777,7 @@ func TestBasicAuth(t *testing.T) { registry := prometheus.NewRegistry() testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result := ProbeHTTP(testCTX, ts.URL, + result := ProbeHTTP(testCTX, probeOpts{target: ts.URL}, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{ IPProtocolFallback: true, HTTPClientConfig: pconfig.HTTPClientConfig{ @@ -799,7 +800,7 @@ func TestBearerToken(t *testing.T) { registry := prometheus.NewRegistry() testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result := ProbeHTTP(testCTX, ts.URL, + result := ProbeHTTP(testCTX, probeOpts{target: ts.URL}, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{ IPProtocolFallback: true, HTTPClientConfig: pconfig.HTTPClientConfig{ @@ -821,7 +822,7 @@ func TestFailIfNotSSL(t *testing.T) { registry := prometheus.NewRegistry() testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result := ProbeHTTP(testCTX, ts.URL, + result := ProbeHTTP(testCTX, probeOpts{target: ts.URL}, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, FailIfNotSSL: true}}, registry, log.NewNopLogger()) body := recorder.Body.String() if result { @@ -915,7 +916,7 @@ func TestFailIfNotSSLLogMsg(t *testing.T) { testCTX, cancel := context.WithTimeout(context.Background(), Timeout) defer cancel() - result := ProbeHTTP(testCTX, tc.URL, tc.Config, registry, &recorder) + result := ProbeHTTP(testCTX, probeOpts{target: tc.URL}, tc.Config, registry, &recorder) if result != tc.Success { t.Fatalf("Expected success=%v, got=%v", tc.Success, result) } @@ -968,7 +969,7 @@ func TestFailIfBodyMatchesRegexp(t *testing.T) { registry := prometheus.NewRegistry() testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result := ProbeHTTP(testCTX, ts.URL, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, FailIfBodyMatchesRegexp: testcase.regexps}}, registry, log.NewNopLogger()) + result := ProbeHTTP(testCTX, probeOpts{target: ts.URL}, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, FailIfBodyMatchesRegexp: testcase.regexps}}, registry, log.NewNopLogger()) if testcase.expectedResult && !result { t.Fatalf("Regexp test failed unexpectedly, got %s", recorder.Body.String()) } else if !testcase.expectedResult && result { @@ -1004,7 +1005,7 @@ func TestFailIfBodyNotMatchesRegexp(t *testing.T) { registry := prometheus.NewRegistry() testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result := ProbeHTTP(testCTX, ts.URL, + result := ProbeHTTP(testCTX, probeOpts{target: ts.URL}, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, FailIfBodyNotMatchesRegexp: []config.Regexp{config.MustNewRegexp("Download the latest version here")}}}, registry, log.NewNopLogger()) body := recorder.Body.String() if result { @@ -1018,7 +1019,7 @@ func TestFailIfBodyNotMatchesRegexp(t *testing.T) { recorder = httptest.NewRecorder() registry = prometheus.NewRegistry() - result = ProbeHTTP(testCTX, ts.URL, + result = ProbeHTTP(testCTX, probeOpts{target: ts.URL}, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, FailIfBodyNotMatchesRegexp: []config.Regexp{config.MustNewRegexp("Download the latest version here")}}}, registry, log.NewNopLogger()) body = recorder.Body.String() if !result { @@ -1034,7 +1035,7 @@ func TestFailIfBodyNotMatchesRegexp(t *testing.T) { recorder = httptest.NewRecorder() registry = prometheus.NewRegistry() - result = ProbeHTTP(testCTX, ts.URL, + result = ProbeHTTP(testCTX, probeOpts{target: ts.URL}, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, FailIfBodyNotMatchesRegexp: []config.Regexp{config.MustNewRegexp("Download the latest version here"), config.MustNewRegexp("Copyright 2015")}}}, registry, log.NewNopLogger()) body = recorder.Body.String() if result { @@ -1048,7 +1049,7 @@ func TestFailIfBodyNotMatchesRegexp(t *testing.T) { recorder = httptest.NewRecorder() registry = prometheus.NewRegistry() - result = ProbeHTTP(testCTX, ts.URL, + result = ProbeHTTP(testCTX, probeOpts{target: ts.URL}, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, FailIfBodyNotMatchesRegexp: []config.Regexp{config.MustNewRegexp("Download the latest version here"), config.MustNewRegexp("Copyright 2015")}}}, registry, log.NewNopLogger()) body = recorder.Body.String() if !result { @@ -1084,7 +1085,7 @@ func TestFailIfHeaderMatchesRegexp(t *testing.T) { testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result := ProbeHTTP(testCTX, ts.URL, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, FailIfHeaderMatchesRegexp: []config.HeaderMatch{test.Rule}}}, registry, log.NewNopLogger()) + result := ProbeHTTP(testCTX, probeOpts{target: ts.URL}, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, FailIfHeaderMatchesRegexp: []config.HeaderMatch{test.Rule}}}, registry, log.NewNopLogger()) if result != test.ShouldSucceed { t.Fatalf("Test %d had unexpected result: succeeded: %t, expected: %+v", i, result, test) } @@ -1132,7 +1133,7 @@ func TestFailIfHeaderNotMatchesRegexp(t *testing.T) { testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result := ProbeHTTP(testCTX, ts.URL, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, FailIfHeaderNotMatchesRegexp: []config.HeaderMatch{test.Rule}}}, registry, log.NewNopLogger()) + result := ProbeHTTP(testCTX, probeOpts{target: ts.URL}, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, FailIfHeaderNotMatchesRegexp: []config.HeaderMatch{test.Rule}}}, registry, log.NewNopLogger()) if result != test.ShouldSucceed { t.Fatalf("Test %d had unexpected result: succeeded: %t, expected: %+v", i, result, test) } @@ -1177,7 +1178,7 @@ func TestHTTPHeaders(t *testing.T) { registry := prometheus.NewRegistry() testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result := ProbeHTTP(testCTX, ts.URL, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{ + result := ProbeHTTP(testCTX, probeOpts{target: ts.URL}, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{ IPProtocolFallback: true, Headers: headers, }}, registry, log.NewNopLogger()) @@ -1195,7 +1196,7 @@ func TestFailIfSelfSignedCA(t *testing.T) { registry := prometheus.NewRegistry() testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result := ProbeHTTP(testCTX, ts.URL, + result := ProbeHTTP(testCTX, probeOpts{target: ts.URL}, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{ IPProtocolFallback: true, HTTPClientConfig: pconfig.HTTPClientConfig{ @@ -1225,7 +1226,7 @@ func TestSucceedIfSelfSignedCA(t *testing.T) { registry := prometheus.NewRegistry() testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result := ProbeHTTP(testCTX, ts.URL, + result := ProbeHTTP(testCTX, probeOpts{target: ts.URL}, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{ IPProtocolFallback: true, HTTPClientConfig: pconfig.HTTPClientConfig{ @@ -1255,7 +1256,7 @@ func TestTLSConfigIsIgnoredForPlainHTTP(t *testing.T) { registry := prometheus.NewRegistry() testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result := ProbeHTTP(testCTX, ts.URL, + result := ProbeHTTP(testCTX, probeOpts{target: ts.URL}, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{ IPProtocolFallback: true, HTTPClientConfig: pconfig.HTTPClientConfig{ @@ -1328,7 +1329,7 @@ func TestHTTPUsesTargetAsTLSServerName(t *testing.T) { url := strings.Replace(ts.URL, "127.0.0.1", "localhost", -1) url = strings.Replace(url, "[::1]", "localhost", -1) - result := ProbeHTTP(context.Background(), url, module, registry, log.NewNopLogger()) + result := ProbeHTTP(context.Background(), probeOpts{target: url}, module, registry, log.NewNopLogger()) if !result { t.Fatalf("TLS probe failed unexpectedly") } @@ -1347,7 +1348,8 @@ func TestRedirectToTLSHostWorks(t *testing.T) { registry := prometheus.NewRegistry() testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result := ProbeHTTP(testCTX, ts.URL, + + result := ProbeHTTP(testCTX, probeOpts{target: ts.URL}, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, HTTPClientConfig: pconfig.DefaultHTTPClientConfig}}, registry, log.NewNopLogger()) if !result { t.Fatalf("Redirect test failed unexpectedly") @@ -1366,7 +1368,7 @@ func TestHTTPPhases(t *testing.T) { testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result := ProbeHTTP(testCTX, ts.URL, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{ + result := ProbeHTTP(testCTX, probeOpts{target: ts.URL}, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{ IPProtocolFallback: true, HTTPClientConfig: pconfig.HTTPClientConfig{ TLSConfig: pconfig.TLSConfig{InsecureSkipVerify: true}, @@ -1421,7 +1423,7 @@ func TestCookieJar(t *testing.T) { registry := prometheus.NewRegistry() testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result := ProbeHTTP(testCTX, ts.URL, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, HTTPClientConfig: pconfig.DefaultHTTPClientConfig}}, registry, log.NewNopLogger()) + result := ProbeHTTP(testCTX, probeOpts{target: ts.URL}, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, HTTPClientConfig: pconfig.DefaultHTTPClientConfig}}, registry, log.NewNopLogger()) body := recorder.Body.String() if !result { t.Fatalf("Redirect test failed unexpectedly, got %s", body) @@ -1441,7 +1443,7 @@ func TestSkipResolvePhase(t *testing.T) { registry := prometheus.NewRegistry() testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - result := ProbeHTTP(testCTX, ts.URL, + result := ProbeHTTP(testCTX, probeOpts{target: ts.URL}, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, HTTPClientConfig: pconfig.DefaultHTTPClientConfig, SkipResolvePhaseWithProxy: true}}, registry, log.NewNopLogger()) if !result { t.Fatalf("Probe unsuccessful") @@ -1476,7 +1478,7 @@ func TestSkipResolvePhase(t *testing.T) { httpCfg.ProxyURL = pconfig.URL{ URL: u, } - ProbeHTTP(testCTX, ts.URL, + ProbeHTTP(testCTX, probeOpts{target: ts.URL}, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, HTTPClientConfig: httpCfg, SkipResolvePhaseWithProxy: true}}, registry, log.NewNopLogger()) mfs, err := registry.Gather() if err != nil { @@ -1532,7 +1534,7 @@ func TestBody(t *testing.T) { defer cancel() result := ProbeHTTP( testCTX, - ts.URL, + probeOpts{target: ts.URL}, config.Module{ Timeout: time.Second, HTTP: test}, diff --git a/prober/icmp.go b/prober/icmp.go index f78075285..ed2ada48e 100644 --- a/prober/icmp.go +++ b/prober/icmp.go @@ -63,7 +63,7 @@ func getICMPSequence() uint16 { return icmpSequence } -func ProbeICMP(ctx context.Context, target string, module config.Module, registry *prometheus.Registry, logger log.Logger) (success bool) { +func ProbeICMP(ctx context.Context, opts probeOpts, module config.Module, registry *prometheus.Registry, logger log.Logger) (success bool) { var ( requestType icmp.Type replyType icmp.Type @@ -88,7 +88,7 @@ func ProbeICMP(ctx context.Context, target string, module config.Module, registr registry.MustRegister(durationGaugeVec) - dstIPAddr, lookupTime, err := chooseProtocol(ctx, module.ICMP.IPProtocol, module.ICMP.IPProtocolFallback, target, registry, logger) + dstIPAddr, lookupTime, err := chooseProtocol(ctx, module.ICMP.IPProtocol, module.ICMP.IPProtocolFallback, opts.target, registry, logger) if err != nil { level.Error(logger).Log("msg", "Error resolving address", "err", err) @@ -97,9 +97,10 @@ func ProbeICMP(ctx context.Context, target string, module config.Module, registr durationGaugeVec.WithLabelValues("resolve").Add(lookupTime) var srcIP net.IP - if len(module.ICMP.SourceIPAddress) > 0 { - if srcIP = net.ParseIP(module.ICMP.SourceIPAddress); srcIP == nil { - level.Error(logger).Log("msg", "Error parsing source ip address", "srcIP", module.ICMP.SourceIPAddress) + SourceIPAddress := coalesce(opts.sourceIPAddress, module.ICMP.SourceIPAddress) + if len(SourceIPAddress) > 0 { + if srcIP = net.ParseIP(SourceIPAddress); srcIP == nil { + level.Error(logger).Log("msg", "Error parsing source ip address", "srcIP", SourceIPAddress) return false } level.Info(logger).Log("msg", "Using source address", "srcIP", srcIP) diff --git a/prober/prober.go b/prober/prober.go index 93d4e3d6a..27ff9ce3c 100644 --- a/prober/prober.go +++ b/prober/prober.go @@ -22,7 +22,12 @@ import ( "github.com/prometheus/blackbox_exporter/config" ) -type ProbeFn func(ctx context.Context, target string, config config.Module, registry *prometheus.Registry, logger log.Logger) bool +type probeOpts struct { + target string + sourceIPAddress string +} + +type ProbeFn func(ctx context.Context, opts probeOpts, config config.Module, registry *prometheus.Registry, logger log.Logger) bool const ( helpSSLEarliestCertExpiry = "Returns last SSL chain expiry in unixtime" diff --git a/prober/tcp.go b/prober/tcp.go index de960db26..b093693fd 100644 --- a/prober/tcp.go +++ b/prober/tcp.go @@ -28,10 +28,10 @@ import ( "github.com/prometheus/blackbox_exporter/config" ) -func dialTCP(ctx context.Context, target string, module config.Module, registry *prometheus.Registry, logger log.Logger) (net.Conn, error) { +func dialTCP(ctx context.Context, opts probeOpts, module config.Module, registry *prometheus.Registry, logger log.Logger) (net.Conn, error) { var dialProtocol, dialTarget string dialer := &net.Dialer{} - targetAddress, port, err := net.SplitHostPort(target) + targetAddress, port, err := net.SplitHostPort(opts.target) if err != nil { level.Error(logger).Log("msg", "Error splitting target address and port", "err", err) return nil, err @@ -49,11 +49,12 @@ func dialTCP(ctx context.Context, target string, module config.Module, registry dialProtocol = "tcp4" } - if len(module.TCP.SourceIPAddress) > 0 { - srcIP := net.ParseIP(module.TCP.SourceIPAddress) + sourceIPAddress := coalesce(opts.sourceIPAddress, module.TCP.SourceIPAddress) + if len(sourceIPAddress) > 0 { + srcIP := net.ParseIP(sourceIPAddress) if srcIP == nil { - level.Error(logger).Log("msg", "Error parsing source ip address", "srcIP", module.TCP.SourceIPAddress) - return nil, fmt.Errorf("error parsing source ip address: %s", module.TCP.SourceIPAddress) + level.Error(logger).Log("msg", "Error parsing source ip address", "srcIP", sourceIPAddress) + return nil, fmt.Errorf("error parsing source ip address: %s", sourceIPAddress) } level.Info(logger).Log("msg", "Using local address", "srcIP", srcIP) dialer.LocalAddr = &net.TCPAddr{IP: srcIP} @@ -88,7 +89,7 @@ func dialTCP(ctx context.Context, target string, module config.Module, registry return tls.DialWithDialer(dialer, dialProtocol, dialTarget, tlsConfig) } -func ProbeTCP(ctx context.Context, target string, module config.Module, registry *prometheus.Registry, logger log.Logger) bool { +func ProbeTCP(ctx context.Context, opts probeOpts, module config.Module, registry *prometheus.Registry, logger log.Logger) bool { probeSSLEarliestCertExpiry := prometheus.NewGauge(sslEarliestCertExpiryGaugeOpts) probeSSLLastChainExpiryTimestampSeconds := prometheus.NewGauge(sslChainExpiryInTimeStampGaugeOpts) probeSSLLastInformation := prometheus.NewGaugeVec( @@ -109,7 +110,7 @@ func ProbeTCP(ctx context.Context, target string, module config.Module, registry registry.MustRegister(probeFailedDueToRegex) deadline, _ := ctx.Deadline() - conn, err := dialTCP(ctx, target, module, registry, logger) + conn, err := dialTCP(ctx, opts, module, registry, logger) if err != nil { level.Error(logger).Log("msg", "Error dialing TCP", "err", err) return false @@ -175,7 +176,7 @@ func ProbeTCP(ctx context.Context, target string, module config.Module, registry } if tlsConfig.ServerName == "" { // Use target-hostname as default for TLS-servername. - targetAddress, _, _ := net.SplitHostPort(target) // Had succeeded in dialTCP already. + targetAddress, _, _ := net.SplitHostPort(opts.target) // Had succeeded in dialTCP already. tlsConfig.ServerName = targetAddress } tlsConn := tls.Client(conn, tlsConfig) diff --git a/prober/tcp_test.go b/prober/tcp_test.go index 0deba3a83..8923c3902 100644 --- a/prober/tcp_test.go +++ b/prober/tcp_test.go @@ -55,7 +55,8 @@ func TestTCPConnection(t *testing.T) { testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() registry := prometheus.NewRegistry() - if !ProbeTCP(testCTX, ln.Addr().String(), config.Module{TCP: config.TCPProbe{IPProtocolFallback: true}}, registry, log.NewNopLogger()) { + + if !ProbeTCP(testCTX, probeOpts{target: ln.Addr().String()}, config.Module{TCP: config.TCPProbe{IPProtocolFallback: true}}, registry, log.NewNopLogger()) { t.Fatalf("TCP module failed, expected success.") } <-ch @@ -66,7 +67,7 @@ func TestTCPConnectionFails(t *testing.T) { registry := prometheus.NewRegistry() testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - if ProbeTCP(testCTX, ":0", config.Module{TCP: config.TCPProbe{}}, registry, log.NewNopLogger()) { + if ProbeTCP(testCTX, probeOpts{target: ":0"}, config.Module{TCP: config.TCPProbe{}}, registry, log.NewNopLogger()) { t.Fatalf("TCP module succeeded, expected failure.") } } @@ -155,7 +156,7 @@ func TestTCPConnectionWithTLS(t *testing.T) { registry := prometheus.NewRegistry() go serverFunc() // Test name-verification failure (IP without IPs in cert's SAN). - if ProbeTCP(testCTX, ln.Addr().String(), module, registry, log.NewNopLogger()) { + if ProbeTCP(testCTX, probeOpts{target: ln.Addr().String()}, module, registry, log.NewNopLogger()) { t.Fatalf("TCP module succeeded, expected failure.") } <-ch @@ -164,7 +165,7 @@ func TestTCPConnectionWithTLS(t *testing.T) { go serverFunc() // Test name-verification with name from target. target := net.JoinHostPort("localhost", listenPort) - if !ProbeTCP(testCTX, target, module, registry, log.NewNopLogger()) { + if !ProbeTCP(testCTX, probeOpts{target: target}, module, registry, log.NewNopLogger()) { t.Fatalf("TCP module failed, expected success.") } <-ch @@ -173,7 +174,7 @@ func TestTCPConnectionWithTLS(t *testing.T) { go serverFunc() // Test name-verification against name from tls_config. module.TCP.TLSConfig.ServerName = "localhost" - if !ProbeTCP(testCTX, ln.Addr().String(), module, registry, log.NewNopLogger()) { + if !ProbeTCP(testCTX, probeOpts{target: ln.Addr().String()}, module, registry, log.NewNopLogger()) { t.Fatalf("TCP module failed, expected success.") } <-ch @@ -304,7 +305,7 @@ func TestTCPConnectionWithTLSAndVerifiedCertificateChain(t *testing.T) { go serverFunc() // Test name-verification with name from target. target := net.JoinHostPort("localhost", listenPort) - if !ProbeTCP(testCTX, target, module, registry, log.NewNopLogger()) { + if !ProbeTCP(testCTX, probeOpts{target: target}, module, registry, log.NewNopLogger()) { t.Fatalf("TCP module failed, expected success.") } <-ch @@ -425,7 +426,7 @@ func TestTCPConnectionQueryResponseStartTLS(t *testing.T) { // Do the client side of this test. registry := prometheus.NewRegistry() - if !ProbeTCP(testCTX, ln.Addr().String(), module, registry, log.NewNopLogger()) { + if !ProbeTCP(testCTX, probeOpts{target: ln.Addr().String()}, module, registry, log.NewNopLogger()) { t.Fatalf("TCP module failed, expected success.") } <-ch @@ -477,7 +478,7 @@ func TestTCPConnectionQueryResponseIRC(t *testing.T) { ch <- struct{}{} }() registry := prometheus.NewRegistry() - if !ProbeTCP(testCTX, ln.Addr().String(), module, registry, log.NewNopLogger()) { + if !ProbeTCP(testCTX, probeOpts{target: ln.Addr().String()}, module, registry, log.NewNopLogger()) { t.Fatalf("TCP module failed, expected success.") } <-ch @@ -496,7 +497,7 @@ func TestTCPConnectionQueryResponseIRC(t *testing.T) { ch <- struct{}{} }() registry = prometheus.NewRegistry() - if ProbeTCP(testCTX, ln.Addr().String(), module, registry, log.NewNopLogger()) { + if ProbeTCP(testCTX, probeOpts{target: ln.Addr().String()}, module, registry, log.NewNopLogger()) { t.Fatalf("TCP module succeeded, expected failure.") } mfs, err := registry.Gather() @@ -546,7 +547,7 @@ func TestTCPConnectionQueryResponseMatching(t *testing.T) { ch <- version }() registry := prometheus.NewRegistry() - if !ProbeTCP(testCTX, ln.Addr().String(), module, registry, log.NewNopLogger()) { + if !ProbeTCP(testCTX, probeOpts{target: ln.Addr().String()}, module, registry, log.NewNopLogger()) { t.Fatalf("TCP module failed, expected success.") } if got, want := <-ch, "OpenSSH_6.9p1"; got != want { @@ -598,7 +599,8 @@ func TestTCPConnectionProtocol(t *testing.T) { } registry := prometheus.NewRegistry() - result := ProbeTCP(testCTX, net.JoinHostPort("localhost", port), module, registry, log.NewNopLogger()) + + result := ProbeTCP(testCTX, probeOpts{target: net.JoinHostPort("localhost", port)}, module, registry, log.NewNopLogger()) if !result { t.Fatalf("TCP protocol: \"tcp\", prefer: \"ip4\" connection test failed, expected success.") } @@ -619,7 +621,7 @@ func TestTCPConnectionProtocol(t *testing.T) { } registry = prometheus.NewRegistry() - result = ProbeTCP(testCTX, net.JoinHostPort("localhost", port), module, registry, log.NewNopLogger()) + result = ProbeTCP(testCTX, probeOpts{target: net.JoinHostPort("localhost", port)}, module, registry, log.NewNopLogger()) if !result { t.Fatalf("TCP protocol: \"tcp\", prefer: \"ip6\" connection test failed, expected success.") } @@ -638,7 +640,7 @@ func TestTCPConnectionProtocol(t *testing.T) { } registry = prometheus.NewRegistry() - result = ProbeTCP(testCTX, net.JoinHostPort("localhost", port), module, registry, log.NewNopLogger()) + result = ProbeTCP(testCTX, probeOpts{target: net.JoinHostPort("localhost", port)}, module, registry, log.NewNopLogger()) if !result { t.Fatalf("TCP protocol: \"tcp\" connection test failed, expected success.") } @@ -671,7 +673,7 @@ func TestPrometheusTimeoutTCP(t *testing.T) { testCTX, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() registry := prometheus.NewRegistry() - if ProbeTCP(testCTX, ln.Addr().String(), config.Module{TCP: config.TCPProbe{ + if ProbeTCP(testCTX, probeOpts{target: ln.Addr().String()}, config.Module{TCP: config.TCPProbe{ IPProtocolFallback: true, QueryResponse: []config.QueryResponse{ { diff --git a/prober/utils.go b/prober/utils.go index cde1f3eda..944ca43d9 100644 --- a/prober/utils.go +++ b/prober/utils.go @@ -142,3 +142,13 @@ func ipHash(ip net.IP) float64 { } return float64(h.Sum32()) } + +// return first non empty string +func coalesce(strs ...string) string { + for _, str := range strs { + if len(str) > 0 { + return str + } + } + return "" +}