mirror of
https://github.com/bloomberg/goldpinger.git
synced 2026-02-20 21:10:09 +00:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f99ba84d17 | ||
|
|
75315a872a | ||
|
|
ae67cf3594 | ||
|
|
433a6b8b88 | ||
|
|
0660db7922 | ||
|
|
58285945f6 | ||
|
|
8a014ad7f4 | ||
|
|
096b149afa | ||
|
|
5840353d9f | ||
|
|
ac9bf4224d | ||
|
|
bcbc2ac6fc | ||
|
|
192fc433a2 | ||
|
|
dc3864f170 | ||
|
|
8a759433eb | ||
|
|
1b12b7dc6b | ||
|
|
9adf26e2cb | ||
|
|
927125bbc5 | ||
|
|
7585d010a6 | ||
|
|
5e0ecdd8d1 | ||
|
|
d834433d30 | ||
|
|
b64f5152f2 | ||
|
|
bbac97c17e | ||
|
|
74ffcc8d2e | ||
|
|
9586e16237 | ||
|
|
6541250aa9 | ||
|
|
31a503d831 | ||
|
|
195691518b | ||
|
|
aa66c94d47 | ||
|
|
7b2affea29 | ||
|
|
d1a86eae93 | ||
|
|
a2e5925210 | ||
|
|
d243f0fb59 | ||
|
|
477ba69a72 |
@@ -36,4 +36,5 @@ deploy:
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
||||
go: "1.10.x"
|
||||
condition: -n "$DOCKER_PASSWORD"
|
||||
|
||||
2
Makefile
2
Makefile
@@ -1,5 +1,5 @@
|
||||
name ?= goldpinger
|
||||
version ?= 1.5.0
|
||||
version ?= 2.0.0
|
||||
bin ?= goldpinger
|
||||
pkg ?= "github.com/bloomberg/goldpinger"
|
||||
tag = $(name):$(version)
|
||||
|
||||
41
README.md
41
README.md
@@ -18,6 +18,7 @@ Oh, and it gives you the graph below for your cluster. Check out the [video expl
|
||||
- [Installation](#installation)
|
||||
- [Authentication with Kubernetes API](#authentication-with-kubernetes-api)
|
||||
- [Example YAML](#example-yaml)
|
||||
- [Note on DNS](#note-on-dns)
|
||||
- [Usage](#usage)
|
||||
- [UI](#ui)
|
||||
- [API](#api)
|
||||
@@ -126,12 +127,12 @@ spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: goldpinger
|
||||
version: "1.0.0"
|
||||
version: "1.5.0"
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: goldpinger
|
||||
version: "1.0.0"
|
||||
version: "1.5.0"
|
||||
spec:
|
||||
containers:
|
||||
- name: goldpinger
|
||||
@@ -150,10 +151,22 @@ spec:
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: status.podIP
|
||||
image: "docker.io/bloomberg/goldpinger:1.4.0"
|
||||
image: "docker.io/bloomberg/goldpinger:1.5.0"
|
||||
ports:
|
||||
- containerPort: 80
|
||||
name: http
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 80
|
||||
initialDelaySeconds: 20
|
||||
periodSeconds: 5
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 80
|
||||
initialDelaySeconds: 20
|
||||
periodSeconds: 5
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
@@ -191,6 +204,28 @@ subjects:
|
||||
|
||||
You can also see [an example of using `kubeconfig` in the `./extras`](./extras/example-with-kubeconfig.yaml).
|
||||
|
||||
### Note on DNS
|
||||
|
||||
Note, that on top of resolving the other pods, all instances can also try to resolve arbitrary DNS. This allows you to test your DNS setup.
|
||||
|
||||
From `--help`:
|
||||
|
||||
```sh
|
||||
--host-to-resolve= A host to attempt dns resolve on (space delimited) [$HOSTS_TO_RESOLVE]
|
||||
```
|
||||
|
||||
So in order to test two domains, we could add an extra env var to the example above:
|
||||
|
||||
```yaml
|
||||
- name: HOSTS_TO_RESOLVE
|
||||
value: "www.bloomberg.com one.two.three"
|
||||
```
|
||||
|
||||
and `goldpinger` should show something like this:
|
||||
|
||||

|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
### UI
|
||||
|
||||
BIN
extras/dns-screenshot.png
Normal file
BIN
extras/dns-screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 374 KiB |
@@ -5,19 +5,19 @@ metadata:
|
||||
name: goldpinger
|
||||
labels:
|
||||
app: goldpinger
|
||||
version: "1.1.0"
|
||||
version: "1.5.0"
|
||||
spec:
|
||||
updateStrategy:
|
||||
type: RollingUpdate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: goldpinger
|
||||
version: "1.1.0"
|
||||
version: "1.5.0"
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: goldpinger
|
||||
version: "1.1.0"
|
||||
version: "1.5.0"
|
||||
spec:
|
||||
# if you'd like to use a secret to inject a kubeconfig, you can do it like this
|
||||
volumes:
|
||||
@@ -48,10 +48,22 @@ spec:
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: status.podIP
|
||||
image: "docker.io/bloomberg/goldpinger:1.4.0"
|
||||
image: "docker.io/bloomberg/goldpinger:1.5.0"
|
||||
ports:
|
||||
- containerPort: 80
|
||||
name: http
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 80
|
||||
initialDelaySeconds: 20
|
||||
periodSeconds: 5
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 80
|
||||
initialDelaySeconds: 20
|
||||
periodSeconds: 5
|
||||
volumeMounts:
|
||||
- mountPath: /.kube/
|
||||
name: kubeconfig
|
||||
|
||||
@@ -47,7 +47,7 @@ func NewCheckServicePodsOK() *CheckServicePodsOK {
|
||||
Success, return response
|
||||
*/
|
||||
type CheckServicePodsOK struct {
|
||||
Payload models.CheckResults
|
||||
Payload *models.CheckResults
|
||||
}
|
||||
|
||||
func (o *CheckServicePodsOK) Error() string {
|
||||
@@ -56,8 +56,10 @@ func (o *CheckServicePodsOK) Error() string {
|
||||
|
||||
func (o *CheckServicePodsOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error {
|
||||
|
||||
o.Payload = new(models.CheckResults)
|
||||
|
||||
// response payload
|
||||
if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF {
|
||||
if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ package goldpinger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -27,7 +28,7 @@ import (
|
||||
|
||||
// CheckNeighbours queries the kubernetes API server for all other goldpinger pods
|
||||
// then calls Ping() on each one
|
||||
func CheckNeighbours(ps *PodSelecter) models.CheckResults {
|
||||
func CheckNeighbours(ps *PodSelecter) *models.CheckResults {
|
||||
return PingAllPods(ps.SelectPods())
|
||||
}
|
||||
|
||||
@@ -50,7 +51,25 @@ func pickPodHostIP(podIP, hostIP string) string {
|
||||
return podIP
|
||||
}
|
||||
|
||||
func PingAllPods(pods map[string]string) models.CheckResults {
|
||||
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
|
||||
}
|
||||
return &results
|
||||
}
|
||||
|
||||
func PingAllPods(pods map[string]string) *models.CheckResults {
|
||||
|
||||
result := models.CheckResults{}
|
||||
|
||||
@@ -84,11 +103,15 @@ func PingAllPods(pods map[string]string) models.CheckResults {
|
||||
wg.Done()
|
||||
}(podIP, hostIP)
|
||||
}
|
||||
if len(GoldpingerConfig.DnsHosts) > 0 {
|
||||
result.DNSResults = *checkDNS()
|
||||
}
|
||||
wg.Wait()
|
||||
close(ch)
|
||||
|
||||
counterHealthy, counterUnhealthy := 0.0, 0.0
|
||||
|
||||
result.PodResults = make(map[string]models.PodResult)
|
||||
for response := range ch {
|
||||
var podIPv4 strfmt.IPv4
|
||||
podIPv4.UnmarshalText([]byte(response.podIP))
|
||||
@@ -97,10 +120,10 @@ func PingAllPods(pods map[string]string) models.CheckResults {
|
||||
} else {
|
||||
counterUnhealthy++
|
||||
}
|
||||
result[response.podIP] = response.podResult
|
||||
result.PodResults[response.podIP] = response.podResult
|
||||
}
|
||||
CountHealthyUnhealthyNodes(counterHealthy, counterUnhealthy)
|
||||
return result
|
||||
return &result
|
||||
}
|
||||
|
||||
type CheckServicePodsResult struct {
|
||||
@@ -161,6 +184,18 @@ func CheckAllPods(pods map[string]string) *models.CheckAllResults {
|
||||
HostIP: response.hostIPv4,
|
||||
PodIP: podIPv4,
|
||||
})
|
||||
if response.checkAllPodResult.Response != nil &&
|
||||
response.checkAllPodResult.Response.DNSResults != nil {
|
||||
if result.DNSResults == nil {
|
||||
result.DNSResults = make(map[string]models.DNSResults)
|
||||
}
|
||||
for host := range response.checkAllPodResult.Response.DNSResults {
|
||||
if result.DNSResults[host] == nil {
|
||||
result.DNSResults[host] = make(map[string]models.DNSResult)
|
||||
}
|
||||
result.DNSResults[host][response.podIP] = response.checkAllPodResult.Response.DNSResults[host]
|
||||
}
|
||||
}
|
||||
}
|
||||
return &result
|
||||
}
|
||||
|
||||
@@ -31,4 +31,6 @@ var GoldpingerConfig = struct {
|
||||
LabelSelector string `long:"label-selector" description:"label selector to use to discover goldpinger pods in the cluster" env:"LABEL_SELECTOR" default:"app=goldpinger"`
|
||||
KubernetesClient *kubernetes.Clientset
|
||||
*PodSelecter
|
||||
|
||||
DnsHosts []string `long:"host-to-resolve" description:"A host to attempt dns resolve on (space delimited)" env:"HOSTS_TO_RESOLVE" env-delim:" "`
|
||||
}{}
|
||||
|
||||
@@ -115,7 +115,7 @@ func HeatmapHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// draw all the boxes
|
||||
for sourceIP, results := range checkResults.Responses {
|
||||
if *results.OK {
|
||||
for destinationIP, response := range results.Response {
|
||||
for destinationIP, response := range results.Response.PodResults {
|
||||
x, y := getPingBoxCoordinates(order[sourceIP], order[destinationIP], boxSize, paddingSize)
|
||||
color := getPingBoxColor(response.ResponseTimeMs, tresholdLatencies)
|
||||
drawPingBox(canvas, boxSize+x, boxSize+y, boxSize, color)
|
||||
|
||||
@@ -82,6 +82,16 @@ var (
|
||||
"type",
|
||||
},
|
||||
)
|
||||
goldpingerDnsErrorsCounter = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "goldpinger_dns_errors_total",
|
||||
Help: "Statistics of DNS errors per instance",
|
||||
},
|
||||
[]string{
|
||||
"goldpinger_instance",
|
||||
"host",
|
||||
},
|
||||
)
|
||||
|
||||
bootTime = time.Now()
|
||||
)
|
||||
@@ -92,6 +102,7 @@ func init() {
|
||||
prometheus.MustRegister(goldpingerResponseTimePeersHistogram)
|
||||
prometheus.MustRegister(goldpingerResponseTimeKubernetesHistogram)
|
||||
prometheus.MustRegister(goldpingerErrorsCounter)
|
||||
prometheus.MustRegister(goldpingerDnsErrorsCounter)
|
||||
log.Println("Metrics setup - see /metrics")
|
||||
}
|
||||
|
||||
@@ -131,6 +142,14 @@ func CountError(errorType string) {
|
||||
).Inc()
|
||||
}
|
||||
|
||||
// counts instances of dns errors
|
||||
func CountDnsError(host string) {
|
||||
goldpingerDnsErrorsCounter.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(
|
||||
|
||||
@@ -31,7 +31,7 @@ func StartUpdater() {
|
||||
for {
|
||||
results := PingAllPods(GoldpingerConfig.PodSelecter.SelectPods())
|
||||
var troublemakers []string
|
||||
for podIP, value := range results {
|
||||
for podIP, value := range results.PodResults {
|
||||
if *value.OK != true {
|
||||
troublemakers = append(troublemakers, fmt.Sprintf("%s (%s)", podIP, value.HostIP.String()))
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ type CheckAllPodResult struct {
|
||||
Error string `json:"error,omitempty"`
|
||||
|
||||
// response
|
||||
Response CheckResults `json:"response,omitempty"`
|
||||
Response *CheckResults `json:"response,omitempty"`
|
||||
|
||||
// status code
|
||||
StatusCode int32 `json:"status-code,omitempty"`
|
||||
@@ -71,11 +71,13 @@ func (m *CheckAllPodResult) validateResponse(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := m.Response.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("response")
|
||||
if m.Response != nil {
|
||||
if err := m.Response.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("response")
|
||||
}
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -22,6 +22,9 @@ type CheckAllResults struct {
|
||||
// o k
|
||||
OK *bool `json:"OK,omitempty"`
|
||||
|
||||
// dns results
|
||||
DNSResults map[string]DNSResults `json:"dnsResults,omitempty"`
|
||||
|
||||
// hosts
|
||||
Hosts []*CheckAllResultsHostsItems0 `json:"hosts"`
|
||||
|
||||
@@ -39,6 +42,10 @@ type CheckAllResults struct {
|
||||
func (m *CheckAllResults) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateDNSResults(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateHosts(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
@@ -53,6 +60,25 @@ 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
|
||||
|
||||
@@ -9,28 +9,31 @@ import (
|
||||
strfmt "github.com/go-openapi/strfmt"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/go-openapi/validate"
|
||||
)
|
||||
|
||||
// CheckResults check results
|
||||
// swagger:model CheckResults
|
||||
type CheckResults map[string]PodResult
|
||||
type CheckResults struct {
|
||||
|
||||
// dns results
|
||||
DNSResults DNSResults `json:"dnsResults,omitempty"`
|
||||
|
||||
// pod results
|
||||
PodResults map[string]PodResult `json:"podResults,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this check results
|
||||
func (m CheckResults) Validate(formats strfmt.Registry) error {
|
||||
func (m *CheckResults) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
for k := range m {
|
||||
|
||||
if err := validate.Required(k, "body", m[k]); err != nil {
|
||||
return err
|
||||
}
|
||||
if val, ok := m[k]; ok {
|
||||
if err := val.Validate(formats); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := m.validateDNSResults(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validatePodResults(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
@@ -38,3 +41,59 @@ 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 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
|
||||
}
|
||||
|
||||
for k := range m.PodResults {
|
||||
|
||||
if err := validate.Required("podResults"+"."+k, "body", m.PodResults[k]); err != nil {
|
||||
return err
|
||||
}
|
||||
if val, ok := m.PodResults[k]; ok {
|
||||
if err := val.Validate(formats); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *CheckResults) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *CheckResults) UnmarshalBinary(b []byte) error {
|
||||
var res CheckResults
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
|
||||
46
pkg/models/dns_result.go
Normal file
46
pkg/models/dns_result.go
Normal file
@@ -0,0 +1,46 @@
|
||||
// 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 (
|
||||
strfmt "github.com/go-openapi/strfmt"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// DNSResult Dns result
|
||||
// swagger:model DnsResult
|
||||
type DNSResult struct {
|
||||
|
||||
// error
|
||||
Error string `json:"error,omitempty"`
|
||||
|
||||
// response time ms
|
||||
ResponseTimeMs int64 `json:"response-time-ms,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this Dns result
|
||||
func (m *DNSResult) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *DNSResult) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *DNSResult) UnmarshalBinary(b []byte) error {
|
||||
var res DNSResult
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
40
pkg/models/dns_results.go
Normal file
40
pkg/models/dns_results.go
Normal file
@@ -0,0 +1,40 @@
|
||||
// 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 (
|
||||
strfmt "github.com/go-openapi/strfmt"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/validate"
|
||||
)
|
||||
|
||||
// DNSResults Dns results
|
||||
// swagger:model DnsResults
|
||||
type DNSResults map[string]DNSResult
|
||||
|
||||
// Validate validates this Dns results
|
||||
func (m DNSResults) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
for k := range m {
|
||||
|
||||
if err := validate.Required(k, "body", m[k]); err != nil {
|
||||
return err
|
||||
}
|
||||
if val, ok := m[k]; ok {
|
||||
if err := val.Validate(formats); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -7,7 +7,7 @@ Package restapi Goldpinger
|
||||
http
|
||||
Host: localhost
|
||||
BasePath: /
|
||||
Version: 1.0.0
|
||||
Version: 2.0.0
|
||||
|
||||
Consumes:
|
||||
- application/json
|
||||
|
||||
@@ -21,7 +21,7 @@ func init() {
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"title": "Goldpinger",
|
||||
"version": "1.0.0"
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"paths": {
|
||||
"/check": {
|
||||
@@ -143,6 +143,12 @@ func init() {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"dnsResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/DnsResults"
|
||||
}
|
||||
},
|
||||
"hosts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -176,9 +182,34 @@ func init() {
|
||||
}
|
||||
},
|
||||
"CheckResults": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"dnsResults": {
|
||||
"$ref": "#/definitions/DnsResults"
|
||||
},
|
||||
"podResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/PodResult"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"DnsResult": {
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"response-time-ms": {
|
||||
"type": "number",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
},
|
||||
"DnsResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/PodResult"
|
||||
"$ref": "#/definitions/DnsResult"
|
||||
}
|
||||
},
|
||||
"HealthCheckResults": {
|
||||
@@ -244,7 +275,7 @@ func init() {
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"title": "Goldpinger",
|
||||
"version": "1.0.0"
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"paths": {
|
||||
"/check": {
|
||||
@@ -366,6 +397,12 @@ func init() {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"dnsResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/DnsResults"
|
||||
}
|
||||
},
|
||||
"hosts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -399,9 +436,34 @@ func init() {
|
||||
}
|
||||
},
|
||||
"CheckResults": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"dnsResults": {
|
||||
"$ref": "#/definitions/DnsResults"
|
||||
},
|
||||
"podResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/PodResult"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"DnsResult": {
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"response-time-ms": {
|
||||
"type": "number",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
},
|
||||
"DnsResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/PodResult"
|
||||
"$ref": "#/definitions/DnsResult"
|
||||
}
|
||||
},
|
||||
"HealthCheckResults": {
|
||||
|
||||
@@ -25,7 +25,7 @@ type CheckServicePodsOK struct {
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload models.CheckResults `json:"body,omitempty"`
|
||||
Payload *models.CheckResults `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewCheckServicePodsOK creates CheckServicePodsOK with default headers values
|
||||
@@ -35,13 +35,13 @@ func NewCheckServicePodsOK() *CheckServicePodsOK {
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the check service pods o k response
|
||||
func (o *CheckServicePodsOK) WithPayload(payload models.CheckResults) *CheckServicePodsOK {
|
||||
func (o *CheckServicePodsOK) WithPayload(payload *models.CheckResults) *CheckServicePodsOK {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the check service pods o k response
|
||||
func (o *CheckServicePodsOK) SetPayload(payload models.CheckResults) {
|
||||
func (o *CheckServicePodsOK) SetPayload(payload *models.CheckResults) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
@@ -49,13 +49,10 @@ func (o *CheckServicePodsOK) SetPayload(payload models.CheckResults) {
|
||||
func (o *CheckServicePodsOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(200)
|
||||
payload := o.Payload
|
||||
if payload == nil {
|
||||
// return empty map
|
||||
payload = models.CheckResults{}
|
||||
}
|
||||
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,13 +65,13 @@ type GoldpingerAPI struct {
|
||||
Middleware func(middleware.Builder) http.Handler
|
||||
|
||||
// BasicAuthenticator generates a runtime.Authenticator from the supplied basic auth function.
|
||||
// It has a default implemention in the security package, however you can replace it for your particular usage.
|
||||
// It has a default implementation in the security package, however you can replace it for your particular usage.
|
||||
BasicAuthenticator func(security.UserPassAuthentication) runtime.Authenticator
|
||||
// APIKeyAuthenticator generates a runtime.Authenticator from the supplied token auth function.
|
||||
// It has a default implemention in the security package, however you can replace it for your particular usage.
|
||||
// It has a default implementation in the security package, however you can replace it for your particular usage.
|
||||
APIKeyAuthenticator func(string, string, security.TokenAuthentication) runtime.Authenticator
|
||||
// BearerAuthenticator generates a runtime.Authenticator from the supplied bearer token auth function.
|
||||
// It has a default implemention in the security package, however you can replace it for your particular usage.
|
||||
// It has a default implementation in the security package, however you can replace it for your particular usage.
|
||||
BearerAuthenticator func(string, security.ScopedTokenAuthentication) runtime.Authenticator
|
||||
|
||||
// JSONConsumer registers a consumer for a "application/json" mime type
|
||||
|
||||
@@ -87,7 +87,7 @@ type Server struct {
|
||||
TLSHost string `long:"tls-host" description:"the IP to listen on for tls, when not specified it's the same as --host" env:"TLS_HOST"`
|
||||
TLSPort int `long:"tls-port" description:"the port to listen on for secure connections, defaults to a random value" env:"TLS_PORT"`
|
||||
TLSCertificate flags.Filename `long:"tls-certificate" description:"the certificate to use for secure connections" env:"TLS_CERTIFICATE"`
|
||||
TLSCertificateKey flags.Filename `long:"tls-key" description:"the private key to use for secure conections" env:"TLS_PRIVATE_KEY"`
|
||||
TLSCertificateKey flags.Filename `long:"tls-key" description:"the private key to use for secure connections" env:"TLS_PRIVATE_KEY"`
|
||||
TLSCACertificate flags.Filename `long:"tls-ca" description:"the certificate authority file to be used with mutual tls auth" env:"TLS_CA_CERTIFICATE"`
|
||||
TLSListenLimit int `long:"tls-listen-limit" description:"limit the number of outstanding requests"`
|
||||
TLSKeepAlive time.Duration `long:"tls-keep-alive" description:"sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)"`
|
||||
|
||||
@@ -49,7 +49,7 @@ limitations under the License.
|
||||
<script type="text/javascript" src="static/sigma.js/1.1.0/plugins/sigma.renderers.parallelEdges.min.js"></script>
|
||||
<script type="text/javascript" src="static/sigma.js/1.1.0/plugins/sigma.renderers.snapshot.min.js"></script>
|
||||
<script type="text/javascript" src="static/sigma.js/1.1.0/plugins/sigma.statistics.HITS.min.js"></script>
|
||||
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link rel="stylesheet" href="static/bootstrap/3.3.7/css/bootstrap.min.css" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="static/bootstrap/3.3.7/css/bootstrap-theme.min.css" crossorigin="anonymous">
|
||||
@@ -219,7 +219,7 @@ var main = function(timeout){
|
||||
|
||||
loadingDialog(true);
|
||||
|
||||
fetchJSON("/check_all?timeout="+timeout)
|
||||
fetchJSON("check_all?timeout="+timeout)
|
||||
.then(function(data){
|
||||
|
||||
loadingDialog(false);
|
||||
@@ -229,6 +229,7 @@ var main = function(timeout){
|
||||
|
||||
// prepare nodes
|
||||
var nodes = [];
|
||||
var dnsCheckNodes = [];
|
||||
var podIPs = [];
|
||||
var resp = data.responses;
|
||||
for (let podIP in resp) {
|
||||
@@ -247,7 +248,7 @@ var main = function(timeout){
|
||||
var edges = [];
|
||||
var resp = data.responses;
|
||||
for (let callId in resp) {
|
||||
var call = resp[callId].response;
|
||||
var call = resp[callId].response['podResults'];
|
||||
if (typeof call !== 'string'){
|
||||
for (let target in call) {
|
||||
edges.push({
|
||||
@@ -258,6 +259,7 @@ var main = function(timeout){
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
console.log(edges);
|
||||
console.log(nodes);
|
||||
|
||||
@@ -279,6 +281,39 @@ 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: {
|
||||
"OK": podOk,
|
||||
"elapsed": elapsed,
|
||||
"isDNSCheckNode": true,
|
||||
}
|
||||
});
|
||||
}
|
||||
value["OK"] = allOk
|
||||
dnsCheckNodes.push({
|
||||
"label": checkedHost,
|
||||
"id": checkedHost,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
_data: value,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// build the actual graph
|
||||
s = new sigma({
|
||||
@@ -311,6 +346,19 @@ var main = function(timeout){
|
||||
s.graph.addNode(node);
|
||||
});
|
||||
|
||||
// generate any dns nodes on the graph
|
||||
dnsCheckNodes.forEach(function(node, i, a){
|
||||
node.x = 2;
|
||||
node.y = (0.6 * i / a.length) - 0.8;
|
||||
node.size = 10;
|
||||
node.color = "#4CC40B";
|
||||
if (!node._data.OK) {
|
||||
node.color = "red";
|
||||
}
|
||||
//console.log(node);
|
||||
s.graph.addNode(node);
|
||||
});
|
||||
|
||||
// generate the edges
|
||||
edges.forEach(function(edge, i){
|
||||
var color = "#ccc";
|
||||
@@ -322,6 +370,9 @@ var main = function(timeout){
|
||||
if (!edge._data.OK) {
|
||||
color = "red";
|
||||
}
|
||||
if ("isDNSCheckNode" in edge._data) {
|
||||
type = "dashed";
|
||||
}
|
||||
var edge = {
|
||||
id: "e" + i,
|
||||
source: edge.source,
|
||||
@@ -372,7 +423,7 @@ var main = function(timeout){
|
||||
$('#modal-title').html(node.id);
|
||||
$('#modal-body').html(prepareJSON(node._data));
|
||||
|
||||
// show the things
|
||||
// show the things
|
||||
$('#modal-window-code').modal('show');
|
||||
});
|
||||
s.bind("clickStage", function (e) {
|
||||
@@ -388,7 +439,7 @@ var main = function(timeout){
|
||||
$('#modal-title').html(edge.id);
|
||||
$('#modal-body').html(prepareJSON(edge._data));
|
||||
|
||||
// show the things
|
||||
// show the things
|
||||
$('#modal-window-code').modal('show');
|
||||
});
|
||||
})
|
||||
@@ -404,10 +455,10 @@ main();
|
||||
|
||||
$("#show-data").click(function (e) {
|
||||
// fill the voids
|
||||
$('#modal-title').html("/check_all");
|
||||
$('#modal-title').html("check_all");
|
||||
$('#modal-body').html(prepareJSON(global_data));
|
||||
|
||||
// show the things
|
||||
// show the things
|
||||
$('#modal-window-code').modal('show');
|
||||
});
|
||||
$("#reload-graph").click(function (e) {
|
||||
@@ -431,3 +482,4 @@ $("#update-heatmap").click(function (e) {
|
||||
</html>
|
||||
|
||||
|
||||
|
||||
|
||||
26
swagger.yml
26
swagger.yml
@@ -1,7 +1,7 @@
|
||||
---
|
||||
swagger: '2.0'
|
||||
info:
|
||||
version: 1.0.0
|
||||
version: 2.0.0
|
||||
title: Goldpinger
|
||||
definitions:
|
||||
CallStats:
|
||||
@@ -12,6 +12,17 @@ definitions:
|
||||
type: integer
|
||||
ping:
|
||||
type: integer
|
||||
DnsResult:
|
||||
properties:
|
||||
response-time-ms:
|
||||
type: number
|
||||
format: int64
|
||||
error:
|
||||
type: string
|
||||
DnsResults:
|
||||
type: object
|
||||
additionalProperties:
|
||||
$ref: '#/definitions/DnsResult'
|
||||
PingResults:
|
||||
type: object
|
||||
properties:
|
||||
@@ -42,8 +53,13 @@ definitions:
|
||||
description: wall clock time in milliseconds
|
||||
CheckResults:
|
||||
type: object
|
||||
additionalProperties:
|
||||
$ref: '#/definitions/PodResult'
|
||||
properties:
|
||||
dnsResults:
|
||||
$ref: '#/definitions/DnsResults'
|
||||
podResults:
|
||||
type: object
|
||||
additionalProperties:
|
||||
$ref: '#/definitions/PodResult'
|
||||
CheckAllPodResult:
|
||||
type: object
|
||||
properties:
|
||||
@@ -83,6 +99,10 @@ definitions:
|
||||
podIP:
|
||||
type: string
|
||||
format: ipv4
|
||||
dnsResults:
|
||||
type: object
|
||||
additionalProperties:
|
||||
$ref: '#/definitions/DnsResults'
|
||||
responses:
|
||||
type: object
|
||||
additionalProperties:
|
||||
|
||||
Reference in New Issue
Block a user