mirror of
https://github.com/cloudflare/cloudflared.git
synced 2026-06-23 04:10:20 +00:00
TUN-10390: Call prechecks
Final run method, which runs cloudlflared pre-checks for both the normal startup procedure, as well as cloudflared diag.
For cloudflared diag, this produces a new json output to which is added to the final zip file.
Also added in a new flag to prevent this from running all the time, at least for now until we are 100% sure this works as intended. We will later remove this flag, only leaving in `--no-prechecks`, so this runs by default for everyone using cloudflared.
Tested pre-checks locally with origintunneld. The results show all pre-checks succeeding. In this case, it ran with only 1 region, since locally we run it with `--edge origintunneld1:7844`.
{width=900 height=217}
This commit is contained in:
@@ -126,6 +126,9 @@ const (
|
|||||||
// NoPrechecks is the command line flag to skip connectivity pre-checks at startup.
|
// NoPrechecks is the command line flag to skip connectivity pre-checks at startup.
|
||||||
NoPrechecks = "no-prechecks"
|
NoPrechecks = "no-prechecks"
|
||||||
|
|
||||||
|
// Prechecks is the command line flag to run connectivity pre-checks at startup.
|
||||||
|
Prechecks = "prechecks"
|
||||||
|
|
||||||
// LogLevel is the command line flag for the cloudflared logging level
|
// LogLevel is the command line flag for the cloudflared logging level
|
||||||
LogLevel = "loglevel"
|
LogLevel = "loglevel"
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -31,11 +32,13 @@ import (
|
|||||||
"github.com/cloudflare/cloudflared/credentials"
|
"github.com/cloudflare/cloudflared/credentials"
|
||||||
"github.com/cloudflare/cloudflared/diagnostic"
|
"github.com/cloudflare/cloudflared/diagnostic"
|
||||||
"github.com/cloudflare/cloudflared/edgediscovery"
|
"github.com/cloudflare/cloudflared/edgediscovery"
|
||||||
|
"github.com/cloudflare/cloudflared/edgediscovery/allregions"
|
||||||
"github.com/cloudflare/cloudflared/ingress"
|
"github.com/cloudflare/cloudflared/ingress"
|
||||||
"github.com/cloudflare/cloudflared/logger"
|
"github.com/cloudflare/cloudflared/logger"
|
||||||
"github.com/cloudflare/cloudflared/management"
|
"github.com/cloudflare/cloudflared/management"
|
||||||
"github.com/cloudflare/cloudflared/metrics"
|
"github.com/cloudflare/cloudflared/metrics"
|
||||||
"github.com/cloudflare/cloudflared/orchestration"
|
"github.com/cloudflare/cloudflared/orchestration"
|
||||||
|
"github.com/cloudflare/cloudflared/prechecks"
|
||||||
"github.com/cloudflare/cloudflared/signal"
|
"github.com/cloudflare/cloudflared/signal"
|
||||||
"github.com/cloudflare/cloudflared/supervisor"
|
"github.com/cloudflare/cloudflared/supervisor"
|
||||||
"github.com/cloudflare/cloudflared/tlsconfig"
|
"github.com/cloudflare/cloudflared/tlsconfig"
|
||||||
@@ -372,6 +375,17 @@ func StartServer(
|
|||||||
info.Log(log)
|
info.Log(log)
|
||||||
logClientOptions(c, log)
|
logClientOptions(c, log)
|
||||||
|
|
||||||
|
// Run connectivity pre-checks for cloudflared. This runs in a separate
|
||||||
|
// goroutine, as we want to keep initializing cloudflared while prechecks
|
||||||
|
// are running.
|
||||||
|
if c.Bool(cfdflags.Prechecks) && !c.Bool(cfdflags.NoPrechecks) {
|
||||||
|
resolvedRegion := c.String(cfdflags.Region)
|
||||||
|
if resolvedRegion == "" && namedTunnel != nil {
|
||||||
|
resolvedRegion = namedTunnel.Credentials.Endpoint
|
||||||
|
}
|
||||||
|
go runPrechecks(c, log, resolvedRegion)
|
||||||
|
}
|
||||||
|
|
||||||
// this context drives the server, when it's canceled tunnel and all other components (origins, dns, etc...) should stop
|
// this context drives the server, when it's canceled tunnel and all other components (origins, dns, etc...) should stop
|
||||||
ctx, cancel := context.WithCancel(c.Context)
|
ctx, cancel := context.WithCancel(c.Context)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -513,6 +527,49 @@ func StartServer(
|
|||||||
return waitToShutdown(&wg, cancel, errC, graceShutdownC, gracePeriod, log)
|
return waitToShutdown(&wg, cancel, errC, graceShutdownC, gracePeriod, log)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// runPrechecks executes connectivity pre-checks and logs the results.
|
||||||
|
// Pre-checks are diagnostic only and do not gate tunnel startup.
|
||||||
|
func runPrechecks(c *cli.Context, log *zerolog.Logger, region string) {
|
||||||
|
ipVersion := allregions.Auto
|
||||||
|
if ipVersionStr := c.String(cfdflags.EdgeIpVersion); ipVersionStr != "" {
|
||||||
|
parsedVersion, err := parseConfigIPVersion(ipVersionStr)
|
||||||
|
if err == nil {
|
||||||
|
ipVersion = parsedVersion
|
||||||
|
} else {
|
||||||
|
log.Warn().Str("edgeIpVersion", ipVersionStr).Err(err).Msg("Invalid edge-ip-version value, using auto")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := prechecks.Config{
|
||||||
|
Region: region,
|
||||||
|
IPVersion: ipVersion,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mirror the static/dynamic edge selection from supervisor/supervisor.go:
|
||||||
|
// when --edge addresses are provided, bypass DNS discovery entirely.
|
||||||
|
var dnsResolver prechecks.DNSResolver
|
||||||
|
if edgeAddrs := c.StringSlice(cfdflags.Edge); len(edgeAddrs) > 0 {
|
||||||
|
dnsResolver = &prechecks.StaticEdgeDNSResolver{Addrs: edgeAddrs, Log: log}
|
||||||
|
} else {
|
||||||
|
dnsResolver = &prechecks.EdgeDNSResolver{Log: log}
|
||||||
|
}
|
||||||
|
|
||||||
|
dialers := prechecks.RunDialers{
|
||||||
|
DNSResolver: dnsResolver,
|
||||||
|
TCPDialer: &prechecks.EdgeTCPDialer{},
|
||||||
|
QUICDialer: &prechecks.EdgeQUICDialer{},
|
||||||
|
ManagementDialer: &prechecks.NetManagementDialer{Dialer: net.Dialer{}},
|
||||||
|
}
|
||||||
|
|
||||||
|
report := prechecks.Run(c.Context, c.String(cfdflags.CACert), cfg, log, dialers)
|
||||||
|
|
||||||
|
// Output the human-readable table to console
|
||||||
|
fmt.Println(report.String())
|
||||||
|
|
||||||
|
// Also log structured results for log aggregation
|
||||||
|
report.LogEvent(log)
|
||||||
|
}
|
||||||
|
|
||||||
func waitToShutdown(wg *sync.WaitGroup,
|
func waitToShutdown(wg *sync.WaitGroup,
|
||||||
cancelServerContext func(),
|
cancelServerContext func(),
|
||||||
errC <-chan error,
|
errC <-chan error,
|
||||||
@@ -889,6 +946,13 @@ func configureCloudflaredFlags(shouldHide bool) []cli.Flag {
|
|||||||
Value: false,
|
Value: false,
|
||||||
Hidden: shouldHide,
|
Hidden: shouldHide,
|
||||||
}),
|
}),
|
||||||
|
altsrc.NewBoolFlag(&cli.BoolFlag{
|
||||||
|
Name: cfdflags.Prechecks,
|
||||||
|
Usage: "Run connectivity pre-checks at startup.",
|
||||||
|
EnvVars: []string{"TUNNEL_PRECHECKS"},
|
||||||
|
Value: false,
|
||||||
|
Hidden: shouldHide,
|
||||||
|
}),
|
||||||
altsrc.NewStringFlag(&cli.StringFlag{
|
altsrc.NewStringFlag(&cli.StringFlag{
|
||||||
Name: cfdflags.Metrics,
|
Name: cfdflags.Metrics,
|
||||||
Value: metrics.GetMetricsDefaultAddress(metrics.Runtime),
|
Value: metrics.GetMetricsDefaultAddress(metrics.Runtime),
|
||||||
|
|||||||
@@ -262,6 +262,7 @@ func prepareTunnelConfig(
|
|||||||
QUICConnectionLevelFlowControlLimit: c.Uint64(flags.QuicConnLevelFlowControlLimit),
|
QUICConnectionLevelFlowControlLimit: c.Uint64(flags.QuicConnLevelFlowControlLimit),
|
||||||
QUICStreamLevelFlowControlLimit: c.Uint64(flags.QuicStreamLevelFlowControlLimit),
|
QUICStreamLevelFlowControlLimit: c.Uint64(flags.QuicStreamLevelFlowControlLimit),
|
||||||
NoPrechecks: c.Bool(flags.NoPrechecks),
|
NoPrechecks: c.Bool(flags.NoPrechecks),
|
||||||
|
Prechecks: c.Bool(flags.Prechecks),
|
||||||
OriginDNSService: dnsService,
|
OriginDNSService: dnsService,
|
||||||
OriginDialerService: originDialerService,
|
OriginDialerService: originDialerService,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -489,7 +489,7 @@ func readyCommand(c *cli.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// nolint: gosec
|
// nolint: gosec // URL is constructed from the user-configured local metrics endpoint.
|
||||||
res, err := http.DefaultClient.Do(req)
|
res, err := http.DefaultClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -1109,6 +1109,7 @@ func diagCommand(ctx *cli.Context) error {
|
|||||||
Address: sctx.c.String(flags.Metrics),
|
Address: sctx.c.String(flags.Metrics),
|
||||||
ContainerID: sctx.c.String(diagContainerIDFlagName),
|
ContainerID: sctx.c.String(diagContainerIDFlagName),
|
||||||
PodID: sctx.c.String(diagPodFlagName),
|
PodID: sctx.c.String(diagPodFlagName),
|
||||||
|
Region: sctx.c.String(flags.Region),
|
||||||
Toggles: diagnostic.Toggles{
|
Toggles: diagnostic.Toggles{
|
||||||
NoDiagLogs: sctx.c.Bool(noDiagLogsFlagName),
|
NoDiagLogs: sctx.c.Bool(noDiagLogsFlagName),
|
||||||
NoDiagMetrics: sctx.c.Bool(noDiagMetricsFlagName),
|
NoDiagMetrics: sctx.c.Bool(noDiagMetricsFlagName),
|
||||||
|
|||||||
@@ -34,4 +34,5 @@ const (
|
|||||||
cliConfigurationBaseName = "cli-configuration.json"
|
cliConfigurationBaseName = "cli-configuration.json"
|
||||||
configurationBaseName = "configuration.json"
|
configurationBaseName = "configuration.json"
|
||||||
taskResultBaseName = "task-result.json"
|
taskResultBaseName = "task-result.json"
|
||||||
|
prechecksBaseName = "prechecks.json"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -16,6 +17,8 @@ import (
|
|||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
|
|
||||||
network "github.com/cloudflare/cloudflared/diagnostic/network"
|
network "github.com/cloudflare/cloudflared/diagnostic/network"
|
||||||
|
"github.com/cloudflare/cloudflared/edgediscovery/allregions"
|
||||||
|
"github.com/cloudflare/cloudflared/prechecks"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -32,6 +35,7 @@ const (
|
|||||||
networkInformationJobName = "network information"
|
networkInformationJobName = "network information"
|
||||||
cliConfigurationJobName = "cli configuration"
|
cliConfigurationJobName = "cli configuration"
|
||||||
configurationJobName = "configuration"
|
configurationJobName = "configuration"
|
||||||
|
prechecksJobName = "connectivity pre-checks"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Struct used to hold the results of different routines executing the network collection.
|
// Struct used to hold the results of different routines executing the network collection.
|
||||||
@@ -92,6 +96,7 @@ type Options struct {
|
|||||||
Address string
|
Address string
|
||||||
ContainerID string
|
ContainerID string
|
||||||
PodID string
|
PodID string
|
||||||
|
Region string
|
||||||
Toggles Toggles
|
Toggles Toggles
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,7 +235,7 @@ func networkInformationCollectors() (rawNetworkCollector, jsonNetworkCollector c
|
|||||||
}
|
}
|
||||||
|
|
||||||
func rawNetworkInformationWriter(resultMap map[string]networkCollectionResult) (string, error) {
|
func rawNetworkInformationWriter(resultMap map[string]networkCollectionResult) (string, error) {
|
||||||
// nolint: gosec
|
// nolint: gosec // Intentionally creating a temporary diagnostic file in the OS temp directory.
|
||||||
networkDumpHandle, err := os.Create(filepath.Join(os.TempDir(), rawNetworkBaseName))
|
networkDumpHandle, err := os.Create(filepath.Join(os.TempDir(), rawNetworkBaseName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", ErrCreatingTemporaryFile
|
return "", ErrCreatingTemporaryFile
|
||||||
@@ -372,6 +377,7 @@ func resolveInstanceBaseURL(
|
|||||||
func createJobs(
|
func createJobs(
|
||||||
client *httpClient,
|
client *httpClient,
|
||||||
tunnel *TunnelState,
|
tunnel *TunnelState,
|
||||||
|
region string,
|
||||||
diagContainer string,
|
diagContainer string,
|
||||||
diagPod string,
|
diagPod string,
|
||||||
noDiagSystem bool,
|
noDiagSystem bool,
|
||||||
@@ -434,11 +440,55 @@ func createJobs(
|
|||||||
fn: collectFromEndpointAdapter(client.GetTunnelConfiguration, configurationBaseName),
|
fn: collectFromEndpointAdapter(client.GetTunnelConfiguration, configurationBaseName),
|
||||||
bypass: false,
|
bypass: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
jobName: prechecksJobName,
|
||||||
|
fn: collectPrechecks(region),
|
||||||
|
bypass: noDiagNetwork,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return jobs
|
return jobs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// collectPrechecks runs connectivity pre-checks and writes the results to a JSON file.
|
||||||
|
func collectPrechecks(region string) collectFunc {
|
||||||
|
return func(ctx context.Context) (string, error) {
|
||||||
|
cfg := prechecks.Config{
|
||||||
|
Region: region,
|
||||||
|
IPVersion: allregions.Auto,
|
||||||
|
Timeout: defaultTimeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a no-op logger since we don't want to spam logs during diagnostic collection
|
||||||
|
log := zerolog.New(io.Discard)
|
||||||
|
|
||||||
|
dialers := prechecks.RunDialers{
|
||||||
|
DNSResolver: &prechecks.EdgeDNSResolver{Log: &log},
|
||||||
|
TCPDialer: &prechecks.EdgeTCPDialer{},
|
||||||
|
QUICDialer: &prechecks.EdgeQUICDialer{},
|
||||||
|
ManagementDialer: &prechecks.NetManagementDialer{Dialer: net.Dialer{}},
|
||||||
|
}
|
||||||
|
|
||||||
|
emptyCert := ""
|
||||||
|
report := prechecks.Run(ctx, emptyCert, cfg, &log, dialers)
|
||||||
|
|
||||||
|
// Write the report to a JSON file
|
||||||
|
// nolint: gosec
|
||||||
|
dumpHandle, err := os.Create(filepath.Join(os.TempDir(), prechecksBaseName))
|
||||||
|
if err != nil {
|
||||||
|
return "", ErrCreatingTemporaryFile
|
||||||
|
}
|
||||||
|
defer func() { _ = dumpHandle.Close() }()
|
||||||
|
|
||||||
|
encoder := newFormattedEncoder(dumpHandle)
|
||||||
|
if err := encoder.Encode(report); err != nil {
|
||||||
|
return dumpHandle.Name(), fmt.Errorf("error encoding prechecks report: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return dumpHandle.Name(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func createTaskReport(taskReport map[string]taskResult) (string, error) {
|
func createTaskReport(taskReport map[string]taskResult) (string, error) {
|
||||||
// nolint: gosec
|
// nolint: gosec
|
||||||
dumpHandle, err := os.Create(filepath.Join(os.TempDir(), taskResultBaseName))
|
dumpHandle, err := os.Create(filepath.Join(os.TempDir(), taskResultBaseName))
|
||||||
@@ -527,6 +577,7 @@ func RunDiagnostic(
|
|||||||
jobs := createJobs(
|
jobs := createJobs(
|
||||||
client,
|
client,
|
||||||
tunnel,
|
tunnel,
|
||||||
|
options.Region,
|
||||||
options.ContainerID,
|
options.ContainerID,
|
||||||
options.PodID,
|
options.PodID,
|
||||||
options.Toggles.NoDiagSystem,
|
options.Toggles.NoDiagSystem,
|
||||||
|
|||||||
@@ -65,6 +65,9 @@ type TunnelConfig struct {
|
|||||||
// NoPrechecks disables connectivity pre-checks at startup.
|
// NoPrechecks disables connectivity pre-checks at startup.
|
||||||
NoPrechecks bool
|
NoPrechecks bool
|
||||||
|
|
||||||
|
// Prechecks enables connectivity pre-checks at startup.
|
||||||
|
Prechecks bool
|
||||||
|
|
||||||
NamedTunnel *connection.TunnelProperties
|
NamedTunnel *connection.TunnelProperties
|
||||||
ProtocolSelector connection.ProtocolSelector
|
ProtocolSelector connection.ProtocolSelector
|
||||||
EdgeTLSConfigs map[connection.Protocol]*tls.Config
|
EdgeTLSConfigs map[connection.Protocol]*tls.Config
|
||||||
|
|||||||
Reference in New Issue
Block a user