mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-07-27 16:39:58 +00:00
TUN-8728: implement diag/tunnel endpoint
## Summary The new endpoint returns the current information to be used when calling the diagnostic procedure. This also adds: - add indexed connection info and method to extract active connections from connTracker - add edge address to Event struct and conn tracker - remove unnecessary event send - add tunnel configuration handler - adjust cmd and metrics to create diagnostic server Closes TUN-8728
This commit is contained in:
@@ -3,7 +3,8 @@ package diagnostic
|
||||
import "time"
|
||||
|
||||
const (
|
||||
defaultCollectorTimeout = time.Second * 10 // This const define the timeout value of a collector operation.
|
||||
collectorField = "collector" // used for logging purposes
|
||||
systemCollectorName = "system" // used for logging purposes
|
||||
defaultCollectorTimeout = time.Second * 10 // This const define the timeout value of a collector operation.
|
||||
collectorField = "collector" // used for logging purposes
|
||||
systemCollectorName = "system" // used for logging purposes
|
||||
tunnelStateCollectorName = "tunnelState" // used for logging purposes
|
||||
)
|
||||
|
@@ -6,28 +6,41 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
"github.com/cloudflare/cloudflared/tunnelstate"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
log *zerolog.Logger
|
||||
timeout time.Duration
|
||||
systemCollector SystemCollector
|
||||
tunnelID uuid.UUID
|
||||
connectorID uuid.UUID
|
||||
tracker *tunnelstate.ConnTracker
|
||||
}
|
||||
|
||||
func NewDiagnosticHandler(
|
||||
log *zerolog.Logger,
|
||||
timeout time.Duration,
|
||||
systemCollector SystemCollector,
|
||||
tunnelID uuid.UUID,
|
||||
connectorID uuid.UUID,
|
||||
tracker *tunnelstate.ConnTracker,
|
||||
) *Handler {
|
||||
logger := log.With().Logger()
|
||||
if timeout == 0 {
|
||||
timeout = defaultCollectorTimeout
|
||||
}
|
||||
|
||||
return &Handler{
|
||||
log,
|
||||
timeout,
|
||||
systemCollector,
|
||||
log: &logger,
|
||||
timeout: timeout,
|
||||
systemCollector: systemCollector,
|
||||
tunnelID: tunnelID,
|
||||
connectorID: connectorID,
|
||||
tracker: tracker,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,9 +48,7 @@ func (handler *Handler) SystemHandler(writer http.ResponseWriter, request *http.
|
||||
logger := handler.log.With().Str(collectorField, systemCollectorName).Logger()
|
||||
logger.Info().Msg("Collection started")
|
||||
|
||||
defer func() {
|
||||
logger.Info().Msg("Collection finished")
|
||||
}()
|
||||
defer logger.Info().Msg("Collection finished")
|
||||
|
||||
ctx, cancel := context.WithTimeout(request.Context(), handler.timeout)
|
||||
|
||||
@@ -73,6 +84,32 @@ func (handler *Handler) SystemHandler(writer http.ResponseWriter, request *http.
|
||||
}
|
||||
}
|
||||
|
||||
type tunnelStateResponse struct {
|
||||
TunnelID uuid.UUID `json:"tunnelID,omitempty"`
|
||||
ConnectorID uuid.UUID `json:"connectorID,omitempty"`
|
||||
Connections []tunnelstate.IndexedConnectionInfo `json:"connections,omitempty"`
|
||||
}
|
||||
|
||||
func (handler *Handler) TunnelStateHandler(writer http.ResponseWriter, _ *http.Request) {
|
||||
log := handler.log.With().Str(collectorField, tunnelStateCollectorName).Logger()
|
||||
log.Info().Msg("Collection started")
|
||||
|
||||
defer log.Info().Msg("Collection finished")
|
||||
|
||||
body := tunnelStateResponse{
|
||||
handler.tunnelID,
|
||||
handler.connectorID,
|
||||
handler.tracker.GetActiveConnections(),
|
||||
}
|
||||
encoder := json.NewEncoder(writer)
|
||||
|
||||
err := encoder.Encode(body)
|
||||
if err != nil {
|
||||
handler.log.Error().Err(err).Msgf("error occurred whilst serializing information")
|
||||
writer.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func writeResponse(writer http.ResponseWriter, bytes []byte, logger *zerolog.Logger) {
|
||||
bytesWritten, err := writer.Write(bytes)
|
||||
if err != nil {
|
||||
|
@@ -5,15 +5,19 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cloudflare/cloudflared/connection"
|
||||
"github.com/cloudflare/cloudflared/diagnostic"
|
||||
"github.com/cloudflare/cloudflared/tunnelstate"
|
||||
)
|
||||
|
||||
type SystemCollectorMock struct{}
|
||||
@@ -24,6 +28,23 @@ const (
|
||||
errorKey = "errkey"
|
||||
)
|
||||
|
||||
func newTrackerFromConns(t *testing.T, connections []tunnelstate.IndexedConnectionInfo) *tunnelstate.ConnTracker {
|
||||
t.Helper()
|
||||
|
||||
log := zerolog.Nop()
|
||||
tracker := tunnelstate.NewConnTracker(&log)
|
||||
|
||||
for _, conn := range connections {
|
||||
tracker.OnTunnelEvent(connection.Event{
|
||||
Index: conn.Index,
|
||||
EventType: connection.Connected,
|
||||
Protocol: conn.Protocol,
|
||||
EdgeAddress: conn.EdgeAddress,
|
||||
})
|
||||
}
|
||||
|
||||
return tracker
|
||||
}
|
||||
func setCtxValuesForSystemCollector(
|
||||
systemInfo *diagnostic.SystemInformation,
|
||||
rawInfo string,
|
||||
@@ -83,7 +104,7 @@ func TestSystemHandler(t *testing.T) {
|
||||
for _, tCase := range tests {
|
||||
t.Run(tCase.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
handler := diagnostic.NewDiagnosticHandler(&log, 0, &SystemCollectorMock{})
|
||||
handler := diagnostic.NewDiagnosticHandler(&log, 0, &SystemCollectorMock{}, uuid.New(), uuid.New(), nil)
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx := setCtxValuesForSystemCollector(tCase.systemInfo, tCase.rawInfo, tCase.err)
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, "/diag/syste,", nil)
|
||||
@@ -106,3 +127,58 @@ func TestSystemHandler(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTunnelStateHandler(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
log := zerolog.Nop()
|
||||
tests := []struct {
|
||||
name string
|
||||
tunnelID uuid.UUID
|
||||
clientID uuid.UUID
|
||||
connections []tunnelstate.IndexedConnectionInfo
|
||||
}{
|
||||
{
|
||||
name: "case1",
|
||||
tunnelID: uuid.New(),
|
||||
clientID: uuid.New(),
|
||||
},
|
||||
{
|
||||
name: "case2",
|
||||
tunnelID: uuid.New(),
|
||||
clientID: uuid.New(),
|
||||
connections: []tunnelstate.IndexedConnectionInfo{{
|
||||
ConnectionInfo: tunnelstate.ConnectionInfo{
|
||||
IsConnected: true,
|
||||
Protocol: connection.QUIC,
|
||||
EdgeAddress: net.IPv4(100, 100, 100, 100),
|
||||
},
|
||||
Index: 0,
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tCase := range tests {
|
||||
t.Run(tCase.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
tracker := newTrackerFromConns(t, tCase.connections)
|
||||
handler := diagnostic.NewDiagnosticHandler(&log, 0, nil, tCase.tunnelID, tCase.clientID, tracker)
|
||||
recorder := httptest.NewRecorder()
|
||||
handler.TunnelStateHandler(recorder, nil)
|
||||
decoder := json.NewDecoder(recorder.Body)
|
||||
|
||||
var response struct {
|
||||
TunnelID uuid.UUID `json:"tunnelID,omitempty"`
|
||||
ConnectorID uuid.UUID `json:"connectorID,omitempty"`
|
||||
Connections []tunnelstate.IndexedConnectionInfo `json:"connections,omitempty"`
|
||||
}
|
||||
|
||||
err := decoder.Decode(&response)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, recorder.Code)
|
||||
assert.Equal(t, tCase.tunnelID, response.TunnelID)
|
||||
assert.Equal(t, tCase.clientID, response.ConnectorID)
|
||||
assert.Equal(t, tCase.connections, response.Connections)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user