tstest/natlab/vmtest, cmd/tta: add TestTaildrop
Add a vmtest that brings up two Ubuntu nodes, each behind its own EasyNAT, joined to the tailnet. The sender pushes a small file via "tailscale file cp" and the receiver fetches it via "tailscale file get --wait", asserting that the filename and contents round-trip unchanged. To make Taildrop work in vmtest, three small pieces were needed: The Linux/FreeBSD cloud-init now starts tailscaled with --statedir as well as --state=mem:, so the daemon has a VarRoot to host Taildrop's incoming-files directory. State itself remains in-memory (so nothing persists across reboots); only the var-root scratch space is on disk. vmtest.New grows a variadic EnvOption parameter and a SameTailnetUser helper. When the option is passed, Start sets AllNodesSameUser=true on the embedded testcontrol.Server. Cross-node Taildrop requires the sender and receiver to share a Tailnet user (or have an explicit PeerCapabilityFileSharingTarget granted between them, which we don't plumb here), so TestTaildrop opts in. Existing tests don't. cmd/tta gains /taildrop-send and /taildrop-recv handlers that wrap "tailscale file cp" and "tailscale file get --wait", plus Env.SendTaildropFile and Env.RecvTaildropFile helpers in vmtest that drive them. Updates #13038 Change-Id: I8f5f70f88106e6e2ee07780dd46fe00f8efcfdf1 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
committed by
Brad Fitzpatrick
parent
4b8e0ede6d
commit
ec7b11d986
@@ -24,6 +24,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
@@ -263,6 +264,72 @@ func main() {
|
||||
}()
|
||||
io.WriteString(w, "OK\n")
|
||||
})
|
||||
ttaMux.HandleFunc("/taildrop-send", func(w http.ResponseWriter, r *http.Request) {
|
||||
to := r.URL.Query().Get("to") // peer's Tailscale IP
|
||||
name := r.URL.Query().Get("name")
|
||||
if to == "" || name == "" {
|
||||
http.Error(w, "missing to or name", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if strings.ContainsAny(name, "/\\") {
|
||||
http.Error(w, "bad name", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
dir, err := os.MkdirTemp("", "taildrop-send-")
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
path := filepath.Join(dir, name)
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if _, err := io.Copy(f, r.Body); err != nil {
|
||||
f.Close()
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
serveCmd(w, "tailscale", "file", "cp", path, to+":")
|
||||
})
|
||||
ttaMux.HandleFunc("/taildrop-recv", func(w http.ResponseWriter, r *http.Request) {
|
||||
dir, err := os.MkdirTemp("", "taildrop-recv-")
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 60*time.Second)
|
||||
defer cancel()
|
||||
cmd := exec.CommandContext(ctx, absify("tailscale"), "file", "get", "--wait", dir)
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
http.Error(w, fmt.Sprintf("tailscale file get: %v\n%s", err, out), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
ents, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if len(ents) != 1 {
|
||||
http.Error(w, fmt.Sprintf("got %d files, want 1", len(ents)), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
data, err := os.ReadFile(filepath.Join(dir, ents[0].Name()))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Taildrop-Filename", ents[0].Name())
|
||||
w.Header().Set("Content-Type", "application/octet-stream")
|
||||
w.Write(data)
|
||||
})
|
||||
ttaMux.HandleFunc("/http-get", func(w http.ResponseWriter, r *http.Request) {
|
||||
targetURL := r.URL.Query().Get("url")
|
||||
if targetURL == "" {
|
||||
|
||||
Reference in New Issue
Block a user