mirror of
https://github.com/bloomberg/goldpinger.git
synced 2026-02-19 20:39:53 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95363554e4 | ||
|
|
a913318ae3 | ||
|
|
2f54105404 | ||
|
|
f6dbd2632b | ||
|
|
8ad0802439 | ||
|
|
7cd571d15f | ||
|
|
14ea96999a | ||
|
|
05ab610f10 | ||
|
|
1a9ae3a9ba | ||
|
|
d976605bc6 | ||
|
|
5cc9bd55ad | ||
|
|
72832bcbc4 | ||
|
|
ef300d5ca2 |
@@ -23,6 +23,7 @@ RUN go mod vendor
|
||||
FROM scratch as simple
|
||||
COPY --from=builder /w/bin/goldpinger /goldpinger
|
||||
COPY ./static /static
|
||||
COPY ./config /config
|
||||
ENTRYPOINT ["/goldpinger", "--static-file-path", "/static"]
|
||||
|
||||
# For vendor builds, use the simple build and add the vendor'd files
|
||||
|
||||
2
Makefile
2
Makefile
@@ -1,5 +1,5 @@
|
||||
name ?= goldpinger
|
||||
version ?= v3.4.0
|
||||
version ?= v3.7.0
|
||||
bin ?= goldpinger
|
||||
pkg ?= "github.com/bloomberg/goldpinger"
|
||||
tag = $(name):$(version)
|
||||
|
||||
23
README.md
23
README.md
@@ -231,7 +231,7 @@ You can also see [an example of using `kubeconfig` in the `./extras`](./extras/e
|
||||
|
||||
### Using with IPv4/IPv6 dual-stack
|
||||
|
||||
If your pods having IPv4 and IPv6 addresses assigned and you want to test communication over IPv6, you can specify the `USE_IPV6` environment variable which will use the IPv6 address on the pod and host.
|
||||
If your cluster IPv4/IPv6 dual-stack and you want to force IPv6, you can set the `IP_VERSIONS` environment variable to "6" (default is "4") which will use the IPv6 address on the pod and host.
|
||||
|
||||

|
||||
|
||||
@@ -256,6 +256,27 @@ and `goldpinger` should show something like this:
|
||||
|
||||

|
||||
|
||||
### TCP and HTTP checks to external targets
|
||||
|
||||
Instances can also be configured to do simple TCP or HTTP checks on external targets. This is useful for visualizing more nuanced connectivity flows.
|
||||
|
||||
```sh
|
||||
--tcp-targets= A list of external targets(<host>:<port> or <ip>:<port>) to attempt a TCP check on (space delimited) [$TCP_TARGETS]
|
||||
--http-targets= A list of external targets(<http or https>://<url>) to attempt an HTTP{S} check on. A 200 HTTP code is considered successful. (space delimited) [$HTTP_TARGETS]
|
||||
--tcp-targets-timeout= The timeout for a tcp check on the provided tcp-targets (default: 500) [$TCP_TARGETS_TIMEOUT]
|
||||
--dns-targets-timeout= The timeout for a tcp check on the provided udp-targets (default: 500) [$DNS_TARGETS_TIMEOUT]
|
||||
```
|
||||
|
||||
```yaml
|
||||
- name: HTTP_TARGETS
|
||||
value: http://bloomberg.com
|
||||
- name: TCP_TARGETS
|
||||
value: 10.34.5.141:5000 10.34.195.193:6442
|
||||
```
|
||||
|
||||
the timeouts for the TCP, DNS and HTTP checks can be configured via `TCP_TARGETS_TIMEOUT`, `DNS_TARGETS_TIMEOUT` and `HTTP_TARGETS_TIMEOUT` respectively.
|
||||
|
||||

|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
@@ -15,8 +15,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/loads"
|
||||
"go.uber.org/zap"
|
||||
@@ -36,37 +40,32 @@ var (
|
||||
Version, Build string
|
||||
)
|
||||
|
||||
func getLogger() *zap.Logger {
|
||||
func getLogger(zapconfigpath string) (*zap.Logger, error) {
|
||||
var logger *zap.Logger
|
||||
var err error
|
||||
|
||||
// We haven't parsed flags at this stage and that might be error prone
|
||||
// so just use an envvar
|
||||
if debug, err := strconv.ParseBool(os.Getenv("DEBUG")); err == nil && debug {
|
||||
logger, err = zap.NewDevelopment()
|
||||
} else {
|
||||
logger, err = zap.NewProduction()
|
||||
}
|
||||
zapconfigJSON, err := ioutil.ReadFile(zapconfigpath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, fmt.Errorf("Could not read zap config file: %w", err)
|
||||
}
|
||||
zap.ReplaceGlobals(logger)
|
||||
return logger
|
||||
|
||||
var cfg zap.Config
|
||||
if err := json.Unmarshal(zapconfigJSON, &cfg); err != nil {
|
||||
return nil, fmt.Errorf("Could not read zap config as json: %w", err)
|
||||
}
|
||||
logger, err = cfg.Build()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not build zap config: %w", err)
|
||||
}
|
||||
|
||||
return logger, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
logger := getLogger()
|
||||
defer logger.Sync()
|
||||
|
||||
undo := zap.RedirectStdLog(logger)
|
||||
defer undo()
|
||||
|
||||
logger.Info("Goldpinger", zap.String("version", Version), zap.String("build", Build))
|
||||
|
||||
// load embedded swagger file
|
||||
swaggerSpec, err := loads.Analyzed(restapi.SwaggerJSON, "")
|
||||
if err != nil {
|
||||
logger.Error("Coud not parse swagger", zap.Error(err))
|
||||
log.Fatalf("Could not parse swagger: %v", err)
|
||||
}
|
||||
|
||||
// create new service API
|
||||
@@ -83,7 +82,7 @@ func main() {
|
||||
for _, optsGroup := range api.CommandLineOptionsGroups {
|
||||
_, err := parser.AddGroup(optsGroup.ShortDescription, optsGroup.LongDescription, optsGroup.Options)
|
||||
if err != nil {
|
||||
logger.Error("Coud not add flag group", zap.Error(err))
|
||||
log.Fatalf("Could not add flag group: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +96,23 @@ func main() {
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
// Configure logger
|
||||
logger, err := getLogger(goldpinger.GoldpingerConfig.ZapConfigPath)
|
||||
if err != nil {
|
||||
var errDev error
|
||||
logger, errDev = zap.NewDevelopment()
|
||||
if errDev != nil {
|
||||
log.Fatalf("Could not build a development logger: %v", errDev)
|
||||
}
|
||||
logger.Warn("Logger could not be built, defaulting to development settings", zap.String("error", fmt.Sprintf("%v", err)))
|
||||
}
|
||||
defer logger.Sync()
|
||||
|
||||
undo := zap.RedirectStdLog(logger)
|
||||
defer undo()
|
||||
|
||||
logger.Info("Goldpinger", zap.String("version", Version), zap.String("build", Build))
|
||||
|
||||
if goldpinger.GoldpingerConfig.Namespace == nil {
|
||||
goldpinger.GoldpingerConfig.Namespace = &goldpinger.PodNamespace
|
||||
} else {
|
||||
@@ -144,6 +160,23 @@ func main() {
|
||||
logger.Error("Unknown IP version specified: expected values are 4 or 6", zap.Strings("IPVersions", goldpinger.GoldpingerConfig.IPVersions))
|
||||
}
|
||||
|
||||
// Handle deprecated flags
|
||||
if int(goldpinger.GoldpingerConfig.PingTimeout) == 0 {
|
||||
logger.Warn("ping-timeout-ms is deprecated in favor of ping-timeout and will be removed in the future",
|
||||
zap.Int64("ping-timeout-ms", goldpinger.GoldpingerConfig.PingTimeoutMs))
|
||||
goldpinger.GoldpingerConfig.PingTimeout = time.Duration(goldpinger.GoldpingerConfig.PingTimeoutMs) * time.Millisecond
|
||||
}
|
||||
if int(goldpinger.GoldpingerConfig.CheckTimeout) == 0 {
|
||||
logger.Warn("check-timeout-ms is deprecated in favor of check-timeout and will be removed in the future",
|
||||
zap.Int64("check-timeout-ms", goldpinger.GoldpingerConfig.CheckTimeoutMs))
|
||||
goldpinger.GoldpingerConfig.CheckTimeout = time.Duration(goldpinger.GoldpingerConfig.CheckTimeoutMs) * time.Millisecond
|
||||
}
|
||||
if int(goldpinger.GoldpingerConfig.CheckAllTimeout) == 0 {
|
||||
logger.Warn("check-all-timeout-ms is deprecated in favor of check-all-timeout will be removed in the future",
|
||||
zap.Int64("check-all-timeout-ms", goldpinger.GoldpingerConfig.CheckAllTimeoutMs))
|
||||
goldpinger.GoldpingerConfig.CheckAllTimeout = time.Duration(goldpinger.GoldpingerConfig.CheckAllTimeoutMs) * time.Millisecond
|
||||
}
|
||||
|
||||
server.ConfigureAPI()
|
||||
goldpinger.StartUpdater()
|
||||
|
||||
|
||||
21
config/zap.json
Normal file
21
config/zap.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"level": "info",
|
||||
"encoding": "json",
|
||||
"outputPaths": [
|
||||
"stdout"
|
||||
],
|
||||
"errorOutputPaths": [
|
||||
"stderr"
|
||||
],
|
||||
"initialFields": {
|
||||
},
|
||||
"encoderConfig": {
|
||||
"messageKey": "message",
|
||||
"levelKey": "level",
|
||||
"levelEncoder": "lowercase",
|
||||
"timeKey": "ts",
|
||||
"timeEncoder": "ISO8601",
|
||||
"callerKey": "caller",
|
||||
"callerEncoder": "Short"
|
||||
}
|
||||
}
|
||||
BIN
extras/tcp-checks-screenshot.png
Normal file
BIN
extras/tcp-checks-screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 576 KiB |
2
go.mod
2
go.mod
@@ -5,7 +5,7 @@ go 1.15
|
||||
require (
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 // indirect
|
||||
github.com/cespare/xxhash v1.1.0
|
||||
github.com/go-openapi/errors v0.20.0
|
||||
github.com/go-openapi/errors v0.20.2
|
||||
github.com/go-openapi/loads v0.19.5
|
||||
github.com/go-openapi/runtime v0.19.26
|
||||
github.com/go-openapi/spec v0.19.8
|
||||
|
||||
2
go.sum
2
go.sum
@@ -91,6 +91,8 @@ github.com/go-openapi/errors v0.19.6/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpX
|
||||
github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
||||
github.com/go-openapi/errors v0.20.0 h1:Sxpo9PjEHDzhs3FbnGNonvDgWcMW2U7wGTcDDSFSceM=
|
||||
github.com/go-openapi/errors v0.20.0/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
||||
github.com/go-openapi/errors v0.20.2 h1:dxy7PGTqEh94zj2E3h1cUmQQWiM1+aeCROfAr02EmK8=
|
||||
github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
||||
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
|
||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
|
||||
@@ -37,15 +37,12 @@ func CheckNeighbours(ctx context.Context) *models.CheckResults {
|
||||
// Mux to prevent concurrent map address
|
||||
checkResultsMux.Lock()
|
||||
defer checkResultsMux.Unlock()
|
||||
|
||||
final := models.CheckResults{}
|
||||
final.PodResults = make(map[string]models.PodResult)
|
||||
for podName, podResult := range checkResults.PodResults {
|
||||
final.PodResults[podName] = podResult
|
||||
}
|
||||
if len(GoldpingerConfig.DnsHosts) > 0 {
|
||||
final.DNSResults = *checkDNS()
|
||||
}
|
||||
final.ProbeResults = checkTargets()
|
||||
return &final
|
||||
}
|
||||
|
||||
@@ -127,22 +124,58 @@ func pickPodHostIP(podIP, hostIP string) string {
|
||||
return podIP
|
||||
}
|
||||
|
||||
func checkDNS() *models.DNSResults {
|
||||
results := models.DNSResults{}
|
||||
for _, host := range GoldpingerConfig.DnsHosts {
|
||||
|
||||
var dnsResult models.DNSResult
|
||||
|
||||
start := time.Now()
|
||||
_, err := net.LookupIP(host)
|
||||
if err != nil {
|
||||
dnsResult.Error = err.Error()
|
||||
CountDnsError(host)
|
||||
}
|
||||
dnsResult.ResponseTimeMs = time.Since(start).Nanoseconds() / int64(time.Millisecond)
|
||||
results[host] = dnsResult
|
||||
func checkTargets() models.ProbeResults {
|
||||
results := make(map[string][]models.ProbeResult)
|
||||
probes := []struct {
|
||||
protocol string
|
||||
hosts []string
|
||||
probeFn func(addr string, timeout time.Duration) error
|
||||
statFn func(host string)
|
||||
timeout time.Duration
|
||||
}{
|
||||
{
|
||||
protocol: "dns",
|
||||
hosts: GoldpingerConfig.DnsHosts,
|
||||
probeFn: doDNSProbe,
|
||||
statFn: CountDnsError,
|
||||
timeout: GoldpingerConfig.DnsCheckTimeout,
|
||||
},
|
||||
{
|
||||
protocol: "http",
|
||||
hosts: GoldpingerConfig.HTTPTargets,
|
||||
probeFn: doHTTPProbe,
|
||||
statFn: CountHttpError,
|
||||
timeout: GoldpingerConfig.HTTPCheckTimeout,
|
||||
},
|
||||
{
|
||||
protocol: "tcp",
|
||||
hosts: GoldpingerConfig.TCPTargets,
|
||||
probeFn: doTCPProbe,
|
||||
statFn: CountTcpError,
|
||||
timeout: GoldpingerConfig.TCPCheckTimeout,
|
||||
},
|
||||
}
|
||||
return &results
|
||||
|
||||
for _, probe := range probes {
|
||||
for _, host := range probe.hosts {
|
||||
if _, ok := results[host]; !ok {
|
||||
results[host] = []models.ProbeResult{}
|
||||
}
|
||||
|
||||
res := models.ProbeResult{Protocol: probe.protocol}
|
||||
start := time.Now()
|
||||
err := probe.probeFn(host, probe.timeout)
|
||||
if err != nil {
|
||||
res.Error = err.Error()
|
||||
probe.statFn(host)
|
||||
}
|
||||
|
||||
res.ResponseTimeMs = time.Since(start).Milliseconds()
|
||||
results[host] = append(results[host], res)
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
// CheckServicePodsResult results of the /check operation
|
||||
@@ -195,7 +228,7 @@ func CheckAllPods(checkAllCtx context.Context, pods map[string]*GoldpingerPod) *
|
||||
} else {
|
||||
checkCtx, cancel := context.WithTimeout(
|
||||
checkAllCtx,
|
||||
time.Duration(GoldpingerConfig.CheckTimeoutMs)*time.Millisecond,
|
||||
GoldpingerConfig.CheckTimeout,
|
||||
)
|
||||
defer cancel()
|
||||
|
||||
@@ -238,15 +271,15 @@ func CheckAllPods(checkAllCtx context.Context, pods map[string]*GoldpingerPod) *
|
||||
PodIP: response.podIPv4,
|
||||
})
|
||||
if response.checkAllPodResult.Response != nil &&
|
||||
response.checkAllPodResult.Response.DNSResults != nil {
|
||||
if result.DNSResults == nil {
|
||||
result.DNSResults = make(map[string]models.DNSResults)
|
||||
response.checkAllPodResult.Response.ProbeResults != nil {
|
||||
if result.ProbeResults == nil {
|
||||
result.ProbeResults = make(map[string]models.ProbeResults)
|
||||
}
|
||||
for host := range response.checkAllPodResult.Response.DNSResults {
|
||||
if result.DNSResults[host] == nil {
|
||||
result.DNSResults[host] = make(map[string]models.DNSResult)
|
||||
for host := range response.checkAllPodResult.Response.ProbeResults {
|
||||
if result.ProbeResults[host] == nil {
|
||||
result.ProbeResults[host] = make(map[string][]models.ProbeResult)
|
||||
}
|
||||
result.DNSResults[host][response.podName] = response.checkAllPodResult.Response.DNSResults[host]
|
||||
result.ProbeResults[host][response.podName] = response.checkAllPodResult.Response.ProbeResults[host]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,15 @@
|
||||
package goldpinger
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"k8s.io/client-go/kubernetes"
|
||||
)
|
||||
|
||||
// GoldpingerConfig represents the configuration for goldpinger
|
||||
var GoldpingerConfig = struct {
|
||||
StaticFilePath string `long:"static-file-path" description:"Folder for serving static files" env:"STATIC_FILE_PATH"`
|
||||
ZapConfigPath string `long:"zap-config" description:"Path to zap config file" env:"ZAP_CONFIG" default:"/config/zap.json"`
|
||||
KubeConfigPath string `long:"kubeconfig" description:"Path to kubeconfig file" env:"KUBECONFIG"`
|
||||
RefreshInterval int `long:"refresh-interval" description:"If > 0, will create a thread and collect stats every n seconds" env:"REFRESH_INTERVAL" default:"30"`
|
||||
JitterFactor float64 `long:"jitter-factor" description:"The amount of jitter to add while pinging clients" env:"JITTER_FACTOR" default:"0.05"`
|
||||
@@ -35,12 +38,20 @@ var GoldpingerConfig = struct {
|
||||
DisplayNodeName bool `long:"display-nodename" description:"Display nodename other than podname in UI (defaults is podname)." env:"DISPLAY_NODENAME"`
|
||||
KubernetesClient *kubernetes.Clientset
|
||||
|
||||
DnsHosts []string `long:"host-to-resolve" description:"A host to attempt dns resolve on (space delimited)" env:"HOSTS_TO_RESOLVE" env-delim:" "`
|
||||
DnsHosts []string `long:"host-to-resolve" description:"A host to attempt dns resolve on (space delimited)" env:"HOSTS_TO_RESOLVE" env-delim:" "`
|
||||
TCPTargets []string `long:"tcp-targets" description:"A list of external targets(<host>:<port> or <ip>:<port>) to attempt a TCP check on (space delimited)" env:"TCP_TARGETS" env-delim:" "`
|
||||
HTTPTargets []string `long:"http-targets" description:"A list of external targets(<http or https>://<url>) to attempt an HTTP{S} check on. A 200 HTTP code is considered successful.(space delimited)" env:"HTTP_TARGETS" env-delim:" "`
|
||||
|
||||
IPVersions []string `long:"ip-versions" description:"The IP versions to use (space delimited). Possible values are 4 and 6 (defaults to 4)." env:"IP_VERSIONS" env-delim:" "`
|
||||
|
||||
// Timeouts
|
||||
PingTimeoutMs int64 `long:"ping-timeout-ms" description:"The timeout in milliseconds for a ping call to other goldpinger pods" env:"PING_TIMEOUT_MS" default:"300"`
|
||||
CheckTimeoutMs int64 `long:"check-timeout-ms" description:"The timeout in milliseconds for a check call to other goldpinger pods" env:"CHECK_TIMEOUT_MS" default:"1000"`
|
||||
CheckAllTimeoutMs int64 `long:"check-all-timeout-ms" description:"The timeout in milliseconds for a check-all call to other goldpinger pods" env:"CHECK_ALL_TIMEOUT_MS" default:"5000"`
|
||||
PingTimeoutMs int64 `long:"ping-timeout-ms" description:"The timeout in milliseconds for a ping call to other goldpinger pods(deprecated)" env:"PING_TIMEOUT_MS" default:"300"`
|
||||
CheckTimeoutMs int64 `long:"check-timeout-ms" description:"The timeout in milliseconds for a check call to other goldpinger pods(deprecated)" env:"CHECK_TIMEOUT_MS" default:"1000"`
|
||||
CheckAllTimeoutMs int64 `long:"check-all-timeout-ms" description:"The timeout in milliseconds for a check-all call to other goldpinger pods(deprecated)" env:"CHECK_ALL_TIMEOUT_MS" default:"5000"`
|
||||
PingTimeout time.Duration `long:"ping-timeout" description:"The timeout for a ping call to other goldpinger pods" env:"PING_TIMEOUT" default:"300ms"`
|
||||
CheckTimeout time.Duration `long:"check-timeout" description:"The timeout for a check call to other goldpinger pods" env:"CHECK_TIMEOUT" default:"1000ms"`
|
||||
CheckAllTimeout time.Duration `long:"check-all-timeout" description:"The timeout for a check-all call to other goldpinger pods" env:"CHECK_ALL_TIMEOUT" default:"5000ms"`
|
||||
TCPCheckTimeout time.Duration `long:"tcp-targets-timeout" description:"The timeout for a tcp check on the provided tcp-targets" env:"TCP_TARGETS_TIMEOUT" default:"500ms"`
|
||||
DnsCheckTimeout time.Duration `long:"dns-targets-timeout" description:"The timeout for a dns check on the provided dns-targets" env:"DNS_TARGETS_TIMEOUT" default:"500ms"`
|
||||
HTTPCheckTimeout time.Duration `long:"http-targets-timeout" description:"The timeout for a http check on the provided http-targets" env:"HTTP_TARGETS_TIMEOUT" default:"500ms"`
|
||||
}{}
|
||||
|
||||
@@ -26,7 +26,6 @@ import (
|
||||
"net/http"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/image/font"
|
||||
@@ -83,7 +82,7 @@ func HeatmapHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
ctx, cancel := context.WithTimeout(
|
||||
r.Context(),
|
||||
time.Duration(GoldpingerConfig.CheckAllTimeoutMs)*time.Millisecond,
|
||||
GoldpingerConfig.CheckAllTimeout,
|
||||
)
|
||||
defer cancel()
|
||||
|
||||
|
||||
@@ -69,7 +69,8 @@ func getHostIP(p v1.Pod) string {
|
||||
|
||||
var hostIP string
|
||||
for _, addr := range node.Status.Addresses {
|
||||
if ipMatchesConfig(addr.Address) {
|
||||
if (addr.Type == v1.NodeInternalIP || addr.Type == v1.NodeExternalIP) &&
|
||||
ipMatchesConfig(addr.Address) {
|
||||
hostIP = addr.Address
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ type Pinger struct {
|
||||
func NewPinger(pod *GoldpingerPod, resultsChan chan<- PingAllPodsResult) *Pinger {
|
||||
p := Pinger{
|
||||
pod: pod,
|
||||
timeout: time.Duration(GoldpingerConfig.PingTimeoutMs) * time.Millisecond,
|
||||
timeout: GoldpingerConfig.PingTimeout,
|
||||
resultsChan: resultsChan,
|
||||
stopChan: make(chan struct{}),
|
||||
|
||||
|
||||
72
pkg/goldpinger/probes.go
Normal file
72
pkg/goldpinger/probes.go
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright 2018 Bloomberg Finance L.P.
|
||||
//
|
||||
// Licensed 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 goldpinger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
func doDNSProbe(addr string, timeout time.Duration) error {
|
||||
resolver := net.Resolver{}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
ips, err := resolver.LookupHost(ctx, addr)
|
||||
if len(ips) == 0 {
|
||||
return fmt.Errorf("%s was resolved to 0 ips", addr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func doTCPProbe(addr string, timeout time.Duration) error {
|
||||
conn, err := net.DialTimeout("tcp", addr, timeout)
|
||||
if conn != nil {
|
||||
defer conn.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func doHTTPProbe(addr string, timeout time.Duration) error {
|
||||
client := http.Client{Timeout: timeout}
|
||||
u, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if u.Scheme != "http" && u.Scheme != "https" {
|
||||
return fmt.Errorf("invalid url scheme: '%s' in address", u.Scheme)
|
||||
}
|
||||
if u.Scheme == "https" {
|
||||
client.Transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
resp, err := client.Get(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != 200 {
|
||||
return fmt.Errorf("%s returned non-200 resp: %d", addr, resp.StatusCode)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -103,7 +103,26 @@ var (
|
||||
"host",
|
||||
},
|
||||
)
|
||||
|
||||
goldPingerTcpErrorsCounter = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "goldpinger_tcp_errors_total",
|
||||
Help: "Statistics of TCP probe errors per instance",
|
||||
},
|
||||
[]string{
|
||||
"goldpinger_instance",
|
||||
"host",
|
||||
},
|
||||
)
|
||||
goldPingerHttpErrorsCounter = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "goldpinger_http_errors_total",
|
||||
Help: "Statistics of HTTP probe errors per instance",
|
||||
},
|
||||
[]string{
|
||||
"goldpinger_instance",
|
||||
"host",
|
||||
},
|
||||
)
|
||||
bootTime = time.Now()
|
||||
)
|
||||
|
||||
@@ -115,6 +134,8 @@ func init() {
|
||||
prometheus.MustRegister(goldpingerResponseTimeKubernetesHistogram)
|
||||
prometheus.MustRegister(goldpingerErrorsCounter)
|
||||
prometheus.MustRegister(goldpingerDnsErrorsCounter)
|
||||
prometheus.MustRegister(goldPingerHttpErrorsCounter)
|
||||
prometheus.MustRegister(goldPingerTcpErrorsCounter)
|
||||
zap.L().Info("Metrics setup - see /metrics")
|
||||
}
|
||||
|
||||
@@ -173,6 +194,22 @@ func CountDnsError(host string) {
|
||||
).Inc()
|
||||
}
|
||||
|
||||
// CountTcpError counts instances of tcp errors for prober
|
||||
func CountTcpError(host string) {
|
||||
goldPingerTcpErrorsCounter.WithLabelValues(
|
||||
GoldpingerConfig.Hostname,
|
||||
host,
|
||||
).Inc()
|
||||
}
|
||||
|
||||
// CountHttpError counts instances of tcp errors for prober
|
||||
func CountHttpError(host string) {
|
||||
goldPingerHttpErrorsCounter.WithLabelValues(
|
||||
GoldpingerConfig.Hostname,
|
||||
host,
|
||||
).Inc()
|
||||
}
|
||||
|
||||
// returns a timer for easy observing of the durations of calls to kubernetes API
|
||||
func GetLabeledKubernetesCallsTimer() *prometheus.Timer {
|
||||
return prometheus.NewTimer(
|
||||
|
||||
@@ -34,8 +34,8 @@ var checkResultsMux = sync.Mutex{}
|
||||
// - there is already a pinger with the same name
|
||||
// - the pinger has the same podIP
|
||||
// - the pinger has the same hostIP
|
||||
func exists(existingPods map[string]*GoldpingerPod, new *GoldpingerPod) bool {
|
||||
old, exists := existingPods[new.Name]
|
||||
func exists(existingPods map[string]*GoldpingerPod, podName string, new *GoldpingerPod) bool {
|
||||
old, exists := existingPods[podName]
|
||||
return exists && (old.PodIP == new.PodIP) && (old.HostIP == new.HostIP)
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ func updatePingers(resultsChan chan<- PingAllPodsResult) {
|
||||
|
||||
latest := SelectPods()
|
||||
for podName, pod := range latest {
|
||||
if exists(existingPods, pod) {
|
||||
if exists(existingPods, podName, pod) {
|
||||
// This pod continues to exist in the latest iteration of the update
|
||||
// without any changes
|
||||
// Delete it from the set of pods that we wish to delete
|
||||
@@ -153,14 +153,17 @@ func updateCounters() {
|
||||
}
|
||||
}
|
||||
CountHealthyUnhealthyNodes(counterHealthy, float64(len(checkResults.PodResults))-counterHealthy)
|
||||
// check DNS, but don't block the access to checkResultsMux
|
||||
// check external targets, don't block the access to checkResultsMux
|
||||
nodesHealthy := int(counterHealthy) == len(checkResults.PodResults)
|
||||
go func(healthySoFar bool) {
|
||||
if healthySoFar {
|
||||
for _, response := range *checkDNS() {
|
||||
if response.Error != "" {
|
||||
healthySoFar = false
|
||||
break
|
||||
probeResults := checkTargets()
|
||||
for host := range probeResults {
|
||||
for _, response := range probeResults[host] {
|
||||
if response.Error != "" {
|
||||
healthySoFar = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,6 +95,8 @@ func (m *CheckAllPodResult) validateResponse(formats strfmt.Registry) error {
|
||||
if err := m.Response.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("response")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("response")
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -123,6 +125,8 @@ func (m *CheckAllPodResult) contextValidateResponse(ctx context.Context, formats
|
||||
if err := m.Response.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("response")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("response")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -23,9 +23,6 @@ type CheckAllResults struct {
|
||||
// o k
|
||||
OK *bool `json:"OK,omitempty"`
|
||||
|
||||
// dns results
|
||||
DNSResults map[string]DNSResults `json:"dnsResults,omitempty"`
|
||||
|
||||
// hosts
|
||||
Hosts []*CheckAllResultsHostsItems0 `json:"hosts"`
|
||||
|
||||
@@ -35,6 +32,9 @@ type CheckAllResults struct {
|
||||
// hosts number
|
||||
HostsNumber int32 `json:"hosts-number,omitempty"`
|
||||
|
||||
// probe results
|
||||
ProbeResults map[string]ProbeResults `json:"probeResults,omitempty"`
|
||||
|
||||
// responses
|
||||
Responses map[string]CheckAllPodResult `json:"responses,omitempty"`
|
||||
}
|
||||
@@ -43,11 +43,11 @@ type CheckAllResults struct {
|
||||
func (m *CheckAllResults) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateDNSResults(formats); err != nil {
|
||||
if err := m.validateHosts(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateHosts(formats); err != nil {
|
||||
if err := m.validateProbeResults(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
@@ -61,24 +61,6 @@ func (m *CheckAllResults) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CheckAllResults) validateDNSResults(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.DNSResults) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
for k := range m.DNSResults {
|
||||
|
||||
if val, ok := m.DNSResults[k]; ok {
|
||||
if err := val.Validate(formats); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CheckAllResults) validateHosts(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.Hosts) { // not required
|
||||
return nil
|
||||
@@ -93,6 +75,8 @@ func (m *CheckAllResults) validateHosts(formats strfmt.Registry) error {
|
||||
if err := m.Hosts[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("hosts" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("hosts" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -103,6 +87,24 @@ func (m *CheckAllResults) validateHosts(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CheckAllResults) validateProbeResults(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.ProbeResults) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
for k := range m.ProbeResults {
|
||||
|
||||
if val, ok := m.ProbeResults[k]; ok {
|
||||
if err := val.Validate(formats); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CheckAllResults) validateResponses(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.Responses) { // not required
|
||||
return nil
|
||||
@@ -115,6 +117,11 @@ func (m *CheckAllResults) validateResponses(formats strfmt.Registry) error {
|
||||
}
|
||||
if val, ok := m.Responses[k]; ok {
|
||||
if err := val.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("responses" + "." + k)
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("responses" + "." + k)
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -128,11 +135,11 @@ func (m *CheckAllResults) validateResponses(formats strfmt.Registry) error {
|
||||
func (m *CheckAllResults) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.contextValidateDNSResults(ctx, formats); err != nil {
|
||||
if err := m.contextValidateHosts(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.contextValidateHosts(ctx, formats); err != nil {
|
||||
if err := m.contextValidateProbeResults(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
@@ -146,12 +153,17 @@ func (m *CheckAllResults) ContextValidate(ctx context.Context, formats strfmt.Re
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CheckAllResults) contextValidateDNSResults(ctx context.Context, formats strfmt.Registry) error {
|
||||
func (m *CheckAllResults) contextValidateHosts(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
for k := range m.DNSResults {
|
||||
for i := 0; i < len(m.Hosts); i++ {
|
||||
|
||||
if val, ok := m.DNSResults[k]; ok {
|
||||
if err := val.ContextValidate(ctx, formats); err != nil {
|
||||
if m.Hosts[i] != nil {
|
||||
if err := m.Hosts[i].ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("hosts" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("hosts" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -161,15 +173,12 @@ func (m *CheckAllResults) contextValidateDNSResults(ctx context.Context, formats
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CheckAllResults) contextValidateHosts(ctx context.Context, formats strfmt.Registry) error {
|
||||
func (m *CheckAllResults) contextValidateProbeResults(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
for i := 0; i < len(m.Hosts); i++ {
|
||||
for k := range m.ProbeResults {
|
||||
|
||||
if m.Hosts[i] != nil {
|
||||
if err := m.Hosts[i].ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("hosts" + "." + strconv.Itoa(i))
|
||||
}
|
||||
if val, ok := m.ProbeResults[k]; ok {
|
||||
if err := val.ContextValidate(ctx, formats); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,22 +19,22 @@ import (
|
||||
// swagger:model CheckResults
|
||||
type CheckResults struct {
|
||||
|
||||
// dns results
|
||||
DNSResults DNSResults `json:"dnsResults,omitempty"`
|
||||
|
||||
// pod results
|
||||
PodResults map[string]PodResult `json:"podResults,omitempty"`
|
||||
|
||||
// probe results
|
||||
ProbeResults ProbeResults `json:"probeResults,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this check results
|
||||
func (m *CheckResults) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateDNSResults(formats); err != nil {
|
||||
if err := m.validatePodResults(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validatePodResults(formats); err != nil {
|
||||
if err := m.validateProbeResults(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
@@ -44,23 +44,6 @@ func (m *CheckResults) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CheckResults) validateDNSResults(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.DNSResults) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.DNSResults != nil {
|
||||
if err := m.DNSResults.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("dnsResults")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CheckResults) validatePodResults(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.PodResults) { // not required
|
||||
return nil
|
||||
@@ -73,6 +56,11 @@ func (m *CheckResults) validatePodResults(formats strfmt.Registry) error {
|
||||
}
|
||||
if val, ok := m.PodResults[k]; ok {
|
||||
if err := val.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("podResults" + "." + k)
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("podResults" + "." + k)
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -82,15 +70,34 @@ func (m *CheckResults) validatePodResults(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CheckResults) validateProbeResults(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.ProbeResults) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.ProbeResults != nil {
|
||||
if err := m.ProbeResults.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("probeResults")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("probeResults")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validate this check results based on the context it is used
|
||||
func (m *CheckResults) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.contextValidateDNSResults(ctx, formats); err != nil {
|
||||
if err := m.contextValidatePodResults(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.contextValidatePodResults(ctx, formats); err != nil {
|
||||
if err := m.contextValidateProbeResults(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
@@ -100,18 +107,6 @@ func (m *CheckResults) ContextValidate(ctx context.Context, formats strfmt.Regis
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CheckResults) contextValidateDNSResults(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if err := m.DNSResults.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("dnsResults")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CheckResults) contextValidatePodResults(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
for k := range m.PodResults {
|
||||
@@ -127,6 +122,20 @@ func (m *CheckResults) contextValidatePodResults(ctx context.Context, formats st
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CheckResults) contextValidateProbeResults(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if err := m.ProbeResults.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("probeResults")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("probeResults")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *CheckResults) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
|
||||
@@ -66,6 +66,8 @@ func (m *PingResults) validateReceived(formats strfmt.Registry) error {
|
||||
if err := m.Received.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("received")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("received")
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -94,6 +96,8 @@ func (m *PingResults) contextValidateReceived(ctx context.Context, formats strfm
|
||||
if err := m.Received.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("received")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("received")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -118,6 +118,8 @@ func (m *PodResult) validateResponse(formats strfmt.Registry) error {
|
||||
if err := m.Response.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("response")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("response")
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -146,6 +148,8 @@ func (m *PodResult) contextValidateResponse(ctx context.Context, formats strfmt.
|
||||
if err := m.Response.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("response")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("response")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
56
pkg/models/probe_result.go
Normal file
56
pkg/models/probe_result.go
Normal file
@@ -0,0 +1,56 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// ProbeResult probe result
|
||||
//
|
||||
// swagger:model ProbeResult
|
||||
type ProbeResult struct {
|
||||
|
||||
// error
|
||||
Error string `json:"error,omitempty"`
|
||||
|
||||
// protocol
|
||||
Protocol string `json:"protocol,omitempty"`
|
||||
|
||||
// response time ms
|
||||
ResponseTimeMs int64 `json:"response-time-ms,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this probe result
|
||||
func (m *ProbeResult) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this probe result based on context it is used
|
||||
func (m *ProbeResult) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *ProbeResult) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *ProbeResult) UnmarshalBinary(b []byte) error {
|
||||
var res ProbeResult
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
78
pkg/models/probe_results.go
Normal file
78
pkg/models/probe_results.go
Normal file
@@ -0,0 +1,78 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/validate"
|
||||
)
|
||||
|
||||
// ProbeResults probe results
|
||||
//
|
||||
// swagger:model ProbeResults
|
||||
type ProbeResults map[string][]ProbeResult
|
||||
|
||||
// Validate validates this probe results
|
||||
func (m ProbeResults) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
for k := range m {
|
||||
|
||||
if err := validate.Required(k, "body", m[k]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := 0; i < len(m[k]); i++ {
|
||||
|
||||
if err := m[k][i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName(k + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName(k + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validate this probe results based on the context it is used
|
||||
func (m ProbeResults) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
for k := range m {
|
||||
|
||||
for i := 0; i < len(m[k]); i++ {
|
||||
|
||||
if err := m[k][i].ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName(k + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName(k + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"strings"
|
||||
|
||||
@@ -61,7 +60,7 @@ func configureAPI(api *operations.GoldpingerAPI) http.Handler {
|
||||
|
||||
ctx, cancel := context.WithTimeout(
|
||||
params.HTTPRequest.Context(),
|
||||
time.Duration(goldpinger.GoldpingerConfig.PingTimeoutMs)*time.Millisecond,
|
||||
goldpinger.GoldpingerConfig.PingTimeout,
|
||||
)
|
||||
defer cancel()
|
||||
|
||||
@@ -74,7 +73,7 @@ func configureAPI(api *operations.GoldpingerAPI) http.Handler {
|
||||
|
||||
ctx, cancel := context.WithTimeout(
|
||||
params.HTTPRequest.Context(),
|
||||
time.Duration(goldpinger.GoldpingerConfig.CheckTimeoutMs)*time.Millisecond,
|
||||
goldpinger.GoldpingerConfig.CheckTimeout,
|
||||
)
|
||||
defer cancel()
|
||||
|
||||
@@ -87,7 +86,7 @@ func configureAPI(api *operations.GoldpingerAPI) http.Handler {
|
||||
|
||||
ctx, cancel := context.WithTimeout(
|
||||
params.HTTPRequest.Context(),
|
||||
time.Duration(goldpinger.GoldpingerConfig.CheckAllTimeoutMs)*time.Millisecond,
|
||||
goldpinger.GoldpingerConfig.CheckAllTimeout,
|
||||
)
|
||||
defer cancel()
|
||||
|
||||
@@ -100,7 +99,7 @@ func configureAPI(api *operations.GoldpingerAPI) http.Handler {
|
||||
|
||||
ctx, cancel := context.WithTimeout(
|
||||
params.HTTPRequest.Context(),
|
||||
time.Duration(goldpinger.GoldpingerConfig.CheckAllTimeoutMs)*time.Millisecond,
|
||||
goldpinger.GoldpingerConfig.CheckAllTimeout,
|
||||
)
|
||||
defer cancel()
|
||||
|
||||
|
||||
@@ -203,11 +203,23 @@ func init() {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"httpResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/HttpResults"
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/CheckAllPodResult"
|
||||
}
|
||||
},
|
||||
"tcpResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/TcpResults"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -217,11 +229,17 @@ func init() {
|
||||
"dnsResults": {
|
||||
"$ref": "#/definitions/DnsResults"
|
||||
},
|
||||
"httpResults": {
|
||||
"$ref": "#/definitions/HttpResults"
|
||||
},
|
||||
"podResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/PodResult"
|
||||
}
|
||||
},
|
||||
"tcpResults": {
|
||||
"$ref": "#/definitions/TcpResults"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -261,21 +279,10 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"DnsResult": {
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"response-time-ms": {
|
||||
"type": "number",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
},
|
||||
"DnsResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/DnsResult"
|
||||
"$ref": "#/definitions/ProbeResult"
|
||||
}
|
||||
},
|
||||
"HealthCheckResults": {
|
||||
@@ -295,6 +302,12 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"HttpResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/ProbeResult"
|
||||
}
|
||||
},
|
||||
"PingResults": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -342,6 +355,23 @@ func init() {
|
||||
"format": "int32"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ProbeResult": {
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"response-time-ms": {
|
||||
"type": "number",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
},
|
||||
"TcpResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/ProbeResult"
|
||||
}
|
||||
}
|
||||
}
|
||||
}`))
|
||||
@@ -518,11 +548,23 @@ func init() {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"httpResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/HttpResults"
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/CheckAllPodResult"
|
||||
}
|
||||
},
|
||||
"tcpResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/TcpResults"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -548,11 +590,17 @@ func init() {
|
||||
"dnsResults": {
|
||||
"$ref": "#/definitions/DnsResults"
|
||||
},
|
||||
"httpResults": {
|
||||
"$ref": "#/definitions/HttpResults"
|
||||
},
|
||||
"podResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/PodResult"
|
||||
}
|
||||
},
|
||||
"tcpResults": {
|
||||
"$ref": "#/definitions/TcpResults"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -592,21 +640,10 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"DnsResult": {
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"response-time-ms": {
|
||||
"type": "number",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
},
|
||||
"DnsResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/DnsResult"
|
||||
"$ref": "#/definitions/ProbeResult"
|
||||
}
|
||||
},
|
||||
"HealthCheckResults": {
|
||||
@@ -626,6 +663,12 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"HttpResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/ProbeResult"
|
||||
}
|
||||
},
|
||||
"PingResults": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -673,6 +716,23 @@ func init() {
|
||||
"format": "int32"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ProbeResult": {
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"response-time-ms": {
|
||||
"type": "number",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
},
|
||||
"TcpResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/ProbeResult"
|
||||
}
|
||||
}
|
||||
}
|
||||
}`))
|
||||
|
||||
@@ -42,7 +42,7 @@ type CheckAllPods struct {
|
||||
func (o *CheckAllPods) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
r = rCtx
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewCheckAllPodsParams()
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
|
||||
@@ -42,7 +42,7 @@ type CheckServicePods struct {
|
||||
func (o *CheckServicePods) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
r = rCtx
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewCheckServicePodsParams()
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
|
||||
@@ -42,7 +42,7 @@ type ClusterHealth struct {
|
||||
func (o *ClusterHealth) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
r = rCtx
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewClusterHealthParams()
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
|
||||
@@ -42,7 +42,7 @@ type Healthz struct {
|
||||
func (o *Healthz) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
r = rCtx
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewHealthzParams()
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
|
||||
@@ -42,7 +42,7 @@ type Ping struct {
|
||||
func (o *Ping) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
r = rCtx
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewPingParams()
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
|
||||
@@ -305,9 +305,6 @@ func (s *Server) Serve() (err error) {
|
||||
s.Fatalf("no certificate was configured for TLS")
|
||||
}
|
||||
|
||||
// must have at least one certificate or panics
|
||||
httpsServer.TLSConfig.BuildNameToCertificate()
|
||||
|
||||
configureServer(httpsServer, "https", s.httpsServerL.Addr().String())
|
||||
|
||||
servers = append(servers, httpsServer)
|
||||
|
||||
@@ -213,6 +213,7 @@ var loadingDialog = function(enable){
|
||||
var global_data = undefined;
|
||||
var s; // the sigma graph
|
||||
|
||||
|
||||
var main = function(timeout){
|
||||
|
||||
timeout = timeout || Number($("#timeout").val()) || 5.0;
|
||||
@@ -229,7 +230,6 @@ var main = function(timeout){
|
||||
|
||||
// prepare nodes
|
||||
var nodes = [];
|
||||
var dnsCheckNodes = [];
|
||||
var podIPs = [];
|
||||
var resp = data.responses;
|
||||
for (let podIP in resp) {
|
||||
@@ -284,39 +284,44 @@ var main = function(timeout){
|
||||
})
|
||||
})
|
||||
console.log(nodes);
|
||||
|
||||
if ('dnsResults' in data) {
|
||||
var yoffset = 0;
|
||||
for (let checkedHost in data.dnsResults) {
|
||||
let value = data.dnsResults[checkedHost];
|
||||
var allOk = true;
|
||||
for (let pod in value) {
|
||||
var podData = value[pod];
|
||||
var elapsed = 0;
|
||||
if ('response-time-ms' in podData) {
|
||||
elapsed = podData['response-time-ms'];
|
||||
}
|
||||
var podOk = (!('error' in podData));
|
||||
allOk = allOk && podOk
|
||||
edges.push({
|
||||
source: pod,
|
||||
target: checkedHost,
|
||||
_data: {
|
||||
|
||||
var probeResultsNodes = [];
|
||||
if ('probeResults' in data) {
|
||||
var yoffset = 0;
|
||||
for (let checkedHost in data.probeResults) {
|
||||
let value = data.probeResults[checkedHost];
|
||||
var allOk = true;
|
||||
for (let pod in value) {
|
||||
for (var i = 0; i < value[pod].length; i++){
|
||||
var elapsed = 0;
|
||||
var podData = value[pod][i];
|
||||
if ('response-time-ms' in podData) {
|
||||
elapsed = podData['response-time-ms'];
|
||||
}
|
||||
var podOk = (!('error' in podData));
|
||||
allOk = allOk && podOk
|
||||
edges.push({
|
||||
source: pod,
|
||||
target: checkedHost,
|
||||
_data: {
|
||||
"OK": podOk,
|
||||
"elapsed": elapsed,
|
||||
"isDNSCheckNode": true,
|
||||
}
|
||||
});
|
||||
"isprobeResultsNode": true,
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
value["OK"] = allOk
|
||||
dnsCheckNodes.push({
|
||||
"label": checkedHost,
|
||||
"id": checkedHost,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
_data: value,
|
||||
});
|
||||
}
|
||||
probeResultsNodes.push({
|
||||
"label": checkedHost,
|
||||
"id": checkedHost,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
_data: value,
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// build the actual graph
|
||||
@@ -351,7 +356,7 @@ var main = function(timeout){
|
||||
});
|
||||
|
||||
// generate any dns nodes on the graph
|
||||
dnsCheckNodes.forEach(function(node, i, a){
|
||||
probeResultsNodes.forEach(function(node, i, a){
|
||||
node.x = 2;
|
||||
node.y = (0.6 * i / a.length) - 0.8;
|
||||
node.size = 10;
|
||||
@@ -359,7 +364,6 @@ var main = function(timeout){
|
||||
if (!node._data.OK) {
|
||||
node.color = "red";
|
||||
}
|
||||
//console.log(node);
|
||||
s.graph.addNode(node);
|
||||
});
|
||||
|
||||
@@ -374,7 +378,7 @@ var main = function(timeout){
|
||||
if (!edge._data.OK) {
|
||||
color = "red";
|
||||
}
|
||||
if ("isDNSCheckNode" in edge._data) {
|
||||
if ("isprobeResultsNode" in edge._data) {
|
||||
type = "dashed";
|
||||
}
|
||||
var edge = {
|
||||
|
||||
18
swagger.yml
18
swagger.yml
@@ -12,17 +12,21 @@ definitions:
|
||||
type: integer
|
||||
ping:
|
||||
type: integer
|
||||
DnsResult:
|
||||
ProbeResult:
|
||||
properties:
|
||||
response-time-ms:
|
||||
type: number
|
||||
format: int64
|
||||
error:
|
||||
type: string
|
||||
DnsResults:
|
||||
protocol:
|
||||
type: string
|
||||
ProbeResults:
|
||||
type: object
|
||||
additionalProperties:
|
||||
$ref: '#/definitions/DnsResult'
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/ProbeResult'
|
||||
PingResults:
|
||||
type: object
|
||||
properties:
|
||||
@@ -60,8 +64,8 @@ definitions:
|
||||
CheckResults:
|
||||
type: object
|
||||
properties:
|
||||
dnsResults:
|
||||
$ref: '#/definitions/DnsResults'
|
||||
probeResults:
|
||||
$ref: '#/definitions/ProbeResults'
|
||||
podResults:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -110,10 +114,10 @@ definitions:
|
||||
podIP:
|
||||
type: string
|
||||
format: ipv4
|
||||
dnsResults:
|
||||
probeResults:
|
||||
type: object
|
||||
additionalProperties:
|
||||
$ref: '#/definitions/DnsResults'
|
||||
$ref: '#/definitions/ProbeResults'
|
||||
responses:
|
||||
type: object
|
||||
additionalProperties:
|
||||
|
||||
Reference in New Issue
Block a user