mirror of
https://github.com/bloomberg/goldpinger.git
synced 2026-04-06 18:36:51 +00:00
Compare commits
5 Commits
goldpinger
...
v3.11.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7935a11f9d | ||
|
|
de7f4e9004 | ||
|
|
145d2bf000 | ||
|
|
641b658f23 | ||
|
|
832bc7b598 |
2
Makefile
2
Makefile
@@ -1,5 +1,5 @@
|
||||
name ?= goldpinger
|
||||
version ?= v3.10.3
|
||||
version ?= v3.11.0
|
||||
bin ?= goldpinger
|
||||
pkg ?= "github.com/bloomberg/goldpinger"
|
||||
tag = $(name):$(version)
|
||||
|
||||
53
README.md
53
README.md
@@ -25,6 +25,7 @@ Oh, and it gives you the graph below for your cluster. Check out the [video expl
|
||||
- [Authentication with Kubernetes API](#authentication-with-kubernetes-api)
|
||||
- [Example YAML](#example-yaml)
|
||||
- [Note on DNS](#note-on-dns)
|
||||
- [UDP probe for packet loss, hop count, and RTT](#udp-probe-for-packet-loss-hop-count-and-rtt)
|
||||
- [Usage](#usage)
|
||||
- [UI](#ui)
|
||||
- [API](#api)
|
||||
@@ -287,10 +288,56 @@ Instances can also be configured to do simple TCP or HTTP checks on external tar
|
||||
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.
|
||||
the timeouts for the TCP, DNS and HTTP checks can be configured via `TCP_TARGETS_TIMEOUT`, `DNS_TARGETS_TIMEOUT` and `HTTP_TARGETS_TIMEOUT` respectively.
|
||||
|
||||

|
||||
|
||||
### UDP probe for packet loss, hop count, and RTT
|
||||
|
||||
In natively routed Kubernetes environments (e.g. Cilium, Calico in BGP mode), the existing HTTP ping can mask network issues: TCP retransmits hide packet loss, and HTTP latency includes the 3-way handshake, TLS, and application overhead. The UDP probe gives you visibility into the actual network layer.
|
||||
|
||||
When enabled, each goldpinger pod runs a UDP echo listener. During each ping cycle, the prober sends a configurable number of sequenced UDP packets to each peer; the peer echoes them back. From the replies, goldpinger computes:
|
||||
|
||||
- **Packet loss** — percentage of packets that were not returned, surfacing degraded links before they impact applications
|
||||
- **Hop count** — estimated from the IPv4 TTL or IPv6 HopLimit on received replies, useful for detecting asymmetric routing or unexpected topology changes
|
||||
- **UDP RTT** — average round-trip time with sub-millisecond precision, isolating network latency from TCP/HTTP overhead
|
||||
|
||||
The feature is disabled by default and can be enabled with the following environment variables:
|
||||
|
||||
```sh
|
||||
UDP_ENABLED=true # enable UDP probing and echo listener
|
||||
UDP_PORT=6969 # listener port (default: 6969)
|
||||
UDP_PACKET_COUNT=10 # packets per probe (default: 10)
|
||||
UDP_PACKET_SIZE=64 # bytes per packet (default: 64)
|
||||
UDP_TIMEOUT=1s # probe timeout (default: 1s)
|
||||
```
|
||||
|
||||
Or via the Helm chart:
|
||||
|
||||
```yaml
|
||||
goldpinger:
|
||||
udp:
|
||||
enabled: true
|
||||
port: 6969
|
||||
```
|
||||
|
||||
This adds three Prometheus metrics:
|
||||
|
||||
```sh
|
||||
goldpinger_peers_loss_pct # gauge: UDP packet loss percentage (0-100)
|
||||
goldpinger_peers_hop_count # gauge: estimated hop count
|
||||
goldpinger_peers_udp_rtt_s # histogram: UDP round-trip time in seconds
|
||||
goldpinger_udp_errors_total # counter: UDP probe errors
|
||||
```
|
||||
|
||||
Links with partial loss are shown as yellow edges in the graph UI, and edge labels display the UDP RTT instead of HTTP latency when available.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
No new dependencies are required (`golang.org/x/net` is already in go.mod), and no additional container capabilities are needed.
|
||||
|
||||
## Usage
|
||||
|
||||
### UI
|
||||
@@ -317,10 +364,12 @@ These are probably the droids you are looking for:
|
||||
|
||||
```sh
|
||||
goldpinger_peers_response_time_s_*
|
||||
goldpinger_peers_response_time_s_*
|
||||
goldpinger_nodes_health_total
|
||||
goldpinger_stats_total
|
||||
goldpinger_errors_total
|
||||
goldpinger_peers_loss_pct # (UDP probe, when enabled)
|
||||
goldpinger_peers_hop_count # (UDP probe, when enabled)
|
||||
goldpinger_peers_udp_rtt_s_* # (UDP probe, when enabled)
|
||||
```
|
||||
|
||||
### Grafana
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
apiVersion: v1
|
||||
name: goldpinger
|
||||
appVersion: "3.10.3"
|
||||
version: 1.0.2
|
||||
appVersion: "3.11.0"
|
||||
version: 1.1.0
|
||||
description: Goldpinger is a tool to help debug, troubleshoot and visualize network connectivity and slowness issues.
|
||||
home: https://github.com/bloomberg/goldpinger
|
||||
sources:
|
||||
|
||||
@@ -50,6 +50,12 @@ spec:
|
||||
value: "{{ .Values.goldpinger.port }}"
|
||||
- name: LABEL_SELECTOR
|
||||
value: "app.kubernetes.io/name={{ include "goldpinger.name" . }}"
|
||||
{{- if .Values.goldpinger.udp.enabled }}
|
||||
- name: UDP_ENABLED
|
||||
value: "true"
|
||||
- name: UDP_PORT
|
||||
value: "{{ .Values.goldpinger.udp.port }}"
|
||||
{{- end }}
|
||||
{{- if .Values.extraEnv -}}
|
||||
{{ toYaml .Values.extraEnv | nindent 12 }}
|
||||
{{- end }}
|
||||
@@ -66,6 +72,11 @@ spec:
|
||||
hostPort: {{ $.Values.goldpinger.port }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.goldpinger.udp.enabled }}
|
||||
- name: udp-probe
|
||||
containerPort: {{ .Values.goldpinger.udp.port }}
|
||||
protocol: UDP
|
||||
{{- end }}
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
|
||||
@@ -18,6 +18,12 @@ spec:
|
||||
targetPort: {{ .Values.goldpinger.port }}
|
||||
protocol: TCP
|
||||
name: http
|
||||
{{- if .Values.goldpinger.udp.enabled }}
|
||||
- port: {{ .Values.goldpinger.udp.port }}
|
||||
targetPort: {{ .Values.goldpinger.udp.port }}
|
||||
protocol: UDP
|
||||
name: udp-probe
|
||||
{{- end }}
|
||||
selector:
|
||||
{{- include "goldpinger.selectorLabels" . | nindent 4 }}
|
||||
{{- if .Values.service.loadBalancerSourceRanges }}
|
||||
|
||||
@@ -23,6 +23,9 @@ serviceAccount:
|
||||
|
||||
goldpinger:
|
||||
port: 8080
|
||||
udp:
|
||||
enabled: false
|
||||
port: 6969
|
||||
zapConfig: |
|
||||
{
|
||||
"level": "info",
|
||||
|
||||
@@ -186,6 +186,10 @@ func main() {
|
||||
server.ConfigureAPI()
|
||||
goldpinger.StartUpdater()
|
||||
|
||||
if goldpinger.GoldpingerConfig.UDPEnabled {
|
||||
go goldpinger.StartUDPListener(goldpinger.GoldpingerConfig.UDPPort)
|
||||
}
|
||||
|
||||
logger.Info("All good, starting serving the API")
|
||||
|
||||
// serve API
|
||||
|
||||
BIN
extras/udp-grafana-dashboards.png
Normal file
BIN
extras/udp-grafana-dashboards.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 191 KiB |
BIN
extras/udp-yellow-edges.png
Normal file
BIN
extras/udp-yellow-edges.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 456 KiB |
@@ -2,14 +2,12 @@
|
||||
|
||||
package client
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"github.com/bloomberg/goldpinger/v3/pkg/client/operations"
|
||||
"github.com/go-openapi/runtime"
|
||||
httptransport "github.com/go-openapi/runtime/client"
|
||||
"github.com/go-openapi/strfmt"
|
||||
|
||||
"github.com/bloomberg/goldpinger/v3/pkg/client/operations"
|
||||
)
|
||||
|
||||
// Default goldpinger HTTP client.
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
@@ -52,10 +49,12 @@ func NewCheckAllPodsParamsWithHTTPClient(client *http.Client) *CheckAllPodsParam
|
||||
}
|
||||
}
|
||||
|
||||
/* CheckAllPodsParams contains all the parameters to send to the API endpoint
|
||||
for the check all pods operation.
|
||||
/*
|
||||
CheckAllPodsParams contains all the parameters to send to the API endpoint
|
||||
|
||||
Typically these are written to a http.Request.
|
||||
for the check all pods operation.
|
||||
|
||||
Typically these are written to a http.Request.
|
||||
*/
|
||||
type CheckAllPodsParams struct {
|
||||
timeout time.Duration
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
stderrors "errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
@@ -21,7 +20,7 @@ type CheckAllPodsReader struct {
|
||||
}
|
||||
|
||||
// ReadResponse reads a server response into the received o.
|
||||
func (o *CheckAllPodsReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
|
||||
func (o *CheckAllPodsReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (any, error) {
|
||||
switch response.Code() {
|
||||
case 200:
|
||||
result := NewCheckAllPodsOK()
|
||||
@@ -30,7 +29,7 @@ func (o *CheckAllPodsReader) ReadResponse(response runtime.ClientResponse, consu
|
||||
}
|
||||
return result, nil
|
||||
default:
|
||||
return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code())
|
||||
return nil, runtime.NewAPIError("[GET /check_all] checkAllPods", response, response.Code())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +38,8 @@ func NewCheckAllPodsOK() *CheckAllPodsOK {
|
||||
return &CheckAllPodsOK{}
|
||||
}
|
||||
|
||||
/* CheckAllPodsOK describes a response with status code 200, with default header values.
|
||||
/*
|
||||
CheckAllPodsOK describes a response with status code 200, with default header values.
|
||||
|
||||
Success, return response
|
||||
*/
|
||||
@@ -47,9 +47,46 @@ type CheckAllPodsOK struct {
|
||||
Payload *models.CheckAllResults
|
||||
}
|
||||
|
||||
func (o *CheckAllPodsOK) Error() string {
|
||||
return fmt.Sprintf("[GET /check_all][%d] checkAllPodsOK %+v", 200, o.Payload)
|
||||
// IsSuccess returns true when this check all pods o k response has a 2xx status code
|
||||
func (o *CheckAllPodsOK) IsSuccess() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IsRedirect returns true when this check all pods o k response has a 3xx status code
|
||||
func (o *CheckAllPodsOK) IsRedirect() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsClientError returns true when this check all pods o k response has a 4xx status code
|
||||
func (o *CheckAllPodsOK) IsClientError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsServerError returns true when this check all pods o k response has a 5xx status code
|
||||
func (o *CheckAllPodsOK) IsServerError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCode returns true when this check all pods o k response a status code equal to that given
|
||||
func (o *CheckAllPodsOK) IsCode(code int) bool {
|
||||
return code == 200
|
||||
}
|
||||
|
||||
// Code gets the status code for the check all pods o k response
|
||||
func (o *CheckAllPodsOK) Code() int {
|
||||
return 200
|
||||
}
|
||||
|
||||
func (o *CheckAllPodsOK) Error() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[GET /check_all][%d] checkAllPodsOK %s", 200, payload)
|
||||
}
|
||||
|
||||
func (o *CheckAllPodsOK) String() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[GET /check_all][%d] checkAllPodsOK %s", 200, payload)
|
||||
}
|
||||
|
||||
func (o *CheckAllPodsOK) GetPayload() *models.CheckAllResults {
|
||||
return o.Payload
|
||||
}
|
||||
@@ -59,7 +96,7 @@ func (o *CheckAllPodsOK) readResponse(response runtime.ClientResponse, consumer
|
||||
o.Payload = new(models.CheckAllResults)
|
||||
|
||||
// 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 && !stderrors.Is(err, io.EOF) {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
@@ -52,10 +49,12 @@ func NewCheckServicePodsParamsWithHTTPClient(client *http.Client) *CheckServiceP
|
||||
}
|
||||
}
|
||||
|
||||
/* CheckServicePodsParams contains all the parameters to send to the API endpoint
|
||||
for the check service pods operation.
|
||||
/*
|
||||
CheckServicePodsParams contains all the parameters to send to the API endpoint
|
||||
|
||||
Typically these are written to a http.Request.
|
||||
for the check service pods operation.
|
||||
|
||||
Typically these are written to a http.Request.
|
||||
*/
|
||||
type CheckServicePodsParams struct {
|
||||
timeout time.Duration
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
stderrors "errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
@@ -21,7 +20,7 @@ type CheckServicePodsReader struct {
|
||||
}
|
||||
|
||||
// ReadResponse reads a server response into the received o.
|
||||
func (o *CheckServicePodsReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
|
||||
func (o *CheckServicePodsReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (any, error) {
|
||||
switch response.Code() {
|
||||
case 200:
|
||||
result := NewCheckServicePodsOK()
|
||||
@@ -30,7 +29,7 @@ func (o *CheckServicePodsReader) ReadResponse(response runtime.ClientResponse, c
|
||||
}
|
||||
return result, nil
|
||||
default:
|
||||
return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code())
|
||||
return nil, runtime.NewAPIError("[GET /check] checkServicePods", response, response.Code())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +38,8 @@ func NewCheckServicePodsOK() *CheckServicePodsOK {
|
||||
return &CheckServicePodsOK{}
|
||||
}
|
||||
|
||||
/* CheckServicePodsOK describes a response with status code 200, with default header values.
|
||||
/*
|
||||
CheckServicePodsOK describes a response with status code 200, with default header values.
|
||||
|
||||
Success, return response
|
||||
*/
|
||||
@@ -47,9 +47,46 @@ type CheckServicePodsOK struct {
|
||||
Payload *models.CheckResults
|
||||
}
|
||||
|
||||
func (o *CheckServicePodsOK) Error() string {
|
||||
return fmt.Sprintf("[GET /check][%d] checkServicePodsOK %+v", 200, o.Payload)
|
||||
// IsSuccess returns true when this check service pods o k response has a 2xx status code
|
||||
func (o *CheckServicePodsOK) IsSuccess() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IsRedirect returns true when this check service pods o k response has a 3xx status code
|
||||
func (o *CheckServicePodsOK) IsRedirect() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsClientError returns true when this check service pods o k response has a 4xx status code
|
||||
func (o *CheckServicePodsOK) IsClientError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsServerError returns true when this check service pods o k response has a 5xx status code
|
||||
func (o *CheckServicePodsOK) IsServerError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCode returns true when this check service pods o k response a status code equal to that given
|
||||
func (o *CheckServicePodsOK) IsCode(code int) bool {
|
||||
return code == 200
|
||||
}
|
||||
|
||||
// Code gets the status code for the check service pods o k response
|
||||
func (o *CheckServicePodsOK) Code() int {
|
||||
return 200
|
||||
}
|
||||
|
||||
func (o *CheckServicePodsOK) Error() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[GET /check][%d] checkServicePodsOK %s", 200, payload)
|
||||
}
|
||||
|
||||
func (o *CheckServicePodsOK) String() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[GET /check][%d] checkServicePodsOK %s", 200, payload)
|
||||
}
|
||||
|
||||
func (o *CheckServicePodsOK) GetPayload() *models.CheckResults {
|
||||
return o.Payload
|
||||
}
|
||||
@@ -59,7 +96,7 @@ func (o *CheckServicePodsOK) readResponse(response runtime.ClientResponse, consu
|
||||
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 && !stderrors.Is(err, io.EOF) {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
@@ -52,10 +49,12 @@ func NewClusterHealthParamsWithHTTPClient(client *http.Client) *ClusterHealthPar
|
||||
}
|
||||
}
|
||||
|
||||
/* ClusterHealthParams contains all the parameters to send to the API endpoint
|
||||
for the cluster health operation.
|
||||
/*
|
||||
ClusterHealthParams contains all the parameters to send to the API endpoint
|
||||
|
||||
Typically these are written to a http.Request.
|
||||
for the cluster health operation.
|
||||
|
||||
Typically these are written to a http.Request.
|
||||
*/
|
||||
type ClusterHealthParams struct {
|
||||
timeout time.Duration
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
stderrors "errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
@@ -21,7 +20,7 @@ type ClusterHealthReader struct {
|
||||
}
|
||||
|
||||
// ReadResponse reads a server response into the received o.
|
||||
func (o *ClusterHealthReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
|
||||
func (o *ClusterHealthReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (any, error) {
|
||||
switch response.Code() {
|
||||
case 200:
|
||||
result := NewClusterHealthOK()
|
||||
@@ -36,7 +35,7 @@ func (o *ClusterHealthReader) ReadResponse(response runtime.ClientResponse, cons
|
||||
}
|
||||
return nil, result
|
||||
default:
|
||||
return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code())
|
||||
return nil, runtime.NewAPIError("[GET /cluster_health] clusterHealth", response, response.Code())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +44,8 @@ func NewClusterHealthOK() *ClusterHealthOK {
|
||||
return &ClusterHealthOK{}
|
||||
}
|
||||
|
||||
/* ClusterHealthOK describes a response with status code 200, with default header values.
|
||||
/*
|
||||
ClusterHealthOK describes a response with status code 200, with default header values.
|
||||
|
||||
Healthy cluster
|
||||
*/
|
||||
@@ -53,9 +53,46 @@ type ClusterHealthOK struct {
|
||||
Payload *models.ClusterHealthResults
|
||||
}
|
||||
|
||||
func (o *ClusterHealthOK) Error() string {
|
||||
return fmt.Sprintf("[GET /cluster_health][%d] clusterHealthOK %+v", 200, o.Payload)
|
||||
// IsSuccess returns true when this cluster health o k response has a 2xx status code
|
||||
func (o *ClusterHealthOK) IsSuccess() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IsRedirect returns true when this cluster health o k response has a 3xx status code
|
||||
func (o *ClusterHealthOK) IsRedirect() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsClientError returns true when this cluster health o k response has a 4xx status code
|
||||
func (o *ClusterHealthOK) IsClientError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsServerError returns true when this cluster health o k response has a 5xx status code
|
||||
func (o *ClusterHealthOK) IsServerError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCode returns true when this cluster health o k response a status code equal to that given
|
||||
func (o *ClusterHealthOK) IsCode(code int) bool {
|
||||
return code == 200
|
||||
}
|
||||
|
||||
// Code gets the status code for the cluster health o k response
|
||||
func (o *ClusterHealthOK) Code() int {
|
||||
return 200
|
||||
}
|
||||
|
||||
func (o *ClusterHealthOK) Error() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[GET /cluster_health][%d] clusterHealthOK %s", 200, payload)
|
||||
}
|
||||
|
||||
func (o *ClusterHealthOK) String() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[GET /cluster_health][%d] clusterHealthOK %s", 200, payload)
|
||||
}
|
||||
|
||||
func (o *ClusterHealthOK) GetPayload() *models.ClusterHealthResults {
|
||||
return o.Payload
|
||||
}
|
||||
@@ -65,7 +102,7 @@ func (o *ClusterHealthOK) readResponse(response runtime.ClientResponse, consumer
|
||||
o.Payload = new(models.ClusterHealthResults)
|
||||
|
||||
// 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 && !stderrors.Is(err, io.EOF) {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -77,7 +114,8 @@ func NewClusterHealthIMATeapot() *ClusterHealthIMATeapot {
|
||||
return &ClusterHealthIMATeapot{}
|
||||
}
|
||||
|
||||
/* ClusterHealthIMATeapot describes a response with status code 418, with default header values.
|
||||
/*
|
||||
ClusterHealthIMATeapot describes a response with status code 418, with default header values.
|
||||
|
||||
Unhealthy cluster
|
||||
*/
|
||||
@@ -85,9 +123,46 @@ type ClusterHealthIMATeapot struct {
|
||||
Payload *models.ClusterHealthResults
|
||||
}
|
||||
|
||||
func (o *ClusterHealthIMATeapot) Error() string {
|
||||
return fmt.Sprintf("[GET /cluster_health][%d] clusterHealthIMATeapot %+v", 418, o.Payload)
|
||||
// IsSuccess returns true when this cluster health i m a teapot response has a 2xx status code
|
||||
func (o *ClusterHealthIMATeapot) IsSuccess() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsRedirect returns true when this cluster health i m a teapot response has a 3xx status code
|
||||
func (o *ClusterHealthIMATeapot) IsRedirect() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsClientError returns true when this cluster health i m a teapot response has a 4xx status code
|
||||
func (o *ClusterHealthIMATeapot) IsClientError() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IsServerError returns true when this cluster health i m a teapot response has a 5xx status code
|
||||
func (o *ClusterHealthIMATeapot) IsServerError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCode returns true when this cluster health i m a teapot response a status code equal to that given
|
||||
func (o *ClusterHealthIMATeapot) IsCode(code int) bool {
|
||||
return code == 418
|
||||
}
|
||||
|
||||
// Code gets the status code for the cluster health i m a teapot response
|
||||
func (o *ClusterHealthIMATeapot) Code() int {
|
||||
return 418
|
||||
}
|
||||
|
||||
func (o *ClusterHealthIMATeapot) Error() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[GET /cluster_health][%d] clusterHealthIMATeapot %s", 418, payload)
|
||||
}
|
||||
|
||||
func (o *ClusterHealthIMATeapot) String() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[GET /cluster_health][%d] clusterHealthIMATeapot %s", 418, payload)
|
||||
}
|
||||
|
||||
func (o *ClusterHealthIMATeapot) GetPayload() *models.ClusterHealthResults {
|
||||
return o.Payload
|
||||
}
|
||||
@@ -97,7 +172,7 @@ func (o *ClusterHealthIMATeapot) readResponse(response runtime.ClientResponse, c
|
||||
o.Payload = new(models.ClusterHealthResults)
|
||||
|
||||
// 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 && !stderrors.Is(err, io.EOF) {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
@@ -52,10 +49,12 @@ func NewHealthzParamsWithHTTPClient(client *http.Client) *HealthzParams {
|
||||
}
|
||||
}
|
||||
|
||||
/* HealthzParams contains all the parameters to send to the API endpoint
|
||||
for the healthz operation.
|
||||
/*
|
||||
HealthzParams contains all the parameters to send to the API endpoint
|
||||
|
||||
Typically these are written to a http.Request.
|
||||
for the healthz operation.
|
||||
|
||||
Typically these are written to a http.Request.
|
||||
*/
|
||||
type HealthzParams struct {
|
||||
timeout time.Duration
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
stderrors "errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
@@ -21,7 +20,7 @@ type HealthzReader struct {
|
||||
}
|
||||
|
||||
// ReadResponse reads a server response into the received o.
|
||||
func (o *HealthzReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
|
||||
func (o *HealthzReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (any, error) {
|
||||
switch response.Code() {
|
||||
case 200:
|
||||
result := NewHealthzOK()
|
||||
@@ -36,7 +35,7 @@ func (o *HealthzReader) ReadResponse(response runtime.ClientResponse, consumer r
|
||||
}
|
||||
return nil, result
|
||||
default:
|
||||
return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code())
|
||||
return nil, runtime.NewAPIError("[GET /healthz] healthz", response, response.Code())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +44,8 @@ func NewHealthzOK() *HealthzOK {
|
||||
return &HealthzOK{}
|
||||
}
|
||||
|
||||
/* HealthzOK describes a response with status code 200, with default header values.
|
||||
/*
|
||||
HealthzOK describes a response with status code 200, with default header values.
|
||||
|
||||
Health check report
|
||||
*/
|
||||
@@ -53,9 +53,46 @@ type HealthzOK struct {
|
||||
Payload *models.HealthCheckResults
|
||||
}
|
||||
|
||||
func (o *HealthzOK) Error() string {
|
||||
return fmt.Sprintf("[GET /healthz][%d] healthzOK %+v", 200, o.Payload)
|
||||
// IsSuccess returns true when this healthz o k response has a 2xx status code
|
||||
func (o *HealthzOK) IsSuccess() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IsRedirect returns true when this healthz o k response has a 3xx status code
|
||||
func (o *HealthzOK) IsRedirect() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsClientError returns true when this healthz o k response has a 4xx status code
|
||||
func (o *HealthzOK) IsClientError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsServerError returns true when this healthz o k response has a 5xx status code
|
||||
func (o *HealthzOK) IsServerError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCode returns true when this healthz o k response a status code equal to that given
|
||||
func (o *HealthzOK) IsCode(code int) bool {
|
||||
return code == 200
|
||||
}
|
||||
|
||||
// Code gets the status code for the healthz o k response
|
||||
func (o *HealthzOK) Code() int {
|
||||
return 200
|
||||
}
|
||||
|
||||
func (o *HealthzOK) Error() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[GET /healthz][%d] healthzOK %s", 200, payload)
|
||||
}
|
||||
|
||||
func (o *HealthzOK) String() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[GET /healthz][%d] healthzOK %s", 200, payload)
|
||||
}
|
||||
|
||||
func (o *HealthzOK) GetPayload() *models.HealthCheckResults {
|
||||
return o.Payload
|
||||
}
|
||||
@@ -65,7 +102,7 @@ func (o *HealthzOK) readResponse(response runtime.ClientResponse, consumer runti
|
||||
o.Payload = new(models.HealthCheckResults)
|
||||
|
||||
// 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 && !stderrors.Is(err, io.EOF) {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -77,7 +114,8 @@ func NewHealthzServiceUnavailable() *HealthzServiceUnavailable {
|
||||
return &HealthzServiceUnavailable{}
|
||||
}
|
||||
|
||||
/* HealthzServiceUnavailable describes a response with status code 503, with default header values.
|
||||
/*
|
||||
HealthzServiceUnavailable describes a response with status code 503, with default header values.
|
||||
|
||||
Unhealthy service
|
||||
*/
|
||||
@@ -85,9 +123,46 @@ type HealthzServiceUnavailable struct {
|
||||
Payload *models.HealthCheckResults
|
||||
}
|
||||
|
||||
func (o *HealthzServiceUnavailable) Error() string {
|
||||
return fmt.Sprintf("[GET /healthz][%d] healthzServiceUnavailable %+v", 503, o.Payload)
|
||||
// IsSuccess returns true when this healthz service unavailable response has a 2xx status code
|
||||
func (o *HealthzServiceUnavailable) IsSuccess() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsRedirect returns true when this healthz service unavailable response has a 3xx status code
|
||||
func (o *HealthzServiceUnavailable) IsRedirect() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsClientError returns true when this healthz service unavailable response has a 4xx status code
|
||||
func (o *HealthzServiceUnavailable) IsClientError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsServerError returns true when this healthz service unavailable response has a 5xx status code
|
||||
func (o *HealthzServiceUnavailable) IsServerError() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IsCode returns true when this healthz service unavailable response a status code equal to that given
|
||||
func (o *HealthzServiceUnavailable) IsCode(code int) bool {
|
||||
return code == 503
|
||||
}
|
||||
|
||||
// Code gets the status code for the healthz service unavailable response
|
||||
func (o *HealthzServiceUnavailable) Code() int {
|
||||
return 503
|
||||
}
|
||||
|
||||
func (o *HealthzServiceUnavailable) Error() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[GET /healthz][%d] healthzServiceUnavailable %s", 503, payload)
|
||||
}
|
||||
|
||||
func (o *HealthzServiceUnavailable) String() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[GET /healthz][%d] healthzServiceUnavailable %s", 503, payload)
|
||||
}
|
||||
|
||||
func (o *HealthzServiceUnavailable) GetPayload() *models.HealthCheckResults {
|
||||
return o.Payload
|
||||
}
|
||||
@@ -97,7 +172,7 @@ func (o *HealthzServiceUnavailable) readResponse(response runtime.ClientResponse
|
||||
o.Payload = new(models.HealthCheckResults)
|
||||
|
||||
// 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 && !stderrors.Is(err, io.EOF) {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,11 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
httptransport "github.com/go-openapi/runtime/client"
|
||||
"github.com/go-openapi/strfmt"
|
||||
)
|
||||
|
||||
@@ -17,6 +15,31 @@ func New(transport runtime.ClientTransport, formats strfmt.Registry) ClientServi
|
||||
return &Client{transport: transport, formats: formats}
|
||||
}
|
||||
|
||||
// New creates a new operations API client with basic auth credentials.
|
||||
// It takes the following parameters:
|
||||
// - host: http host (github.com).
|
||||
// - basePath: any base path for the API client ("/v1", "/v3").
|
||||
// - scheme: http scheme ("http", "https").
|
||||
// - user: user for basic authentication header.
|
||||
// - password: password for basic authentication header.
|
||||
func NewClientWithBasicAuth(host, basePath, scheme, user, password string) ClientService {
|
||||
transport := httptransport.New(host, basePath, []string{scheme})
|
||||
transport.DefaultAuthentication = httptransport.BasicAuth(user, password)
|
||||
return &Client{transport: transport, formats: strfmt.Default}
|
||||
}
|
||||
|
||||
// New creates a new operations API client with a bearer token for authentication.
|
||||
// It takes the following parameters:
|
||||
// - host: http host (github.com).
|
||||
// - basePath: any base path for the API client ("/v1", "/v3").
|
||||
// - scheme: http scheme ("http", "https").
|
||||
// - bearerToken: bearer token for Bearer authentication header.
|
||||
func NewClientWithBearerToken(host, basePath, scheme, bearerToken string) ClientService {
|
||||
transport := httptransport.New(host, basePath, []string{scheme})
|
||||
transport.DefaultAuthentication = httptransport.BearerToken(bearerToken)
|
||||
return &Client{transport: transport, formats: strfmt.Default}
|
||||
}
|
||||
|
||||
/*
|
||||
Client for operations API
|
||||
*/
|
||||
@@ -25,7 +48,7 @@ type Client struct {
|
||||
formats strfmt.Registry
|
||||
}
|
||||
|
||||
// ClientOption is the option for Client methods
|
||||
// ClientOption may be used to customize the behavior of Client methods.
|
||||
type ClientOption func(*runtime.ClientOperation)
|
||||
|
||||
// ClientService is the interface for Client methods
|
||||
@@ -44,10 +67,10 @@ type ClientService interface {
|
||||
}
|
||||
|
||||
/*
|
||||
CheckAllPods Queries the API server for all other pods in this service, and makes all of them query all of their neighbours, using their pods IPs. Calls their /check endpoint.
|
||||
CheckAllPods Queries the API server for all other pods in this service, and makes all of them query all of their neighbours, using their pods IPs. Calls their /check endpoint.
|
||||
*/
|
||||
func (a *Client) CheckAllPods(params *CheckAllPodsParams, opts ...ClientOption) (*CheckAllPodsOK, error) {
|
||||
// TODO: Validate the params before sending
|
||||
// NOTE: parameters are not validated before sending
|
||||
if params == nil {
|
||||
params = NewCheckAllPodsParams()
|
||||
}
|
||||
@@ -66,26 +89,31 @@ func (a *Client) CheckAllPods(params *CheckAllPodsParams, opts ...ClientOption)
|
||||
for _, opt := range opts {
|
||||
opt(op)
|
||||
}
|
||||
|
||||
result, err := a.transport.Submit(op)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// only one success response has to be checked
|
||||
success, ok := result.(*CheckAllPodsOK)
|
||||
if ok {
|
||||
return success, nil
|
||||
}
|
||||
// unexpected success response
|
||||
// safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue
|
||||
|
||||
// unexpected success response.
|
||||
|
||||
// no default response is defined.
|
||||
//
|
||||
// safeguard: normally, in the absence of a default response, unknown success responses return an error above: so this is a codegen issue
|
||||
msg := fmt.Sprintf("unexpected success response for checkAllPods: API contract not enforced by server. Client expected to get an error, but got: %T", result)
|
||||
panic(msg)
|
||||
}
|
||||
|
||||
/*
|
||||
CheckServicePods Queries the API server for all other pods in this service, and pings them via their pods IPs. Calls their /ping endpoint
|
||||
CheckServicePods Queries the API server for all other pods in this service, and pings them via their pods IPs. Calls their /ping endpoint
|
||||
*/
|
||||
func (a *Client) CheckServicePods(params *CheckServicePodsParams, opts ...ClientOption) (*CheckServicePodsOK, error) {
|
||||
// TODO: Validate the params before sending
|
||||
// NOTE: parameters are not validated before sending
|
||||
if params == nil {
|
||||
params = NewCheckServicePodsParams()
|
||||
}
|
||||
@@ -104,26 +132,31 @@ func (a *Client) CheckServicePods(params *CheckServicePodsParams, opts ...Client
|
||||
for _, opt := range opts {
|
||||
opt(op)
|
||||
}
|
||||
|
||||
result, err := a.transport.Submit(op)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// only one success response has to be checked
|
||||
success, ok := result.(*CheckServicePodsOK)
|
||||
if ok {
|
||||
return success, nil
|
||||
}
|
||||
// unexpected success response
|
||||
// safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue
|
||||
|
||||
// unexpected success response.
|
||||
|
||||
// no default response is defined.
|
||||
//
|
||||
// safeguard: normally, in the absence of a default response, unknown success responses return an error above: so this is a codegen issue
|
||||
msg := fmt.Sprintf("unexpected success response for checkServicePods: API contract not enforced by server. Client expected to get an error, but got: %T", result)
|
||||
panic(msg)
|
||||
}
|
||||
|
||||
/*
|
||||
ClusterHealth Checks the full graph. Returns a binary OK or not OK.
|
||||
ClusterHealth Checks the full graph. Returns a binary OK or not OK.
|
||||
*/
|
||||
func (a *Client) ClusterHealth(params *ClusterHealthParams, opts ...ClientOption) (*ClusterHealthOK, error) {
|
||||
// TODO: Validate the params before sending
|
||||
// NOTE: parameters are not validated before sending
|
||||
if params == nil {
|
||||
params = NewClusterHealthParams()
|
||||
}
|
||||
@@ -142,26 +175,31 @@ func (a *Client) ClusterHealth(params *ClusterHealthParams, opts ...ClientOption
|
||||
for _, opt := range opts {
|
||||
opt(op)
|
||||
}
|
||||
|
||||
result, err := a.transport.Submit(op)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// only one success response has to be checked
|
||||
success, ok := result.(*ClusterHealthOK)
|
||||
if ok {
|
||||
return success, nil
|
||||
}
|
||||
// unexpected success response
|
||||
// safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue
|
||||
|
||||
// unexpected success response.
|
||||
|
||||
// no default response is defined.
|
||||
//
|
||||
// safeguard: normally, in the absence of a default response, unknown success responses return an error above: so this is a codegen issue
|
||||
msg := fmt.Sprintf("unexpected success response for clusterHealth: API contract not enforced by server. Client expected to get an error, but got: %T", result)
|
||||
panic(msg)
|
||||
}
|
||||
|
||||
/*
|
||||
Healthz The healthcheck endpoint provides detailed information about the health of a web service. If each of the components required by the service are healthy, then the service is considered healthy and will return a 200 OK response. If any of the components needed by the service are unhealthy, then a 503 Service Unavailable response will be provided.
|
||||
Healthz The healthcheck endpoint provides detailed information about the health of a web service. If each of the components required by the service are healthy, then the service is considered healthy and will return a 200 OK response. If any of the components needed by the service are unhealthy, then a 503 Service Unavailable response will be provided.
|
||||
*/
|
||||
func (a *Client) Healthz(params *HealthzParams, opts ...ClientOption) (*HealthzOK, error) {
|
||||
// TODO: Validate the params before sending
|
||||
// NOTE: parameters are not validated before sending
|
||||
if params == nil {
|
||||
params = NewHealthzParams()
|
||||
}
|
||||
@@ -180,26 +218,31 @@ func (a *Client) Healthz(params *HealthzParams, opts ...ClientOption) (*HealthzO
|
||||
for _, opt := range opts {
|
||||
opt(op)
|
||||
}
|
||||
|
||||
result, err := a.transport.Submit(op)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// only one success response has to be checked
|
||||
success, ok := result.(*HealthzOK)
|
||||
if ok {
|
||||
return success, nil
|
||||
}
|
||||
// unexpected success response
|
||||
// safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue
|
||||
|
||||
// unexpected success response.
|
||||
|
||||
// no default response is defined.
|
||||
//
|
||||
// safeguard: normally, in the absence of a default response, unknown success responses return an error above: so this is a codegen issue
|
||||
msg := fmt.Sprintf("unexpected success response for healthz: API contract not enforced by server. Client expected to get an error, but got: %T", result)
|
||||
panic(msg)
|
||||
}
|
||||
|
||||
/*
|
||||
Ping return query stats
|
||||
Ping return query stats
|
||||
*/
|
||||
func (a *Client) Ping(params *PingParams, opts ...ClientOption) (*PingOK, error) {
|
||||
// TODO: Validate the params before sending
|
||||
// NOTE: parameters are not validated before sending
|
||||
if params == nil {
|
||||
params = NewPingParams()
|
||||
}
|
||||
@@ -218,17 +261,22 @@ func (a *Client) Ping(params *PingParams, opts ...ClientOption) (*PingOK, error)
|
||||
for _, opt := range opts {
|
||||
opt(op)
|
||||
}
|
||||
|
||||
result, err := a.transport.Submit(op)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// only one success response has to be checked
|
||||
success, ok := result.(*PingOK)
|
||||
if ok {
|
||||
return success, nil
|
||||
}
|
||||
// unexpected success response
|
||||
// safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue
|
||||
|
||||
// unexpected success response.
|
||||
|
||||
// no default response is defined.
|
||||
//
|
||||
// safeguard: normally, in the absence of a default response, unknown success responses return an error above: so this is a codegen issue
|
||||
msg := fmt.Sprintf("unexpected success response for ping: API contract not enforced by server. Client expected to get an error, but got: %T", result)
|
||||
panic(msg)
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
@@ -52,10 +49,12 @@ func NewPingParamsWithHTTPClient(client *http.Client) *PingParams {
|
||||
}
|
||||
}
|
||||
|
||||
/* PingParams contains all the parameters to send to the API endpoint
|
||||
for the ping operation.
|
||||
/*
|
||||
PingParams contains all the parameters to send to the API endpoint
|
||||
|
||||
Typically these are written to a http.Request.
|
||||
for the ping operation.
|
||||
|
||||
Typically these are written to a http.Request.
|
||||
*/
|
||||
type PingParams struct {
|
||||
timeout time.Duration
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
stderrors "errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
@@ -21,7 +20,7 @@ type PingReader struct {
|
||||
}
|
||||
|
||||
// ReadResponse reads a server response into the received o.
|
||||
func (o *PingReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
|
||||
func (o *PingReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (any, error) {
|
||||
switch response.Code() {
|
||||
case 200:
|
||||
result := NewPingOK()
|
||||
@@ -30,7 +29,7 @@ func (o *PingReader) ReadResponse(response runtime.ClientResponse, consumer runt
|
||||
}
|
||||
return result, nil
|
||||
default:
|
||||
return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code())
|
||||
return nil, runtime.NewAPIError("[GET /ping] ping", response, response.Code())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +38,8 @@ func NewPingOK() *PingOK {
|
||||
return &PingOK{}
|
||||
}
|
||||
|
||||
/* PingOK describes a response with status code 200, with default header values.
|
||||
/*
|
||||
PingOK describes a response with status code 200, with default header values.
|
||||
|
||||
return success
|
||||
*/
|
||||
@@ -47,9 +47,46 @@ type PingOK struct {
|
||||
Payload *models.PingResults
|
||||
}
|
||||
|
||||
func (o *PingOK) Error() string {
|
||||
return fmt.Sprintf("[GET /ping][%d] pingOK %+v", 200, o.Payload)
|
||||
// IsSuccess returns true when this ping o k response has a 2xx status code
|
||||
func (o *PingOK) IsSuccess() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IsRedirect returns true when this ping o k response has a 3xx status code
|
||||
func (o *PingOK) IsRedirect() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsClientError returns true when this ping o k response has a 4xx status code
|
||||
func (o *PingOK) IsClientError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsServerError returns true when this ping o k response has a 5xx status code
|
||||
func (o *PingOK) IsServerError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCode returns true when this ping o k response a status code equal to that given
|
||||
func (o *PingOK) IsCode(code int) bool {
|
||||
return code == 200
|
||||
}
|
||||
|
||||
// Code gets the status code for the ping o k response
|
||||
func (o *PingOK) Code() int {
|
||||
return 200
|
||||
}
|
||||
|
||||
func (o *PingOK) Error() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[GET /ping][%d] pingOK %s", 200, payload)
|
||||
}
|
||||
|
||||
func (o *PingOK) String() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[GET /ping][%d] pingOK %s", 200, payload)
|
||||
}
|
||||
|
||||
func (o *PingOK) GetPayload() *models.PingResults {
|
||||
return o.Payload
|
||||
}
|
||||
@@ -59,7 +96,7 @@ func (o *PingOK) readResponse(response runtime.ClientResponse, consumer runtime.
|
||||
o.Payload = new(models.PingResults)
|
||||
|
||||
// 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 && !stderrors.Is(err, io.EOF) {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,13 @@ var GoldpingerConfig = struct {
|
||||
|
||||
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:" "`
|
||||
|
||||
// UDP probe settings
|
||||
UDPEnabled bool `long:"udp-enabled" description:"Enable UDP probe for loss and hop metrics" env:"UDP_ENABLED"`
|
||||
UDPPort int `long:"udp-port" description:"Port for UDP echo listener" env:"UDP_PORT" default:"6969"`
|
||||
UDPPacketCount int `long:"udp-packet-count" description:"Number of UDP packets to send per probe" env:"UDP_PACKET_COUNT" default:"10"`
|
||||
UDPPacketSize int `long:"udp-packet-size" description:"Size of each UDP probe packet in bytes" env:"UDP_PACKET_SIZE" default:"64"`
|
||||
UDPTimeout time.Duration `long:"udp-timeout" description:"Timeout for UDP probe" env:"UDP_TIMEOUT" default:"1s"`
|
||||
|
||||
// Timeouts
|
||||
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"`
|
||||
|
||||
@@ -16,6 +16,7 @@ package goldpinger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
@@ -111,6 +112,24 @@ func (p *Pinger) Ping() {
|
||||
CountCall("made", "ping")
|
||||
start := time.Now()
|
||||
|
||||
// Run HTTP ping and UDP probe concurrently
|
||||
var udpResult UDPProbeResult
|
||||
var wg sync.WaitGroup
|
||||
if GoldpingerConfig.UDPEnabled {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
targetIP := pickPodHostIP(p.pod.PodIP, p.pod.HostIP)
|
||||
udpResult = ProbeUDP(
|
||||
targetIP,
|
||||
GoldpingerConfig.UDPPort,
|
||||
GoldpingerConfig.UDPPacketCount,
|
||||
GoldpingerConfig.UDPPacketSize,
|
||||
GoldpingerConfig.UDPTimeout,
|
||||
)
|
||||
}()
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), p.timeout)
|
||||
defer cancel()
|
||||
|
||||
@@ -120,7 +139,34 @@ func (p *Pinger) Ping() {
|
||||
responseTimeMs := responseTime.Nanoseconds() / int64(time.Millisecond)
|
||||
p.histogram.Observe(responseTime.Seconds())
|
||||
|
||||
// Wait for UDP probe to complete
|
||||
wg.Wait()
|
||||
|
||||
OK := (err == nil)
|
||||
|
||||
var lossPct float64
|
||||
var hopCount int32
|
||||
var udpRttMs float64
|
||||
if GoldpingerConfig.UDPEnabled {
|
||||
lossPct = udpResult.LossPct
|
||||
hopCount = udpResult.HopCount
|
||||
udpRttMs = udpResult.AvgRttS * 1000.0
|
||||
if udpResult.Err != nil {
|
||||
p.logger.Warn("UDP probe error", zap.Error(udpResult.Err))
|
||||
} else {
|
||||
p.logger.Debug("UDP probe complete",
|
||||
zap.Float64("lossPct", lossPct),
|
||||
zap.Int32("hopCount", hopCount),
|
||||
zap.Float64("udpRttMs", udpRttMs),
|
||||
)
|
||||
}
|
||||
SetPeerLossPct(p.pod.HostIP, p.pod.PodIP, lossPct)
|
||||
SetPeerHopCount(p.pod.HostIP, p.pod.PodIP, hopCount)
|
||||
if udpResult.AvgRttS > 0 {
|
||||
ObservePeerUDPRtt(p.pod.HostIP, p.pod.PodIP, udpResult.AvgRttS)
|
||||
}
|
||||
}
|
||||
|
||||
if OK {
|
||||
p.resultsChan <- PingAllPodsResult{
|
||||
podName: p.pod.Name,
|
||||
@@ -132,6 +178,9 @@ func (p *Pinger) Ping() {
|
||||
Response: resp.Payload,
|
||||
StatusCode: 200,
|
||||
ResponseTimeMs: responseTimeMs,
|
||||
LossPct: lossPct,
|
||||
HopCount: hopCount,
|
||||
UDPRttMs: udpRttMs,
|
||||
},
|
||||
}
|
||||
p.logger.Debug("Success pinging pod", zap.Duration("responseTime", responseTime))
|
||||
@@ -146,6 +195,9 @@ func (p *Pinger) Ping() {
|
||||
Error: err.Error(),
|
||||
StatusCode: 504,
|
||||
ResponseTimeMs: responseTimeMs,
|
||||
LossPct: lossPct,
|
||||
HopCount: hopCount,
|
||||
UDPRttMs: udpRttMs,
|
||||
},
|
||||
}
|
||||
p.logger.Warn("Ping returned error", zap.Duration("responseTime", responseTime), zap.Error(err))
|
||||
|
||||
@@ -123,6 +123,50 @@ var (
|
||||
"host",
|
||||
},
|
||||
)
|
||||
goldpingerPeersLossPct = prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "goldpinger_peers_loss_pct",
|
||||
Help: "UDP packet loss percentage to peer (0-100)",
|
||||
},
|
||||
[]string{
|
||||
"goldpinger_instance",
|
||||
"host_ip",
|
||||
"pod_ip",
|
||||
},
|
||||
)
|
||||
goldpingerPeersHopCount = prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "goldpinger_peers_hop_count",
|
||||
Help: "Estimated network hop count to peer from UDP TTL",
|
||||
},
|
||||
[]string{
|
||||
"goldpinger_instance",
|
||||
"host_ip",
|
||||
"pod_ip",
|
||||
},
|
||||
)
|
||||
goldpingerPeersUDPRtt = prometheus.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "goldpinger_peers_udp_rtt_s",
|
||||
Help: "Histogram of UDP round-trip times to peers in seconds",
|
||||
Buckets: []float64{.0001, .00025, .0005, .001, .0025, .005, .01, .025, .05, .1, .25, .5, 1},
|
||||
},
|
||||
[]string{
|
||||
"goldpinger_instance",
|
||||
"host_ip",
|
||||
"pod_ip",
|
||||
},
|
||||
)
|
||||
goldpingerUDPErrorsCounter = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "goldpinger_udp_errors_total",
|
||||
Help: "Statistics of UDP probe errors per instance",
|
||||
},
|
||||
[]string{
|
||||
"goldpinger_instance",
|
||||
"host",
|
||||
},
|
||||
)
|
||||
bootTime = time.Now()
|
||||
)
|
||||
|
||||
@@ -136,6 +180,10 @@ func init() {
|
||||
prometheus.MustRegister(goldpingerDnsErrorsCounter)
|
||||
prometheus.MustRegister(goldPingerHttpErrorsCounter)
|
||||
prometheus.MustRegister(goldPingerTcpErrorsCounter)
|
||||
prometheus.MustRegister(goldpingerPeersLossPct)
|
||||
prometheus.MustRegister(goldpingerPeersHopCount)
|
||||
prometheus.MustRegister(goldpingerPeersUDPRtt)
|
||||
prometheus.MustRegister(goldpingerUDPErrorsCounter)
|
||||
zap.L().Info("Metrics setup - see /metrics")
|
||||
}
|
||||
|
||||
@@ -210,6 +258,48 @@ func CountHttpError(host string) {
|
||||
).Inc()
|
||||
}
|
||||
|
||||
// SetPeerLossPct sets the UDP packet loss percentage gauge for a peer
|
||||
func SetPeerLossPct(hostIP, podIP string, lossPct float64) {
|
||||
goldpingerPeersLossPct.WithLabelValues(
|
||||
GoldpingerConfig.Hostname,
|
||||
hostIP,
|
||||
podIP,
|
||||
).Set(lossPct)
|
||||
}
|
||||
|
||||
// SetPeerHopCount sets the estimated hop count gauge for a peer
|
||||
func SetPeerHopCount(hostIP, podIP string, hopCount int32) {
|
||||
goldpingerPeersHopCount.WithLabelValues(
|
||||
GoldpingerConfig.Hostname,
|
||||
hostIP,
|
||||
podIP,
|
||||
).Set(float64(hopCount))
|
||||
}
|
||||
|
||||
// DeletePeerUDPMetrics removes stale UDP metric labels for a destroyed peer
|
||||
func DeletePeerUDPMetrics(hostIP, podIP string) {
|
||||
goldpingerPeersLossPct.DeleteLabelValues(GoldpingerConfig.Hostname, hostIP, podIP)
|
||||
goldpingerPeersHopCount.DeleteLabelValues(GoldpingerConfig.Hostname, hostIP, podIP)
|
||||
goldpingerPeersUDPRtt.DeleteLabelValues(GoldpingerConfig.Hostname, hostIP, podIP)
|
||||
}
|
||||
|
||||
// ObservePeerUDPRtt records a UDP RTT observation in seconds
|
||||
func ObservePeerUDPRtt(hostIP, podIP string, rttS float64) {
|
||||
goldpingerPeersUDPRtt.WithLabelValues(
|
||||
GoldpingerConfig.Hostname,
|
||||
hostIP,
|
||||
podIP,
|
||||
).Observe(rttS)
|
||||
}
|
||||
|
||||
// CountUDPError counts instances of UDP probe errors
|
||||
func CountUDPError(host string) {
|
||||
goldpingerUDPErrorsCounter.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(
|
||||
|
||||
225
pkg/goldpinger/udp_probe.go
Normal file
225
pkg/goldpinger/udp_probe.go
Normal file
@@ -0,0 +1,225 @@
|
||||
// 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 (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
const (
|
||||
udpMagic = 0x47504E47 // "GPNG"
|
||||
udpHeaderSize = 16 // 4 magic + 4 seq + 8 timestamp
|
||||
|
||||
// udpMaxPacketSize is the maximum UDP packet we will read. Sized to the
|
||||
// standard Ethernet MTU (1500 bytes) which is the largest packet that
|
||||
// will survive most networks without fragmentation. Any configured
|
||||
// UDP_PACKET_SIZE larger than this will be clamped to this value to
|
||||
// avoid silent truncation on the receive side.
|
||||
udpMaxPacketSize = 1500
|
||||
)
|
||||
|
||||
// UDPProbeResult holds the results of a UDP probe to a peer
|
||||
type UDPProbeResult struct {
|
||||
LossPct float64
|
||||
HopCount int32
|
||||
AvgRttS float64
|
||||
Err error
|
||||
}
|
||||
|
||||
// StartUDPListener starts a UDP echo listener on the given port.
|
||||
// It echoes back any packet that starts with the GPNG magic number.
|
||||
func StartUDPListener(port int) {
|
||||
addr := net.JoinHostPort("::", strconv.Itoa(port))
|
||||
pc, err := net.ListenPacket("udp", addr)
|
||||
if err != nil {
|
||||
zap.L().Fatal("Failed to start UDP listener", zap.String("addr", addr), zap.Error(err))
|
||||
}
|
||||
defer pc.Close()
|
||||
|
||||
zap.L().Info("UDP echo listener started", zap.String("addr", addr))
|
||||
|
||||
buf := make([]byte, udpMaxPacketSize)
|
||||
for {
|
||||
n, remoteAddr, err := pc.ReadFrom(buf)
|
||||
if err != nil {
|
||||
zap.L().Warn("UDP read error", zap.Error(err))
|
||||
continue
|
||||
}
|
||||
if n < udpHeaderSize {
|
||||
continue
|
||||
}
|
||||
magic := binary.BigEndian.Uint32(buf[0:4])
|
||||
if magic != udpMagic {
|
||||
continue
|
||||
}
|
||||
// Echo back the packet as-is
|
||||
_, err = pc.WriteTo(buf[:n], remoteAddr)
|
||||
if err != nil {
|
||||
zap.L().Warn("UDP write error", zap.Error(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ProbeUDP sends count UDP packets to the target and measures loss and hop count.
|
||||
func ProbeUDP(targetIP string, port, count, size int, timeout time.Duration) UDPProbeResult {
|
||||
if count <= 0 {
|
||||
return UDPProbeResult{Err: fmt.Errorf("packet count must be > 0, got %d", count)}
|
||||
}
|
||||
if size < udpHeaderSize {
|
||||
size = udpHeaderSize
|
||||
}
|
||||
if size > udpMaxPacketSize {
|
||||
size = udpMaxPacketSize
|
||||
}
|
||||
|
||||
addr := net.JoinHostPort(targetIP, strconv.Itoa(port))
|
||||
conn, err := net.Dial("udp", addr)
|
||||
if err != nil {
|
||||
CountUDPError(targetIP)
|
||||
return UDPProbeResult{LossPct: 100, Err: fmt.Errorf("dial: %w", err)}
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
// Determine if this is IPv4 or IPv6 and set up TTL/HopLimit reading
|
||||
isIPv6 := net.ParseIP(targetIP).To4() == nil
|
||||
var ttlValue int
|
||||
ttlFound := false
|
||||
|
||||
if isIPv6 {
|
||||
p := ipv6.NewPacketConn(conn.(*net.UDPConn))
|
||||
if err := p.SetControlMessage(ipv6.FlagHopLimit, true); err != nil {
|
||||
zap.L().Debug("Cannot set IPv6 hop limit flag", zap.Error(err))
|
||||
}
|
||||
} else {
|
||||
p := ipv4.NewPacketConn(conn.(*net.UDPConn))
|
||||
if err := p.SetControlMessage(ipv4.FlagTTL, true); err != nil {
|
||||
zap.L().Debug("Cannot set IPv4 TTL flag", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
// Build packet
|
||||
pkt := make([]byte, size)
|
||||
binary.BigEndian.PutUint32(pkt[0:4], udpMagic)
|
||||
|
||||
// Send all packets
|
||||
for i := 0; i < count; i++ {
|
||||
binary.BigEndian.PutUint32(pkt[4:8], uint32(i))
|
||||
binary.BigEndian.PutUint64(pkt[8:16], uint64(time.Now().UnixNano()))
|
||||
_, err := conn.Write(pkt)
|
||||
if err != nil {
|
||||
CountUDPError(targetIP)
|
||||
zap.L().Debug("UDP send error", zap.Int("seq", i), zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
// Receive replies
|
||||
received := 0
|
||||
var totalRttNs int64
|
||||
deadline := time.Now().Add(timeout)
|
||||
conn.SetReadDeadline(deadline)
|
||||
|
||||
recvBuf := make([]byte, udpMaxPacketSize)
|
||||
|
||||
if isIPv6 {
|
||||
p := ipv6.NewPacketConn(conn.(*net.UDPConn))
|
||||
for received < count {
|
||||
n, cm, _, err := p.ReadFrom(recvBuf)
|
||||
now := time.Now()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if n < udpHeaderSize {
|
||||
continue
|
||||
}
|
||||
magic := binary.BigEndian.Uint32(recvBuf[0:4])
|
||||
if magic != udpMagic {
|
||||
continue
|
||||
}
|
||||
sentNs := int64(binary.BigEndian.Uint64(recvBuf[8:16]))
|
||||
totalRttNs += now.UnixNano() - sentNs
|
||||
received++
|
||||
if cm != nil && cm.HopLimit > 0 && !ttlFound {
|
||||
ttlValue = cm.HopLimit
|
||||
ttlFound = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
p := ipv4.NewPacketConn(conn.(*net.UDPConn))
|
||||
for received < count {
|
||||
n, cm, _, err := p.ReadFrom(recvBuf)
|
||||
now := time.Now()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if n < udpHeaderSize {
|
||||
continue
|
||||
}
|
||||
magic := binary.BigEndian.Uint32(recvBuf[0:4])
|
||||
if magic != udpMagic {
|
||||
continue
|
||||
}
|
||||
sentNs := int64(binary.BigEndian.Uint64(recvBuf[8:16]))
|
||||
totalRttNs += now.UnixNano() - sentNs
|
||||
received++
|
||||
if cm != nil && cm.TTL > 0 && !ttlFound {
|
||||
ttlValue = cm.TTL
|
||||
ttlFound = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lossPct := float64(count-received) / float64(count) * 100.0
|
||||
|
||||
var hopCount int32
|
||||
if ttlFound {
|
||||
hopCount = estimateHops(ttlValue)
|
||||
}
|
||||
|
||||
var avgRttS float64
|
||||
if received > 0 {
|
||||
avgRttS = float64(totalRttNs) / float64(received) / 1e9
|
||||
}
|
||||
|
||||
return UDPProbeResult{
|
||||
LossPct: lossPct,
|
||||
HopCount: hopCount,
|
||||
AvgRttS: avgRttS,
|
||||
}
|
||||
}
|
||||
|
||||
// estimateHops estimates the number of hops from the received TTL.
|
||||
// Common initial TTL values are 64 (Linux) and 128 (Windows).
|
||||
func estimateHops(receivedTTL int) int32 {
|
||||
if receivedTTL <= 0 {
|
||||
return 0
|
||||
}
|
||||
initialTTL := 64
|
||||
if receivedTTL > 64 {
|
||||
initialTTL = 128
|
||||
}
|
||||
hops := initialTTL - receivedTTL
|
||||
if hops < 0 {
|
||||
hops = 0
|
||||
}
|
||||
return int32(hops)
|
||||
}
|
||||
180
pkg/goldpinger/udp_probe_test.go
Normal file
180
pkg/goldpinger/udp_probe_test.go
Normal file
@@ -0,0 +1,180 @@
|
||||
package goldpinger
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func startTestEchoListener(t *testing.T) (int, func()) {
|
||||
t.Helper()
|
||||
pc, err := net.ListenPacket("udp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
port := pc.LocalAddr().(*net.UDPAddr).Port
|
||||
|
||||
go func() {
|
||||
buf := make([]byte, udpMaxPacketSize)
|
||||
for {
|
||||
n, addr, err := pc.ReadFrom(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if n >= udpHeaderSize {
|
||||
magic := binary.BigEndian.Uint32(buf[0:4])
|
||||
if magic == udpMagic {
|
||||
pc.WriteTo(buf[:n], addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return port, func() { pc.Close() }
|
||||
}
|
||||
|
||||
// startLossyEchoListener echoes back packets but drops every dropEveryN-th
|
||||
// packet (1-indexed). For example, dropEveryN=3 drops packets 3, 6, 9, etc.
|
||||
func startLossyEchoListener(t *testing.T, dropEveryN int) (int, func()) {
|
||||
t.Helper()
|
||||
pc, err := net.ListenPacket("udp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
port := pc.LocalAddr().(*net.UDPAddr).Port
|
||||
|
||||
var counter atomic.Int64
|
||||
|
||||
go func() {
|
||||
buf := make([]byte, udpMaxPacketSize)
|
||||
for {
|
||||
n, addr, err := pc.ReadFrom(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if n >= udpHeaderSize {
|
||||
magic := binary.BigEndian.Uint32(buf[0:4])
|
||||
if magic == udpMagic {
|
||||
seq := counter.Add(1)
|
||||
if seq%int64(dropEveryN) == 0 {
|
||||
// Drop this packet — don't echo
|
||||
continue
|
||||
}
|
||||
pc.WriteTo(buf[:n], addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return port, func() { pc.Close() }
|
||||
}
|
||||
|
||||
func TestProbeUDP_NoLoss(t *testing.T) {
|
||||
port, cleanup := startTestEchoListener(t)
|
||||
defer cleanup()
|
||||
|
||||
result := ProbeUDP("127.0.0.1", port, 10, 64, 2*time.Second)
|
||||
if result.Err != nil {
|
||||
t.Fatalf("unexpected error: %v", result.Err)
|
||||
}
|
||||
if result.LossPct != 0 {
|
||||
t.Errorf("expected 0%% loss, got %.1f%%", result.LossPct)
|
||||
}
|
||||
if result.AvgRttS <= 0 {
|
||||
t.Errorf("expected positive RTT, got %.6f s", result.AvgRttS)
|
||||
}
|
||||
t.Logf("avg UDP RTT: %.4f ms", result.AvgRttS*1000)
|
||||
}
|
||||
|
||||
func TestProbeUDP_FullLoss(t *testing.T) {
|
||||
// Bind a port then close it so nothing is listening.
|
||||
// This avoids assuming a hardcoded port like 19999 is free.
|
||||
pc, err := net.ListenPacket("udp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
port := pc.LocalAddr().(*net.UDPAddr).Port
|
||||
pc.Close()
|
||||
|
||||
result := ProbeUDP("127.0.0.1", port, 5, 64, 200*time.Millisecond)
|
||||
if result.LossPct != 100 {
|
||||
t.Errorf("expected 100%% loss, got %.1f%%", result.LossPct)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProbeUDP_PartialLoss(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
count int
|
||||
dropEveryN int
|
||||
expectedPct float64
|
||||
}{
|
||||
{"drop every 2nd (50%)", 10, 2, 50.0},
|
||||
{"drop every 3rd (33.3%)", 9, 3, 100.0 / 3.0},
|
||||
{"drop every 5th (20%)", 10, 5, 20.0},
|
||||
{"drop every 10th (10%)", 10, 10, 10.0},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
port, cleanup := startLossyEchoListener(t, tt.dropEveryN)
|
||||
defer cleanup()
|
||||
|
||||
result := ProbeUDP("127.0.0.1", port, tt.count, 64, 2*time.Second)
|
||||
if result.Err != nil {
|
||||
t.Fatalf("unexpected error: %v", result.Err)
|
||||
}
|
||||
// Allow small floating point tolerance
|
||||
diff := result.LossPct - tt.expectedPct
|
||||
if diff < -0.1 || diff > 0.1 {
|
||||
t.Errorf("expected %.1f%% loss, got %.1f%%", tt.expectedPct, result.LossPct)
|
||||
}
|
||||
t.Logf("loss: %.1f%% (expected %.1f%%)", result.LossPct, tt.expectedPct)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProbeUDP_ZeroCount(t *testing.T) {
|
||||
result := ProbeUDP("127.0.0.1", 12345, 0, 64, 200*time.Millisecond)
|
||||
if result.Err == nil {
|
||||
t.Error("expected error for count=0, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProbeUDP_PacketFormat(t *testing.T) {
|
||||
pkt := make([]byte, 64)
|
||||
binary.BigEndian.PutUint32(pkt[0:4], udpMagic)
|
||||
binary.BigEndian.PutUint32(pkt[4:8], 42)
|
||||
binary.BigEndian.PutUint64(pkt[8:16], uint64(time.Now().UnixNano()))
|
||||
|
||||
magic := binary.BigEndian.Uint32(pkt[0:4])
|
||||
if magic != 0x47504E47 {
|
||||
t.Errorf("expected magic 0x47504E47, got 0x%X", magic)
|
||||
}
|
||||
seq := binary.BigEndian.Uint32(pkt[4:8])
|
||||
if seq != 42 {
|
||||
t.Errorf("expected seq 42, got %d", seq)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEstimateHops(t *testing.T) {
|
||||
tests := []struct {
|
||||
ttl int
|
||||
want int32
|
||||
}{
|
||||
{64, 0}, // same host, Linux
|
||||
{63, 1}, // 1 hop, Linux
|
||||
{56, 8}, // 8 hops, Linux
|
||||
{128, 0}, // same host, Windows (TTL > 64 → initial=128)
|
||||
{127, 1}, // 1 hop, Windows
|
||||
{0, 0}, // invalid
|
||||
}
|
||||
for _, tt := range tests {
|
||||
got := estimateHops(tt.ttl)
|
||||
if got != tt.want {
|
||||
t.Errorf("estimateHops(%d) = %d, want %d", tt.ttl, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,10 +93,11 @@ func updatePingers(resultsChan chan<- PingAllPodsResult) {
|
||||
|
||||
// createPingers allocates a new pinger object for each new goldpinger Pod that's been discovered
|
||||
// It also:
|
||||
// (a) initializes a result object in checkResults to store info on that pod
|
||||
// (b) starts a new goroutines to continuously ping the given pod.
|
||||
// Each new goroutine waits for a given time before starting the continuous ping
|
||||
// to prevent a thundering herd
|
||||
//
|
||||
// (a) initializes a result object in checkResults to store info on that pod
|
||||
// (b) starts a new goroutines to continuously ping the given pod.
|
||||
// Each new goroutine waits for a given time before starting the continuous ping
|
||||
// to prevent a thundering herd
|
||||
func createPingers(pingers map[string]*Pinger, newPods map[string]*GoldpingerPod, resultsChan chan<- PingAllPodsResult, refreshPeriod time.Duration) {
|
||||
if len(newPods) == 0 {
|
||||
// I have nothing to do
|
||||
@@ -136,6 +137,11 @@ func destroyPingers(pingers map[string]*Pinger, deletedPods map[string]*Goldping
|
||||
// Close the channel to stop pinging
|
||||
close(pinger.stopChan)
|
||||
|
||||
// Clean up stale UDP metric labels for this peer
|
||||
if GoldpingerConfig.UDPEnabled {
|
||||
DeletePeerUDPMetrics(pod.HostIP, pod.PodIP)
|
||||
}
|
||||
|
||||
// delete from pingers
|
||||
delete(pingers, podName)
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
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"
|
||||
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
|
||||
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"
|
||||
stderrors "errors"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
@@ -93,11 +91,15 @@ func (m *CheckAllPodResult) validateResponse(formats strfmt.Registry) error {
|
||||
|
||||
if m.Response != nil {
|
||||
if err := m.Response.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
ve := new(errors.Validation)
|
||||
if stderrors.As(err, &ve) {
|
||||
return ve.ValidateName("response")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
}
|
||||
ce := new(errors.CompositeError)
|
||||
if stderrors.As(err, &ce) {
|
||||
return ce.ValidateName("response")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -122,12 +124,21 @@ func (m *CheckAllPodResult) ContextValidate(ctx context.Context, formats strfmt.
|
||||
func (m *CheckAllPodResult) contextValidateResponse(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if m.Response != nil {
|
||||
|
||||
if swag.IsZero(m.Response) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := m.Response.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
ve := new(errors.Validation)
|
||||
if stderrors.As(err, &ve) {
|
||||
return ve.ValidateName("response")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
}
|
||||
ce := new(errors.CompositeError)
|
||||
if stderrors.As(err, &ce) {
|
||||
return ce.ValidateName("response")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
|
||||
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"
|
||||
stderrors "errors"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
@@ -73,11 +71,15 @@ func (m *CheckAllResults) validateHosts(formats strfmt.Registry) error {
|
||||
|
||||
if m.Hosts[i] != nil {
|
||||
if err := m.Hosts[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
ve := new(errors.Validation)
|
||||
if stderrors.As(err, &ve) {
|
||||
return ve.ValidateName("hosts" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
}
|
||||
ce := new(errors.CompositeError)
|
||||
if stderrors.As(err, &ce) {
|
||||
return ce.ValidateName("hosts" + "." + strconv.Itoa(i))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -117,11 +119,15 @@ 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 {
|
||||
ve := new(errors.Validation)
|
||||
if stderrors.As(err, &ve) {
|
||||
return ve.ValidateName("responses" + "." + k)
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
}
|
||||
ce := new(errors.CompositeError)
|
||||
if stderrors.As(err, &ce) {
|
||||
return ce.ValidateName("responses" + "." + k)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -158,12 +164,21 @@ func (m *CheckAllResults) contextValidateHosts(ctx context.Context, formats strf
|
||||
for i := 0; i < len(m.Hosts); i++ {
|
||||
|
||||
if m.Hosts[i] != nil {
|
||||
|
||||
if swag.IsZero(m.Hosts[i]) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := m.Hosts[i].ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
ve := new(errors.Validation)
|
||||
if stderrors.As(err, &ve) {
|
||||
return ve.ValidateName("hosts" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
}
|
||||
ce := new(errors.CompositeError)
|
||||
if stderrors.As(err, &ce) {
|
||||
return ce.ValidateName("hosts" + "." + strconv.Itoa(i))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
|
||||
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"
|
||||
stderrors "errors"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
@@ -56,11 +54,15 @@ 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 {
|
||||
ve := new(errors.Validation)
|
||||
if stderrors.As(err, &ve) {
|
||||
return ve.ValidateName("podResults" + "." + k)
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
}
|
||||
ce := new(errors.CompositeError)
|
||||
if stderrors.As(err, &ce) {
|
||||
return ce.ValidateName("podResults" + "." + k)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -77,11 +79,15 @@ func (m *CheckResults) validateProbeResults(formats strfmt.Registry) error {
|
||||
|
||||
if m.ProbeResults != nil {
|
||||
if err := m.ProbeResults.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
ve := new(errors.Validation)
|
||||
if stderrors.As(err, &ve) {
|
||||
return ve.ValidateName("probeResults")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
}
|
||||
ce := new(errors.CompositeError)
|
||||
if stderrors.As(err, &ce) {
|
||||
return ce.ValidateName("probeResults")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -124,12 +130,20 @@ func (m *CheckResults) contextValidatePodResults(ctx context.Context, formats st
|
||||
|
||||
func (m *CheckResults) contextValidateProbeResults(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.ProbeResults) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := m.ProbeResults.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
ve := new(errors.Validation)
|
||||
if stderrors.As(err, &ve) {
|
||||
return ve.ValidateName("probeResults")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
}
|
||||
ce := new(errors.CompositeError)
|
||||
if stderrors.As(err, &ce) {
|
||||
return ce.ValidateName("probeResults")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
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"
|
||||
|
||||
@@ -60,7 +57,7 @@ func (m *ClusterHealthResults) Validate(formats strfmt.Registry) error {
|
||||
|
||||
func (m *ClusterHealthResults) validateOK(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("OK", "body", bool(m.OK)); err != nil {
|
||||
if err := validate.Required("OK", "body", m.OK); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
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"
|
||||
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
|
||||
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"
|
||||
stderrors "errors"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
@@ -64,11 +62,15 @@ func (m *PingResults) validateReceived(formats strfmt.Registry) error {
|
||||
|
||||
if m.Received != nil {
|
||||
if err := m.Received.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
ve := new(errors.Validation)
|
||||
if stderrors.As(err, &ve) {
|
||||
return ve.ValidateName("received")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
}
|
||||
ce := new(errors.CompositeError)
|
||||
if stderrors.As(err, &ce) {
|
||||
return ce.ValidateName("received")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -93,12 +95,21 @@ func (m *PingResults) ContextValidate(ctx context.Context, formats strfmt.Regist
|
||||
func (m *PingResults) contextValidateReceived(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if m.Received != nil {
|
||||
|
||||
if swag.IsZero(m.Received) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := m.Received.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
ve := new(errors.Validation)
|
||||
if stderrors.As(err, &ve) {
|
||||
return ve.ValidateName("received")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
}
|
||||
ce := new(errors.CompositeError)
|
||||
if stderrors.As(err, &ce) {
|
||||
return ce.ValidateName("received")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
|
||||
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"
|
||||
stderrors "errors"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
@@ -37,6 +35,12 @@ type PodResult struct {
|
||||
// error
|
||||
Error string `json:"error,omitempty"`
|
||||
|
||||
// estimated network hop count from UDP TTL
|
||||
HopCount int32 `json:"hop-count,omitempty"`
|
||||
|
||||
// UDP packet loss percentage (0-100)
|
||||
LossPct float64 `json:"loss-pct,omitempty"`
|
||||
|
||||
// response
|
||||
Response *PingResults `json:"response,omitempty"`
|
||||
|
||||
@@ -45,6 +49,9 @@ type PodResult struct {
|
||||
|
||||
// status code
|
||||
StatusCode int32 `json:"status-code,omitempty"`
|
||||
|
||||
// average UDP round-trip time in milliseconds
|
||||
UDPRttMs float64 `json:"udp-rtt-ms,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this pod result
|
||||
@@ -116,11 +123,15 @@ func (m *PodResult) validateResponse(formats strfmt.Registry) error {
|
||||
|
||||
if m.Response != nil {
|
||||
if err := m.Response.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
ve := new(errors.Validation)
|
||||
if stderrors.As(err, &ve) {
|
||||
return ve.ValidateName("response")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
}
|
||||
ce := new(errors.CompositeError)
|
||||
if stderrors.As(err, &ce) {
|
||||
return ce.ValidateName("response")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -145,12 +156,21 @@ func (m *PodResult) ContextValidate(ctx context.Context, formats strfmt.Registry
|
||||
func (m *PodResult) contextValidateResponse(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if m.Response != nil {
|
||||
|
||||
if swag.IsZero(m.Response) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := m.Response.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
ve := new(errors.Validation)
|
||||
if stderrors.As(err, &ve) {
|
||||
return ve.ValidateName("response")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
}
|
||||
ce := new(errors.CompositeError)
|
||||
if stderrors.As(err, &ce) {
|
||||
return ce.ValidateName("response")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
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"
|
||||
|
||||
|
||||
@@ -2,15 +2,14 @@
|
||||
|
||||
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"
|
||||
stderrors "errors"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/go-openapi/validate"
|
||||
)
|
||||
|
||||
@@ -32,11 +31,15 @@ func (m ProbeResults) Validate(formats strfmt.Registry) error {
|
||||
for i := 0; i < len(m[k]); i++ {
|
||||
|
||||
if err := m[k][i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
ve := new(errors.Validation)
|
||||
if stderrors.As(err, &ve) {
|
||||
return ve.ValidateName(k + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
}
|
||||
ce := new(errors.CompositeError)
|
||||
if stderrors.As(err, &ce) {
|
||||
return ce.ValidateName(k + "." + strconv.Itoa(i))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -58,12 +61,20 @@ func (m ProbeResults) ContextValidate(ctx context.Context, formats strfmt.Regist
|
||||
|
||||
for i := 0; i < len(m[k]); i++ {
|
||||
|
||||
if swag.IsZero(m[k][i]) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := m[k][i].ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
ve := new(errors.Validation)
|
||||
if stderrors.As(err, &ve) {
|
||||
return ve.ValidateName(k + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
}
|
||||
ce := new(errors.CompositeError)
|
||||
if stderrors.As(err, &ce) {
|
||||
return ce.ValidateName(k + "." + strconv.Itoa(i))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
|
||||
// Package restapi Goldpinger
|
||||
//
|
||||
// Schemes:
|
||||
// http
|
||||
// Host: localhost
|
||||
// BasePath: /
|
||||
// Version: 3.0.0
|
||||
// Schemes:
|
||||
// http
|
||||
// Host: localhost
|
||||
// BasePath: /
|
||||
// Version: 3.0.0
|
||||
//
|
||||
// Consumes:
|
||||
// - application/json
|
||||
// Consumes:
|
||||
// - application/json
|
||||
//
|
||||
// Produces:
|
||||
// - application/json
|
||||
// Produces:
|
||||
// - application/json
|
||||
//
|
||||
// swagger:meta
|
||||
package restapi
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package restapi
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
@@ -170,12 +167,6 @@ func init() {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"dnsResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/DnsResults"
|
||||
}
|
||||
},
|
||||
"hosts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -203,10 +194,10 @@ func init() {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"httpResults": {
|
||||
"probeResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/HttpResults"
|
||||
"$ref": "#/definitions/ProbeResults"
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
@@ -214,32 +205,20 @@ func init() {
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/CheckAllPodResult"
|
||||
}
|
||||
},
|
||||
"tcpResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/TcpResults"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"CheckResults": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"dnsResults": {
|
||||
"$ref": "#/definitions/DnsResults"
|
||||
},
|
||||
"httpResults": {
|
||||
"$ref": "#/definitions/HttpResults"
|
||||
},
|
||||
"podResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/PodResult"
|
||||
}
|
||||
},
|
||||
"tcpResults": {
|
||||
"$ref": "#/definitions/TcpResults"
|
||||
"probeResults": {
|
||||
"$ref": "#/definitions/ProbeResults"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -279,12 +258,6 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"DnsResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/ProbeResult"
|
||||
}
|
||||
},
|
||||
"HealthCheckResults": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -302,12 +275,6 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"HttpResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/ProbeResult"
|
||||
}
|
||||
},
|
||||
"PingResults": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -342,6 +309,16 @@ func init() {
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"hop-count": {
|
||||
"description": "estimated network hop count from UDP TTL",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"loss-pct": {
|
||||
"description": "UDP packet loss percentage (0-100)",
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
},
|
||||
"response": {
|
||||
"$ref": "#/definitions/PingResults"
|
||||
},
|
||||
@@ -353,6 +330,11 @@ func init() {
|
||||
"status-code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"udp-rtt-ms": {
|
||||
"description": "average UDP round-trip time in milliseconds",
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -361,16 +343,22 @@ func init() {
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"protocol": {
|
||||
"type": "string"
|
||||
},
|
||||
"response-time-ms": {
|
||||
"type": "number",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
},
|
||||
"TcpResults": {
|
||||
"ProbeResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/ProbeResult"
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ProbeResult"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -528,12 +516,6 @@ func init() {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"dnsResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/DnsResults"
|
||||
}
|
||||
},
|
||||
"hosts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -548,10 +530,10 @@ func init() {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"httpResults": {
|
||||
"probeResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/HttpResults"
|
||||
"$ref": "#/definitions/ProbeResults"
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
@@ -559,12 +541,6 @@ func init() {
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/CheckAllPodResult"
|
||||
}
|
||||
},
|
||||
"tcpResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/TcpResults"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -587,20 +563,14 @@ func init() {
|
||||
"CheckResults": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"dnsResults": {
|
||||
"$ref": "#/definitions/DnsResults"
|
||||
},
|
||||
"httpResults": {
|
||||
"$ref": "#/definitions/HttpResults"
|
||||
},
|
||||
"podResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/PodResult"
|
||||
}
|
||||
},
|
||||
"tcpResults": {
|
||||
"$ref": "#/definitions/TcpResults"
|
||||
"probeResults": {
|
||||
"$ref": "#/definitions/ProbeResults"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -640,12 +610,6 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"DnsResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/ProbeResult"
|
||||
}
|
||||
},
|
||||
"HealthCheckResults": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -663,12 +627,6 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"HttpResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/ProbeResult"
|
||||
}
|
||||
},
|
||||
"PingResults": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -703,6 +661,16 @@ func init() {
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"hop-count": {
|
||||
"description": "estimated network hop count from UDP TTL",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"loss-pct": {
|
||||
"description": "UDP packet loss percentage (0-100)",
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
},
|
||||
"response": {
|
||||
"$ref": "#/definitions/PingResults"
|
||||
},
|
||||
@@ -714,6 +682,11 @@ func init() {
|
||||
"status-code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"udp-rtt-ms": {
|
||||
"description": "average UDP round-trip time in milliseconds",
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -722,16 +695,22 @@ func init() {
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"protocol": {
|
||||
"type": "string"
|
||||
},
|
||||
"response-time-ms": {
|
||||
"type": "number",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
},
|
||||
"TcpResults": {
|
||||
"ProbeResults": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/ProbeResult"
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ProbeResult"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
@@ -29,10 +26,10 @@ func NewCheckAllPods(ctx *middleware.Context, handler CheckAllPodsHandler) *Chec
|
||||
return &CheckAllPods{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/* CheckAllPods swagger:route GET /check_all checkAllPods
|
||||
/*
|
||||
CheckAllPods swagger:route GET /check_all checkAllPods
|
||||
|
||||
Queries the API server for all other pods in this service, and makes all of them query all of their neighbours, using their pods IPs. Calls their /check endpoint.
|
||||
|
||||
*/
|
||||
type CheckAllPods struct {
|
||||
Context *middleware.Context
|
||||
@@ -51,6 +48,7 @@ func (o *CheckAllPods) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params) // actually handle the request
|
||||
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
@@ -25,7 +22,6 @@ func NewCheckAllPodsParams() CheckAllPodsParams {
|
||||
//
|
||||
// swagger:parameters checkAllPods
|
||||
type CheckAllPodsParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
@@ -16,7 +13,8 @@ import (
|
||||
// CheckAllPodsOKCode is the HTTP code returned for type CheckAllPodsOK
|
||||
const CheckAllPodsOKCode int = 200
|
||||
|
||||
/*CheckAllPodsOK Success, return response
|
||||
/*
|
||||
CheckAllPodsOK Success, return response
|
||||
|
||||
swagger:response checkAllPodsOK
|
||||
*/
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
@@ -29,10 +26,10 @@ func NewCheckServicePods(ctx *middleware.Context, handler CheckServicePodsHandle
|
||||
return &CheckServicePods{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/* CheckServicePods swagger:route GET /check checkServicePods
|
||||
/*
|
||||
CheckServicePods swagger:route GET /check checkServicePods
|
||||
|
||||
Queries the API server for all other pods in this service, and pings them via their pods IPs. Calls their /ping endpoint
|
||||
|
||||
*/
|
||||
type CheckServicePods struct {
|
||||
Context *middleware.Context
|
||||
@@ -51,6 +48,7 @@ func (o *CheckServicePods) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params) // actually handle the request
|
||||
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
@@ -25,7 +22,6 @@ func NewCheckServicePodsParams() CheckServicePodsParams {
|
||||
//
|
||||
// swagger:parameters checkServicePods
|
||||
type CheckServicePodsParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
@@ -16,7 +13,8 @@ import (
|
||||
// CheckServicePodsOKCode is the HTTP code returned for type CheckServicePodsOK
|
||||
const CheckServicePodsOKCode int = 200
|
||||
|
||||
/*CheckServicePodsOK Success, return response
|
||||
/*
|
||||
CheckServicePodsOK Success, return response
|
||||
|
||||
swagger:response checkServicePodsOK
|
||||
*/
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
@@ -29,10 +26,10 @@ func NewClusterHealth(ctx *middleware.Context, handler ClusterHealthHandler) *Cl
|
||||
return &ClusterHealth{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/* ClusterHealth swagger:route GET /cluster_health clusterHealth
|
||||
/*
|
||||
ClusterHealth swagger:route GET /cluster_health clusterHealth
|
||||
|
||||
Checks the full graph. Returns a binary OK or not OK.
|
||||
|
||||
*/
|
||||
type ClusterHealth struct {
|
||||
Context *middleware.Context
|
||||
@@ -51,6 +48,7 @@ func (o *ClusterHealth) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params) // actually handle the request
|
||||
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
@@ -25,7 +22,6 @@ func NewClusterHealthParams() ClusterHealthParams {
|
||||
//
|
||||
// swagger:parameters clusterHealth
|
||||
type ClusterHealthParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
@@ -16,7 +13,8 @@ import (
|
||||
// ClusterHealthOKCode is the HTTP code returned for type ClusterHealthOK
|
||||
const ClusterHealthOKCode int = 200
|
||||
|
||||
/*ClusterHealthOK Healthy cluster
|
||||
/*
|
||||
ClusterHealthOK Healthy cluster
|
||||
|
||||
swagger:response clusterHealthOK
|
||||
*/
|
||||
@@ -60,7 +58,8 @@ func (o *ClusterHealthOK) WriteResponse(rw http.ResponseWriter, producer runtime
|
||||
// ClusterHealthIMATeapotCode is the HTTP code returned for type ClusterHealthIMATeapot
|
||||
const ClusterHealthIMATeapotCode int = 418
|
||||
|
||||
/*ClusterHealthIMATeapot Unhealthy cluster
|
||||
/*
|
||||
ClusterHealthIMATeapot Unhealthy cluster
|
||||
|
||||
swagger:response clusterHealthIMATeapot
|
||||
*/
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -43,18 +40,32 @@ func NewGoldpingerAPI(spec *loads.Document) *GoldpingerAPI {
|
||||
JSONProducer: runtime.JSONProducer(),
|
||||
|
||||
CheckAllPodsHandler: CheckAllPodsHandlerFunc(func(params CheckAllPodsParams) middleware.Responder {
|
||||
_ = params
|
||||
|
||||
return middleware.NotImplemented("operation CheckAllPods has not yet been implemented")
|
||||
}),
|
||||
|
||||
CheckServicePodsHandler: CheckServicePodsHandlerFunc(func(params CheckServicePodsParams) middleware.Responder {
|
||||
_ = params
|
||||
|
||||
return middleware.NotImplemented("operation CheckServicePods has not yet been implemented")
|
||||
}),
|
||||
|
||||
ClusterHealthHandler: ClusterHealthHandlerFunc(func(params ClusterHealthParams) middleware.Responder {
|
||||
_ = params
|
||||
|
||||
return middleware.NotImplemented("operation ClusterHealth has not yet been implemented")
|
||||
}),
|
||||
|
||||
HealthzHandler: HealthzHandlerFunc(func(params HealthzParams) middleware.Responder {
|
||||
_ = params
|
||||
|
||||
return middleware.NotImplemented("operation Healthz has not yet been implemented")
|
||||
}),
|
||||
|
||||
PingHandler: PingHandlerFunc(func(params PingParams) middleware.Responder {
|
||||
_ = params
|
||||
|
||||
return middleware.NotImplemented("operation Ping has not yet been implemented")
|
||||
}),
|
||||
}
|
||||
@@ -120,7 +131,7 @@ type GoldpingerAPI struct {
|
||||
CommandLineOptionsGroups []swag.CommandLineOptionsGroup
|
||||
|
||||
// User defined logger function.
|
||||
Logger func(string, ...interface{})
|
||||
Logger func(string, ...any)
|
||||
}
|
||||
|
||||
// UseRedoc for documentation at /docs
|
||||
@@ -219,12 +230,12 @@ func (o *GoldpingerAPI) Authorizer() runtime.Authorizer {
|
||||
}
|
||||
|
||||
// ConsumersFor gets the consumers for the specified media types.
|
||||
//
|
||||
// MIME type parameters are ignored here.
|
||||
func (o *GoldpingerAPI) ConsumersFor(mediaTypes []string) map[string]runtime.Consumer {
|
||||
result := make(map[string]runtime.Consumer, len(mediaTypes))
|
||||
for _, mt := range mediaTypes {
|
||||
switch mt {
|
||||
case "application/json":
|
||||
if mt == "application/json" {
|
||||
result["application/json"] = o.JSONConsumer
|
||||
}
|
||||
|
||||
@@ -232,16 +243,17 @@ func (o *GoldpingerAPI) ConsumersFor(mediaTypes []string) map[string]runtime.Con
|
||||
result[mt] = c
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// ProducersFor gets the producers for the specified media types.
|
||||
//
|
||||
// MIME type parameters are ignored here.
|
||||
func (o *GoldpingerAPI) ProducersFor(mediaTypes []string) map[string]runtime.Producer {
|
||||
result := make(map[string]runtime.Producer, len(mediaTypes))
|
||||
for _, mt := range mediaTypes {
|
||||
switch mt {
|
||||
case "application/json":
|
||||
if mt == "application/json" {
|
||||
result["application/json"] = o.JSONProducer
|
||||
}
|
||||
|
||||
@@ -249,6 +261,7 @@ func (o *GoldpingerAPI) ProducersFor(mediaTypes []string) map[string]runtime.Pro
|
||||
result[mt] = p
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -344,6 +357,6 @@ func (o *GoldpingerAPI) AddMiddlewareFor(method, path string, builder middleware
|
||||
}
|
||||
o.Init()
|
||||
if h, ok := o.handlers[um][path]; ok {
|
||||
o.handlers[method][path] = builder(h)
|
||||
o.handlers[um][path] = builder(h)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
@@ -29,10 +26,10 @@ func NewHealthz(ctx *middleware.Context, handler HealthzHandler) *Healthz {
|
||||
return &Healthz{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/* Healthz swagger:route GET /healthz healthz
|
||||
/*
|
||||
Healthz swagger:route GET /healthz healthz
|
||||
|
||||
The healthcheck endpoint provides detailed information about the health of a web service. If each of the components required by the service are healthy, then the service is considered healthy and will return a 200 OK response. If any of the components needed by the service are unhealthy, then a 503 Service Unavailable response will be provided.
|
||||
|
||||
*/
|
||||
type Healthz struct {
|
||||
Context *middleware.Context
|
||||
@@ -51,6 +48,7 @@ func (o *Healthz) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params) // actually handle the request
|
||||
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
@@ -25,7 +22,6 @@ func NewHealthzParams() HealthzParams {
|
||||
//
|
||||
// swagger:parameters healthz
|
||||
type HealthzParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
@@ -16,7 +13,8 @@ import (
|
||||
// HealthzOKCode is the HTTP code returned for type HealthzOK
|
||||
const HealthzOKCode int = 200
|
||||
|
||||
/*HealthzOK Health check report
|
||||
/*
|
||||
HealthzOK Health check report
|
||||
|
||||
swagger:response healthzOK
|
||||
*/
|
||||
@@ -60,7 +58,8 @@ func (o *HealthzOK) WriteResponse(rw http.ResponseWriter, producer runtime.Produ
|
||||
// HealthzServiceUnavailableCode is the HTTP code returned for type HealthzServiceUnavailable
|
||||
const HealthzServiceUnavailableCode int = 503
|
||||
|
||||
/*HealthzServiceUnavailable Unhealthy service
|
||||
/*
|
||||
HealthzServiceUnavailable Unhealthy service
|
||||
|
||||
swagger:response healthzServiceUnavailable
|
||||
*/
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
@@ -29,10 +26,10 @@ func NewPing(ctx *middleware.Context, handler PingHandler) *Ping {
|
||||
return &Ping{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/* Ping swagger:route GET /ping ping
|
||||
/*
|
||||
Ping swagger:route GET /ping ping
|
||||
|
||||
return query stats
|
||||
|
||||
*/
|
||||
type Ping struct {
|
||||
Context *middleware.Context
|
||||
@@ -51,6 +48,7 @@ func (o *Ping) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params) // actually handle the request
|
||||
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
@@ -25,7 +22,6 @@ func NewPingParams() PingParams {
|
||||
//
|
||||
// swagger:parameters ping
|
||||
type PingParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
@@ -16,7 +13,8 @@ import (
|
||||
// PingOKCode is the HTTP code returned for type PingOK
|
||||
const PingOKCode int = 200
|
||||
|
||||
/*PingOK return success
|
||||
/*
|
||||
PingOK return success
|
||||
|
||||
swagger:response pingOK
|
||||
*/
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package operations
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
|
||||
@@ -7,8 +7,6 @@ import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -20,11 +18,12 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/runtime/flagext"
|
||||
"github.com/go-openapi/swag"
|
||||
flags "github.com/jessevdk/go-flags"
|
||||
"golang.org/x/net/netutil"
|
||||
|
||||
"github.com/go-openapi/runtime/flagext"
|
||||
"github.com/go-openapi/swag"
|
||||
|
||||
"github.com/bloomberg/goldpinger/v3/pkg/restapi/operations"
|
||||
)
|
||||
|
||||
@@ -81,7 +80,7 @@ type Server struct {
|
||||
ListenLimit int `long:"listen-limit" description:"limit the number of outstanding requests"`
|
||||
KeepAlive time.Duration `long:"keep-alive" description:"sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)" default:"3m"`
|
||||
ReadTimeout time.Duration `long:"read-timeout" description:"maximum duration before timing out read of the request" default:"30s"`
|
||||
WriteTimeout time.Duration `long:"write-timeout" description:"maximum duration before timing out write of the response" default:"60s"`
|
||||
WriteTimeout time.Duration `long:"write-timeout" description:"maximum duration before timing out write of the response" default:"30s"`
|
||||
httpServerL net.Listener
|
||||
|
||||
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"`
|
||||
@@ -105,7 +104,7 @@ type Server struct {
|
||||
}
|
||||
|
||||
// Logf logs message either via defined user logger or via system one if no user logger is defined.
|
||||
func (s *Server) Logf(f string, args ...interface{}) {
|
||||
func (s *Server) Logf(f string, args ...any) {
|
||||
if s.api != nil && s.api.Logger != nil {
|
||||
s.api.Logger(f, args...)
|
||||
} else {
|
||||
@@ -115,7 +114,7 @@ func (s *Server) Logf(f string, args ...interface{}) {
|
||||
|
||||
// Fatalf logs message either via defined user logger or via system one if no user logger is defined.
|
||||
// Exits with non-zero status after printing
|
||||
func (s *Server) Fatalf(f string, args ...interface{}) {
|
||||
func (s *Server) Fatalf(f string, args ...any) {
|
||||
if s.api != nil && s.api.Logger != nil {
|
||||
s.api.Logger(f, args...)
|
||||
os.Exit(1)
|
||||
@@ -189,8 +188,8 @@ func (s *Server) Serve() (err error) {
|
||||
s.Logf("Serving goldpinger at unix://%s", s.SocketPath)
|
||||
go func(l net.Listener) {
|
||||
defer wg.Done()
|
||||
if err := domainSocket.Serve(l); err != nil && err != http.ErrServerClosed {
|
||||
s.Fatalf("%v", err)
|
||||
if errServe := domainSocket.Serve(l); errServe != nil && !errors.Is(errServe, http.ErrServerClosed) {
|
||||
s.Fatalf("%v", errServe)
|
||||
}
|
||||
s.Logf("Stopped serving goldpinger at unix://%s", s.SocketPath)
|
||||
}(s.domainSocketL)
|
||||
@@ -219,8 +218,8 @@ func (s *Server) Serve() (err error) {
|
||||
s.Logf("Serving goldpinger at http://%s", s.httpServerL.Addr())
|
||||
go func(l net.Listener) {
|
||||
defer wg.Done()
|
||||
if err := httpServer.Serve(l); err != nil && err != http.ErrServerClosed {
|
||||
s.Fatalf("%v", err)
|
||||
if errServe := httpServer.Serve(l); errServe != nil && !errors.Is(errServe, http.ErrServerClosed) {
|
||||
s.Fatalf("%v", errServe)
|
||||
}
|
||||
s.Logf("Stopped serving goldpinger at http://%s", l.Addr())
|
||||
}(s.httpServerL)
|
||||
@@ -274,14 +273,14 @@ func (s *Server) Serve() (err error) {
|
||||
|
||||
if s.TLSCACertificate != "" {
|
||||
// include specified CA certificate
|
||||
caCert, caCertErr := ioutil.ReadFile(string(s.TLSCACertificate))
|
||||
caCert, caCertErr := os.ReadFile(string(s.TLSCACertificate))
|
||||
if caCertErr != nil {
|
||||
return caCertErr
|
||||
}
|
||||
caCertPool := x509.NewCertPool()
|
||||
ok := caCertPool.AppendCertsFromPEM(caCert)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot parse CA certificate")
|
||||
return errors.New("cannot parse CA certificate")
|
||||
}
|
||||
httpsServer.TLSConfig.ClientCAs = caCertPool
|
||||
httpsServer.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
@@ -312,8 +311,8 @@ func (s *Server) Serve() (err error) {
|
||||
s.Logf("Serving goldpinger at https://%s", s.httpsServerL.Addr())
|
||||
go func(l net.Listener) {
|
||||
defer wg.Done()
|
||||
if err := httpsServer.Serve(l); err != nil && err != http.ErrServerClosed {
|
||||
s.Fatalf("%v", err)
|
||||
if errServe := httpsServer.Serve(l); errServe != nil && !errors.Is(errServe, http.ErrServerClosed) {
|
||||
s.Fatalf("%v", errServe)
|
||||
}
|
||||
s.Logf("Stopped serving goldpinger at https://%s", l.Addr())
|
||||
}(tls.NewListener(s.httpsServerL, httpsServer.TLSConfig))
|
||||
|
||||
@@ -378,16 +378,33 @@ var main = function(timeout){
|
||||
if (!edge._data.OK) {
|
||||
color = "red";
|
||||
}
|
||||
if (edge._data.OK && edge._data["loss-pct"] > 0) {
|
||||
color = "#f0ad4e";
|
||||
}
|
||||
if ("isprobeResultsNode" in edge._data) {
|
||||
type = "dashed";
|
||||
}
|
||||
var label = "";
|
||||
if (edge._data["udp-rtt-ms"] > 0) {
|
||||
label = edge._data["udp-rtt-ms"].toFixed(2) + "ms udp";
|
||||
} else if (edge._data["response-time-ms"]) {
|
||||
label = edge._data["response-time-ms"] + "ms";
|
||||
} else if (edge._data.elapsed) {
|
||||
label = edge._data.elapsed;
|
||||
}
|
||||
if (edge._data["loss-pct"] > 0) {
|
||||
label += " " + edge._data["loss-pct"].toFixed(1) + "% loss";
|
||||
}
|
||||
if (edge._data["hop-count"] > 0) {
|
||||
label += " " + edge._data["hop-count"] + " hops";
|
||||
}
|
||||
var edge = {
|
||||
id: "e" + i,
|
||||
source: edge.source,
|
||||
target: edge.target,
|
||||
type: type,
|
||||
color: color,
|
||||
label: edge._data.elapsed,
|
||||
label: label,
|
||||
size: 7
|
||||
}
|
||||
//console.log(edge);
|
||||
|
||||
12
swagger.yml
12
swagger.yml
@@ -61,6 +61,18 @@ definitions:
|
||||
type: number
|
||||
format: int64
|
||||
description: wall clock time in milliseconds
|
||||
loss-pct:
|
||||
type: number
|
||||
format: double
|
||||
description: UDP packet loss percentage (0-100)
|
||||
hop-count:
|
||||
type: integer
|
||||
format: int32
|
||||
description: estimated network hop count from UDP TTL
|
||||
udp-rtt-ms:
|
||||
type: number
|
||||
format: double
|
||||
description: average UDP round-trip time in milliseconds
|
||||
CheckResults:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
Reference in New Issue
Block a user