Expand tsic to offer PingViaDerp
This commit is contained in:
parent
a5afe4bd06
commit
bb07aec82c
3 changed files with 129 additions and 3 deletions
|
@ -4,6 +4,7 @@ import (
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/juanfont/headscale/integration/dockertestutil"
|
||||||
"github.com/juanfont/headscale/integration/tsic"
|
"github.com/juanfont/headscale/integration/tsic"
|
||||||
"tailscale.com/ipn/ipnstate"
|
"tailscale.com/ipn/ipnstate"
|
||||||
)
|
)
|
||||||
|
@ -13,7 +14,7 @@ type TailscaleClient interface {
|
||||||
Hostname() string
|
Hostname() string
|
||||||
Shutdown() error
|
Shutdown() error
|
||||||
Version() string
|
Version() string
|
||||||
Execute(command []string) (string, string, error)
|
Execute(command []string, options ...dockertestutil.ExecuteCommandOption) (string, string, error)
|
||||||
Up(loginServer, authKey string) error
|
Up(loginServer, authKey string) error
|
||||||
UpWithLoginURL(loginServer string) (*url.URL, error)
|
UpWithLoginURL(loginServer string) (*url.URL, error)
|
||||||
Logout() error
|
Logout() error
|
||||||
|
@ -24,6 +25,7 @@ type TailscaleClient interface {
|
||||||
WaitForLogout() error
|
WaitForLogout() error
|
||||||
WaitForPeers(expected int) error
|
WaitForPeers(expected int) error
|
||||||
Ping(hostnameOrIP string, opts ...tsic.PingOption) error
|
Ping(hostnameOrIP string, opts ...tsic.PingOption) error
|
||||||
|
PingViaDERP(hostnameOrIP string, opts ...tsic.PingOption) error
|
||||||
Curl(url string, opts ...tsic.CurlOption) (string, error)
|
Curl(url string, opts ...tsic.CurlOption) (string, error)
|
||||||
ID() string
|
ID() string
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ const (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errTailscalePingFailed = errors.New("ping failed")
|
errTailscalePingFailed = errors.New("ping failed")
|
||||||
|
errTailscalePingNotDERP = errors.New("ping not via DERP")
|
||||||
errTailscaleNotLoggedIn = errors.New("tailscale not logged in")
|
errTailscaleNotLoggedIn = errors.New("tailscale not logged in")
|
||||||
errTailscaleWrongPeerCount = errors.New("wrong peer count")
|
errTailscaleWrongPeerCount = errors.New("wrong peer count")
|
||||||
errTailscaleCannotUpWithoutAuthkey = errors.New("cannot up without authkey")
|
errTailscaleCannotUpWithoutAuthkey = errors.New("cannot up without authkey")
|
||||||
|
@ -56,6 +57,7 @@ type TailscaleInContainer struct {
|
||||||
withSSH bool
|
withSSH bool
|
||||||
withTags []string
|
withTags []string
|
||||||
withEntrypoint []string
|
withEntrypoint []string
|
||||||
|
withExtraHosts []string
|
||||||
workdir string
|
workdir string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,6 +126,12 @@ func WithDockerWorkdir(dir string) Option {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithExtraHosts(hosts []string) Option {
|
||||||
|
return func(tsic *TailscaleInContainer) {
|
||||||
|
tsic.withExtraHosts = hosts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithDockerEntrypoint allows the docker entrypoint of the container
|
// WithDockerEntrypoint allows the docker entrypoint of the container
|
||||||
// to be overridden. This is a dangerous option which can make
|
// to be overridden. This is a dangerous option which can make
|
||||||
// the container not work as intended as a typo might prevent
|
// the container not work as intended as a typo might prevent
|
||||||
|
@ -169,11 +177,12 @@ func New(
|
||||||
|
|
||||||
tailscaleOptions := &dockertest.RunOptions{
|
tailscaleOptions := &dockertest.RunOptions{
|
||||||
Name: hostname,
|
Name: hostname,
|
||||||
Networks: []*dockertest.Network{network},
|
Networks: []*dockertest.Network{tsic.network},
|
||||||
// Cmd: []string{
|
// Cmd: []string{
|
||||||
// "tailscaled", "--tun=tsdev",
|
// "tailscaled", "--tun=tsdev",
|
||||||
// },
|
// },
|
||||||
Entrypoint: tsic.withEntrypoint,
|
Entrypoint: tsic.withEntrypoint,
|
||||||
|
ExtraHosts: tsic.withExtraHosts,
|
||||||
}
|
}
|
||||||
|
|
||||||
if tsic.headscaleHostname != "" {
|
if tsic.headscaleHostname != "" {
|
||||||
|
@ -248,11 +257,13 @@ func (t *TailscaleInContainer) ID() string {
|
||||||
// result of stdout as a string.
|
// result of stdout as a string.
|
||||||
func (t *TailscaleInContainer) Execute(
|
func (t *TailscaleInContainer) Execute(
|
||||||
command []string,
|
command []string,
|
||||||
|
options ...dockertestutil.ExecuteCommandOption,
|
||||||
) (string, string, error) {
|
) (string, string, error) {
|
||||||
stdout, stderr, err := dockertestutil.ExecuteCommand(
|
stdout, stderr, err := dockertestutil.ExecuteCommand(
|
||||||
t.container,
|
t.container,
|
||||||
command,
|
command,
|
||||||
[]string{},
|
[]string{},
|
||||||
|
options...,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("command stderr: %s\n", stderr)
|
log.Printf("command stderr: %s\n", stderr)
|
||||||
|
@ -477,7 +488,7 @@ func (t *TailscaleInContainer) WaitForPeers(expected int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// PingOption repreent optional settings that can be given
|
// PingOption represent optional settings that can be given
|
||||||
// to ping another host.
|
// to ping another host.
|
||||||
PingOption = func(args *pingArgs)
|
PingOption = func(args *pingArgs)
|
||||||
|
|
||||||
|
@ -488,6 +499,15 @@ type (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
DERPPingOption = func(args *derpPingArgs)
|
||||||
|
|
||||||
|
derpPingArgs struct {
|
||||||
|
timeout time.Duration
|
||||||
|
count int
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// WithPingTimeout sets the timeout for the ping command.
|
// WithPingTimeout sets the timeout for the ping command.
|
||||||
func WithPingTimeout(timeout time.Duration) PingOption {
|
func WithPingTimeout(timeout time.Duration) PingOption {
|
||||||
return func(args *pingArgs) {
|
return func(args *pingArgs) {
|
||||||
|
@ -555,6 +575,62 @@ func (t *TailscaleInContainer) Ping(hostnameOrIP string, opts ...PingOption) err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PingViaDERP executes the Tailscale ping command and pings a hostname
|
||||||
|
// or IP via the DERP network (i.e., not a direct connection). It accepts a series of DERPPingOption.
|
||||||
|
// TODO(kradalby): Make multiping, go routine magic.
|
||||||
|
func (t *TailscaleInContainer) PingViaDERP(hostnameOrIP string, opts ...PingOption) error {
|
||||||
|
args := pingArgs{
|
||||||
|
timeout: time.Second,
|
||||||
|
count: defaultPingCount,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&args)
|
||||||
|
}
|
||||||
|
|
||||||
|
command := []string{
|
||||||
|
"tailscale", "ping",
|
||||||
|
fmt.Sprintf("--timeout=%s", args.timeout),
|
||||||
|
fmt.Sprintf("--c=%d", args.count),
|
||||||
|
"--until-direct=false",
|
||||||
|
}
|
||||||
|
|
||||||
|
command = append(command, hostnameOrIP)
|
||||||
|
|
||||||
|
return t.pool.Retry(func() error {
|
||||||
|
result, _, err := t.Execute(
|
||||||
|
command,
|
||||||
|
dockertestutil.ExecuteCommandTimeout(
|
||||||
|
time.Duration(int64(args.timeout)*int64(args.count)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(
|
||||||
|
"failed to run ping command from %s to %s, err: %s",
|
||||||
|
t.Hostname(),
|
||||||
|
hostnameOrIP,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(result, "is local") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(result, "pong") {
|
||||||
|
return backoff.Permanent(errTailscalePingFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(result, "via DERP") {
|
||||||
|
return backoff.Permanent(errTailscalePingNotDERP)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// CurlOption repreent optional settings that can be given
|
// CurlOption repreent optional settings that can be given
|
||||||
// to curl another host.
|
// to curl another host.
|
||||||
|
|
|
@ -2,6 +2,9 @@ package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/juanfont/headscale/integration/tsic"
|
||||||
)
|
)
|
||||||
|
|
||||||
func pingAllHelper(t *testing.T, clients []TailscaleClient, addrs []string) int {
|
func pingAllHelper(t *testing.T, clients []TailscaleClient, addrs []string) int {
|
||||||
|
@ -22,6 +25,51 @@ func pingAllHelper(t *testing.T, clients []TailscaleClient, addrs []string) int
|
||||||
return success
|
return success
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func pingDerpAllHelper(t *testing.T, clients []TailscaleClient, addrs []string) int {
|
||||||
|
t.Helper()
|
||||||
|
success := 0
|
||||||
|
|
||||||
|
for _, client := range clients {
|
||||||
|
for _, addr := range addrs {
|
||||||
|
if isSelfClient(client, addr) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err := client.PingViaDERP(
|
||||||
|
addr,
|
||||||
|
tsic.WithPingTimeout(2*time.Second),
|
||||||
|
tsic.WithPingCount(10),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to ping %s from %s: %s", addr, client.Hostname(), err)
|
||||||
|
} else {
|
||||||
|
success++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return success
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSelfClient(client TailscaleClient, addr string) bool {
|
||||||
|
if addr == client.Hostname() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
ips, err := client.IPs()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ip := range ips {
|
||||||
|
if ip.String() == addr {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// pingAllNegativeHelper is intended to have 1 or more nodes timeing out from the ping,
|
// pingAllNegativeHelper is intended to have 1 or more nodes timeing out from the ping,
|
||||||
// it counts failures instead of successes.
|
// it counts failures instead of successes.
|
||||||
// func pingAllNegativeHelper(t *testing.T, clients []TailscaleClient, addrs []string) int {
|
// func pingAllNegativeHelper(t *testing.T, clients []TailscaleClient, addrs []string) int {
|
||||||
|
|
Loading…
Reference in a new issue