WIP snapshot

Next up: view support for maps, etc.
This commit is contained in:
Josh Bleecher Snyder 2021-09-16 16:50:31 -07:00
parent b14db5d943
commit c7b7546587
15 changed files with 680 additions and 60 deletions

216
cmd/viewer/viewer.go Normal file
View File

@ -0,0 +1,216 @@
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Viewer is a tool to automate the creation of a view type.
//
// The generated View method provides a readonly view of the struct.
//
// This tool makes lots of implicit assumptions about the types you feed it.
// In particular, it can only write relatively "shallow" View methods.
// That is, if a type contains another named struct type, viewer assumes that
// named type will also have a View method.
package main
import (
"bytes"
"flag"
"fmt"
"go/types"
"log"
"os"
"strings"
"golang.org/x/tools/go/packages"
"tailscale.com/util/codegen"
)
var (
flagTypes = flag.String("type", "", "comma-separated list of types; required")
flagOutput = flag.String("output", "", "output file; required")
flagBuildTags = flag.String("tags", "", "compiler build tags to apply")
)
func main() {
log.SetFlags(0)
log.SetPrefix("viewer: ")
flag.Parse()
if len(*flagTypes) == 0 {
flag.Usage()
os.Exit(2)
}
typeNames := strings.Split(*flagTypes, ",")
cfg := &packages.Config{
Mode: packages.NeedTypes | packages.NeedTypesInfo | packages.NeedSyntax | packages.NeedName,
Tests: false,
}
if *flagBuildTags != "" {
cfg.BuildFlags = []string{"-tags=" + *flagBuildTags}
}
pkgs, err := packages.Load(cfg, ".")
if err != nil {
log.Fatal(err)
}
if len(pkgs) != 1 {
log.Fatalf("wrong number of packages: %d", len(pkgs))
}
pkg := pkgs[0]
buf := new(bytes.Buffer)
imports := make(map[string]struct{})
namedTypes := codegen.NamedTypes(pkg)
for _, typeName := range typeNames {
typ, ok := namedTypes[typeName]
if !ok {
log.Fatalf("could not find type %s", typeName)
}
gen(buf, imports, typ, pkg.Types)
}
contents := new(bytes.Buffer)
fmt.Fprintf(contents, header, *flagTypes, pkg.Name)
fmt.Fprintf(contents, "import (\n")
for s := range imports {
fmt.Fprintf(contents, "\t%q\n", s)
}
fmt.Fprintf(contents, ")\n\n")
contents.Write(buf.Bytes())
output := *flagOutput
if output == "" {
flag.Usage()
os.Exit(2)
}
if err := codegen.WriteFormatted(contents.Bytes(), output); err != nil {
log.Fatal(err)
}
}
const header = `// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Code generated by the following command; DO NOT EDIT.
// tailscale.com/cmd/viewer -type %s
package %s
`
func gen(buf *bytes.Buffer, imports map[string]struct{}, typ *types.Named, thisPkg *types.Package) {
pkgQual := func(pkg *types.Package) string {
if thisPkg == pkg {
return ""
}
imports[pkg.Path()] = struct{}{}
return pkg.Name()
}
importedName := func(t types.Type) string {
return types.TypeString(t, pkgQual)
}
t, ok := typ.Underlying().(*types.Struct)
if !ok {
return
}
name := typ.Obj().Name()
viewName := name + "View"
fmt.Fprintf(buf, "// View makes a readonly view of %s.\n", name)
fmt.Fprintf(buf, "func (src *%s) View() %s {\n", name, viewName)
fmt.Fprintf(buf, " return %s{src}\n", viewName)
fmt.Fprintf(buf, "}\n")
fmt.Fprintf(buf, "// %s is a readonly view of %s.\n", viewName, name)
fmt.Fprintf(buf, "type %s struct{ ж *%s }\n", viewName, name)
fmt.Fprintf(buf, "func (v %s) Valid() bool { return v.ж != nil }\n", viewName)
for i := 0; i < t.NumFields(); i++ {
fname := t.Field(i).Name()
ft := t.Field(i).Type()
if !codegen.ContainsPointers(ft) {
fmt.Fprintf(buf, "func (v %s) %s() %s { return v.ж.%s }\n", viewName, fname, importedName(ft), fname)
continue
}
if named, _ := ft.(*types.Named); named != nil && !hasBasicUnderlying(ft) {
genViewCall(buf, viewName, fname, importedName(ft))
continue
}
switch ft := ft.Underlying().(type) {
case *types.Slice:
if !codegen.ContainsPointers(ft.Elem()) {
// OK to return the slice as-is, since they can't modify the contents.
fmt.Fprintf(buf, "func (v %s) %s() %s { return v.ж.%s }\n", viewName, fname, importedName(ft), fname)
continue
}
n := importedName(ft.Elem())
if ptrTyp, isPtr := ft.Elem().(*types.Pointer); isPtr {
n = importedName(ptrTyp.Elem())
}
// Generate slice view.
styp := fmt.Sprintf("_%s_%s", viewName, fname)
fmt.Fprintf(buf, "type %s []%s\n", styp, importedName(ft.Elem()))
fmt.Fprintf(buf, "func (s %s) Len() int { return len(s) }\n", styp)
fmt.Fprintf(buf, "func (s %s) At(i int) %sView { return s[i].View() }\n", styp, n)
fmt.Fprintf(buf, "func (v %s) %s() interface { Len() int; At(int) %sView } {\n", viewName, fname, n)
fmt.Fprintf(buf, " return %s(v.ж.%s)\n", styp, fname)
fmt.Fprintf(buf, "}\n")
case *types.Pointer:
if named, _ := ft.Elem().(*types.Named); named != nil && codegen.ContainsPointers(ft.Elem()) {
genViewCall(buf, viewName, fname, importedName(named))
continue
}
if codegen.ContainsPointers(ft.Elem()) {
log.Fatalf("unhandled: pointers in pointers (%v)", ft)
}
n := importedName(ft.Elem())
fmt.Fprintf(buf, "func (v %s) %s() *%s {\n", viewName, fname, n)
fmt.Fprintf(buf, " ptr := v.ж.%s\n", fname)
fmt.Fprintf(buf, " if ptr == nil {\n")
fmt.Fprintf(buf, " return nil\n")
fmt.Fprintf(buf, " }\n")
fmt.Fprintf(buf, " cp := *ptr\n")
fmt.Fprintf(buf, " return &cp\n")
fmt.Fprintf(buf, "}\n")
case *types.Map:
// TODO: Generate map view, like the slice view.
// We need:
// * Len() int
// * Load(k) v
// * LoadOK(k) (v, bool)
// * Range(func(k, v) bool)
//
// Note that we need to handle a variety of elem types:
// basic types (float64), types with a View method,
// slices of the foregoing.
//
// This may require recursion to handle completely,
// or we can follow cloner's lead and just manually
// inline one level deep the code generation
// that we happen to need right now.
// (If we figure out recursion in this context,
// we might want to backport to cloner, too.)
log.Printf("TODO: Handle %s (%s)", name, ft)
default:
fmt.Fprintf(buf, `panic("TODO: %s (%T)")`, fname, ft)
}
}
buf.Write(codegen.AssertStructUnchanged(t, thisPkg, name, "View", imports))
}
func genViewCall(buf *bytes.Buffer, viewName, fieldName, importedName string) {
fmt.Fprintf(buf, "func (v %s) %s() %sView { return v.ж.%s.View() }\n", viewName, fieldName, importedName, fieldName)
}
func hasBasicUnderlying(typ types.Type) bool {
switch typ.Underlying().(type) {
case *types.Slice, *types.Map:
return true
default:
return false
}
}

View File

@ -285,7 +285,7 @@ func (c *Auto) authRoutine() {
// don't send status updates for context errors,
// since context cancelation is always on purpose.
if ctx.Err() == nil {
c.sendStatus("authRoutine-report", err, "", nil)
c.sendStatus("authRoutine-report", err, "", netmap.NetworkMapView{})
}
}
@ -313,7 +313,7 @@ func (c *Auto) authRoutine() {
c.synced = false
c.mu.Unlock()
c.sendStatus("authRoutine-wantout", nil, "", nil)
c.sendStatus("authRoutine-wantout", nil, "", netmap.NetworkMapView{})
bo.BackOff(ctx, nil)
} else { // ie. goal.wantLoggedIn
c.mu.Lock()
@ -355,7 +355,7 @@ func (c *Auto) authRoutine() {
c.synced = false
c.mu.Unlock()
c.sendStatus("authRoutine-url", err, url, nil)
c.sendStatus("authRoutine-url", err, url, netmap.NetworkMapView{})
bo.BackOff(ctx, err)
continue
}
@ -367,7 +367,7 @@ func (c *Auto) authRoutine() {
c.state = StateAuthenticated
c.mu.Unlock()
c.sendStatus("authRoutine-success", nil, "", nil)
c.sendStatus("authRoutine-success", nil, "", netmap.NetworkMapView{})
c.cancelMapSafely()
bo.BackOff(ctx, nil)
}
@ -435,7 +435,7 @@ func (c *Auto) mapRoutine() {
// don't send status updates for context errors,
// since context cancelation is always on purpose.
if ctx.Err() == nil {
c.sendStatus("mapRoutine1", err, "", nil)
c.sendStatus("mapRoutine1", err, "", netmap.NetworkMapView{})
}
}
@ -461,7 +461,7 @@ func (c *Auto) mapRoutine() {
c.mu.Unlock()
health.SetInPollNetMap(false)
err := c.direct.PollNetMap(ctx, -1, func(nm *netmap.NetworkMap) {
err := c.direct.PollNetMap(ctx, -1, func(nm netmap.NetworkMapView) {
health.SetInPollNetMap(true)
c.mu.Lock()
@ -482,7 +482,7 @@ func (c *Auto) mapRoutine() {
if c.loggedIn {
c.state = StateSynchronized
}
exp := nm.Expiry
exp := nm.Expiry()
c.expiry = &exp
stillAuthed := c.loggedIn
state := c.state
@ -563,7 +563,7 @@ func (c *Auto) SetNetInfo(ni *tailcfg.NetInfo) {
c.sendNewMapRequest()
}
func (c *Auto) sendStatus(who string, err error, url string, nm *netmap.NetworkMap) {
func (c *Auto) sendStatus(who string, err error, url string, nm netmap.NetworkMapView) {
c.mu.Lock()
state := c.state
loggedIn := c.loggedIn
@ -583,13 +583,13 @@ func (c *Auto) sendStatus(who string, err error, url string, nm *netmap.NetworkM
if state == StateNotAuthenticated {
logoutFin = new(empty.Message)
}
if nm != nil && loggedIn && synced {
if nm.Valid() && loggedIn && synced {
pp := c.direct.GetPersist()
p = &pp
} else {
// don't send netmap status, as it's misleading when we're
// not logged in.
nm = nil
nm = netmap.NetworkMapView{}
}
new := Status{
LoginFinished: loginFin,

View File

@ -535,7 +535,7 @@ func inTest() bool { return flag.Lookup("test.v") != nil }
//
// maxPolls is how many network maps to download; common values are 1
// or -1 (to keep a long-poll query open to the server).
func (c *Direct) PollNetMap(ctx context.Context, maxPolls int, cb func(*netmap.NetworkMap)) error {
func (c *Direct) PollNetMap(ctx context.Context, maxPolls int, cb func(netmap.NetworkMapView)) error {
return c.sendMapRequest(ctx, maxPolls, cb)
}
@ -552,7 +552,7 @@ func (c *Direct) SendLiteMapUpdate(ctx context.Context) error {
const pollTimeout = 120 * time.Second
// cb nil means to omit peers.
func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, cb func(*netmap.NetworkMap)) error {
func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, cb func(netmap.NetworkMapView)) error {
c.mu.Lock()
persist := c.persist
serverURL := c.serverURL
@ -822,7 +822,7 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, cb func(*netm
c.expiry = &nm.Expiry
c.mu.Unlock()
cb(nm)
cb(nm.View())
}
if ctx.Err() != nil {
return ctx.Err()

View File

@ -139,7 +139,7 @@ func (ms *mapSession) netmapForResponse(resp *tailcfg.MapResponse) *netmap.Netwo
}
ms.addUserProfile(nm.User)
magicDNSSuffix := nm.MagicDNSSuffix()
magicDNSSuffix := nm.View().MagicDNSSuffix()
if nm.SelfNode != nil {
nm.SelfNode.InitDisplayNames(magicDNSSuffix)
}

View File

@ -68,8 +68,8 @@ type Status struct {
LoginFinished *empty.Message // nonempty when login finishes
LogoutFinished *empty.Message // nonempty when logout finishes
Err string
URL string // interactive URL to visit to finish logging in
NetMap *netmap.NetworkMap // server-pushed configuration
URL string // interactive URL to visit to finish logging in
NetMap netmap.NetworkMapView // server-pushed configuration
// The internal state should not be exposed outside this
// package, but we have some automated tests elsewhere that need to

View File

@ -114,9 +114,8 @@ type LocalBackend struct {
state ipn.State
capFileSharing bool // whether netMap contains the file sharing capability
// hostinfo is mutated in-place while mu is held.
hostinfo *tailcfg.Hostinfo
// netMap is not mutated in-place once set.
netMap *netmap.NetworkMap
hostinfo *tailcfg.Hostinfo
netMap netmap.NetworkMapView
nodeByAddr map[netaddr.IP]*tailcfg.Node
activeLogin string // last logged LoginName from netMap
engineStatus ipn.EngineStatus
@ -224,7 +223,7 @@ func (b *LocalBackend) maybePauseControlClientLocked() {
return
}
networkUp := b.prevIfState.AnyInterfaceUp()
b.cc.SetPaused((b.state == ipn.Stopped && b.netMap != nil) || !networkUp)
b.cc.SetPaused((b.state == ipn.Stopped && b.netMap.Valid()) || !networkUp)
}
// linkChange is our link monitor callback, called whenever the network changes.
@ -253,8 +252,8 @@ func (b *LocalBackend) linkChange(major bool, ifst *interfaces.State) {
// need updating to tweak default routes.
b.updateFilter(b.netMap, b.prefs)
if peerAPIListenAsync && b.netMap != nil && b.state == ipn.Running {
want := len(b.netMap.Addresses)
if peerAPIListenAsync && b.netMap.Valid() && b.state == ipn.Running {
want := len(b.netMap.Addresses())
if len(b.peerAPIListeners) < want {
b.logf("linkChange: peerAPIListeners too low; trying again")
go b.initPeerAPIListener()
@ -343,14 +342,14 @@ func (b *LocalBackend) updateStatus(sb *ipnstate.StatusBuilder, extraLocked func
s.Health = append(s.Health, err.Error())
}
}
if b.netMap != nil {
if b.netMap.Valid() {
s.MagicDNSSuffix = b.netMap.MagicDNSSuffix()
s.CertDomains = append([]string(nil), b.netMap.DNS.CertDomains...)
s.CertDomains = append([]string(nil), b.netMap.DNS().CertDomains()...)
}
})
sb.MutateSelfStatus(func(ss *ipnstate.PeerStatus) {
if b.netMap != nil && b.netMap.SelfNode != nil {
ss.ID = b.netMap.SelfNode.StableID
if b.netMap.Valid() && b.netMap.SelfNode != nil {
ss.ID = b.netMap.SelfNode().StableID()
}
for _, pln := range b.peerAPIListeners {
ss.PeerAPIURL = append(ss.PeerAPIURL, pln.urlStr)
@ -365,20 +364,21 @@ func (b *LocalBackend) updateStatus(sb *ipnstate.StatusBuilder, extraLocked func
}
func (b *LocalBackend) populatePeerStatusLocked(sb *ipnstate.StatusBuilder) {
if b.netMap == nil {
if !b.netMap.Valid() {
return
}
for id, up := range b.netMap.UserProfiles {
for _, id := range b.netMap.UserIDs() {
up := b.netMap.UserProfile(id)
sb.AddUser(id, up)
}
for _, p := range b.netMap.Peers {
for _, p := range b.netMap.Peers() {
var lastSeen time.Time
if p.LastSeen != nil {
lastSeen = *p.LastSeen
if p.LastSeen() != nil {
lastSeen = *p.LastSeen()
}
var tailAddr4 string
var tailscaleIPs = make([]netaddr.IP, 0, len(p.Addresses))
for _, addr := range p.Addresses {
var tailscaleIPs = make([]netaddr.IP, 0, len(p.Addresses()))
for _, addr := range p.Addresses() {
if addr.IsSingleIP() && tsaddr.IsTailscaleIP(addr.IP()) {
if addr.IP().Is4() && tailAddr4 == "" {
// The peer struct previously only allowed a single
@ -389,20 +389,20 @@ func (b *LocalBackend) populatePeerStatusLocked(sb *ipnstate.StatusBuilder) {
tailscaleIPs = append(tailscaleIPs, addr.IP())
}
}
sb.AddPeer(key.Public(p.Key), &ipnstate.PeerStatus{
sb.AddPeer(key.Public(p.Key()), &ipnstate.PeerStatus{
InNetworkMap: true,
ID: p.StableID,
UserID: p.User,
ID: p.StableID(),
UserID: p.User(),
TailAddrDeprecated: tailAddr4,
TailscaleIPs: tailscaleIPs,
HostName: p.Hostinfo.Hostname,
DNSName: p.Name,
OS: p.Hostinfo.OS,
KeepAlive: p.KeepAlive,
Created: p.Created,
HostName: p.Hostinfo().Hostname(),
DNSName: p.Name(),
OS: p.Hostinfo().OS(),
KeepAlive: p.KeepAlive(),
Created: p.Created(),
LastSeen: lastSeen,
ShareeNode: p.Hostinfo.ShareeNode,
ExitNode: p.StableID != "" && p.StableID == b.prefs.ExitNodeID,
ShareeNode: p.Hostinfo().ShareeNode(),
ExitNode: p.StableID() != "" && p.StableID() == b.prefs.ExitNodeID,
})
}
}
@ -478,7 +478,7 @@ func (b *LocalBackend) setClientStatus(st controlclient.Status) {
// Since we're logged out now, our netmap cache is invalid.
// Since st.NetMap==nil means "netmap is unchanged", there is
// no other way to represent this change.
b.setNetMapLocked(nil)
b.setNetMapLocked(netmap.NetworkMapView{})
}
prefs := b.prefs
@ -501,7 +501,7 @@ func (b *LocalBackend) setClientStatus(st controlclient.Status) {
b.prefs.Persist = st.Persist.Clone()
}
}
if st.NetMap != nil {
if st.NetMap.Valid() {
if b.findExitNodeIDLocked(st.NetMap) {
prefsChanged = true
}
@ -537,8 +537,8 @@ func (b *LocalBackend) setClientStatus(st controlclient.Status) {
}
b.send(ipn.Notify{Prefs: prefs})
}
if st.NetMap != nil {
if netMap != nil {
if st.NetMap.Valid() {
if netMap.Valid() {
diff := st.NetMap.ConciseDiffFrom(netMap)
if strings.TrimSpace(diff) == "" {
b.logf("[v1] netmap diff: (none)")
@ -567,7 +567,7 @@ func (b *LocalBackend) setClientStatus(st controlclient.Status) {
// findExitNodeIDLocked updates b.prefs to reference an exit node by ID,
// rather than by IP. It returns whether prefs was mutated.
func (b *LocalBackend) findExitNodeIDLocked(nm *netmap.NetworkMap) (prefsChanged bool) {
func (b *LocalBackend) findExitNodeIDLocked(nm netmap.NetworkMapView) (prefsChanged bool) {
// If we have a desired IP on file, try to find the corresponding
// node.
if b.prefs.ExitNodeIP.IsZero() {
@ -580,14 +580,16 @@ func (b *LocalBackend) findExitNodeIDLocked(nm *netmap.NetworkMap) (prefsChanged
prefsChanged = true
}
for _, peer := range nm.Peers {
for _, addr := range peer.Addresses {
peers := nm.Peers()
for i := 0; i < peers.Len(); i++ {
peer := peers.At(i)
for _, addr := range peer.Addresses() {
if !addr.IsSingleIP() || addr.IP() != b.prefs.ExitNodeIP {
continue
}
// Found the node being referenced, upgrade prefs to
// reference it directly for next time.
b.prefs.ExitNodeID = peer.StableID
b.prefs.ExitNodeID = peer.StableID()
b.prefs.ExitNodeIP = netaddr.IP{}
return true
}
@ -922,14 +924,14 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
// updateFilter updates the packet filter in wgengine based on the
// given netMap and user preferences.
func (b *LocalBackend) updateFilter(netMap *netmap.NetworkMap, prefs *ipn.Prefs) {
func (b *LocalBackend) updateFilter(netMap netmap.NetworkMapView, prefs *ipn.Prefs) {
// NOTE(danderson): keep change detection as the first thing in
// this function. Don't try to optimize by returning early, more
// likely than not you'll just end up breaking the change
// detection and end up with the wrong filter installed. This is
// quite hard to debug, so save yourself the trouble.
var (
haveNetmap = netMap != nil
haveNetmap = netMap.Valid()
addrs []netaddr.IPPrefix
packetFilter []filter.Match
localNetsB netaddr.IPSetBuilder
@ -941,7 +943,7 @@ func (b *LocalBackend) updateFilter(netMap *netmap.NetworkMap, prefs *ipn.Prefs)
logNetsB.AddPrefix(tsaddr.TailscaleULARange())
logNetsB.RemovePrefix(tsaddr.ChromeOSVMRange())
if haveNetmap {
addrs = netMap.Addresses
addrs = netMap.Addresses()
for _, p := range addrs {
localNetsB.AddPrefix(p)
}
@ -2533,10 +2535,10 @@ func hasCapability(nm *netmap.NetworkMap, cap string) bool {
return false
}
func (b *LocalBackend) setNetMapLocked(nm *netmap.NetworkMap) {
func (b *LocalBackend) setNetMapLocked(nm netmap.NetworkMapView) {
var login string
if nm != nil {
login = nm.UserProfiles[nm.User].LoginName
if nm.Valid() {
login = nm.UserProfile(nm.User()).LoginName
if login == "" {
login = "<missing-profile>"
}

View File

@ -5,6 +5,7 @@
package tailcfg
//go:generate go run tailscale.com/cmd/cloner --type=User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,DERPRegion,DERPMap,DERPNode --clonefunc=true --output=tailcfg_clone.go
//go:generate go run tailscale.com/cmd/viewer --type=Node,Hostinfo,DNSConfig,NetInfo,DERPMap --output=tailcfg_view.go
import (
"encoding/hex"

239
tailcfg/tailcfg_view.go Normal file
View File

@ -0,0 +1,239 @@
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Code generated by the following command; DO NOT EDIT.
// tailscale.com/cmd/viewer -type Node,Hostinfo,DNSConfig,NetInfo,DERPMap
package tailcfg
import (
"inet.af/netaddr"
"tailscale.com/types/dnstype"
"tailscale.com/types/key"
"tailscale.com/types/opt"
"time"
)
// View makes a readonly view of Node.
func (src *Node) View() NodeView {
return NodeView{src}
}
// NodeView is a readonly view of Node.
type NodeView struct{ ж *Node }
func (v NodeView) Valid() bool { return v.ж != nil }
func (v NodeView) ID() NodeID { return v.ж.ID }
func (v NodeView) StableID() StableNodeID { return v.ж.StableID }
func (v NodeView) Name() string { return v.ж.Name }
func (v NodeView) User() UserID { return v.ж.User }
func (v NodeView) Sharer() UserID { return v.ж.Sharer }
func (v NodeView) Key() NodeKey { return v.ж.Key }
func (v NodeView) KeyExpiry() time.Time { return v.ж.KeyExpiry }
func (v NodeView) Machine() key.MachinePublic { return v.ж.Machine }
func (v NodeView) DiscoKey() DiscoKey { return v.ж.DiscoKey }
func (v NodeView) Addresses() []netaddr.IPPrefix { return v.ж.Addresses }
func (v NodeView) AllowedIPs() []netaddr.IPPrefix { return v.ж.AllowedIPs }
func (v NodeView) Endpoints() []string { return v.ж.Endpoints }
func (v NodeView) DERP() string { return v.ж.DERP }
func (v NodeView) Hostinfo() HostinfoView { return v.ж.Hostinfo.View() }
func (v NodeView) Created() time.Time { return v.ж.Created }
func (v NodeView) PrimaryRoutes() []netaddr.IPPrefix { return v.ж.PrimaryRoutes }
func (v NodeView) LastSeen() *time.Time {
ptr := v.ж.LastSeen
if ptr == nil {
return nil
}
cp := *ptr
return &cp
}
func (v NodeView) Online() *bool {
ptr := v.ж.Online
if ptr == nil {
return nil
}
cp := *ptr
return &cp
}
func (v NodeView) KeepAlive() bool { return v.ж.KeepAlive }
func (v NodeView) MachineAuthorized() bool { return v.ж.MachineAuthorized }
func (v NodeView) Capabilities() []string { return v.ж.Capabilities }
func (v NodeView) ComputedName() string { return v.ж.ComputedName }
func (v NodeView) computedHostIfDifferent() string { return v.ж.computedHostIfDifferent }
func (v NodeView) ComputedNameWithHost() string { return v.ж.ComputedNameWithHost }
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _NodeViewNeedsRegeneration = Node(struct {
ID NodeID
StableID StableNodeID
Name string
User UserID
Sharer UserID
Key NodeKey
KeyExpiry time.Time
Machine key.MachinePublic
DiscoKey DiscoKey
Addresses []netaddr.IPPrefix
AllowedIPs []netaddr.IPPrefix
Endpoints []string
DERP string
Hostinfo Hostinfo
Created time.Time
PrimaryRoutes []netaddr.IPPrefix
LastSeen *time.Time
Online *bool
KeepAlive bool
MachineAuthorized bool
Capabilities []string
ComputedName string
computedHostIfDifferent string
ComputedNameWithHost string
}{})
// View makes a readonly view of Hostinfo.
func (src *Hostinfo) View() HostinfoView {
return HostinfoView{src}
}
// HostinfoView is a readonly view of Hostinfo.
type HostinfoView struct{ ж *Hostinfo }
func (v HostinfoView) Valid() bool { return v.ж != nil }
func (v HostinfoView) IPNVersion() string { return v.ж.IPNVersion }
func (v HostinfoView) FrontendLogID() string { return v.ж.FrontendLogID }
func (v HostinfoView) BackendLogID() string { return v.ж.BackendLogID }
func (v HostinfoView) OS() string { return v.ж.OS }
func (v HostinfoView) OSVersion() string { return v.ж.OSVersion }
func (v HostinfoView) Package() string { return v.ж.Package }
func (v HostinfoView) DeviceModel() string { return v.ж.DeviceModel }
func (v HostinfoView) Hostname() string { return v.ж.Hostname }
func (v HostinfoView) ShieldsUp() bool { return v.ж.ShieldsUp }
func (v HostinfoView) ShareeNode() bool { return v.ж.ShareeNode }
func (v HostinfoView) GoArch() string { return v.ж.GoArch }
func (v HostinfoView) RoutableIPs() []netaddr.IPPrefix { return v.ж.RoutableIPs }
func (v HostinfoView) RequestTags() []string { return v.ж.RequestTags }
func (v HostinfoView) Services() []Service { return v.ж.Services }
func (v HostinfoView) NetInfo() NetInfoView { return v.ж.NetInfo.View() }
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _HostinfoViewNeedsRegeneration = Hostinfo(struct {
IPNVersion string
FrontendLogID string
BackendLogID string
OS string
OSVersion string
Package string
DeviceModel string
Hostname string
ShieldsUp bool
ShareeNode bool
GoArch string
RoutableIPs []netaddr.IPPrefix
RequestTags []string
Services []Service
NetInfo *NetInfo
}{})
// View makes a readonly view of DNSConfig.
func (src *DNSConfig) View() DNSConfigView {
return DNSConfigView{src}
}
// DNSConfigView is a readonly view of DNSConfig.
type DNSConfigView struct{ ж *DNSConfig }
func (v DNSConfigView) Valid() bool { return v.ж != nil }
type _DNSConfigView_Resolvers []dnstype.Resolver
func (s _DNSConfigView_Resolvers) Len() int { return len(s) }
func (s _DNSConfigView_Resolvers) At(i int) dnstype.ResolverView { return s[i].View() }
func (v DNSConfigView) Resolvers() interface {
Len() int
At(int) dnstype.ResolverView
} {
return _DNSConfigView_Resolvers(v.ж.Resolvers)
}
type _DNSConfigView_FallbackResolvers []dnstype.Resolver
func (s _DNSConfigView_FallbackResolvers) Len() int { return len(s) }
func (s _DNSConfigView_FallbackResolvers) At(i int) dnstype.ResolverView { return s[i].View() }
func (v DNSConfigView) FallbackResolvers() interface {
Len() int
At(int) dnstype.ResolverView
} {
return _DNSConfigView_FallbackResolvers(v.ж.FallbackResolvers)
}
func (v DNSConfigView) Domains() []string { return v.ж.Domains }
func (v DNSConfigView) Proxied() bool { return v.ж.Proxied }
func (v DNSConfigView) Nameservers() []netaddr.IP { return v.ж.Nameservers }
func (v DNSConfigView) PerDomain() bool { return v.ж.PerDomain }
func (v DNSConfigView) CertDomains() []string { return v.ж.CertDomains }
func (v DNSConfigView) ExtraRecords() []DNSRecord { return v.ж.ExtraRecords }
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _DNSConfigViewNeedsRegeneration = DNSConfig(struct {
Resolvers []dnstype.Resolver
Routes map[string][]dnstype.Resolver
FallbackResolvers []dnstype.Resolver
Domains []string
Proxied bool
Nameservers []netaddr.IP
PerDomain bool
CertDomains []string
ExtraRecords []DNSRecord
}{})
// View makes a readonly view of NetInfo.
func (src *NetInfo) View() NetInfoView {
return NetInfoView{src}
}
// NetInfoView is a readonly view of NetInfo.
type NetInfoView struct{ ж *NetInfo }
func (v NetInfoView) Valid() bool { return v.ж != nil }
func (v NetInfoView) MappingVariesByDestIP() opt.Bool { return v.ж.MappingVariesByDestIP }
func (v NetInfoView) HairPinning() opt.Bool { return v.ж.HairPinning }
func (v NetInfoView) WorkingIPv6() opt.Bool { return v.ж.WorkingIPv6 }
func (v NetInfoView) WorkingUDP() opt.Bool { return v.ж.WorkingUDP }
func (v NetInfoView) HavePortMap() bool { return v.ж.HavePortMap }
func (v NetInfoView) UPnP() opt.Bool { return v.ж.UPnP }
func (v NetInfoView) PMP() opt.Bool { return v.ж.PMP }
func (v NetInfoView) PCP() opt.Bool { return v.ж.PCP }
func (v NetInfoView) PreferredDERP() int { return v.ж.PreferredDERP }
func (v NetInfoView) LinkType() string { return v.ж.LinkType }
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _NetInfoViewNeedsRegeneration = NetInfo(struct {
MappingVariesByDestIP opt.Bool
HairPinning opt.Bool
WorkingIPv6 opt.Bool
WorkingUDP opt.Bool
HavePortMap bool
UPnP opt.Bool
PMP opt.Bool
PCP opt.Bool
PreferredDERP int
LinkType string
DERPLatency map[string]float64
}{})
// View makes a readonly view of DERPMap.
func (src *DERPMap) View() DERPMapView {
return DERPMapView{src}
}
// DERPMapView is a readonly view of DERPMap.
type DERPMapView struct{ ж *DERPMap }
func (v DERPMapView) Valid() bool { return v.ж != nil }
func (v DERPMapView) OmitDefaultRegions() bool { return v.ж.OmitDefaultRegions }
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _DERPMapViewNeedsRegeneration = DERPMap(struct {
Regions map[int]*DERPRegion
OmitDefaultRegions bool
}{})

View File

@ -6,6 +6,7 @@
package dnstype
//go:generate go run tailscale.com/cmd/cloner --type=Resolver --clonefunc=true --output=dnstype_clone.go
//go:generate go run tailscale.com/cmd/viewer --type=Resolver --output=dnstype_view.go
import "inet.af/netaddr"

View File

@ -0,0 +1,30 @@
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Code generated by the following command; DO NOT EDIT.
// tailscale.com/cmd/viewer -type Resolver
package dnstype
import (
"inet.af/netaddr"
)
// View makes a readonly view of Resolver.
func (src *Resolver) View() ResolverView {
return ResolverView{src}
}
// ResolverView is a readonly view of Resolver.
type ResolverView struct{ ж *Resolver }
func (v ResolverView) Valid() bool { return v.ж != nil }
func (v ResolverView) Addr() string { return v.ж.Addr }
func (v ResolverView) BootstrapResolution() []netaddr.IP { return v.ж.BootstrapResolution }
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _ResolverViewNeedsRegeneration = Resolver(struct {
Addr string
BootstrapResolution []netaddr.IP
}{})

View File

@ -5,6 +5,8 @@
// Package netmap contains the netmap.NetworkMap type.
package netmap
//go:generate go run tailscale.com/cmd/viewer --type=NetworkMap --output=netmap_view.go
import (
"encoding/json"
"fmt"
@ -66,8 +68,8 @@ type NetworkMap struct {
// MagicDNS isn't necessarily in use).
//
// It will neither start nor end with a period.
func (nm *NetworkMap) MagicDNSSuffix() string {
name := strings.Trim(nm.Name, ".")
func (nm NetworkMapView) MagicDNSSuffix() string {
name := strings.Trim(nm.Name(), ".")
if i := strings.Index(name, "."); i != -1 {
name = name[i+1:]
}

View File

@ -0,0 +1,95 @@
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Code generated by the following command; DO NOT EDIT.
// tailscale.com/cmd/viewer -type NetworkMap
package netmap
import (
"inet.af/netaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
"tailscale.com/types/wgkey"
"tailscale.com/wgengine/filter"
"time"
)
// View makes a readonly view of NetworkMap.
func (src *NetworkMap) View() NetworkMapView {
return NetworkMapView{src}
}
// NetworkMapView is a readonly view of NetworkMap.
type NetworkMapView struct{ ж *NetworkMap }
func (v NetworkMapView) Valid() bool { return v.ж != nil }
func (v NetworkMapView) SelfNode() tailcfg.NodeView { return v.ж.SelfNode.View() }
func (v NetworkMapView) NodeKey() tailcfg.NodeKey { return v.ж.NodeKey }
func (v NetworkMapView) PrivateKey() wgkey.Private { return v.ж.PrivateKey }
func (v NetworkMapView) Expiry() time.Time { return v.ж.Expiry }
func (v NetworkMapView) Name() string { return v.ж.Name }
func (v NetworkMapView) Addresses() []netaddr.IPPrefix { return v.ж.Addresses }
func (v NetworkMapView) LocalPort() uint16 { return v.ж.LocalPort }
func (v NetworkMapView) MachineStatus() tailcfg.MachineStatus { return v.ж.MachineStatus }
func (v NetworkMapView) MachineKey() key.MachinePublic { return v.ж.MachineKey }
type _NetworkMapView_Peers []*tailcfg.Node
func (s _NetworkMapView_Peers) Len() int { return len(s) }
func (s _NetworkMapView_Peers) At(i int) tailcfg.NodeView { return s[i].View() }
func (v NetworkMapView) Peers() interface {
Len() int
At(int) tailcfg.NodeView
} {
return _NetworkMapView_Peers(v.ж.Peers)
}
func (v NetworkMapView) DNS() tailcfg.DNSConfigView { return v.ж.DNS.View() }
func (v NetworkMapView) Hostinfo() tailcfg.HostinfoView { return v.ж.Hostinfo.View() }
type _NetworkMapView_PacketFilter []filter.Match
func (s _NetworkMapView_PacketFilter) Len() int { return len(s) }
func (s _NetworkMapView_PacketFilter) At(i int) filter.MatchView { return s[i].View() }
func (v NetworkMapView) PacketFilter() interface {
Len() int
At(int) filter.MatchView
} {
return _NetworkMapView_PacketFilter(v.ж.PacketFilter)
}
func (v NetworkMapView) CollectServices() bool { return v.ж.CollectServices }
func (v NetworkMapView) DERPMap() tailcfg.DERPMapView { return v.ж.DERPMap.View() }
func (v NetworkMapView) Debug() *tailcfg.Debug {
ptr := v.ж.Debug
if ptr == nil {
return nil
}
cp := *ptr
return &cp
}
func (v NetworkMapView) User() tailcfg.UserID { return v.ж.User }
func (v NetworkMapView) Domain() string { return v.ж.Domain }
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _NetworkMapViewNeedsRegeneration = NetworkMap(struct {
SelfNode *tailcfg.Node
NodeKey tailcfg.NodeKey
PrivateKey wgkey.Private
Expiry time.Time
Name string
Addresses []netaddr.IPPrefix
LocalPort uint16
MachineStatus tailcfg.MachineStatus
MachineKey key.MachinePublic
Peers []*tailcfg.Node
DNS tailcfg.DNSConfig
Hostinfo tailcfg.Hostinfo
PacketFilter []filter.Match
CollectServices bool
DERPMap *tailcfg.DERPMap
Debug *tailcfg.Debug
User tailcfg.UserID
Domain string
UserProfiles map[tailcfg.UserID]tailcfg.UserProfile
}{})

View File

@ -14,6 +14,7 @@
)
//go:generate go run tailscale.com/cmd/cloner --type=Match --output=match_clone.go
//go:generate go run tailscale.com/cmd/viewer --type=Match --output=match_view.go
// PortRange is a range of TCP and UDP ports.
type PortRange struct {

View File

@ -0,0 +1,33 @@
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Code generated by the following command; DO NOT EDIT.
// tailscale.com/cmd/viewer -type Match
package filter
import (
"inet.af/netaddr"
"tailscale.com/types/ipproto"
)
// View makes a readonly view of Match.
func (src *Match) View() MatchView {
return MatchView{src}
}
// MatchView is a readonly view of Match.
type MatchView struct{ ж *Match }
func (v MatchView) Valid() bool { return v.ж != nil }
func (v MatchView) IPProto() []ipproto.Proto { return v.ж.IPProto }
func (v MatchView) Dsts() []NetPortRange { return v.ж.Dsts }
func (v MatchView) Srcs() []netaddr.IPPrefix { return v.ж.Srcs }
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _MatchViewNeedsRegeneration = Match(struct {
IPProto []ipproto.Proto
Dsts []NetPortRange
Srcs []netaddr.IPPrefix
}{})

View File

@ -176,7 +176,7 @@ func (ns *Impl) Start() error {
func DNSMapFromNetworkMap(nm *netmap.NetworkMap) DNSMap {
ret := make(DNSMap)
suffix := nm.MagicDNSSuffix()
suffix := nm.View().MagicDNSSuffix()
have4 := false
if nm.Name != "" && len(nm.Addresses) > 0 {
ip := nm.Addresses[0].IP()