A joining machine dials the bus once the hub has answered its tunnel
Issuing the token sends the hub its new peer, and the hub applies it on its own time; a bus dialled before then timed out naming the bus. The first tunnel now waits for a handshake with the hub, and says so in the tunnel's words when there is none (novox/hq ADR 0169).
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/novox/mesh-host/internal/apply"
|
||||
"github.com/novox/mesh-host/internal/identity"
|
||||
@@ -115,5 +116,38 @@ func bringTheTunnelUp(ctx context.Context, t *identity.TokenTunnel, keyPath stri
|
||||
return fmt.Errorf("the first tunnel would not start (%s): %v %s", tunnelUnit, err, strings.TrimSpace(out))
|
||||
}
|
||||
fmt.Printf("the tunnel to the hub is up: %s, through %s\n", t.Address, t.HubEndpoint)
|
||||
return nil
|
||||
return waitForTheHub(ctx, run, handshakeWithin)
|
||||
}
|
||||
|
||||
// handshakeWithin is how long the hub has to answer the first tunnel. Issuing the token sent the hub
|
||||
// this machine as a peer; the hub applies that on its own time, and a bus dialled before it has is a
|
||||
// timeout that names the bus rather than the tunnel.
|
||||
var handshakeWithin = 90 * time.Second
|
||||
|
||||
// waitForTheHub waits until the tunnel has shaken hands with the hub, so the bus is dialled over a
|
||||
// tunnel that answers — and says so in the tunnel's own words when it does not.
|
||||
func waitForTheHub(ctx context.Context, run apply.Runner, within time.Duration) error {
|
||||
deadline := time.Now().Add(within)
|
||||
for {
|
||||
out, err := run(ctx, "wg", "show", "mesh0", "latest-handshakes")
|
||||
if err == nil {
|
||||
for _, line := range strings.Split(strings.TrimSpace(out), "\n") {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) == 2 && fields[1] != "0" {
|
||||
fmt.Println("the hub answered the tunnel")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if time.Now().After(deadline) {
|
||||
return fmt.Errorf("the hub has not answered the tunnel in %s: the token may be another machine's, "+
|
||||
"the hub may not have been sent this machine as a peer, or its tunnel's port is not reachable "+
|
||||
"from here", within)
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-time.After(2 * time.Second):
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/novox/mesh-host/internal/identity"
|
||||
)
|
||||
@@ -67,6 +68,9 @@ func TestTheFirstTunnelIsTheMeshsInterfaceWithTheHubAsItsPeer(t *testing.T) {
|
||||
t.Cleanup(func() { tunnelConfigPath = "/etc/wireguard/mesh0.conf" })
|
||||
var ran []string
|
||||
run := func(_ context.Context, name string, args ...string) (string, error) {
|
||||
if name == "wg" {
|
||||
return "HUBKEY\t1759400000\n", nil
|
||||
}
|
||||
ran = append(ran, name+" "+strings.Join(args, " "))
|
||||
return "", nil
|
||||
}
|
||||
@@ -110,3 +114,26 @@ func captureStdout(t *testing.T, fn func()) string {
|
||||
out, _ := io.ReadAll(r)
|
||||
return string(out)
|
||||
}
|
||||
|
||||
// The bus is dialled only once the hub has answered the tunnel, and a hub that never does is said
|
||||
// as the tunnel's fault rather than the bus's.
|
||||
func TestTheBusWaitsForTheHubToAnswer(t *testing.T) {
|
||||
asked := 0
|
||||
answersOnThird := func(_ context.Context, name string, args ...string) (string, error) {
|
||||
asked++
|
||||
if asked < 3 {
|
||||
return "HUBKEY\t0\n", nil
|
||||
}
|
||||
return "HUBKEY\t1759400000\n", nil
|
||||
}
|
||||
captureStdout(t, func() {
|
||||
if err := waitForTheHub(context.Background(), answersOnThird, time.Minute); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
never := func(context.Context, string, ...string) (string, error) { return "HUBKEY\t0\n", nil }
|
||||
err := waitForTheHub(context.Background(), never, 0)
|
||||
if err == nil || !strings.Contains(err.Error(), "has not answered the tunnel") {
|
||||
t.Fatalf("a hub that never answered was not said: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user