mirror of https://github.com/tailscale/tailscale/
feature/taildrop: move rest of Taildrop out of LocalBackend
Updates #12614 Change-Id: If451dec1d796f6a4216fe485975c87f0c62a53e5 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com> Co-authored-by: Nick Khyl <nickk@tailscale.com>pull/15891/head
parent
cf6a593196
commit
068d5ab655
@ -1,15 +0,0 @@
|
|||||||
// Copyright (c) Tailscale Inc & AUTHORS
|
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
|
|
||||||
//go:build ts_omit_taildrop
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"tailscale.com/ipn/ipnlocal"
|
|
||||||
"tailscale.com/types/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
func configureTaildrop(logf logger.Logf, lb *ipnlocal.LocalBackend) {
|
|
||||||
// Nothing.
|
|
||||||
}
|
|
||||||
@ -1,35 +1,38 @@
|
|||||||
// Copyright (c) Tailscale Inc & AUTHORS
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
//go:build !ts_omit_taildrop
|
package taildrop
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"tailscale.com/ipn/ipnlocal"
|
|
||||||
"tailscale.com/types/logger"
|
|
||||||
"tailscale.com/version/distro"
|
"tailscale.com/version/distro"
|
||||||
)
|
)
|
||||||
|
|
||||||
func configureTaildrop(logf logger.Logf, lb *ipnlocal.LocalBackend) {
|
// SetDirectFileRoot sets the directory where received files are written.
|
||||||
|
//
|
||||||
|
// This must be called before Tailscale is started.
|
||||||
|
func (e *Extension) SetDirectFileRoot(root string) {
|
||||||
|
e.directFileRoot = root
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Extension) setPlatformDefaultDirectFileRoot() {
|
||||||
dg := distro.Get()
|
dg := distro.Get()
|
||||||
|
|
||||||
switch dg {
|
switch dg {
|
||||||
case distro.Synology, distro.TrueNAS, distro.QNAP, distro.Unraid:
|
case distro.Synology, distro.TrueNAS, distro.QNAP, distro.Unraid:
|
||||||
// See if they have a "Taildrop" share.
|
// See if they have a "Taildrop" share.
|
||||||
// See https://github.com/tailscale/tailscale/issues/2179#issuecomment-982821319
|
// See https://github.com/tailscale/tailscale/issues/2179#issuecomment-982821319
|
||||||
path, err := findTaildropDir(dg)
|
path, err := findTaildropDir(dg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logf("%s Taildrop support: %v", dg, err)
|
e.logf("%s Taildrop support: %v", dg, err)
|
||||||
} else {
|
} else {
|
||||||
logf("%s Taildrop: using %v", dg, path)
|
e.logf("%s Taildrop: using %v", dg, path)
|
||||||
lb.SetDirectFileRoot(path)
|
e.directFileRoot = path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func findTaildropDir(dg distro.Distro) (string, error) {
|
func findTaildropDir(dg distro.Distro) (string, error) {
|
||||||
@ -0,0 +1,73 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
package taildrop
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"tailscale.com/ipn"
|
||||||
|
"tailscale.com/ipn/ipnext"
|
||||||
|
"tailscale.com/tailcfg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFileTargets(t *testing.T) {
|
||||||
|
e := new(Extension)
|
||||||
|
|
||||||
|
_, err := e.FileTargets()
|
||||||
|
if got, want := fmt.Sprint(err), "not connected to the tailnet"; got != want {
|
||||||
|
t.Errorf("before connect: got %q; want %q", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
e.nodeBackendForTest = testNodeBackend{peers: nil}
|
||||||
|
|
||||||
|
_, err = e.FileTargets()
|
||||||
|
if got, want := fmt.Sprint(err), "not connected to the tailnet"; got != want {
|
||||||
|
t.Errorf("non-running netmap: got %q; want %q", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
e.backendState = ipn.Running
|
||||||
|
_, err = e.FileTargets()
|
||||||
|
if got, want := fmt.Sprint(err), "file sharing not enabled by Tailscale admin"; got != want {
|
||||||
|
t.Errorf("without cap: got %q; want %q", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
e.capFileSharing = true
|
||||||
|
got, err := e.FileTargets()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(got) != 0 {
|
||||||
|
t.Fatalf("unexpected %d peers", len(got))
|
||||||
|
}
|
||||||
|
|
||||||
|
var nodeID tailcfg.NodeID = 1234
|
||||||
|
peer := &tailcfg.Node{
|
||||||
|
ID: nodeID,
|
||||||
|
Hostinfo: (&tailcfg.Hostinfo{OS: "tvOS"}).View(),
|
||||||
|
}
|
||||||
|
e.nodeBackendForTest = testNodeBackend{peers: []tailcfg.NodeView{peer.View()}}
|
||||||
|
|
||||||
|
got, err = e.FileTargets()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(got) != 0 {
|
||||||
|
t.Fatalf("unexpected %d peers", len(got))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type testNodeBackend struct {
|
||||||
|
ipnext.NodeBackend
|
||||||
|
peers []tailcfg.NodeView
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t testNodeBackend) AppendMatchingPeers(peers []tailcfg.NodeView, f func(tailcfg.NodeView) bool) []tailcfg.NodeView {
|
||||||
|
for _, p := range t.peers {
|
||||||
|
if f(p) {
|
||||||
|
peers = append(peers, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return peers
|
||||||
|
}
|
||||||
@ -1,280 +0,0 @@
|
|||||||
// Copyright (c) Tailscale Inc & AUTHORS
|
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
|
|
||||||
//go:build !ts_omit_taildrop
|
|
||||||
|
|
||||||
package ipnlocal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"cmp"
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"maps"
|
|
||||||
"slices"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"tailscale.com/client/tailscale/apitype"
|
|
||||||
"tailscale.com/ipn"
|
|
||||||
"tailscale.com/ipn/ipnstate"
|
|
||||||
"tailscale.com/tailcfg"
|
|
||||||
"tailscale.com/taildrop"
|
|
||||||
"tailscale.com/tstime"
|
|
||||||
"tailscale.com/types/empty"
|
|
||||||
"tailscale.com/util/set"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
hookSetNotifyFilesWaitingLocked = (*LocalBackend).setNotifyFilesWaitingLocked
|
|
||||||
hookSetPeerStatusTaildropTargetLocked = (*LocalBackend).setPeerStatusTaildropTargetLocked
|
|
||||||
}
|
|
||||||
|
|
||||||
type taildrop_Manager = taildrop.Manager
|
|
||||||
|
|
||||||
func (b *LocalBackend) newTaildropManager(fileRoot string) *taildrop.Manager {
|
|
||||||
// TODO(bradfitz): move all this to an ipnext so ipnlocal doesn't need to depend
|
|
||||||
// on taildrop at all.
|
|
||||||
if fileRoot == "" {
|
|
||||||
b.logf("no Taildrop directory configured")
|
|
||||||
}
|
|
||||||
return taildrop.ManagerOptions{
|
|
||||||
Logf: b.logf,
|
|
||||||
Clock: tstime.DefaultClock{Clock: b.clock},
|
|
||||||
State: b.store,
|
|
||||||
Dir: fileRoot,
|
|
||||||
DirectFileMode: b.directFileRoot != "",
|
|
||||||
SendFileNotify: b.sendFileNotify,
|
|
||||||
}.New()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *LocalBackend) sendFileNotify() {
|
|
||||||
var n ipn.Notify
|
|
||||||
|
|
||||||
b.mu.Lock()
|
|
||||||
for _, wakeWaiter := range b.fileWaiters {
|
|
||||||
wakeWaiter()
|
|
||||||
}
|
|
||||||
apiSrv := b.peerAPIServer
|
|
||||||
if apiSrv == nil {
|
|
||||||
b.mu.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
n.IncomingFiles = apiSrv.taildrop.IncomingFiles()
|
|
||||||
b.mu.Unlock()
|
|
||||||
|
|
||||||
b.send(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TaildropManager returns the taildrop manager for this backend.
|
|
||||||
//
|
|
||||||
// TODO(bradfitz): as of 2025-04-15, this is a temporary method during
|
|
||||||
// refactoring; the plan is for all taildrop code to leave the ipnlocal package
|
|
||||||
// and move to an extension. Baby steps.
|
|
||||||
func (b *LocalBackend) TaildropManager() (*taildrop.Manager, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
ps := b.peerAPIServer
|
|
||||||
b.mu.Unlock()
|
|
||||||
if ps == nil {
|
|
||||||
return nil, errors.New("no peer API server initialized")
|
|
||||||
}
|
|
||||||
if ps.taildrop == nil {
|
|
||||||
return nil, errors.New("no taildrop manager initialized")
|
|
||||||
}
|
|
||||||
return ps.taildrop, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *LocalBackend) taildropOrNil() *taildrop.Manager {
|
|
||||||
b.mu.Lock()
|
|
||||||
ps := b.peerAPIServer
|
|
||||||
b.mu.Unlock()
|
|
||||||
if ps == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return ps.taildrop
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *LocalBackend) setNotifyFilesWaitingLocked(n *ipn.Notify) {
|
|
||||||
if ps := b.peerAPIServer; ps != nil {
|
|
||||||
if ps.taildrop.HasFilesWaiting() {
|
|
||||||
n.FilesWaiting = &empty.Message{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *LocalBackend) setPeerStatusTaildropTargetLocked(ps *ipnstate.PeerStatus, p tailcfg.NodeView) {
|
|
||||||
ps.TaildropTarget = b.taildropTargetStatus(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *LocalBackend) removeFileWaiter(handle set.Handle) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
delete(b.fileWaiters, handle)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *LocalBackend) addFileWaiter(wakeWaiter context.CancelFunc) set.Handle {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
return b.fileWaiters.Add(wakeWaiter)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *LocalBackend) WaitingFiles() ([]apitype.WaitingFile, error) {
|
|
||||||
return b.taildropOrNil().WaitingFiles()
|
|
||||||
}
|
|
||||||
|
|
||||||
// AwaitWaitingFiles is like WaitingFiles but blocks while ctx is not done,
|
|
||||||
// waiting for any files to be available.
|
|
||||||
//
|
|
||||||
// On return, exactly one of the results will be non-empty or non-nil,
|
|
||||||
// respectively.
|
|
||||||
func (b *LocalBackend) AwaitWaitingFiles(ctx context.Context) ([]apitype.WaitingFile, error) {
|
|
||||||
if ff, err := b.WaitingFiles(); err != nil || len(ff) > 0 {
|
|
||||||
return ff, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
gotFile, gotFileCancel := context.WithCancel(context.Background())
|
|
||||||
defer gotFileCancel()
|
|
||||||
|
|
||||||
handle := b.addFileWaiter(gotFileCancel)
|
|
||||||
defer b.removeFileWaiter(handle)
|
|
||||||
|
|
||||||
// Now that we've registered ourselves, check again, in case
|
|
||||||
// of race. Otherwise there's a small window where we could
|
|
||||||
// miss a file arrival and wait forever.
|
|
||||||
if ff, err := b.WaitingFiles(); err != nil || len(ff) > 0 {
|
|
||||||
return ff, err
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-gotFile.Done():
|
|
||||||
if ff, err := b.WaitingFiles(); err != nil || len(ff) > 0 {
|
|
||||||
return ff, err
|
|
||||||
}
|
|
||||||
case <-ctx.Done():
|
|
||||||
return nil, ctx.Err()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *LocalBackend) DeleteFile(name string) error {
|
|
||||||
return b.taildropOrNil().DeleteFile(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *LocalBackend) OpenFile(name string) (rc io.ReadCloser, size int64, err error) {
|
|
||||||
return b.taildropOrNil().OpenFile(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasCapFileSharing reports whether the current node has the file
|
|
||||||
// sharing capability enabled.
|
|
||||||
func (b *LocalBackend) HasCapFileSharing() bool {
|
|
||||||
// TODO(bradfitz): remove this method and all Taildrop/Taildrive
|
|
||||||
// references from LocalBackend as part of tailscale/tailscale#12614.
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
return b.capFileSharing
|
|
||||||
}
|
|
||||||
|
|
||||||
// FileTargets lists nodes that the current node can send files to.
|
|
||||||
func (b *LocalBackend) FileTargets() ([]*apitype.FileTarget, error) {
|
|
||||||
var ret []*apitype.FileTarget
|
|
||||||
|
|
||||||
b.mu.Lock() // for b.{state,capFileSharing}
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
cn := b.currentNode()
|
|
||||||
nm := cn.NetMap()
|
|
||||||
self := cn.SelfUserID()
|
|
||||||
if b.state != ipn.Running || nm == nil {
|
|
||||||
return nil, errors.New("not connected to the tailnet")
|
|
||||||
}
|
|
||||||
if !b.capFileSharing {
|
|
||||||
return nil, errors.New("file sharing not enabled by Tailscale admin")
|
|
||||||
}
|
|
||||||
peers := cn.AppendMatchingPeers(nil, func(p tailcfg.NodeView) bool {
|
|
||||||
if !p.Valid() || p.Hostinfo().OS() == "tvOS" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if self == p.User() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if p.Addresses().Len() != 0 && cn.PeerHasCap(p.Addresses().At(0).Addr(), tailcfg.PeerCapabilityFileSharingTarget) {
|
|
||||||
// Explicitly noted in the netmap ACL caps as a target.
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
for _, p := range peers {
|
|
||||||
peerAPI := cn.PeerAPIBase(p)
|
|
||||||
if peerAPI == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ret = append(ret, &apitype.FileTarget{
|
|
||||||
Node: p.AsStruct(),
|
|
||||||
PeerAPIURL: peerAPI,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
slices.SortFunc(ret, func(a, b *apitype.FileTarget) int {
|
|
||||||
return cmp.Compare(a.Node.Name, b.Node.Name)
|
|
||||||
})
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *LocalBackend) taildropTargetStatus(p tailcfg.NodeView) ipnstate.TaildropTargetStatus {
|
|
||||||
if b.state != ipn.Running {
|
|
||||||
return ipnstate.TaildropTargetIpnStateNotRunning
|
|
||||||
}
|
|
||||||
cn := b.currentNode()
|
|
||||||
nm := cn.NetMap()
|
|
||||||
if nm == nil {
|
|
||||||
return ipnstate.TaildropTargetNoNetmapAvailable
|
|
||||||
}
|
|
||||||
if !b.capFileSharing {
|
|
||||||
return ipnstate.TaildropTargetMissingCap
|
|
||||||
}
|
|
||||||
|
|
||||||
if !p.Online().Get() {
|
|
||||||
return ipnstate.TaildropTargetOffline
|
|
||||||
}
|
|
||||||
|
|
||||||
if !p.Valid() {
|
|
||||||
return ipnstate.TaildropTargetNoPeerInfo
|
|
||||||
}
|
|
||||||
if nm.User() != p.User() {
|
|
||||||
// Different user must have the explicit file sharing target capability
|
|
||||||
if p.Addresses().Len() == 0 || !cn.PeerHasCap(p.Addresses().At(0).Addr(), tailcfg.PeerCapabilityFileSharingTarget) {
|
|
||||||
// Explicitly noted in the netmap ACL caps as a target.
|
|
||||||
return ipnstate.TaildropTargetOwnedByOtherUser
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.Hostinfo().OS() == "tvOS" {
|
|
||||||
return ipnstate.TaildropTargetUnsupportedOS
|
|
||||||
}
|
|
||||||
if !cn.PeerHasPeerAPI(p) {
|
|
||||||
return ipnstate.TaildropTargetNoPeerAPI
|
|
||||||
}
|
|
||||||
return ipnstate.TaildropTargetAvailable
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateOutgoingFiles updates b.outgoingFiles to reflect the given updates and
|
|
||||||
// sends an ipn.Notify with the full list of outgoingFiles.
|
|
||||||
func (b *LocalBackend) UpdateOutgoingFiles(updates map[string]*ipn.OutgoingFile) {
|
|
||||||
b.mu.Lock()
|
|
||||||
if b.outgoingFiles == nil {
|
|
||||||
b.outgoingFiles = make(map[string]*ipn.OutgoingFile, len(updates))
|
|
||||||
}
|
|
||||||
maps.Copy(b.outgoingFiles, updates)
|
|
||||||
outgoingFiles := make([]*ipn.OutgoingFile, 0, len(b.outgoingFiles))
|
|
||||||
for _, file := range b.outgoingFiles {
|
|
||||||
outgoingFiles = append(outgoingFiles, file)
|
|
||||||
}
|
|
||||||
b.mu.Unlock()
|
|
||||||
slices.SortFunc(outgoingFiles, func(a, b *ipn.OutgoingFile) int {
|
|
||||||
t := a.Started.Compare(b.Started)
|
|
||||||
if t != 0 {
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
return strings.Compare(a.Name, b.Name)
|
|
||||||
})
|
|
||||||
b.send(ipn.Notify{OutgoingFiles: outgoingFiles})
|
|
||||||
}
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
// Copyright (c) Tailscale Inc & AUTHORS
|
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
|
|
||||||
//go:build ts_omit_taildrop
|
|
||||||
|
|
||||||
package ipnlocal
|
|
||||||
|
|
||||||
type taildrop_Manager = struct{}
|
|
||||||
|
|
||||||
func (b *LocalBackend) newTaildropManager(fileRoot string) *taildrop_Manager {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -1,75 +0,0 @@
|
|||||||
// Copyright (c) Tailscale Inc & AUTHORS
|
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
|
|
||||||
//go:build !ts_omit_taildrop
|
|
||||||
|
|
||||||
package ipnlocal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"tailscale.com/ipn"
|
|
||||||
"tailscale.com/tailcfg"
|
|
||||||
"tailscale.com/tstest/deptest"
|
|
||||||
"tailscale.com/types/netmap"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFileTargets(t *testing.T) {
|
|
||||||
b := new(LocalBackend)
|
|
||||||
_, err := b.FileTargets()
|
|
||||||
if got, want := fmt.Sprint(err), "not connected to the tailnet"; got != want {
|
|
||||||
t.Errorf("before connect: got %q; want %q", got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
b.currentNode().SetNetMap(new(netmap.NetworkMap))
|
|
||||||
_, err = b.FileTargets()
|
|
||||||
if got, want := fmt.Sprint(err), "not connected to the tailnet"; got != want {
|
|
||||||
t.Errorf("non-running netmap: got %q; want %q", got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
b.state = ipn.Running
|
|
||||||
_, err = b.FileTargets()
|
|
||||||
if got, want := fmt.Sprint(err), "file sharing not enabled by Tailscale admin"; got != want {
|
|
||||||
t.Errorf("without cap: got %q; want %q", got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
b.capFileSharing = true
|
|
||||||
got, err := b.FileTargets()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(got) != 0 {
|
|
||||||
t.Fatalf("unexpected %d peers", len(got))
|
|
||||||
}
|
|
||||||
|
|
||||||
nm := &netmap.NetworkMap{
|
|
||||||
Peers: []tailcfg.NodeView{
|
|
||||||
(&tailcfg.Node{
|
|
||||||
ID: 1234,
|
|
||||||
Hostinfo: (&tailcfg.Hostinfo{OS: "tvOS"}).View(),
|
|
||||||
}).View(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
b.currentNode().SetNetMap(nm)
|
|
||||||
got, err = b.FileTargets()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(got) != 0 {
|
|
||||||
t.Fatalf("unexpected %d peers", len(got))
|
|
||||||
}
|
|
||||||
// (other cases handled by TestPeerAPIBase above)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOmitTaildropDeps(t *testing.T) {
|
|
||||||
deptest.DepChecker{
|
|
||||||
Tags: "ts_omit_taildrop",
|
|
||||||
GOOS: "linux",
|
|
||||||
GOARCH: "amd64",
|
|
||||||
BadDeps: map[string]string{
|
|
||||||
"tailscale.com/taildrop": "should be omitted",
|
|
||||||
"tailscale.com/feature/taildrop": "should be omitted",
|
|
||||||
},
|
|
||||||
}.Check(t)
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue