Add wait for peers and status to tsic

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
Kristoffer Dalby 2022-10-18 11:58:49 +02:00
parent c90d0dd843
commit 3951f39868
No known key found for this signature in database

View File

@ -1,16 +1,19 @@
package tsic package tsic
import ( import (
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"log" "log"
"net/netip" "net/netip"
"strings" "strings"
"github.com/cenkalti/backoff/v4"
"github.com/juanfont/headscale" "github.com/juanfont/headscale"
"github.com/juanfont/headscale/integration/dockertestutil" "github.com/juanfont/headscale/integration/dockertestutil"
"github.com/ory/dockertest/v3" "github.com/ory/dockertest/v3"
"github.com/ory/dockertest/v3/docker" "github.com/ory/dockertest/v3/docker"
"tailscale.com/ipn/ipnstate"
) )
const tsicHashLength = 6 const tsicHashLength = 6
@ -87,6 +90,10 @@ func (t *TailscaleInContainer) Shutdown() error {
return t.pool.Purge(t.container) return t.pool.Purge(t.container)
} }
func (t *TailscaleInContainer) Version() string {
return t.version
}
func (t *TailscaleInContainer) Up( func (t *TailscaleInContainer) Up(
loginServer, authKey string, loginServer, authKey string,
) error { ) error {
@ -113,12 +120,17 @@ func (t *TailscaleInContainer) Up(
return err return err
} }
if stdout != "" {
log.Printf("tailscale join stdout: %s\n", stdout) log.Printf("tailscale join stdout: %s\n", stdout)
}
log.Printf("%s joined\n", t.Hostname) log.Printf("%s joined\n", t.Hostname)
return nil return nil
} }
// TODO(kradalby): Make cached/lazy
func (t *TailscaleInContainer) IPs() ([]netip.Addr, error) { func (t *TailscaleInContainer) IPs() ([]netip.Addr, error) {
ips := make([]netip.Addr, 0) ips := make([]netip.Addr, 0)
@ -157,7 +169,53 @@ func (t *TailscaleInContainer) IPs() ([]netip.Addr, error) {
return ips, nil return ips, nil
} }
func (t *TailscaleInContainer) Status() (*ipnstate.Status, error) {
command := []string{
"tailscale",
"status",
"--json",
}
result, _, err := dockertestutil.ExecuteCommand(
t.container,
command,
[]string{},
)
if err != nil {
return nil, fmt.Errorf("failed to execute tailscale status command: %w", err)
}
var status ipnstate.Status
err = json.Unmarshal([]byte(result), &status)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal tailscale status: %w", err)
}
return &status, err
}
func (t *TailscaleInContainer) WaitForPeers(expected int) error {
return t.pool.Retry(func() error {
status, err := t.Status()
if err != nil {
return fmt.Errorf("failed to fetch tailscale status: %w", err)
}
if peers := status.Peers(); len(peers) != expected {
return fmt.Errorf(
"tailscale client does not have the expected clients: %d out of %d",
len(peers),
expected,
)
}
return nil
})
}
// TODO(kradalby): Make multiping, go routine magic
func (t *TailscaleInContainer) Ping(ip netip.Addr) error { func (t *TailscaleInContainer) Ping(ip netip.Addr) error {
return t.pool.Retry(func() error {
command := []string{ command := []string{
"tailscale", "ping", "tailscale", "ping",
"--timeout=1s", "--timeout=1s",
@ -172,14 +230,22 @@ func (t *TailscaleInContainer) Ping(ip netip.Addr) error {
[]string{}, []string{},
) )
if err != nil { if err != nil {
log.Printf(
"failed to run ping command from %s to %s, err: %s",
t.Hostname,
ip.String(),
err,
)
return err return err
} }
if !strings.Contains(result, "pong") || !strings.Contains(result, "is local") { if !strings.Contains(result, "pong") && !strings.Contains(result, "is local") {
return errTailscalePingFailed return backoff.Permanent(errTailscalePingFailed)
} }
return nil return nil
})
} }
func createTailscaleBuildOptions(version string) *dockertest.BuildOptions { func createTailscaleBuildOptions(version string) *dockertest.BuildOptions {